PageRenderTime 129ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 0ms

/src/server/game/Handlers/ItemHandler.cpp

https://gitlab.com/IlluminatiCore/IlluminatiCore
C++ | 1663 lines | 1252 code | 298 blank | 113 comment | 311 complexity | fdf7aa8e18de6e2a92beaeb515f06af6 MD5 | raw file
Possible License(s): GPL-2.0, BSD-2-Clause
  1. /*
  2. * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
  3. * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the
  7. * Free Software Foundation; either version 2 of the License, or (at your
  8. * option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but WITHOUT
  11. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13. * more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "Common.h"
  19. #include "WorldPacket.h"
  20. #include "WorldSession.h"
  21. #include "Opcodes.h"
  22. #include "Log.h"
  23. #include "ObjectMgr.h"
  24. #include "Player.h"
  25. #include "Item.h"
  26. #include "UpdateData.h"
  27. #include "ObjectAccessor.h"
  28. #include "SpellInfo.h"
  29. #include "DB2Stores.h"
  30. #include <vector>
  31. void WorldSession::HandleSplitItemOpcode(WorldPacket& recvData)
  32. {
  33. //TC_LOG_DEBUG("network", "WORLD: CMSG_SPLIT_ITEM");
  34. uint8 srcbag, srcslot, dstbag, dstslot;
  35. uint32 count;
  36. recvData >> srcbag >> srcslot >> dstbag >> dstslot >> count;
  37. //TC_LOG_DEBUG("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u, count = %u", srcbag, srcslot, dstbag, dstslot, count);
  38. uint16 src = ((srcbag << 8) | srcslot);
  39. uint16 dst = ((dstbag << 8) | dstslot);
  40. if (src == dst)
  41. return;
  42. if (count == 0)
  43. return; //check count - if zero it's fake packet
  44. if (!_player->IsValidPos(srcbag, srcslot, true))
  45. {
  46. _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
  47. return;
  48. }
  49. if (!_player->IsValidPos(dstbag, dstslot, false)) // can be autostore pos
  50. {
  51. _player->SendEquipError(EQUIP_ERR_WRONG_SLOT, NULL, NULL);
  52. return;
  53. }
  54. _player->SplitItem(src, dst, count);
  55. }
  56. void WorldSession::HandleSwapInvItemOpcode(WorldPacket& recvData)
  57. {
  58. //TC_LOG_DEBUG("network", "WORLD: CMSG_SWAP_INV_ITEM");
  59. uint8 srcslot, dstslot;
  60. recvData >> dstslot >> srcslot;
  61. //TC_LOG_DEBUG("STORAGE: receive srcslot = %u, dstslot = %u", srcslot, dstslot);
  62. // prevent attempt swap same item to current position generated by client at special checting sequence
  63. if (srcslot == dstslot)
  64. return;
  65. if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, srcslot, true))
  66. {
  67. _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
  68. return;
  69. }
  70. if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, dstslot, true))
  71. {
  72. _player->SendEquipError(EQUIP_ERR_WRONG_SLOT, NULL, NULL);
  73. return;
  74. }
  75. if (_player->IsBankPos(INVENTORY_SLOT_BAG_0, srcslot) && !CanUseBank())
  76. {
  77. TC_LOG_DEBUG("network", "WORLD: HandleSwapInvItemOpcode - Unit (%s) not found or you can't interact with him.", m_currentBankerGUID.ToString().c_str());
  78. return;
  79. }
  80. if (_player->IsBankPos(INVENTORY_SLOT_BAG_0, dstslot) && !CanUseBank())
  81. {
  82. TC_LOG_DEBUG("network", "WORLD: HandleSwapInvItemOpcode - Unit (%s) not found or you can't interact with him.", m_currentBankerGUID.ToString().c_str());
  83. return;
  84. }
  85. uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot);
  86. uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot);
  87. _player->SwapItem(src, dst);
  88. }
  89. void WorldSession::HandleAutoEquipItemSlotOpcode(WorldPacket& recvData)
  90. {
  91. ObjectGuid itemguid;
  92. uint8 dstslot;
  93. recvData >> itemguid >> dstslot;
  94. // cheating attempt, client should never send opcode in that case
  95. if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot))
  96. return;
  97. Item* item = _player->GetItemByGuid(itemguid);
  98. uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8);
  99. if (!item || item->GetPos() == dstpos)
  100. return;
  101. _player->SwapItem(item->GetPos(), dstpos);
  102. }
  103. void WorldSession::HandleSwapItem(WorldPacket& recvData)
  104. {
  105. //TC_LOG_DEBUG("network", "WORLD: CMSG_SWAP_ITEM");
  106. uint8 dstbag, dstslot, srcbag, srcslot;
  107. recvData >> dstbag >> dstslot >> srcbag >> srcslot;
  108. //TC_LOG_DEBUG("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u", srcbag, srcslot, dstbag, dstslot);
  109. uint16 src = ((srcbag << 8) | srcslot);
  110. uint16 dst = ((dstbag << 8) | dstslot);
  111. // prevent attempt swap same item to current position generated by client at special checting sequence
  112. if (src == dst)
  113. return;
  114. if (!_player->IsValidPos(srcbag, srcslot, true))
  115. {
  116. _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
  117. return;
  118. }
  119. if (!_player->IsValidPos(dstbag, dstslot, true))
  120. {
  121. _player->SendEquipError(EQUIP_ERR_WRONG_SLOT, NULL, NULL);
  122. return;
  123. }
  124. if (_player->IsBankPos(srcbag, srcslot) && !CanUseBank())
  125. {
  126. TC_LOG_DEBUG("network", "WORLD: HandleSwapItem - Unit (%s) not found or you can't interact with him.", m_currentBankerGUID.ToString().c_str());
  127. return;
  128. }
  129. if (_player->IsBankPos(dstbag, dstslot) && !CanUseBank())
  130. {
  131. TC_LOG_DEBUG("network", "WORLD: HandleSwapItem - Unit (%s) not found or you can't interact with him.", m_currentBankerGUID.ToString().c_str());
  132. return;
  133. }
  134. _player->SwapItem(src, dst);
  135. }
  136. void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData)
  137. {
  138. //TC_LOG_DEBUG("network", "WORLD: CMSG_AUTOEQUIP_ITEM");
  139. uint8 srcbag, srcslot;
  140. recvData >> srcbag >> srcslot;
  141. //TC_LOG_DEBUG("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
  142. Item* pSrcItem = _player->GetItemByPos(srcbag, srcslot);
  143. if (!pSrcItem)
  144. return; // only at cheat
  145. uint16 dest;
  146. InventoryResult msg = _player->CanEquipItem(NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag());
  147. if (msg != EQUIP_ERR_OK)
  148. {
  149. _player->SendEquipError(msg, pSrcItem, NULL);
  150. return;
  151. }
  152. uint16 src = pSrcItem->GetPos();
  153. if (dest == src) // prevent equip in same slot, only at cheat
  154. return;
  155. Item* pDstItem = _player->GetItemByPos(dest);
  156. if (!pDstItem) // empty slot, simple case
  157. {
  158. _player->RemoveItem(srcbag, srcslot, true);
  159. _player->EquipItem(dest, pSrcItem, true);
  160. _player->AutoUnequipOffhandIfNeed();
  161. }
  162. else // have currently equipped item, not simple case
  163. {
  164. uint8 dstbag = pDstItem->GetBagSlot();
  165. uint8 dstslot = pDstItem->GetSlot();
  166. msg = _player->CanUnequipItem(dest, !pSrcItem->IsBag());
  167. if (msg != EQUIP_ERR_OK)
  168. {
  169. _player->SendEquipError(msg, pDstItem, NULL);
  170. return;
  171. }
  172. // check dest->src move possibility
  173. ItemPosCountVec sSrc;
  174. uint16 eSrc = 0;
  175. if (_player->IsInventoryPos(src))
  176. {
  177. msg = _player->CanStoreItem(srcbag, srcslot, sSrc, pDstItem, true);
  178. if (msg != EQUIP_ERR_OK)
  179. msg = _player->CanStoreItem(srcbag, NULL_SLOT, sSrc, pDstItem, true);
  180. if (msg != EQUIP_ERR_OK)
  181. msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sSrc, pDstItem, true);
  182. }
  183. else if (_player->IsBankPos(src))
  184. {
  185. msg = _player->CanBankItem(srcbag, srcslot, sSrc, pDstItem, true);
  186. if (msg != EQUIP_ERR_OK)
  187. msg = _player->CanBankItem(srcbag, NULL_SLOT, sSrc, pDstItem, true);
  188. if (msg != EQUIP_ERR_OK)
  189. msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, sSrc, pDstItem, true);
  190. }
  191. else if (_player->IsEquipmentPos(src))
  192. {
  193. msg = _player->CanEquipItem(srcslot, eSrc, pDstItem, true);
  194. if (msg == EQUIP_ERR_OK)
  195. msg = _player->CanUnequipItem(eSrc, true);
  196. }
  197. if (msg != EQUIP_ERR_OK)
  198. {
  199. _player->SendEquipError(msg, pDstItem, pSrcItem);
  200. return;
  201. }
  202. // now do moves, remove...
  203. _player->RemoveItem(dstbag, dstslot, false);
  204. _player->RemoveItem(srcbag, srcslot, false);
  205. // add to dest
  206. _player->EquipItem(dest, pSrcItem, true);
  207. // add to src
  208. if (_player->IsInventoryPos(src))
  209. _player->StoreItem(sSrc, pDstItem, true);
  210. else if (_player->IsBankPos(src))
  211. _player->BankItem(sSrc, pDstItem, true);
  212. else if (_player->IsEquipmentPos(src))
  213. _player->EquipItem(eSrc, pDstItem, true);
  214. _player->AutoUnequipOffhandIfNeed();
  215. }
  216. }
  217. void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData)
  218. {
  219. //TC_LOG_DEBUG("network", "WORLD: CMSG_DESTROY_ITEM");
  220. uint8 bag, slot, count, data1, data2, data3;
  221. recvData >> bag >> slot >> count >> data1 >> data2 >> data3;
  222. //TC_LOG_DEBUG("STORAGE: receive bag = %u, slot = %u, count = %u", bag, slot, count);
  223. uint16 pos = (bag << 8) | slot;
  224. // prevent drop unequipable items (in combat, for example) and non-empty bags
  225. if (_player->IsEquipmentPos(pos) || _player->IsBagPos(pos))
  226. {
  227. InventoryResult msg = _player->CanUnequipItem(pos, false);
  228. if (msg != EQUIP_ERR_OK)
  229. {
  230. _player->SendEquipError(msg, _player->GetItemByPos(pos), NULL);
  231. return;
  232. }
  233. }
  234. Item* pItem = _player->GetItemByPos(bag, slot);
  235. if (!pItem)
  236. {
  237. _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
  238. return;
  239. }
  240. if (pItem->GetTemplate()->Flags & ITEM_PROTO_FLAG_INDESTRUCTIBLE)
  241. {
  242. _player->SendEquipError(EQUIP_ERR_DROP_BOUND_ITEM, NULL, NULL);
  243. return;
  244. }
  245. if (count)
  246. {
  247. uint32 i_count = count;
  248. _player->DestroyItemCount(pItem, i_count, true);
  249. }
  250. else
  251. _player->DestroyItem(bag, slot, true);
  252. }
  253. void WorldSession::HandleReadItem(WorldPacket& recvData)
  254. {
  255. uint8 bag, slot;
  256. recvData >> bag >> slot;
  257. Item* pItem = _player->GetItemByPos(bag, slot);
  258. if (pItem && pItem->GetTemplate()->PageText)
  259. {
  260. WorldPacket data;
  261. InventoryResult msg = _player->CanUseItem(pItem);
  262. if (msg == EQUIP_ERR_OK)
  263. {
  264. data.Initialize(SMSG_READ_ITEM_OK, 8);
  265. TC_LOG_INFO("network", "STORAGE: Item page sent");
  266. }
  267. else
  268. {
  269. data.Initialize(SMSG_READ_ITEM_FAILED, 8);
  270. TC_LOG_INFO("network", "STORAGE: Unable to read item");
  271. _player->SendEquipError(msg, pItem, NULL);
  272. }
  273. data << pItem->GetGUID();
  274. SendPacket(&data);
  275. }
  276. else
  277. _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
  278. }
  279. void WorldSession::HandleSellItemOpcode(WorldPacket& recvData)
  280. {
  281. TC_LOG_DEBUG("network", "WORLD: Received CMSG_SELL_ITEM");
  282. ObjectGuid vendorguid, itemguid;
  283. uint32 count;
  284. recvData >> vendorguid >> itemguid >> count;
  285. if (!itemguid)
  286. return;
  287. Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
  288. if (!creature)
  289. {
  290. TC_LOG_DEBUG("network", "WORLD: HandleSellItemOpcode - %s not found or you can not interact with him.", vendorguid.ToString().c_str());
  291. _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, itemguid);
  292. return;
  293. }
  294. // remove fake death
  295. if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
  296. GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
  297. Item* pItem = _player->GetItemByGuid(itemguid);
  298. if (pItem)
  299. {
  300. // prevent sell not owner item
  301. if (_player->GetGUID() != pItem->GetOwnerGUID())
  302. {
  303. _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid);
  304. return;
  305. }
  306. // prevent sell non empty bag by drag-and-drop at vendor's item list
  307. if (pItem->IsNotEmptyBag())
  308. {
  309. _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid);
  310. return;
  311. }
  312. // prevent sell currently looted item
  313. if (_player->GetLootGUID() == pItem->GetGUID())
  314. {
  315. _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid);
  316. return;
  317. }
  318. // prevent selling item for sellprice when the item is still refundable
  319. // this probably happens when right clicking a refundable item, the client sends both
  320. // CMSG_SELL_ITEM and CMSG_REFUND_ITEM (unverified)
  321. if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE))
  322. return; // Therefore, no feedback to client
  323. // special case at auto sell (sell all)
  324. if (count == 0)
  325. count = pItem->GetCount();
  326. else
  327. {
  328. // prevent sell more items that exist in stack (possible only not from client)
  329. if (count > pItem->GetCount())
  330. {
  331. _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid);
  332. return;
  333. }
  334. }
  335. ItemTemplate const* pProto = pItem->GetTemplate();
  336. if (pProto)
  337. {
  338. if (pProto->SellPrice > 0)
  339. {
  340. if (count < pItem->GetCount()) // need split items
  341. {
  342. Item* pNewItem = pItem->CloneItem(count, _player);
  343. if (!pNewItem)
  344. {
  345. TC_LOG_ERROR("network", "WORLD: HandleSellItemOpcode - could not create clone of item %u; count = %u", pItem->GetEntry(), count);
  346. _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid);
  347. return;
  348. }
  349. pItem->SetCount(pItem->GetCount() - count);
  350. _player->ItemRemovedQuestCheck(pItem->GetEntry(), count);
  351. if (_player->IsInWorld())
  352. pItem->SendUpdateToPlayer(_player);
  353. pItem->SetState(ITEM_CHANGED, _player);
  354. _player->AddItemToBuyBackSlot(pNewItem);
  355. if (_player->IsInWorld())
  356. pNewItem->SendUpdateToPlayer(_player);
  357. }
  358. else
  359. {
  360. _player->ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
  361. _player->RemoveItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
  362. pItem->RemoveFromUpdateQueueOf(_player);
  363. _player->AddItemToBuyBackSlot(pItem);
  364. }
  365. uint32 money = pProto->SellPrice * count;
  366. _player->ModifyMoney(money);
  367. _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS, money);
  368. }
  369. else
  370. _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid);
  371. return;
  372. }
  373. }
  374. _player->SendSellError(SELL_ERR_CANT_FIND_ITEM, creature, itemguid);
  375. return;
  376. }
  377. void WorldSession::HandleBuybackItem(WorldPacket& recvData)
  378. {
  379. TC_LOG_DEBUG("network", "WORLD: Received CMSG_BUYBACK_ITEM");
  380. ObjectGuid vendorguid;
  381. uint32 slot;
  382. recvData >> vendorguid >> slot;
  383. Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
  384. if (!creature)
  385. {
  386. TC_LOG_DEBUG("network", "WORLD: HandleBuybackItem - Unit (%s) not found or you can not interact with him.", vendorguid.ToString().c_str());
  387. _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, ObjectGuid::Empty);
  388. return;
  389. }
  390. // remove fake death
  391. if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
  392. GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
  393. Item* pItem = _player->GetItemFromBuyBackSlot(slot);
  394. if (pItem)
  395. {
  396. uint32 price = _player->GetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START);
  397. if (!_player->HasEnoughMoney(uint64(price)))
  398. {
  399. _player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, creature, pItem->GetEntry(), 0);
  400. return;
  401. }
  402. ItemPosCountVec dest;
  403. InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
  404. if (msg == EQUIP_ERR_OK)
  405. {
  406. _player->ModifyMoney(-(int32)price);
  407. _player->RemoveItemFromBuyBackSlot(slot, false);
  408. _player->ItemAddedQuestCheck(pItem->GetEntry(), pItem->GetCount());
  409. _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, pItem->GetEntry(), pItem->GetCount());
  410. _player->StoreItem(dest, pItem, true);
  411. }
  412. else
  413. _player->SendEquipError(msg, pItem, NULL);
  414. return;
  415. }
  416. else
  417. _player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, 0, 0);
  418. }
  419. void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recvData)
  420. {
  421. TC_LOG_DEBUG("network", "WORLD: Received CMSG_BUY_ITEM_IN_SLOT");
  422. ObjectGuid vendorguid, bagguid;
  423. uint32 item, slot, count;
  424. uint8 bagslot;
  425. recvData >> vendorguid >> item >> slot >> bagguid >> bagslot >> count;
  426. // client expects count starting at 1, and we send vendorslot+1 to client already
  427. if (slot > 0)
  428. --slot;
  429. else
  430. return; // cheating
  431. uint8 bag = NULL_BAG; // init for case invalid bagGUID
  432. Item* bagItem = NULL;
  433. // find bag slot by bag guid
  434. if (bagguid == _player->GetGUID())
  435. bag = INVENTORY_SLOT_BAG_0;
  436. else
  437. bagItem = _player->GetItemByGuid(bagguid);
  438. if (bagItem && bagItem->IsBag())
  439. bag = bagItem->GetSlot();
  440. // bag not found, cheating?
  441. if (bag == NULL_BAG)
  442. return;
  443. GetPlayer()->BuyItemFromVendorSlot(vendorguid, slot, item, count, bag, bagslot);
  444. }
  445. void WorldSession::HandleBuyItemOpcode(WorldPacket& recvData)
  446. {
  447. TC_LOG_DEBUG("network", "WORLD: Received CMSG_BUY_ITEM");
  448. ObjectGuid vendorguid, bagGuid;
  449. uint32 item, slot, count;
  450. uint8 itemType; // 1 = item, 2 = currency
  451. uint8 bagSlot;
  452. recvData >> vendorguid >> itemType >> item >> slot >> count >> bagGuid >> bagSlot;
  453. // client expects count starting at 1, and we send vendorslot+1 to client already
  454. if (slot > 0)
  455. --slot;
  456. else
  457. return; // cheating
  458. if (itemType == ITEM_VENDOR_TYPE_ITEM)
  459. {
  460. Item* bagItem = _player->GetItemByGuid(bagGuid);
  461. uint8 bag = NULL_BAG;
  462. if (bagItem && bagItem->IsBag())
  463. bag = bagItem->GetSlot();
  464. else if (bagGuid == GetPlayer()->GetGUID()) // The client sends the player guid when trying to store an item in the default backpack
  465. bag = INVENTORY_SLOT_BAG_0;
  466. GetPlayer()->BuyItemFromVendorSlot(vendorguid, slot, item, count, bag, bagSlot);
  467. }
  468. else if (itemType == ITEM_VENDOR_TYPE_CURRENCY)
  469. GetPlayer()->BuyCurrencyFromVendorSlot(vendorguid, slot, item, count);
  470. else
  471. TC_LOG_DEBUG("network", "WORLD: received wrong itemType (%u) in HandleBuyItemOpcode", itemType);
  472. }
  473. void WorldSession::HandleListInventoryOpcode(WorldPacket& recvData)
  474. {
  475. ObjectGuid guid;
  476. recvData >> guid;
  477. if (!GetPlayer()->IsAlive())
  478. return;
  479. TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_LIST_INVENTORY");
  480. SendListInventory(guid);
  481. }
  482. void WorldSession::SendListInventory(ObjectGuid vendorGuid)
  483. {
  484. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_LIST_INVENTORY");
  485. Creature* vendor = GetPlayer()->GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR);
  486. if (!vendor)
  487. {
  488. TC_LOG_DEBUG("network", "WORLD: SendListInventory - %s not found or you can not interact with him.", vendorGuid.ToString().c_str());
  489. _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, ObjectGuid::Empty);
  490. return;
  491. }
  492. // remove fake death
  493. if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
  494. GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
  495. // Stop the npc if moving
  496. if (vendor->HasUnitState(UNIT_STATE_MOVING))
  497. vendor->StopMoving();
  498. VendorItemData const* vendorItems = vendor->GetVendorItems();
  499. uint32 rawItemCount = vendorItems ? vendorItems->GetItemCount() : 0;
  500. //if (rawItemCount > 300),
  501. // rawItemCount = 300; // client cap but uint8 max value is 255
  502. ByteBuffer itemsData(32 * rawItemCount);
  503. std::vector<bool> enablers;
  504. enablers.reserve(2 * rawItemCount);
  505. const float discountMod = _player->GetReputationPriceDiscount(vendor);
  506. uint8 count = 0;
  507. for (uint32 slot = 0; slot < rawItemCount; ++slot)
  508. {
  509. VendorItem const* vendorItem = vendorItems->GetItem(slot);
  510. if (!vendorItem)
  511. continue;
  512. if (vendorItem->Type == ITEM_VENDOR_TYPE_ITEM)
  513. {
  514. ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(vendorItem->item);
  515. if (!itemTemplate)
  516. continue;
  517. uint32 leftInStock = !vendorItem->maxcount ? 0xFFFFFFFF : vendor->GetVendorItemCurrentCount(vendorItem);
  518. if (!_player->IsGameMaster()) // ignore conditions if GM on
  519. {
  520. // Respect allowed class
  521. if (!(itemTemplate->AllowableClass & _player->getClassMask()) && itemTemplate->Bonding == BIND_WHEN_PICKED_UP)
  522. continue;
  523. // Only display items in vendor lists for the team the player is on
  524. if ((itemTemplate->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY && _player->GetTeam() == ALLIANCE) ||
  525. (itemTemplate->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY && _player->GetTeam() == HORDE))
  526. continue;
  527. // Items sold out are not displayed in list
  528. if (leftInStock == 0)
  529. continue;
  530. }
  531. ConditionList conditions = sConditionMgr->GetConditionsForNpcVendorEvent(vendor->GetEntry(), vendorItem->item);
  532. if (!sConditionMgr->IsObjectMeetToConditions(_player, vendor, conditions))
  533. {
  534. TC_LOG_DEBUG("condition", "SendListInventory: conditions not met for creature entry %u item %u", vendor->GetEntry(), vendorItem->item);
  535. continue;
  536. }
  537. int32 price = vendorItem->IsGoldRequired(itemTemplate) ? uint32(floor(itemTemplate->BuyPrice * discountMod)) : 0;
  538. if (int32 priceMod = _player->GetTotalAuraModifier(SPELL_AURA_MOD_VENDOR_ITEMS_PRICES))
  539. price -= CalculatePct(price, priceMod);
  540. itemsData << uint32(slot + 1); // client expects counting to start at 1
  541. itemsData << uint32(itemTemplate->MaxDurability);
  542. if (vendorItem->ExtendedCost)
  543. {
  544. enablers.push_back(0);
  545. itemsData << uint32(vendorItem->ExtendedCost);
  546. }
  547. else
  548. enablers.push_back(1);
  549. enablers.push_back(1); // item is unlocked
  550. itemsData << uint32(vendorItem->item);
  551. itemsData << uint32(vendorItem->Type); // 1 is items, 2 is currency
  552. itemsData << uint32(price);
  553. itemsData << uint32(itemTemplate->DisplayInfoID);
  554. // if (!unk "enabler") data << uint32(something);
  555. itemsData << int32(leftInStock);
  556. itemsData << uint32(itemTemplate->BuyCount);
  557. if (++count >= MAX_VENDOR_ITEMS)
  558. break;
  559. }
  560. else if (vendorItem->Type == ITEM_VENDOR_TYPE_CURRENCY)
  561. {
  562. CurrencyTypesEntry const* currencyTemplate = sCurrencyTypesStore.LookupEntry(vendorItem->item);
  563. if (!currencyTemplate)
  564. continue;
  565. if (!vendorItem->ExtendedCost)
  566. continue; // there's no price defined for currencies, only extendedcost is used
  567. itemsData << uint32(slot + 1); // client expects counting to start at 1
  568. itemsData << uint32(0); // max durability
  569. enablers.push_back(0);
  570. itemsData << uint32(vendorItem->ExtendedCost);
  571. enablers.push_back(1); // item is unlocked
  572. itemsData << uint32(vendorItem->item);
  573. itemsData << uint32(vendorItem->Type); // 1 is items, 2 is currency
  574. itemsData << uint32(0); // price, only seen currency types that have Extended cost
  575. itemsData << uint32(0); // displayId
  576. // if (!unk "enabler") data << uint32(something);
  577. itemsData << int32(-1);
  578. itemsData << uint32(vendorItem->maxcount);
  579. if (++count >= MAX_VENDOR_ITEMS)
  580. break;
  581. }
  582. // else error
  583. }
  584. ObjectGuid guid = vendorGuid;
  585. WorldPacket data(SMSG_LIST_INVENTORY, 12 + itemsData.size());
  586. data.WriteBit(guid[1]);
  587. data.WriteBit(guid[0]);
  588. data.WriteBits(count, 21); // item count
  589. data.WriteBit(guid[3]);
  590. data.WriteBit(guid[6]);
  591. data.WriteBit(guid[5]);
  592. data.WriteBit(guid[2]);
  593. data.WriteBit(guid[7]);
  594. for (std::vector<bool>::const_iterator itr = enablers.begin(); itr != enablers.end(); ++itr)
  595. data.WriteBit(*itr);
  596. data.WriteBit(guid[4]);
  597. data.FlushBits();
  598. data.append(itemsData);
  599. data.WriteByteSeq(guid[5]);
  600. data.WriteByteSeq(guid[4]);
  601. data.WriteByteSeq(guid[1]);
  602. data.WriteByteSeq(guid[0]);
  603. data.WriteByteSeq(guid[6]);
  604. // It doesn't matter what value is used here (PROBABLY its full vendor size)
  605. // What matters is that if count of items we can see is 0 and this field is 1
  606. // then client will open the vendor list, otherwise it won't
  607. if (rawItemCount)
  608. data << uint8(rawItemCount);
  609. else
  610. data << uint8(vendor->IsArmorer());
  611. data.WriteByteSeq(guid[2]);
  612. data.WriteByteSeq(guid[3]);
  613. data.WriteByteSeq(guid[7]);
  614. SendPacket(&data);
  615. }
  616. void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket& recvData)
  617. {
  618. //TC_LOG_DEBUG("network", "WORLD: CMSG_AUTOSTORE_BAG_ITEM");
  619. uint8 srcbag, srcslot, dstbag;
  620. recvData >> srcbag >> srcslot >> dstbag;
  621. //TC_LOG_DEBUG("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u", srcbag, srcslot, dstbag);
  622. Item* pItem = _player->GetItemByPos(srcbag, srcslot);
  623. if (!pItem)
  624. return;
  625. if (!_player->IsValidPos(dstbag, NULL_SLOT, false)) // can be autostore pos
  626. {
  627. _player->SendEquipError(EQUIP_ERR_WRONG_SLOT, NULL, NULL);
  628. return;
  629. }
  630. uint16 src = pItem->GetPos();
  631. // check unequip potability for equipped items and bank bags
  632. if (_player->IsEquipmentPos (src) || _player->IsBagPos (src))
  633. {
  634. InventoryResult msg = _player->CanUnequipItem(src, !_player->IsBagPos (src));
  635. if (msg != EQUIP_ERR_OK)
  636. {
  637. _player->SendEquipError(msg, pItem, NULL);
  638. return;
  639. }
  640. }
  641. ItemPosCountVec dest;
  642. InventoryResult msg = _player->CanStoreItem(dstbag, NULL_SLOT, dest, pItem, false);
  643. if (msg != EQUIP_ERR_OK)
  644. {
  645. _player->SendEquipError(msg, pItem, NULL);
  646. return;
  647. }
  648. // no-op: placed in same slot
  649. if (dest.size() == 1 && dest[0].pos == src)
  650. {
  651. // just remove grey item state
  652. _player->SendEquipError(EQUIP_ERR_INTERNAL_BAG_ERROR, pItem, NULL);
  653. return;
  654. }
  655. _player->RemoveItem(srcbag, srcslot, true);
  656. _player->StoreItem(dest, pItem, true);
  657. }
  658. void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& recvPacket)
  659. {
  660. TC_LOG_DEBUG("network", "WORLD: CMSG_BUY_BANK_SLOT");
  661. ObjectGuid guid;
  662. recvPacket >> guid;
  663. WorldPacket data(SMSG_BUY_BANK_SLOT_RESULT, 4);
  664. if (!CanUseBank(guid))
  665. {
  666. data << uint32(ERR_BANKSLOT_NOTBANKER);
  667. SendPacket(&data);
  668. TC_LOG_DEBUG("network", "WORLD: HandleBuyBankSlotOpcode - %s not found or you can't interact with him.", guid.ToString().c_str());
  669. return;
  670. }
  671. uint32 slot = _player->GetBankBagSlotCount();
  672. // next slot
  673. ++slot;
  674. TC_LOG_INFO("network", "PLAYER: Buy bank bag slot, slot number = %u", slot);
  675. BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot);
  676. if (!slotEntry)
  677. {
  678. data << uint32(ERR_BANKSLOT_FAILED_TOO_MANY);
  679. SendPacket(&data);
  680. return;
  681. }
  682. uint32 price = slotEntry->price;
  683. if (!_player->HasEnoughMoney(uint64(price)))
  684. {
  685. data << uint32(ERR_BANKSLOT_INSUFFICIENT_FUNDS);
  686. SendPacket(&data);
  687. return;
  688. }
  689. _player->SetBankBagSlotCount(slot);
  690. _player->ModifyMoney(-int64(price));
  691. data << uint32(ERR_BANKSLOT_OK);
  692. SendPacket(&data);
  693. _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT);
  694. }
  695. void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket)
  696. {
  697. TC_LOG_DEBUG("network", "WORLD: CMSG_AUTOBANK_ITEM");
  698. uint8 srcbag, srcslot;
  699. recvPacket >> srcbag >> srcslot;
  700. TC_LOG_DEBUG("network", "STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
  701. if (!CanUseBank())
  702. {
  703. TC_LOG_DEBUG("network", "WORLD: HandleAutoBankItemOpcode - Unit (%s) not found or you can't interact with him.", m_currentBankerGUID.ToString().c_str());
  704. return;
  705. }
  706. Item* pItem = _player->GetItemByPos(srcbag, srcslot);
  707. if (!pItem)
  708. return;
  709. ItemPosCountVec dest;
  710. InventoryResult msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
  711. if (msg != EQUIP_ERR_OK)
  712. {
  713. _player->SendEquipError(msg, pItem, NULL);
  714. return;
  715. }
  716. if (dest.size() == 1 && dest[0].pos == pItem->GetPos())
  717. {
  718. _player->SendEquipError(EQUIP_ERR_CANT_SWAP, pItem, NULL);
  719. return;
  720. }
  721. _player->RemoveItem(srcbag, srcslot, true);
  722. _player->ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
  723. _player->BankItem(dest, pItem, true);
  724. }
  725. void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
  726. {
  727. TC_LOG_DEBUG("network", "WORLD: CMSG_AUTOSTORE_BANK_ITEM");
  728. uint8 srcbag, srcslot;
  729. recvPacket >> srcbag >> srcslot;
  730. TC_LOG_DEBUG("network", "STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
  731. if (!CanUseBank())
  732. {
  733. TC_LOG_DEBUG("network", "WORLD: HandleAutoStoreBankItemOpcode - Unit (%s) not found or you can't interact with him.", m_currentBankerGUID.ToString().c_str());
  734. return;
  735. }
  736. Item* pItem = _player->GetItemByPos(srcbag, srcslot);
  737. if (!pItem)
  738. return;
  739. if (_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory
  740. {
  741. ItemPosCountVec dest;
  742. InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
  743. if (msg != EQUIP_ERR_OK)
  744. {
  745. _player->SendEquipError(msg, pItem, NULL);
  746. return;
  747. }
  748. _player->RemoveItem(srcbag, srcslot, true);
  749. if (Item const* storedItem = _player->StoreItem(dest, pItem, true))
  750. _player->ItemAddedQuestCheck(storedItem->GetEntry(), storedItem->GetCount());
  751. }
  752. else // moving from inventory to bank
  753. {
  754. ItemPosCountVec dest;
  755. InventoryResult msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
  756. if (msg != EQUIP_ERR_OK)
  757. {
  758. _player->SendEquipError(msg, pItem, NULL);
  759. return;
  760. }
  761. _player->RemoveItem(srcbag, srcslot, true);
  762. _player->BankItem(dest, pItem, true);
  763. }
  764. }
  765. void WorldSession::SendEnchantmentLog(ObjectGuid target, ObjectGuid caster, uint32 itemId, uint32 enchantId)
  766. {
  767. WorldPacket data(SMSG_ENCHANTMENTLOG, (8+8+4+4));
  768. data << target.WriteAsPacked();
  769. data << caster.WriteAsPacked();
  770. data << uint32(itemId);
  771. data << uint32(enchantId);
  772. GetPlayer()->SendMessageToSet(&data, true);
  773. }
  774. void WorldSession::SendItemEnchantTimeUpdate(ObjectGuid Playerguid, ObjectGuid Itemguid, uint32 slot, uint32 Duration)
  775. {
  776. // last check 2.0.10
  777. WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8+4+4+8));
  778. data << uint64(Itemguid);
  779. data << uint32(slot);
  780. data << uint32(Duration);
  781. data << uint64(Playerguid);
  782. SendPacket(&data);
  783. }
  784. void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData)
  785. {
  786. TC_LOG_DEBUG("network", "Received opcode CMSG_WRAP_ITEM");
  787. uint8 gift_bag, gift_slot, item_bag, item_slot;
  788. recvData >> gift_bag >> gift_slot; // paper
  789. recvData >> item_bag >> item_slot; // item
  790. TC_LOG_DEBUG("network", "WRAP: receive gift_bag = %u, gift_slot = %u, item_bag = %u, item_slot = %u", gift_bag, gift_slot, item_bag, item_slot);
  791. Item* gift = _player->GetItemByPos(gift_bag, gift_slot);
  792. if (!gift)
  793. {
  794. _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL);
  795. return;
  796. }
  797. if (!(gift->GetTemplate()->Flags & ITEM_PROTO_FLAG_WRAPPER)) // cheating: non-wrapper wrapper
  798. {
  799. _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL);
  800. return;
  801. }
  802. Item* item = _player->GetItemByPos(item_bag, item_slot);
  803. if (!item)
  804. {
  805. _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, item, NULL);
  806. return;
  807. }
  808. if (item == gift) // not possable with pacjket from real client
  809. {
  810. _player->SendEquipError(EQUIP_ERR_CANT_WRAP_WRAPPED, item, NULL);
  811. return;
  812. }
  813. if (item->IsEquipped())
  814. {
  815. _player->SendEquipError(EQUIP_ERR_CANT_WRAP_EQUIPPED, item, NULL);
  816. return;
  817. }
  818. if (!item->GetGuidValue(ITEM_FIELD_GIFTCREATOR).IsEmpty()) // HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED);
  819. {
  820. _player->SendEquipError(EQUIP_ERR_CANT_WRAP_WRAPPED, item, NULL);
  821. return;
  822. }
  823. if (item->IsBag())
  824. {
  825. _player->SendEquipError(EQUIP_ERR_CANT_WRAP_BAGS, item, NULL);
  826. return;
  827. }
  828. if (item->IsSoulBound())
  829. {
  830. _player->SendEquipError(EQUIP_ERR_CANT_WRAP_BOUND, item, NULL);
  831. return;
  832. }
  833. if (item->GetMaxStackCount() != 1)
  834. {
  835. _player->SendEquipError(EQUIP_ERR_CANT_WRAP_STACKABLE, item, NULL);
  836. return;
  837. }
  838. // maybe not correct check (it is better than nothing)
  839. if (item->GetTemplate()->MaxCount > 0)
  840. {
  841. _player->SendEquipError(EQUIP_ERR_CANT_WRAP_UNIQUE, item, NULL);
  842. return;
  843. }
  844. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  845. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_GIFT);
  846. stmt->setUInt32(0, item->GetOwnerGUID().GetCounter());
  847. stmt->setUInt32(1, item->GetGUIDLow());
  848. stmt->setUInt32(2, item->GetEntry());
  849. stmt->setUInt32(3, item->GetUInt32Value(ITEM_FIELD_FLAGS));
  850. trans->Append(stmt);
  851. item->SetEntry(gift->GetEntry());
  852. switch (item->GetEntry())
  853. {
  854. case 5042: item->SetEntry(5043); break;
  855. case 5048: item->SetEntry(5044); break;
  856. case 17303: item->SetEntry(17302); break;
  857. case 17304: item->SetEntry(17305); break;
  858. case 17307: item->SetEntry(17308); break;
  859. case 21830: item->SetEntry(21831); break;
  860. }
  861. item->SetGuidValue(ITEM_FIELD_GIFTCREATOR, _player->GetGUID());
  862. item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED);
  863. item->SetState(ITEM_CHANGED, _player);
  864. if (item->GetState() == ITEM_NEW) // save new item, to have alway for `character_gifts` record in `item_instance`
  865. {
  866. // after save it will be impossible to remove the item from the queue
  867. item->RemoveFromUpdateQueueOf(_player);
  868. item->SaveToDB(trans); // item gave inventory record unchanged and can be save standalone
  869. }
  870. CharacterDatabase.CommitTransaction(trans);
  871. uint32 count = 1;
  872. _player->DestroyItemCount(gift, count, true);
  873. }
  874. void WorldSession::HandleSocketOpcode(WorldPacket& recvData)
  875. {
  876. TC_LOG_DEBUG("network", "WORLD: CMSG_SOCKET_GEMS");
  877. ObjectGuid item_guid;
  878. ObjectGuid gem_guids[MAX_GEM_SOCKETS];
  879. recvData >> item_guid;
  880. if (!item_guid)
  881. return;
  882. for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
  883. recvData >> gem_guids[i];
  884. //cheat -> tried to socket same gem multiple times
  885. if ((gem_guids[0] && (gem_guids[0] == gem_guids[1] || gem_guids[0] == gem_guids[2])) ||
  886. (gem_guids[1] && (gem_guids[1] == gem_guids[2])))
  887. return;
  888. Item* itemTarget = _player->GetItemByGuid(item_guid);
  889. if (!itemTarget) //missing item to socket
  890. return;
  891. ItemTemplate const* itemProto = itemTarget->GetTemplate();
  892. if (!itemProto)
  893. return;
  894. //this slot is excepted when applying / removing meta gem bonus
  895. uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : uint8(NULL_SLOT);
  896. Item* Gems[MAX_GEM_SOCKETS];
  897. for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
  898. Gems[i] = gem_guids[i] ? _player->GetItemByGuid(gem_guids[i]) : NULL;
  899. GemPropertiesEntry const* GemProps[MAX_GEM_SOCKETS];
  900. for (int i = 0; i < MAX_GEM_SOCKETS; ++i) //get geminfo from dbc storage
  901. GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetTemplate()->GemProperties) : NULL;
  902. // Find first prismatic socket
  903. int32 firstPrismatic = 0;
  904. while (firstPrismatic < MAX_GEM_SOCKETS && itemProto->Socket[firstPrismatic].Color)
  905. ++firstPrismatic;
  906. for (int i = 0; i < MAX_GEM_SOCKETS; ++i) //check for hack maybe
  907. {
  908. if (!GemProps[i])
  909. continue;
  910. // tried to put gem in socket where no socket exists (take care about prismatic sockets)
  911. if (!itemProto->Socket[i].Color)
  912. {
  913. // no prismatic socket
  914. if (!itemTarget->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT))
  915. return;
  916. if (i != firstPrismatic)
  917. return;
  918. }
  919. // tried to put normal gem in meta socket
  920. if (itemProto->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META)
  921. return;
  922. // tried to put meta gem in normal socket
  923. if (itemProto->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META)
  924. return;
  925. // tried to put normal gem in cogwheel socket
  926. if (itemProto->Socket[i].Color == SOCKET_COLOR_COGWHEEL && GemProps[i]->color != SOCKET_COLOR_COGWHEEL)
  927. return;
  928. // tried to put cogwheel gem in normal socket
  929. if (itemProto->Socket[i].Color != SOCKET_COLOR_COGWHEEL && GemProps[i]->color == SOCKET_COLOR_COGWHEEL)
  930. return;
  931. }
  932. uint32 GemEnchants[MAX_GEM_SOCKETS];
  933. uint32 OldEnchants[MAX_GEM_SOCKETS];
  934. for (int i = 0; i < MAX_GEM_SOCKETS; ++i) //get new and old enchantments
  935. {
  936. GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0;
  937. OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i));
  938. }
  939. // check unique-equipped conditions
  940. for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
  941. {
  942. if (!Gems[i])
  943. continue;
  944. // continue check for case when attempt add 2 similar unique equipped gems in one item.
  945. ItemTemplate const* iGemProto = Gems[i]->GetTemplate();
  946. // unique item (for new and already placed bit removed enchantments
  947. if (iGemProto->Flags & ITEM_PROTO_FLAG_UNIQUE_EQUIPPED)
  948. {
  949. for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
  950. {
  951. if (i == j) // skip self
  952. continue;
  953. if (Gems[j])
  954. {
  955. if (iGemProto->ItemId == Gems[j]->GetEntry())
  956. {
  957. _player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
  958. return;
  959. }
  960. }
  961. else if (OldEnchants[j])
  962. {
  963. if (SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]))
  964. {
  965. if (iGemProto->ItemId == enchantEntry->GemID)
  966. {
  967. _player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
  968. return;
  969. }
  970. }
  971. }
  972. }
  973. }
  974. // unique limit type item
  975. int32 limit_newcount = 0;
  976. if (iGemProto->ItemLimitCategory)
  977. {
  978. if (ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(iGemProto->ItemLimitCategory))
  979. {
  980. // NOTE: limitEntry->mode is not checked because if item has limit then it is applied in equip case
  981. for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
  982. {
  983. if (Gems[j])
  984. {
  985. // new gem
  986. if (iGemProto->ItemLimitCategory == Gems[j]->GetTemplate()->ItemLimitCategory)
  987. ++limit_newcount;
  988. }
  989. else if (OldEnchants[j])
  990. {
  991. // existing gem
  992. if (SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]))
  993. if (ItemTemplate const* jProto = sObjectMgr->GetItemTemplate(enchantEntry->GemID))
  994. if (iGemProto->ItemLimitCategory == jProto->ItemLimitCategory)
  995. ++limit_newcount;
  996. }
  997. }
  998. if (limit_newcount > 0 && uint32(limit_newcount) > limitEntry->maxCount)
  999. {
  1000. _player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
  1001. return;
  1002. }
  1003. }
  1004. }
  1005. // for equipped item check all equipment for duplicate equipped gems
  1006. if (itemTarget->IsEquipped())
  1007. {
  1008. if (InventoryResult res = _player->CanEquipUniqueItem(Gems[i], slot, std::max(limit_newcount, 0)))
  1009. {
  1010. _player->SendEquipError(res, itemTarget, NULL);
  1011. return;
  1012. }
  1013. }
  1014. }
  1015. bool SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus
  1016. _player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item)
  1017. //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
  1018. //remove ALL enchants
  1019. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; ++enchant_slot)
  1020. _player->ApplyEnchantment(itemTarget, EnchantmentSlot(enchant_slot), false);
  1021. for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
  1022. {
  1023. if (GemEnchants[i])
  1024. {
  1025. itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i], 0, 0, _player->GetGUID());
  1026. if (Item* guidItem = _player->GetItemByGuid(gem_guids[i]))
  1027. {
  1028. uint32 gemCount = 1;
  1029. _player->DestroyItemCount(guidItem, gemCount, true);
  1030. }
  1031. }
  1032. }
  1033. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot)
  1034. _player->ApplyEnchantment(itemTarget, EnchantmentSlot(enchant_slot), true);
  1035. bool SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state
  1036. if (SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change...
  1037. {
  1038. _player->ApplyEnchantment(itemTarget, BONUS_ENCHANTMENT_SLOT, false);
  1039. itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetTemplate()->socketBonus : 0), 0, 0, _player->GetGUID());
  1040. _player->ApplyEnchantment(itemTarget, BONUS_ENCHANTMENT_SLOT, true);
  1041. //it is not displayed, client has an inbuilt system to determine if the bonus is activated
  1042. }
  1043. _player->ToggleMetaGemsActive(slot, true); //turn on all metagems (except for target item)
  1044. _player->RemoveTradeableItem(itemTarget);
  1045. itemTarget->ClearSoulboundTradeable(_player); // clear tradeable flag
  1046. itemTarget->SendUpdateSockets();
  1047. }
  1048. void WorldSession::HandleCancelTempEnchantmentOpcode(WorldPacket& recvData)
  1049. {
  1050. TC_LOG_DEBUG("network", "WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT");
  1051. uint32 slot;
  1052. recvData >> slot;
  1053. // apply only to equipped item
  1054. if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, slot))
  1055. return;
  1056. Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
  1057. if (!item)
  1058. return;
  1059. if (!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
  1060. return;
  1061. GetPlayer()->ApplyEnchantment(item, TEMP_ENCHANTMENT_SLOT, false);
  1062. item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
  1063. }
  1064. void WorldSession::HandleItemRefundInfoRequest(WorldPacket& recvData)
  1065. {
  1066. TC_LOG_DEBUG("network", "WORLD: CMSG_ITEM_REFUND_INFO");
  1067. ObjectGuid guid;
  1068. recvData >> guid; // item guid
  1069. Item* item = _player->GetItemByGuid(guid);
  1070. if (!item)
  1071. {
  1072. TC_LOG_DEBUG("network", "Item refund: item not found!");
  1073. return;
  1074. }
  1075. GetPlayer()->SendRefundInfo(item);
  1076. }
  1077. void WorldSession::HandleItemRefund(WorldPacket &recvData)
  1078. {
  1079. TC_LOG_DEBUG("network", "WORLD: CMSG_ITEM_REFUND");
  1080. ObjectGuid guid;
  1081. recvData >> guid; // item guid
  1082. Item* item = _player->GetItemByGuid(guid);
  1083. if (!item)
  1084. {
  1085. TC_LOG_DEBUG("network", "Item refund: item not found!");
  1086. return;
  1087. }
  1088. // Don't try to refund item currently being disenchanted
  1089. if (_player->GetLootGUID() == guid)
  1090. return;
  1091. GetPlayer()->RefundItem(item);
  1092. }
  1093. /**
  1094. * Handles the packet sent by the client when requesting information about item text.
  1095. *
  1096. * This function is called when player clicks on item which has some flag set
  1097. */
  1098. void WorldSession::HandleItemTextQuery(WorldPacket& recvData )
  1099. {
  1100. ObjectGuid itemGuid;
  1101. recvData >> itemGuid;
  1102. TC_LOG_DEBUG("network", "CMSG_ITEM_TEXT_QUERY %s", itemGuid.ToString().c_str());
  1103. WorldPacket data(SMSG_ITEM_TEXT_QUERY_RESPONSE, 14); // guess size
  1104. if (Item* item = _player->GetItemByGuid(itemGuid))
  1105. {
  1106. data << uint8(0); // has text
  1107. data << uint64(itemGuid); // item guid
  1108. data << item->GetText();
  1109. }
  1110. else
  1111. {
  1112. data << uint8(1); // no text
  1113. }
  1114. SendPacket(&data);
  1115. }
  1116. void WorldSession::HandleTransmogrifyItems(WorldPacket& recvData)
  1117. {
  1118. TC_LOG_DEBUG("network", "WORLD: Received CMSG_TRANSMOGRIFY_ITEMS");
  1119. Player* player = GetPlayer();
  1120. // Read data
  1121. uint32 count = recvData.ReadBits(22);
  1122. if (count >= EQUIPMENT_SLOT_END)
  1123. {
  1124. TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) sent a wrong count (%u) when transmogrifying items.", player->GetGUIDLow(), player->GetName().c_str(), count);
  1125. recvData.rfinish();
  1126. return;
  1127. }
  1128. std::vector<ObjectGuid> itemGuids(count, ObjectGuid());
  1129. std::vector<uint32> newEntries(count, 0);
  1130. std::vector<uint32> slots(count, 0);
  1131. for (uint8 i = 0; i < count; ++i)
  1132. {
  1133. itemGuids[i][0] = recvData.ReadBit();
  1134. itemGuids[i][5] = recvData.ReadBit();
  1135. itemGuids[i][6] = recvData.ReadBit();
  1136. itemGuids[i][2] = recvData.ReadBit();
  1137. itemGuids[i][3] = recvData.ReadBit();
  1138. itemGuids[i][7] = recvData.ReadBit();
  1139. itemGuids[i][4] = recvData.ReadBit();
  1140. itemGuids[i][1] = recvData.ReadBit();
  1141. }
  1142. ObjectGuid npcGuid;
  1143. npcGuid[7] = recvData.ReadBit();
  1144. npcGuid[3] = recvData.ReadBit();
  1145. npcGuid[5] = recvData.ReadBit();
  1146. npcGuid[6] = recvData.ReadBit();
  1147. npcGuid[1] = recvData.ReadBit();
  1148. npcGuid[4] = recvData.ReadBit();
  1149. npcGuid[0] = recvData.ReadBit();
  1150. npcGuid[2] = recvData.ReadBit();
  1151. recvData.FlushBits();
  1152. for (uint32 i = 0; i < count; ++i)
  1153. {
  1154. recvData >> newEntries[i];
  1155. recvData.ReadByteSeq(itemGuids[i][1]);
  1156. recvData.ReadByteSeq(itemGuids[i][5]);
  1157. recvData.ReadByteSeq(itemGuids[i][0]);
  1158. recvData.ReadByteSeq(itemGuids[i][4]);
  1159. recvData.ReadByteSeq(itemGuids[i][6]);
  1160. recvData.ReadByteSeq(itemGuids[i][7]);
  1161. recvData.ReadByteSeq(itemGuids[i][3]);
  1162. recvData.ReadByteSeq(itemGuids[i][2]);
  1163. recvData >> slots[i];
  1164. }
  1165. recvData.ReadByteSeq(npcGuid[7]);
  1166. recvData.ReadByteSeq(npcGuid[2]);
  1167. recvData.ReadByteSeq(npcGuid[5]);
  1168. recvData.ReadByteSeq(npcGuid[4]);
  1169. recvData.ReadByteSeq(npcGuid[3]);
  1170. recvData.ReadByteSeq(npcGuid[1]);
  1171. recvData.ReadByteSeq(npcGuid[6]);
  1172. recvData.ReadByteSeq(npcGuid[0]);
  1173. // Validate
  1174. if (!player->GetNPCIfCanInteractWith(npcGuid, UNIT_NPC_FLAG_TRANSMOGRIFIER))
  1175. {
  1176. TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s not found or player can't interact with it.", npcGuid.ToString().c_str());
  1177. return;
  1178. }
  1179. int64 cost = 0;
  1180. std::vector<Item*> transmogrifier(count, NULL);
  1181. std::vector<Item*> transmogrified(count, NULL);
  1182. for (uint8 i = 0; i < count; ++i)
  1183. {
  1184. // slot of the transmogrified item
  1185. if (slots[i] >= EQUIPMENT_SLOT_END)
  1186. {
  1187. TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify %s with a wrong slot (%u) when transmogrifying items.", player->GetGUIDLow(), player->GetName().c_str(), itemGuids[i].ToString().c_str(), slots[i]);
  1188. return;
  1189. }
  1190. // transmogrified item
  1191. Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slots[i]);
  1192. if (!itemTransmogrified)
  1193. {
  1194. TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetGUIDLow(), player->GetName().c_str(), slots[i]);
  1195. return;
  1196. }
  1197. // if not resetting look
  1198. Item* itemTransmogrifier = NULL;
  1199. if (newEntries[i])
  1200. {
  1201. // entry of the transmogrifier item
  1202. ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newEntries[i]);
  1203. if (!proto)
  1204. {
  1205. TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify to an invalid item (entry: %u).", player->GetGUIDLow(), player->GetName().c_str(), newEntries[i]);
  1206. return;
  1207. }
  1208. // guid of the transmogrifier item
  1209. itemTransmogrifier = player->GetItemByGuid(itemGuids[i]);
  1210. if (!itemTransmogrifier)
  1211. {
  1212. TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify with an invalid item (%s).", player->GetGUIDLow(), player->GetName().c_str(), itemGuids[i].ToString().c_str());
  1213. return;
  1214. }
  1215. // entry of transmogrifier and from packet
  1216. if (itemTransmogrifier->GetEntry() != newEntries[i])
  1217. {
  1218. TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify with an invalid entry (entry: %u) for %s.", player->GetGUIDLow(), player->GetName().c_str(), newEntries[i], itemGuids[i].ToString().c_str());
  1219. return;
  1220. }
  1221. // validity of the transmogrification items
  1222. if (!Item::CanTransmogrifyItemWithItem(itemTransmogrified, itemTransmogrifier))
  1223. {
  1224. TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) failed CanTransmogrifyItemWithItem (%u with %u).", player->GetGUIDLow(), player->GetName().c_str(), itemTransmogrified->GetEntry(), itemTransmogrifier->GetEntry());
  1225. return;
  1226. }
  1227. // add cost
  1228. cost += itemTransmogrified->GetSpecialPrice();
  1229. }
  1230. transmogrifier[i] = itemTransmogrifier;
  1231. transmogrified[i] = itemTransmogrified;
  1232. }
  1233. if (cost) // 0 cost if reverting look
  1234. {
  1235. if (!player->HasEnoughMoney(cost))
  1236. return;
  1237. player->ModifyMoney(-cost);
  1238. }
  1239. // Everything is fine, proceed
  1240. for (uint8 i = 0; i < count; ++i)
  1241. {
  1242. if (transmogrifier[i])
  1243. {
  1244. // Transmogrify
  1245. transmogrified[i]->SetEnchantment(TRANSMOGRIFY_ENCHANTMENT_SLOT, newEntries[i], 0, 0);
  1246. player->SetVisibleItemSlot(slots[i], transmogrified[i]);
  1247. transmogrified[i]->UpdatePlayedTime(player);
  1248. transmogrified[i]->SetOwnerGUID(player->GetGUID());
  1249. transmogrified[i]->SetNotRefundable(player);
  1250. transmogrified[i]->ClearSoulboundTradeable(player);
  1251. if (transmogrifier[i]->GetTemplate()->Bonding == BIND_WHEN_EQUIPED || transmogrifier[i]->GetTemplate()->Bonding == BIND_WHEN_USE)
  1252. transmogrifier[i]->SetBinding(true);
  1253. transmogrifier[i]->SetOwnerGUID(player->GetGUID());
  1254. transmogrifier[i]->SetNotRefundable(player);
  1255. transmogrifier[i]->ClearSoulboundTradeable(player);
  1256. }
  1257. else
  1258. {
  1259. // Reset
  1260. transmogrified[i]->ClearEnchantment(TRANSMOGRIFY_ENCHANTMENT_SLOT);
  1261. player->SetVisibleItemSlot(slots[i], transmogrified[i]);
  1262. }
  1263. }
  1264. }
  1265. void WorldSession::SendReforgeResult(bool success)
  1266. {
  1267. WorldPacket data(SMSG_REFORGE_RESULT, 1);
  1268. data.WriteBit(success);
  1269. data.FlushBits();
  1270. SendPacket(&data);
  1271. }
  1272. void WorldSession::HandleReforgeItemOpcode(WorldPacket& recvData)
  1273. {
  1274. uint32 slot, reforgeEntry;
  1275. ObjectGuid guid;
  1276. uint32 bag;
  1277. Player* player = GetPlayer();
  1278. recvData >> reforgeEntry >> slot >> bag;
  1279. guid[2] = recvData.ReadBit();
  1280. guid[6] = recvData.ReadBit();
  1281. guid[3] = recvData.ReadBit();
  1282. guid[4] = recvData.ReadBit();
  1283. guid[1] = recvData.ReadBit();
  1284. guid[0] = recvData.ReadBit();
  1285. guid[7] = recvData.ReadBit();
  1286. guid[5] = recvData.ReadBit();
  1287. recvData.ReadByteSeq(guid[2]);
  1288. recvData.ReadByteSeq(guid[3]);
  1289. recvData.ReadByteSeq(guid[6]);
  1290. recvData.ReadByteSeq(guid[4]);
  1291. recvData.ReadByteSeq(guid[1]);
  1292. recvData.ReadByteSeq(guid[0]);
  1293. recvData.ReadByteSeq(guid[7]);
  1294. recvData.ReadByteSeq(guid[5]);
  1295. if (!player->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_REFORGER))
  1296. {
  1297. TC_LOG_DEBUG("network", "WORLD: HandleReforgeItemOpcode - %s not found or player can't interact with it.", guid.ToString().c_str());
  1298. SendReforgeResult(false);
  1299. return;
  1300. }
  1301. Item* item = player->GetItemByPos(bag, slot);
  1302. if (!item)
  1303. {
  1304. TC_LOG_DEBUG("network", "WORLD: HandleReforgeItemOpcode - Player (Guid: %u Name: %s) tried to reforge an invalid/non-existant item.", player->GetGUIDLow(), player->GetName().c_str());
  1305. SendReforgeResult(false);
  1306. return;
  1307. }
  1308. if (!reforgeEntry)
  1309. {
  1310. if (!item->GetEnchantmentId(REFORGE_ENCHANTMENT_SLOT))
  1311. {
  1312. TC_LOG_ERROR("network", "WORLD: HandleReforgeItemOpcode - Player (Guid: %u Name: %s) tried to remove reforge from non-reforged item (Entry: %u)", player->GetGUIDLow(), player->GetName().c_str(), item->GetEntry());
  1313. SendReforgeResult(false);
  1314. return;
  1315. }
  1316. // Reset the item
  1317. if (item->IsEquipped())
  1318. player->ApplyReforgeEnchantment(item, false);
  1319. item->ClearEnchantment(REFORGE_ENCHANTMENT_SLOT);
  1320. SendReforgeResult(true);
  1321. return;
  1322. }
  1323. if (item->GetEnchantmentId(REFORGE_ENCHANTMENT_SLOT))
  1324. {
  1325. TC_LOG_ERROR("network", "WORLD: HandleReforgeItemOpcode - Player (Guid: %u Name: %s) tried to reforge an already reforged item (Entry: %u)", player->GetGUIDLow(), player->GetName().c_str(), item->GetEntry());
  1326. SendReforgeResult(false);
  1327. return;
  1328. }
  1329. ItemReforgeEntry const* stats = sItemReforgeStore.LookupEntry(reforgeEntry);
  1330. if (!stats)
  1331. {
  1332. TC_LOG_DEBUG("network", "WORLD: HandleReforgeItemOpcode - Player (Guid: %u Name: %s) tried to reforge an item with invalid reforge entry (%u).", player->GetGUIDLow(), player->GetName().c_str(), reforgeEntry);
  1333. SendReforgeResult(false);
  1334. return;
  1335. }
  1336. if (!item->GetReforgableStat(ItemModType(stats->SourceStat)) || item->GetReforgableStat(ItemModType(stats->FinalStat))) // Cheating, you cant reforge to a stat that the item already has, nor reforge from a stat that the item does not have
  1337. {
  1338. SendReforgeResult(false);
  1339. return;
  1340. }
  1341. if (!player->HasEnoughMoney(uint64(item->GetSpecialPrice()))) // cheating
  1342. {
  1343. SendReforgeResult(false);
  1344. return;
  1345. }
  1346. player->ModifyMoney(-int64(item->GetSpecialPrice()));
  1347. item->SetEnchantment(REFORGE_ENCHANTMENT_SLOT, reforgeEntry, 0, 0);
  1348. SendReforgeResult(true);
  1349. if (item->IsEquipped())
  1350. player->ApplyReforgeEnchantment(item, true);
  1351. }
  1352. bool WorldSession::CanUseBank(ObjectGuid bankerGUID) const
  1353. {
  1354. // bankerGUID parameter is optional, set to 0 by default.
  1355. if (!bankerGUID)
  1356. bankerGUID = m_currentBankerGUID;
  1357. bool isUsingBankCommand = (bankerGUID == GetPlayer()->GetGUID() && bankerGUID == m_currentBankerGUID);
  1358. if (!isUsingBankCommand)
  1359. {
  1360. Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(bankerGUID, UNIT_NPC_FLAG_BANKER);
  1361. if (!creature)
  1362. return false;
  1363. }
  1364. return true;
  1365. }