1 | /* |
---|
2 | * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> |
---|
3 | * |
---|
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. |
---|
8 | * |
---|
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. |
---|
13 | * |
---|
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 |
---|
17 | */ |
---|
18 | |
---|
19 | #include "Common.h" |
---|
20 | #include "WorldPacket.h" |
---|
21 | #include "WorldSession.h" |
---|
22 | #include "World.h" |
---|
23 | #include "Opcodes.h" |
---|
24 | #include "Log.h" |
---|
25 | #include "ObjectMgr.h" |
---|
26 | #include "Player.h" |
---|
27 | #include "Item.h" |
---|
28 | #include "UpdateData.h" |
---|
29 | #include "ObjectAccessor.h" |
---|
30 | |
---|
31 | void WorldSession::HandleSplitItemOpcode( WorldPacket & recv_data ) |
---|
32 | { |
---|
33 | CHECK_PACKET_SIZE(recv_data,1+1+1+1+1); |
---|
34 | |
---|
35 | //sLog.outDebug("WORLD: CMSG_SPLIT_ITEM"); |
---|
36 | uint8 srcbag, srcslot, dstbag, dstslot, count; |
---|
37 | |
---|
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); |
---|
40 | |
---|
41 | uint16 src = ( (srcbag << 8) | srcslot ); |
---|
42 | uint16 dst = ( (dstbag << 8) | dstslot ); |
---|
43 | |
---|
44 | if(src==dst) |
---|
45 | return; |
---|
46 | |
---|
47 | if (count==0) |
---|
48 | return; //check count - if zero it's fake packet |
---|
49 | |
---|
50 | _player->SplitItem( src, dst, count ); |
---|
51 | } |
---|
52 | |
---|
53 | void WorldSession::HandleSwapInvItemOpcode( WorldPacket & recv_data ) |
---|
54 | { |
---|
55 | CHECK_PACKET_SIZE(recv_data,1+1); |
---|
56 | |
---|
57 | //sLog.outDebug("WORLD: CMSG_SWAP_INV_ITEM"); |
---|
58 | uint8 srcslot, dstslot; |
---|
59 | |
---|
60 | recv_data >> srcslot >> dstslot; |
---|
61 | //sLog.outDebug("STORAGE: receive srcslot = %u, dstslot = %u", srcslot, dstslot); |
---|
62 | |
---|
63 | // prevent attempt swap same item to current position generated by client at special checting sequence |
---|
64 | if(srcslot==dstslot) |
---|
65 | return; |
---|
66 | |
---|
67 | uint16 src = ( (INVENTORY_SLOT_BAG_0 << 8) | srcslot ); |
---|
68 | uint16 dst = ( (INVENTORY_SLOT_BAG_0 << 8) | dstslot ); |
---|
69 | |
---|
70 | _player->SwapItem( src, dst ); |
---|
71 | } |
---|
72 | |
---|
73 | void WorldSession::HandleAutoEquipItemSlotOpcode( WorldPacket & recv_data ) |
---|
74 | { |
---|
75 | CHECK_PACKET_SIZE(recv_data,8+1); |
---|
76 | uint64 itemguid; |
---|
77 | uint8 dstslot; |
---|
78 | recv_data >> itemguid >> dstslot; |
---|
79 | |
---|
80 | // cheating attempt, client should never send opcode in that case |
---|
81 | if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot)) |
---|
82 | return; |
---|
83 | |
---|
84 | Item* item = _player->GetItemByGuid(itemguid); |
---|
85 | uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8); |
---|
86 | |
---|
87 | if(!item || item->GetPos() == dstpos) |
---|
88 | return; |
---|
89 | |
---|
90 | _player->SwapItem(item->GetPos(), dstpos); |
---|
91 | } |
---|
92 | |
---|
93 | void WorldSession::HandleSwapItem( WorldPacket & recv_data ) |
---|
94 | { |
---|
95 | CHECK_PACKET_SIZE(recv_data,1+1+1+1); |
---|
96 | |
---|
97 | //sLog.outDebug("WORLD: CMSG_SWAP_ITEM"); |
---|
98 | uint8 dstbag, dstslot, srcbag, srcslot; |
---|
99 | |
---|
100 | recv_data >> dstbag >> dstslot >> srcbag >> srcslot ; |
---|
101 | //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u", srcbag, srcslot, dstbag, dstslot); |
---|
102 | |
---|
103 | uint16 src = ( (srcbag << 8) | srcslot ); |
---|
104 | uint16 dst = ( (dstbag << 8) | dstslot ); |
---|
105 | |
---|
106 | // prevent attempt swap same item to current position generated by client at special checting sequence |
---|
107 | if(src==dst) |
---|
108 | return; |
---|
109 | |
---|
110 | _player->SwapItem( src, dst ); |
---|
111 | } |
---|
112 | |
---|
113 | void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data ) |
---|
114 | { |
---|
115 | CHECK_PACKET_SIZE(recv_data,1+1); |
---|
116 | |
---|
117 | //sLog.outDebug("WORLD: CMSG_AUTOEQUIP_ITEM"); |
---|
118 | uint8 srcbag, srcslot; |
---|
119 | |
---|
120 | recv_data >> srcbag >> srcslot; |
---|
121 | //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot); |
---|
122 | |
---|
123 | Item *pSrcItem = _player->GetItemByPos( srcbag, srcslot ); |
---|
124 | if( !pSrcItem ) |
---|
125 | return; // only at cheat |
---|
126 | |
---|
127 | if(pSrcItem->m_lootGenerated) // prevent swap looting item |
---|
128 | { |
---|
129 | //best error message found for attempting to swap while looting |
---|
130 | _player->SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL ); |
---|
131 | return; |
---|
132 | } |
---|
133 | |
---|
134 | uint16 dest; |
---|
135 | uint8 msg = _player->CanEquipItem( NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag() ); |
---|
136 | if( msg != EQUIP_ERR_OK ) |
---|
137 | { |
---|
138 | _player->SendEquipError( msg, pSrcItem, NULL ); |
---|
139 | return; |
---|
140 | } |
---|
141 | |
---|
142 | uint16 src = pSrcItem->GetPos(); |
---|
143 | if(dest==src) // prevent equip in same slot, only at cheat |
---|
144 | return; |
---|
145 | |
---|
146 | Item *pDstItem = _player->GetItemByPos( dest ); |
---|
147 | if( !pDstItem ) // empty slot, simple case |
---|
148 | { |
---|
149 | _player->RemoveItem( srcbag, srcslot, true ); |
---|
150 | _player->EquipItem( dest, pSrcItem, true ); |
---|
151 | _player->AutoUnequipOffhandIfNeed(); |
---|
152 | } |
---|
153 | else // have currently equipped item, not simple case |
---|
154 | { |
---|
155 | uint8 dstbag = pDstItem->GetBagSlot(); |
---|
156 | uint8 dstslot = pDstItem->GetSlot(); |
---|
157 | |
---|
158 | msg = _player->CanUnequipItem( dest, !pSrcItem->IsBag() ); |
---|
159 | if( msg != EQUIP_ERR_OK ) |
---|
160 | { |
---|
161 | _player->SendEquipError( msg, pDstItem, NULL ); |
---|
162 | return; |
---|
163 | } |
---|
164 | |
---|
165 | // check dest->src move possibility |
---|
166 | ItemPosCountVec sSrc; |
---|
167 | uint16 eSrc; |
---|
168 | if( _player->IsInventoryPos( src ) ) |
---|
169 | { |
---|
170 | msg = _player->CanStoreItem( srcbag, srcslot, sSrc, pDstItem, true ); |
---|
171 | if( msg != EQUIP_ERR_OK ) |
---|
172 | msg = _player->CanStoreItem( srcbag, NULL_SLOT, sSrc, pDstItem, true ); |
---|
173 | if( msg != EQUIP_ERR_OK ) |
---|
174 | msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true ); |
---|
175 | } |
---|
176 | else if( _player->IsBankPos( src ) ) |
---|
177 | { |
---|
178 | msg = _player->CanBankItem( srcbag, srcslot, sSrc, pDstItem, true ); |
---|
179 | if( msg != EQUIP_ERR_OK ) |
---|
180 | msg = _player->CanBankItem( srcbag, NULL_SLOT, sSrc, pDstItem, true ); |
---|
181 | if( msg != EQUIP_ERR_OK ) |
---|
182 | msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true ); |
---|
183 | } |
---|
184 | else if( _player->IsEquipmentPos( src ) ) |
---|
185 | { |
---|
186 | msg = _player->CanEquipItem( srcslot, eSrc, pDstItem, true); |
---|
187 | if( msg == EQUIP_ERR_OK ) |
---|
188 | msg = _player->CanUnequipItem( eSrc, true); |
---|
189 | } |
---|
190 | |
---|
191 | if( msg != EQUIP_ERR_OK ) |
---|
192 | { |
---|
193 | _player->SendEquipError( msg, pDstItem, pSrcItem ); |
---|
194 | return; |
---|
195 | } |
---|
196 | |
---|
197 | // now do moves, remove... |
---|
198 | _player->RemoveItem(dstbag, dstslot, false); |
---|
199 | _player->RemoveItem(srcbag, srcslot, false); |
---|
200 | |
---|
201 | // add to dest |
---|
202 | _player->EquipItem(dest, pSrcItem, true); |
---|
203 | |
---|
204 | // add to src |
---|
205 | if( _player->IsInventoryPos( src ) ) |
---|
206 | _player->StoreItem(sSrc, pDstItem, true); |
---|
207 | else if( _player->IsBankPos( src ) ) |
---|
208 | _player->BankItem(sSrc, pDstItem, true); |
---|
209 | else if( _player->IsEquipmentPos( src ) ) |
---|
210 | _player->EquipItem(eSrc, pDstItem, true); |
---|
211 | |
---|
212 | _player->AutoUnequipOffhandIfNeed(); |
---|
213 | } |
---|
214 | } |
---|
215 | |
---|
216 | void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data ) |
---|
217 | { |
---|
218 | CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1); |
---|
219 | |
---|
220 | //sLog.outDebug("WORLD: CMSG_DESTROYITEM"); |
---|
221 | uint8 bag, slot, count, data1, data2, data3; |
---|
222 | |
---|
223 | recv_data >> bag >> slot >> count >> data1 >> data2 >> data3; |
---|
224 | //sLog.outDebug("STORAGE: receive bag = %u, slot = %u, count = %u", bag, slot, count); |
---|
225 | |
---|
226 | uint16 pos = (bag << 8) | slot; |
---|
227 | |
---|
228 | // prevent drop unequipable items (in combat, for example) and non-empty bags |
---|
229 | if(_player->IsEquipmentPos(pos) || _player->IsBagPos(pos)) |
---|
230 | { |
---|
231 | uint8 msg = _player->CanUnequipItem( pos, false ); |
---|
232 | if( msg != EQUIP_ERR_OK ) |
---|
233 | { |
---|
234 | _player->SendEquipError( msg, _player->GetItemByPos(pos), NULL ); |
---|
235 | return; |
---|
236 | } |
---|
237 | } |
---|
238 | |
---|
239 | Item *pItem = _player->GetItemByPos( bag, slot ); |
---|
240 | if(!pItem) |
---|
241 | { |
---|
242 | _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); |
---|
243 | return; |
---|
244 | } |
---|
245 | |
---|
246 | if(count) |
---|
247 | { |
---|
248 | uint32 i_count = count; |
---|
249 | _player->DestroyItemCount( pItem, i_count, true ); |
---|
250 | } |
---|
251 | else |
---|
252 | _player->DestroyItem( bag, slot, true ); |
---|
253 | } |
---|
254 | |
---|
255 | // Only _static_ data send in this packet !!! |
---|
256 | void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data ) |
---|
257 | { |
---|
258 | CHECK_PACKET_SIZE(recv_data, 4); |
---|
259 | |
---|
260 | //sLog.outDebug("WORLD: CMSG_ITEM_QUERY_SINGLE"); |
---|
261 | uint32 item; |
---|
262 | recv_data >> item; |
---|
263 | |
---|
264 | sLog.outDetail("STORAGE: Item Query = %u", item); |
---|
265 | |
---|
266 | ItemPrototype const *pProto = objmgr.GetItemPrototype( item ); |
---|
267 | if( pProto ) |
---|
268 | { |
---|
269 | std::string Name = pProto->Name1; |
---|
270 | std::string Description = pProto->Description; |
---|
271 | |
---|
272 | int loc_idx = GetSessionDbLocaleIndex(); |
---|
273 | if ( loc_idx >= 0 ) |
---|
274 | { |
---|
275 | ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId); |
---|
276 | if (il) |
---|
277 | { |
---|
278 | if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty()) |
---|
279 | Name = il->Name[loc_idx]; |
---|
280 | if (il->Description.size() > loc_idx && !il->Description[loc_idx].empty()) |
---|
281 | Description = il->Description[loc_idx]; |
---|
282 | } |
---|
283 | } |
---|
284 | // guess size |
---|
285 | WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600); |
---|
286 | data << pProto->ItemId; |
---|
287 | data << pProto->Class; |
---|
288 | data << pProto->SubClass; |
---|
289 | data << uint32(-1); // new 2.0.3, not exist in wdb cache? |
---|
290 | data << Name; |
---|
291 | data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name... |
---|
292 | data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00); |
---|
293 | data << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00); |
---|
294 | data << pProto->DisplayInfoID; |
---|
295 | data << pProto->Quality; |
---|
296 | data << pProto->Flags; |
---|
297 | data << pProto->BuyPrice; |
---|
298 | data << pProto->SellPrice; |
---|
299 | data << pProto->InventoryType; |
---|
300 | data << pProto->AllowableClass; |
---|
301 | data << pProto->AllowableRace; |
---|
302 | data << pProto->ItemLevel; |
---|
303 | data << pProto->RequiredLevel; |
---|
304 | data << pProto->RequiredSkill; |
---|
305 | data << pProto->RequiredSkillRank; |
---|
306 | data << pProto->RequiredSpell; |
---|
307 | data << pProto->RequiredHonorRank; |
---|
308 | data << pProto->RequiredCityRank; |
---|
309 | data << pProto->RequiredReputationFaction; |
---|
310 | data << pProto->RequiredReputationRank; |
---|
311 | data << pProto->MaxCount; |
---|
312 | data << pProto->Stackable; |
---|
313 | data << pProto->ContainerSlots; |
---|
314 | for(int i = 0; i < 10; i++) |
---|
315 | { |
---|
316 | data << pProto->ItemStat[i].ItemStatType; |
---|
317 | data << pProto->ItemStat[i].ItemStatValue; |
---|
318 | } |
---|
319 | for(int i = 0; i < 5; i++) |
---|
320 | { |
---|
321 | data << pProto->Damage[i].DamageMin; |
---|
322 | data << pProto->Damage[i].DamageMax; |
---|
323 | data << pProto->Damage[i].DamageType; |
---|
324 | } |
---|
325 | data << pProto->Armor; |
---|
326 | data << pProto->HolyRes; |
---|
327 | data << pProto->FireRes; |
---|
328 | data << pProto->NatureRes; |
---|
329 | data << pProto->FrostRes; |
---|
330 | data << pProto->ShadowRes; |
---|
331 | data << pProto->ArcaneRes; |
---|
332 | data << pProto->Delay; |
---|
333 | data << pProto->Ammo_type; |
---|
334 | |
---|
335 | data << (float)pProto->RangedModRange; |
---|
336 | for(int s = 0; s < 5; s++) |
---|
337 | { |
---|
338 | // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown |
---|
339 | // use `item_template` or if not set then only use spell cooldowns |
---|
340 | SpellEntry const* spell = sSpellStore.LookupEntry(pProto->Spells[s].SpellId); |
---|
341 | if(spell) |
---|
342 | { |
---|
343 | bool db_data = pProto->Spells[s].SpellCooldown >= 0 || pProto->Spells[s].SpellCategoryCooldown >= 0; |
---|
344 | |
---|
345 | data << pProto->Spells[s].SpellId; |
---|
346 | data << pProto->Spells[s].SpellTrigger; |
---|
347 | data << uint32(-abs(pProto->Spells[s].SpellCharges)); |
---|
348 | |
---|
349 | if(db_data) |
---|
350 | { |
---|
351 | data << uint32(pProto->Spells[s].SpellCooldown); |
---|
352 | data << uint32(pProto->Spells[s].SpellCategory); |
---|
353 | data << uint32(pProto->Spells[s].SpellCategoryCooldown); |
---|
354 | } |
---|
355 | else |
---|
356 | { |
---|
357 | data << uint32(spell->RecoveryTime); |
---|
358 | data << uint32(spell->Category); |
---|
359 | data << uint32(spell->CategoryRecoveryTime); |
---|
360 | } |
---|
361 | } |
---|
362 | else |
---|
363 | { |
---|
364 | data << uint32(0); |
---|
365 | data << uint32(0); |
---|
366 | data << uint32(0); |
---|
367 | data << uint32(-1); |
---|
368 | data << uint32(0); |
---|
369 | data << uint32(-1); |
---|
370 | } |
---|
371 | } |
---|
372 | data << pProto->Bonding; |
---|
373 | data << Description; |
---|
374 | data << pProto->PageText; |
---|
375 | data << pProto->LanguageID; |
---|
376 | data << pProto->PageMaterial; |
---|
377 | data << pProto->StartQuest; |
---|
378 | data << pProto->LockID; |
---|
379 | data << pProto->Material; |
---|
380 | data << pProto->Sheath; |
---|
381 | data << pProto->RandomProperty; |
---|
382 | data << pProto->RandomSuffix; |
---|
383 | data << pProto->Block; |
---|
384 | data << pProto->ItemSet; |
---|
385 | data << pProto->MaxDurability; |
---|
386 | data << pProto->Area; |
---|
387 | data << pProto->Map; // Added in 1.12.x & 2.0.1 client branch |
---|
388 | data << pProto->BagFamily; |
---|
389 | data << pProto->TotemCategory; |
---|
390 | for(int s = 0; s < 3; s++) |
---|
391 | { |
---|
392 | data << pProto->Socket[s].Color; |
---|
393 | data << pProto->Socket[s].Content; |
---|
394 | } |
---|
395 | data << pProto->socketBonus; |
---|
396 | data << pProto->GemProperties; |
---|
397 | data << pProto->RequiredDisenchantSkill; |
---|
398 | data << pProto->ArmorDamageModifier; |
---|
399 | data << uint32(0); // added in 2.4.2.8209, duration (seconds) |
---|
400 | SendPacket( &data ); |
---|
401 | } |
---|
402 | else |
---|
403 | { |
---|
404 | sLog.outDebug( "WORLD: CMSG_ITEM_QUERY_SINGLE - NO item INFO! (ENTRY: %u)", item ); |
---|
405 | WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 4); |
---|
406 | data << uint32(item | 0x80000000); |
---|
407 | SendPacket( &data ); |
---|
408 | } |
---|
409 | } |
---|
410 | |
---|
411 | void WorldSession::HandleReadItem( WorldPacket & recv_data ) |
---|
412 | { |
---|
413 | CHECK_PACKET_SIZE(recv_data,1+1); |
---|
414 | |
---|
415 | //sLog.outDebug( "WORLD: CMSG_READ_ITEM"); |
---|
416 | |
---|
417 | uint8 bag, slot; |
---|
418 | recv_data >> bag >> slot; |
---|
419 | |
---|
420 | //sLog.outDetail("STORAGE: Read bag = %u, slot = %u", bag, slot); |
---|
421 | Item *pItem = _player->GetItemByPos( bag, slot ); |
---|
422 | |
---|
423 | if( pItem && pItem->GetProto()->PageText ) |
---|
424 | { |
---|
425 | WorldPacket data; |
---|
426 | |
---|
427 | uint8 msg = _player->CanUseItem( pItem ); |
---|
428 | if( msg == EQUIP_ERR_OK ) |
---|
429 | { |
---|
430 | data.Initialize (SMSG_READ_ITEM_OK, 8); |
---|
431 | sLog.outDetail("STORAGE: Item page sent"); |
---|
432 | } |
---|
433 | else |
---|
434 | { |
---|
435 | data.Initialize( SMSG_READ_ITEM_FAILED, 8 ); |
---|
436 | sLog.outDetail("STORAGE: Unable to read item"); |
---|
437 | _player->SendEquipError( msg, pItem, NULL ); |
---|
438 | } |
---|
439 | data << pItem->GetGUID(); |
---|
440 | SendPacket(&data); |
---|
441 | } |
---|
442 | else |
---|
443 | _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); |
---|
444 | } |
---|
445 | |
---|
446 | void WorldSession::HandlePageQuerySkippedOpcode( WorldPacket & recv_data ) |
---|
447 | { |
---|
448 | CHECK_PACKET_SIZE(recv_data,4+8); |
---|
449 | |
---|
450 | sLog.outDebug( "WORLD: Received CMSG_PAGE_TEXT_QUERY" ); |
---|
451 | |
---|
452 | uint32 itemid; |
---|
453 | uint64 guid; |
---|
454 | |
---|
455 | recv_data >> itemid >> guid; |
---|
456 | |
---|
457 | sLog.outDetail( "Packet Info: itemid: %u guidlow: %u guidentry: %u guidhigh: %u", |
---|
458 | itemid, GUID_LOPART(guid), GUID_ENPART(guid), GUID_HIPART(guid)); |
---|
459 | } |
---|
460 | |
---|
461 | void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data ) |
---|
462 | { |
---|
463 | CHECK_PACKET_SIZE(recv_data,8+8+1); |
---|
464 | |
---|
465 | sLog.outDebug( "WORLD: Received CMSG_SELL_ITEM" ); |
---|
466 | uint64 vendorguid, itemguid; |
---|
467 | uint8 _count; |
---|
468 | |
---|
469 | recv_data >> vendorguid >> itemguid >> _count; |
---|
470 | |
---|
471 | // prevent possible overflow, as mangos uses uint32 for item count |
---|
472 | uint32 count = _count; |
---|
473 | |
---|
474 | if(!itemguid) |
---|
475 | return; |
---|
476 | |
---|
477 | Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR); |
---|
478 | if (!pCreature) |
---|
479 | { |
---|
480 | sLog.outDebug( "WORLD: HandleSellItemOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) ); |
---|
481 | _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, itemguid, 0); |
---|
482 | return; |
---|
483 | } |
---|
484 | |
---|
485 | // remove fake death |
---|
486 | if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) |
---|
487 | GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); |
---|
488 | |
---|
489 | Item *pItem = _player->GetItemByGuid( itemguid ); |
---|
490 | if( pItem ) |
---|
491 | { |
---|
492 | // prevent sell not owner item |
---|
493 | if(_player->GetGUID()!=pItem->GetOwnerGUID()) |
---|
494 | { |
---|
495 | _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); |
---|
496 | return; |
---|
497 | } |
---|
498 | |
---|
499 | // prevent sell non empty bag by drag-and-drop at vendor's item list |
---|
500 | if(pItem->IsBag() && !((Bag*)pItem)->IsEmpty()) |
---|
501 | { |
---|
502 | _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); |
---|
503 | return; |
---|
504 | } |
---|
505 | |
---|
506 | // prevent sell currently looted item |
---|
507 | if(_player->GetLootGUID()==pItem->GetGUID()) |
---|
508 | { |
---|
509 | _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); |
---|
510 | return; |
---|
511 | } |
---|
512 | |
---|
513 | // special case at auto sell (sell all) |
---|
514 | if(count==0) |
---|
515 | { |
---|
516 | count = pItem->GetCount(); |
---|
517 | } |
---|
518 | else |
---|
519 | { |
---|
520 | // prevent sell more items that exist in stack (possable only not from client) |
---|
521 | if(count > pItem->GetCount()) |
---|
522 | { |
---|
523 | _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); |
---|
524 | return; |
---|
525 | } |
---|
526 | } |
---|
527 | |
---|
528 | ItemPrototype const *pProto = pItem->GetProto(); |
---|
529 | if( pProto ) |
---|
530 | { |
---|
531 | if( pProto->SellPrice > 0 ) |
---|
532 | { |
---|
533 | if(count < pItem->GetCount()) // need split items |
---|
534 | { |
---|
535 | Item *pNewItem = pItem->CloneItem( count, _player ); |
---|
536 | if (!pNewItem) |
---|
537 | { |
---|
538 | sLog.outError("WORLD: HandleSellItemOpcode - could not create clone of item %u; count = %u", pItem->GetEntry(), count ); |
---|
539 | _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); |
---|
540 | return; |
---|
541 | } |
---|
542 | |
---|
543 | pItem->SetCount( pItem->GetCount() - count ); |
---|
544 | _player->ItemRemovedQuestCheck( pItem->GetEntry(), count ); |
---|
545 | if( _player->IsInWorld() ) |
---|
546 | pItem->SendUpdateToPlayer( _player ); |
---|
547 | pItem->SetState(ITEM_CHANGED, _player); |
---|
548 | |
---|
549 | _player->AddItemToBuyBackSlot( pNewItem ); |
---|
550 | if( _player->IsInWorld() ) |
---|
551 | pNewItem->SendUpdateToPlayer( _player ); |
---|
552 | } |
---|
553 | else |
---|
554 | { |
---|
555 | _player->ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount()); |
---|
556 | _player->RemoveItem( pItem->GetBagSlot(), pItem->GetSlot(), true); |
---|
557 | pItem->RemoveFromUpdateQueueOf(_player); |
---|
558 | _player->AddItemToBuyBackSlot( pItem ); |
---|
559 | } |
---|
560 | |
---|
561 | _player->ModifyMoney( pProto->SellPrice * count ); |
---|
562 | } |
---|
563 | else |
---|
564 | _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); |
---|
565 | return; |
---|
566 | } |
---|
567 | } |
---|
568 | _player->SendSellError( SELL_ERR_CANT_FIND_ITEM, pCreature, itemguid, 0); |
---|
569 | return; |
---|
570 | } |
---|
571 | |
---|
572 | void WorldSession::HandleBuybackItem(WorldPacket & recv_data) |
---|
573 | { |
---|
574 | CHECK_PACKET_SIZE(recv_data,8+4); |
---|
575 | |
---|
576 | sLog.outDebug( "WORLD: Received CMSG_BUYBACK_ITEM" ); |
---|
577 | uint64 vendorguid; |
---|
578 | uint32 slot; |
---|
579 | |
---|
580 | recv_data >> vendorguid >> slot; |
---|
581 | |
---|
582 | Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR); |
---|
583 | if (!pCreature) |
---|
584 | { |
---|
585 | sLog.outDebug( "WORLD: HandleBuybackItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) ); |
---|
586 | _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0); |
---|
587 | return; |
---|
588 | } |
---|
589 | |
---|
590 | // remove fake death |
---|
591 | if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) |
---|
592 | GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); |
---|
593 | |
---|
594 | Item *pItem = _player->GetItemFromBuyBackSlot( slot ); |
---|
595 | if( pItem ) |
---|
596 | { |
---|
597 | uint32 price = _player->GetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START ); |
---|
598 | if( _player->GetMoney() < price ) |
---|
599 | { |
---|
600 | _player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, pItem->GetEntry(), 0); |
---|
601 | return; |
---|
602 | } |
---|
603 | |
---|
604 | ItemPosCountVec dest; |
---|
605 | uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); |
---|
606 | if( msg == EQUIP_ERR_OK ) |
---|
607 | { |
---|
608 | _player->ModifyMoney( -(int32)price ); |
---|
609 | _player->RemoveItemFromBuyBackSlot( slot, false ); |
---|
610 | _player->ItemAddedQuestCheck( pItem->GetEntry(), pItem->GetCount()); |
---|
611 | _player->StoreItem( dest, pItem, true ); |
---|
612 | } |
---|
613 | else |
---|
614 | _player->SendEquipError( msg, pItem, NULL ); |
---|
615 | return; |
---|
616 | } |
---|
617 | else |
---|
618 | _player->SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, 0, 0); |
---|
619 | } |
---|
620 | |
---|
621 | void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data ) |
---|
622 | { |
---|
623 | CHECK_PACKET_SIZE(recv_data,8+4+8+1+1); |
---|
624 | |
---|
625 | sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM_IN_SLOT" ); |
---|
626 | uint64 vendorguid, bagguid; |
---|
627 | uint32 item; |
---|
628 | uint8 slot, count; |
---|
629 | |
---|
630 | recv_data >> vendorguid >> item >> bagguid >> slot >> count; |
---|
631 | |
---|
632 | GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bagguid,slot); |
---|
633 | } |
---|
634 | |
---|
635 | void WorldSession::HandleBuyItemOpcode( WorldPacket & recv_data ) |
---|
636 | { |
---|
637 | CHECK_PACKET_SIZE(recv_data,8+4+1+1); |
---|
638 | |
---|
639 | sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM" ); |
---|
640 | uint64 vendorguid; |
---|
641 | uint32 item; |
---|
642 | uint8 count, unk1; |
---|
643 | |
---|
644 | recv_data >> vendorguid >> item >> count >> unk1; |
---|
645 | |
---|
646 | GetPlayer()->BuyItemFromVendor(vendorguid,item,count,NULL_BAG,NULL_SLOT); |
---|
647 | } |
---|
648 | |
---|
649 | void WorldSession::HandleListInventoryOpcode( WorldPacket & recv_data ) |
---|
650 | { |
---|
651 | CHECK_PACKET_SIZE(recv_data,8); |
---|
652 | |
---|
653 | uint64 guid; |
---|
654 | |
---|
655 | recv_data >> guid; |
---|
656 | |
---|
657 | if(!GetPlayer()->isAlive()) |
---|
658 | return; |
---|
659 | |
---|
660 | sLog.outDebug( "WORLD: Recvd CMSG_LIST_INVENTORY" ); |
---|
661 | |
---|
662 | SendListInventory( guid ); |
---|
663 | } |
---|
664 | |
---|
665 | void WorldSession::SendListInventory( uint64 vendorguid ) |
---|
666 | { |
---|
667 | sLog.outDebug( "WORLD: Sent SMSG_LIST_INVENTORY" ); |
---|
668 | |
---|
669 | Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR); |
---|
670 | if (!pCreature) |
---|
671 | { |
---|
672 | sLog.outDebug( "WORLD: SendListInventory - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) ); |
---|
673 | _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0); |
---|
674 | return; |
---|
675 | } |
---|
676 | |
---|
677 | // remove fake death |
---|
678 | if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) |
---|
679 | GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); |
---|
680 | |
---|
681 | // Stop the npc if moving |
---|
682 | pCreature->StopMoving(); |
---|
683 | |
---|
684 | VendorItemData const* vItems = pCreature->GetVendorItems(); |
---|
685 | if(!vItems) |
---|
686 | { |
---|
687 | _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0); |
---|
688 | return; |
---|
689 | } |
---|
690 | |
---|
691 | uint8 numitems = vItems->GetItemCount(); |
---|
692 | uint8 count = 0; |
---|
693 | |
---|
694 | WorldPacket data( SMSG_LIST_INVENTORY, (8+1+numitems*8*4) ); |
---|
695 | data << uint64(vendorguid); |
---|
696 | data << uint8(numitems); |
---|
697 | |
---|
698 | float discountMod = _player->GetReputationPriceDiscount(pCreature); |
---|
699 | |
---|
700 | for(int i = 0; i < numitems; i++ ) |
---|
701 | { |
---|
702 | if(VendorItem const* crItem = vItems->GetItem(i)) |
---|
703 | { |
---|
704 | if(ItemPrototype const *pProto = objmgr.GetItemPrototype(crItem->item)) |
---|
705 | { |
---|
706 | if((pProto->AllowableClass & _player->getClassMask()) == 0 && pProto->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster()) |
---|
707 | continue; |
---|
708 | |
---|
709 | ++count; |
---|
710 | |
---|
711 | // reputation discount |
---|
712 | uint32 price = uint32(floor(pProto->BuyPrice * discountMod)); |
---|
713 | |
---|
714 | data << uint32(count); |
---|
715 | data << uint32(crItem->item); |
---|
716 | data << uint32(pProto->DisplayInfoID); |
---|
717 | data << uint32(crItem->maxcount <= 0 ? 0xFFFFFFFF : pCreature->GetVendorItemCurrentCount(crItem)); |
---|
718 | data << uint32(price); |
---|
719 | data << uint32(pProto->MaxDurability); |
---|
720 | data << uint32(pProto->BuyCount); |
---|
721 | data << uint32(crItem->ExtendedCost); |
---|
722 | } |
---|
723 | } |
---|
724 | } |
---|
725 | |
---|
726 | if ( count == 0 || data.size() != 8 + 1 + size_t(count) * 8 * 4 ) |
---|
727 | return; |
---|
728 | |
---|
729 | data.put<uint8>(8, count); |
---|
730 | SendPacket( &data ); |
---|
731 | } |
---|
732 | |
---|
733 | void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data ) |
---|
734 | { |
---|
735 | CHECK_PACKET_SIZE(recv_data,1+1+1); |
---|
736 | |
---|
737 | //sLog.outDebug("WORLD: CMSG_AUTOSTORE_BAG_ITEM"); |
---|
738 | uint8 srcbag, srcslot, dstbag; |
---|
739 | |
---|
740 | recv_data >> srcbag >> srcslot >> dstbag; |
---|
741 | //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u", srcbag, srcslot, dstbag); |
---|
742 | |
---|
743 | Item *pItem = _player->GetItemByPos( srcbag, srcslot ); |
---|
744 | if( !pItem ) |
---|
745 | return; |
---|
746 | |
---|
747 | uint16 src = pItem->GetPos(); |
---|
748 | |
---|
749 | // check unequip potability for equipped items and bank bags |
---|
750 | if(_player->IsEquipmentPos ( src ) || _player->IsBagPos ( src )) |
---|
751 | { |
---|
752 | uint8 msg = _player->CanUnequipItem( src, !_player->IsBagPos ( src )); |
---|
753 | if(msg != EQUIP_ERR_OK) |
---|
754 | { |
---|
755 | _player->SendEquipError( msg, pItem, NULL ); |
---|
756 | return; |
---|
757 | } |
---|
758 | } |
---|
759 | |
---|
760 | ItemPosCountVec dest; |
---|
761 | uint8 msg = _player->CanStoreItem( dstbag, NULL_SLOT, dest, pItem, false ); |
---|
762 | if( msg != EQUIP_ERR_OK ) |
---|
763 | { |
---|
764 | _player->SendEquipError( msg, pItem, NULL ); |
---|
765 | return; |
---|
766 | } |
---|
767 | |
---|
768 | // no-op: placed in same slot |
---|
769 | if(dest.size()==1 && dest[0].pos==src) |
---|
770 | { |
---|
771 | // just remove grey item state |
---|
772 | _player->SendEquipError( EQUIP_ERR_NONE, pItem, NULL ); |
---|
773 | return; |
---|
774 | } |
---|
775 | |
---|
776 | _player->RemoveItem(srcbag, srcslot, true ); |
---|
777 | _player->StoreItem( dest, pItem, true ); |
---|
778 | } |
---|
779 | |
---|
780 | void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& /*recvPacket*/) |
---|
781 | { |
---|
782 | sLog.outDebug("WORLD: CMSG_BUY_BANK_SLOT"); |
---|
783 | |
---|
784 | uint32 slot = _player->GetByteValue(PLAYER_BYTES_2, 2); |
---|
785 | |
---|
786 | // next slot |
---|
787 | ++slot; |
---|
788 | |
---|
789 | sLog.outDetail("PLAYER: Buy bank bag slot, slot number = %u", slot); |
---|
790 | |
---|
791 | BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot); |
---|
792 | |
---|
793 | if(!slotEntry) |
---|
794 | return; |
---|
795 | |
---|
796 | uint32 price = slotEntry->price; |
---|
797 | |
---|
798 | if (_player->GetMoney() < price) |
---|
799 | return; |
---|
800 | |
---|
801 | _player->SetByteValue(PLAYER_BYTES_2, 2, slot); |
---|
802 | _player->ModifyMoney(-int32(price)); |
---|
803 | } |
---|
804 | |
---|
805 | void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket) |
---|
806 | { |
---|
807 | CHECK_PACKET_SIZE(recvPacket,1+1); |
---|
808 | |
---|
809 | sLog.outDebug("WORLD: CMSG_AUTOBANK_ITEM"); |
---|
810 | uint8 srcbag, srcslot; |
---|
811 | |
---|
812 | recvPacket >> srcbag >> srcslot; |
---|
813 | sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot); |
---|
814 | |
---|
815 | Item *pItem = _player->GetItemByPos( srcbag, srcslot ); |
---|
816 | if( !pItem ) |
---|
817 | return; |
---|
818 | |
---|
819 | ItemPosCountVec dest; |
---|
820 | uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); |
---|
821 | if( msg != EQUIP_ERR_OK ) |
---|
822 | { |
---|
823 | _player->SendEquipError( msg, pItem, NULL ); |
---|
824 | return; |
---|
825 | } |
---|
826 | |
---|
827 | _player->RemoveItem(srcbag, srcslot, true); |
---|
828 | _player->BankItem( dest, pItem, true ); |
---|
829 | } |
---|
830 | |
---|
831 | void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket) |
---|
832 | { |
---|
833 | CHECK_PACKET_SIZE(recvPacket,1+1); |
---|
834 | |
---|
835 | sLog.outDebug("WORLD: CMSG_AUTOSTORE_BANK_ITEM"); |
---|
836 | uint8 srcbag, srcslot; |
---|
837 | |
---|
838 | recvPacket >> srcbag >> srcslot; |
---|
839 | sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot); |
---|
840 | |
---|
841 | Item *pItem = _player->GetItemByPos( srcbag, srcslot ); |
---|
842 | if( !pItem ) |
---|
843 | return; |
---|
844 | |
---|
845 | if(_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory |
---|
846 | { |
---|
847 | ItemPosCountVec dest; |
---|
848 | uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); |
---|
849 | if( msg != EQUIP_ERR_OK ) |
---|
850 | { |
---|
851 | _player->SendEquipError( msg, pItem, NULL ); |
---|
852 | return; |
---|
853 | } |
---|
854 | |
---|
855 | _player->RemoveItem(srcbag, srcslot, true); |
---|
856 | _player->StoreItem( dest, pItem, true ); |
---|
857 | } |
---|
858 | else // moving from inventory to bank |
---|
859 | { |
---|
860 | ItemPosCountVec dest; |
---|
861 | uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); |
---|
862 | if( msg != EQUIP_ERR_OK ) |
---|
863 | { |
---|
864 | _player->SendEquipError( msg, pItem, NULL ); |
---|
865 | return; |
---|
866 | } |
---|
867 | |
---|
868 | _player->RemoveItem(srcbag, srcslot, true); |
---|
869 | _player->BankItem( dest, pItem, true ); |
---|
870 | } |
---|
871 | } |
---|
872 | |
---|
873 | void WorldSession::HandleSetAmmoOpcode(WorldPacket & recv_data) |
---|
874 | { |
---|
875 | CHECK_PACKET_SIZE(recv_data,4); |
---|
876 | |
---|
877 | if(!GetPlayer()->isAlive()) |
---|
878 | { |
---|
879 | GetPlayer()->SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, NULL, NULL ); |
---|
880 | return; |
---|
881 | } |
---|
882 | |
---|
883 | sLog.outDebug("WORLD: CMSG_SET_AMMO"); |
---|
884 | uint32 item; |
---|
885 | |
---|
886 | recv_data >> item; |
---|
887 | |
---|
888 | if(!item) |
---|
889 | GetPlayer()->RemoveAmmo(); |
---|
890 | else |
---|
891 | GetPlayer()->SetAmmo(item); |
---|
892 | } |
---|
893 | |
---|
894 | void WorldSession::SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID,uint32 SpellID) |
---|
895 | { |
---|
896 | WorldPacket data(SMSG_ENCHANTMENTLOG, (8+8+4+4+1)); // last check 2.0.10 |
---|
897 | data << Target; |
---|
898 | data << Caster; |
---|
899 | data << ItemID; |
---|
900 | data << SpellID; |
---|
901 | data << uint8(0); |
---|
902 | SendPacket(&data); |
---|
903 | } |
---|
904 | |
---|
905 | void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration) |
---|
906 | { |
---|
907 | // last check 2.0.10 |
---|
908 | WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8+4+4+8)); |
---|
909 | data << uint64(Itemguid); |
---|
910 | data << uint32(slot); |
---|
911 | data << uint32(Duration); |
---|
912 | data << uint64(Playerguid); |
---|
913 | SendPacket(&data); |
---|
914 | } |
---|
915 | |
---|
916 | void WorldSession::HandleItemNameQueryOpcode(WorldPacket & recv_data) |
---|
917 | { |
---|
918 | CHECK_PACKET_SIZE(recv_data,4); |
---|
919 | |
---|
920 | uint32 itemid; |
---|
921 | recv_data >> itemid; |
---|
922 | sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY %u", itemid); |
---|
923 | ItemPrototype const *pProto = objmgr.GetItemPrototype( itemid ); |
---|
924 | if( pProto ) |
---|
925 | { |
---|
926 | std::string Name; |
---|
927 | Name = pProto->Name1; |
---|
928 | |
---|
929 | int loc_idx = GetSessionDbLocaleIndex(); |
---|
930 | if (loc_idx >= 0) |
---|
931 | { |
---|
932 | ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId); |
---|
933 | if (il) |
---|
934 | { |
---|
935 | if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty()) |
---|
936 | Name = il->Name[loc_idx]; |
---|
937 | } |
---|
938 | } |
---|
939 | // guess size |
---|
940 | WorldPacket data(SMSG_ITEM_NAME_QUERY_RESPONSE, (4+10)); |
---|
941 | data << uint32(pProto->ItemId); |
---|
942 | data << Name; |
---|
943 | data << uint32(pProto->InventoryType); |
---|
944 | SendPacket(&data); |
---|
945 | return; |
---|
946 | } |
---|
947 | else |
---|
948 | sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY for item %u failed (unknown item)", itemid); |
---|
949 | } |
---|
950 | |
---|
951 | void WorldSession::HandleWrapItemOpcode(WorldPacket& recv_data) |
---|
952 | { |
---|
953 | CHECK_PACKET_SIZE(recv_data,1+1+1+1); |
---|
954 | |
---|
955 | sLog.outDebug("Received opcode CMSG_WRAP_ITEM"); |
---|
956 | |
---|
957 | uint8 gift_bag, gift_slot, item_bag, item_slot; |
---|
958 | //recv_data.hexlike(); |
---|
959 | |
---|
960 | recv_data >> gift_bag >> gift_slot; // paper |
---|
961 | recv_data >> item_bag >> item_slot; // item |
---|
962 | |
---|
963 | sLog.outDebug("WRAP: receive gift_bag = %u, gift_slot = %u, item_bag = %u, item_slot = %u", gift_bag, gift_slot, item_bag, item_slot); |
---|
964 | |
---|
965 | Item *gift = _player->GetItemByPos( gift_bag, gift_slot ); |
---|
966 | if(!gift) |
---|
967 | { |
---|
968 | _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL ); |
---|
969 | return; |
---|
970 | } |
---|
971 | |
---|
972 | if(!gift->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPER))// cheating: non-wrapper wrapper |
---|
973 | { |
---|
974 | _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL ); |
---|
975 | return; |
---|
976 | } |
---|
977 | |
---|
978 | Item *item = _player->GetItemByPos( item_bag, item_slot ); |
---|
979 | |
---|
980 | if( !item ) |
---|
981 | { |
---|
982 | _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, item, NULL ); |
---|
983 | return; |
---|
984 | } |
---|
985 | |
---|
986 | if(item==gift) // not possable with pacjket from real client |
---|
987 | { |
---|
988 | _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL ); |
---|
989 | return; |
---|
990 | } |
---|
991 | |
---|
992 | if(item->IsEquipped()) |
---|
993 | { |
---|
994 | _player->SendEquipError( EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED, item, NULL ); |
---|
995 | return; |
---|
996 | } |
---|
997 | |
---|
998 | if(item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR)) // HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED); |
---|
999 | { |
---|
1000 | _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL ); |
---|
1001 | return; |
---|
1002 | } |
---|
1003 | |
---|
1004 | if(item->IsBag()) |
---|
1005 | { |
---|
1006 | _player->SendEquipError( EQUIP_ERR_BAGS_CANT_BE_WRAPPED, item, NULL ); |
---|
1007 | return; |
---|
1008 | } |
---|
1009 | |
---|
1010 | if(item->IsSoulBound()) |
---|
1011 | { |
---|
1012 | _player->SendEquipError( EQUIP_ERR_BOUND_CANT_BE_WRAPPED, item, NULL ); |
---|
1013 | return; |
---|
1014 | } |
---|
1015 | |
---|
1016 | if(item->GetMaxStackCount() != 1) |
---|
1017 | { |
---|
1018 | _player->SendEquipError( EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED, item, NULL ); |
---|
1019 | return; |
---|
1020 | } |
---|
1021 | |
---|
1022 | // maybe not correct check (it is better than nothing) |
---|
1023 | if(item->GetProto()->MaxCount>0) |
---|
1024 | { |
---|
1025 | _player->SendEquipError( EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED, item, NULL ); |
---|
1026 | return; |
---|
1027 | } |
---|
1028 | |
---|
1029 | CharacterDatabase.BeginTransaction(); |
---|
1030 | CharacterDatabase.PExecute("INSERT INTO character_gifts VALUES ('%u', '%u', '%u', '%u')", GUID_LOPART(item->GetOwnerGUID()), item->GetGUIDLow(), item->GetEntry(), item->GetUInt32Value(ITEM_FIELD_FLAGS)); |
---|
1031 | item->SetUInt32Value(OBJECT_FIELD_ENTRY, gift->GetUInt32Value(OBJECT_FIELD_ENTRY)); |
---|
1032 | |
---|
1033 | switch (item->GetEntry()) |
---|
1034 | { |
---|
1035 | case 5042: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5043); break; |
---|
1036 | case 5048: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5044); break; |
---|
1037 | case 17303: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17302); break; |
---|
1038 | case 17304: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17305); break; |
---|
1039 | case 17307: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17308); break; |
---|
1040 | case 21830: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 21831); break; |
---|
1041 | } |
---|
1042 | item->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID()); |
---|
1043 | item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED); |
---|
1044 | item->SetState(ITEM_CHANGED, _player); |
---|
1045 | |
---|
1046 | if(item->GetState()==ITEM_NEW) // save new item, to have alway for `character_gifts` record in `item_instance` |
---|
1047 | { |
---|
1048 | // after save it will be impossible to remove the item from the queue |
---|
1049 | item->RemoveFromUpdateQueueOf(_player); |
---|
1050 | item->SaveToDB(); // item gave inventory record unchanged and can be save standalone |
---|
1051 | } |
---|
1052 | CharacterDatabase.CommitTransaction(); |
---|
1053 | |
---|
1054 | uint32 count = 1; |
---|
1055 | _player->DestroyItemCount(gift, count, true); |
---|
1056 | } |
---|
1057 | |
---|
1058 | void WorldSession::HandleSocketOpcode(WorldPacket& recv_data) |
---|
1059 | { |
---|
1060 | sLog.outDebug("WORLD: CMSG_SOCKET_GEMS"); |
---|
1061 | |
---|
1062 | CHECK_PACKET_SIZE(recv_data,8*4); |
---|
1063 | |
---|
1064 | uint64 guids[4]; |
---|
1065 | uint32 GemEnchants[3], OldEnchants[3]; |
---|
1066 | Item *Gems[3]; |
---|
1067 | bool SocketBonusActivated, SocketBonusToBeActivated; |
---|
1068 | |
---|
1069 | for(int i = 0; i < 4; i++) |
---|
1070 | recv_data >> guids[i]; |
---|
1071 | |
---|
1072 | if(!guids[0]) |
---|
1073 | return; |
---|
1074 | |
---|
1075 | //cheat -> tried to socket same gem multiple times |
---|
1076 | if((guids[1] && (guids[1] == guids[2] || guids[1] == guids[3])) || (guids[2] && (guids[2] == guids[3]))) |
---|
1077 | return; |
---|
1078 | |
---|
1079 | Item *itemTarget = _player->GetItemByGuid(guids[0]); |
---|
1080 | if(!itemTarget) //missing item to socket |
---|
1081 | return; |
---|
1082 | |
---|
1083 | //this slot is excepted when applying / removing meta gem bonus |
---|
1084 | uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : NULL_SLOT; |
---|
1085 | |
---|
1086 | for(int i = 0; i < 3; i++) |
---|
1087 | Gems[i] = guids[i + 1] ? _player->GetItemByGuid(guids[i + 1]) : NULL; |
---|
1088 | |
---|
1089 | GemPropertiesEntry const *GemProps[3]; |
---|
1090 | for(int i = 0; i < 3; ++i) //get geminfo from dbc storage |
---|
1091 | { |
---|
1092 | GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetProto()->GemProperties) : NULL; |
---|
1093 | } |
---|
1094 | |
---|
1095 | for(int i = 0; i < 3; ++i) //check for hack maybe |
---|
1096 | { |
---|
1097 | // tried to put gem in socket where no socket exists / tried to put normal gem in meta socket |
---|
1098 | // tried to put meta gem in normal socket |
---|
1099 | if( GemProps[i] && ( !itemTarget->GetProto()->Socket[i].Color || |
---|
1100 | itemTarget->GetProto()->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META || |
---|
1101 | itemTarget->GetProto()->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META ) ) |
---|
1102 | return; |
---|
1103 | } |
---|
1104 | |
---|
1105 | for(int i = 0; i < 3; ++i) //get new and old enchantments |
---|
1106 | { |
---|
1107 | GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0; |
---|
1108 | OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i)); |
---|
1109 | } |
---|
1110 | |
---|
1111 | // check unique-equipped conditions |
---|
1112 | for(int i = 0; i < 3; ++i) |
---|
1113 | { |
---|
1114 | if (Gems[i] && (Gems[i]->GetProto()->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED)) |
---|
1115 | { |
---|
1116 | // for equipped item check all equipment for duplicate equipped gems |
---|
1117 | if(itemTarget->IsEquipped()) |
---|
1118 | { |
---|
1119 | if(GetPlayer()->GetItemOrItemWithGemEquipped(Gems[i]->GetEntry())) |
---|
1120 | { |
---|
1121 | _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE, itemTarget, NULL ); |
---|
1122 | return; |
---|
1123 | } |
---|
1124 | } |
---|
1125 | |
---|
1126 | // continue check for case when attempt add 2 similar unique equipped gems in one item. |
---|
1127 | for (int j = 0; j < 3; ++j) |
---|
1128 | { |
---|
1129 | if ((i != j) && (Gems[j]) && (Gems[i]->GetProto()->ItemId == Gems[j]->GetProto()->ItemId)) |
---|
1130 | { |
---|
1131 | _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL ); |
---|
1132 | return; |
---|
1133 | } |
---|
1134 | } |
---|
1135 | for (int j = 0; j < 3; ++j) |
---|
1136 | { |
---|
1137 | if (OldEnchants[j]) |
---|
1138 | { |
---|
1139 | SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]); |
---|
1140 | if(!enchantEntry) |
---|
1141 | continue; |
---|
1142 | |
---|
1143 | if ((enchantEntry->GemID == Gems[i]->GetProto()->ItemId) && (i != j)) |
---|
1144 | { |
---|
1145 | _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL ); |
---|
1146 | return; |
---|
1147 | } |
---|
1148 | } |
---|
1149 | } |
---|
1150 | } |
---|
1151 | } |
---|
1152 | |
---|
1153 | SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus |
---|
1154 | _player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item) |
---|
1155 | |
---|
1156 | //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 |
---|
1157 | |
---|
1158 | //remove ALL enchants |
---|
1159 | for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) |
---|
1160 | _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),false); |
---|
1161 | |
---|
1162 | for(int i = 0; i < 3; ++i) |
---|
1163 | { |
---|
1164 | if(GemEnchants[i]) |
---|
1165 | { |
---|
1166 | itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i],0,0); |
---|
1167 | if(Item* guidItem = _player->GetItemByGuid(guids[i + 1])) |
---|
1168 | _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true ); |
---|
1169 | } |
---|
1170 | } |
---|
1171 | |
---|
1172 | for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) |
---|
1173 | _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),true); |
---|
1174 | |
---|
1175 | SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state |
---|
1176 | if(SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change... |
---|
1177 | { |
---|
1178 | _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,false); |
---|
1179 | itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetProto()->socketBonus : 0), 0, 0); |
---|
1180 | _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,true); |
---|
1181 | //it is not displayed, client has an inbuilt system to determine if the bonus is activated |
---|
1182 | } |
---|
1183 | |
---|
1184 | _player->ToggleMetaGemsActive(slot, true); //turn on all metagems (except for target item) |
---|
1185 | } |
---|
1186 | |
---|
1187 | void WorldSession::HandleCancelTempItemEnchantmentOpcode(WorldPacket& recv_data) |
---|
1188 | { |
---|
1189 | sLog.outDebug("WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT"); |
---|
1190 | |
---|
1191 | CHECK_PACKET_SIZE(recv_data,4); |
---|
1192 | |
---|
1193 | uint32 eslot; |
---|
1194 | |
---|
1195 | recv_data >> eslot; |
---|
1196 | |
---|
1197 | // apply only to equipped item |
---|
1198 | if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0,eslot)) |
---|
1199 | return; |
---|
1200 | |
---|
1201 | Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot); |
---|
1202 | |
---|
1203 | if(!item) |
---|
1204 | return; |
---|
1205 | |
---|
1206 | if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)) |
---|
1207 | return; |
---|
1208 | |
---|
1209 | GetPlayer()->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false); |
---|
1210 | item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT); |
---|
1211 | } |
---|