Guest User

Untitled

a guest
Jan 19th, 2013
457
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 481.93 KB | None | 0 0
  1.  
  2. // Common operation need to add item from inventory without delete in trade, guild bank, mail....
  3. void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB)
  4. {
  5. // update quest counters
  6. ItemAddedQuestCheck(pItem->GetEntry(), pItem->GetCount());
  7. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, pItem->GetEntry(), pItem->GetCount());
  8.  
  9. // store item
  10. Item* pLastItem = StoreItem(dest, pItem, update);
  11.  
  12. // only set if not merged to existed stack (pItem can be deleted already but we can compare pointers any way)
  13. if (pLastItem == pItem)
  14. {
  15. // update owner for last item (this can be original item with wrong owner
  16. if (pLastItem->GetOwnerGUID() != GetGUID())
  17. pLastItem->SetOwnerGUID(GetGUID());
  18.  
  19. // if this original item then it need create record in inventory
  20. // in case trade we already have item in other player inventory
  21. pLastItem->SetState(in_characterInventoryDB ? ITEM_CHANGED : ITEM_NEW, this);
  22. }
  23.  
  24. if (pLastItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
  25. AddTradeableItem(pLastItem);
  26. }
  27.  
  28. void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
  29. {
  30. Item* pItem = GetItemByPos(bag, slot);
  31. if (pItem)
  32. {
  33. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: DestroyItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
  34. // Also remove all contained items if the item is a bag.
  35. // This if () prevents item saving crashes if the condition for a bag to be empty before being destroyed was bypassed somehow.
  36. if (pItem->IsNotEmptyBag())
  37. for (uint8 i = 0; i < MAX_BAG_SIZE; ++i)
  38. DestroyItem(slot, i, update);
  39.  
  40. if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
  41. {
  42. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
  43.  
  44. stmt->setUInt32(0, pItem->GetGUIDLow());
  45.  
  46. CharacterDatabase.Execute(stmt);
  47. }
  48.  
  49. RemoveEnchantmentDurations(pItem);
  50. RemoveItemDurations(pItem);
  51.  
  52. pItem->SetNotRefundable(this);
  53. pItem->ClearSoulboundTradeable(this);
  54. RemoveTradeableItem(pItem);
  55.  
  56. const ItemTemplate* proto = pItem->GetTemplate();
  57. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  58. if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger
  59. RemoveAurasDueToSpell(proto->Spells[i].SpellId);
  60.  
  61. ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
  62.  
  63. if (bag == INVENTORY_SLOT_BAG_0)
  64. {
  65. SetUInt64Value(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), 0);
  66.  
  67. // equipment and equipped bags can have applied bonuses
  68. if (slot < INVENTORY_SLOT_BAG_END)
  69. {
  70. ItemTemplate const* pProto = pItem->GetTemplate();
  71.  
  72. // item set bonuses applied only at equip and removed at unequip, and still active for broken items
  73. if (pProto && pProto->ItemSet)
  74. RemoveItemsSetItem(this, pProto);
  75.  
  76. _ApplyItemMods(pItem, slot, false);
  77. }
  78.  
  79. if (slot < EQUIPMENT_SLOT_END)
  80. {
  81. // remove item dependent auras and casts (only weapon and armor slots)
  82. RemoveItemDependentAurasAndCasts(pItem);
  83.  
  84. // update expertise and armor penetration - passive auras may need it
  85. switch (slot)
  86. {
  87. case EQUIPMENT_SLOT_MAINHAND:
  88. case EQUIPMENT_SLOT_OFFHAND:
  89. case EQUIPMENT_SLOT_RANGED:
  90. RecalculateRating(CR_ARMOR_PENETRATION);
  91. default:
  92. break;
  93. }
  94.  
  95. if (slot == EQUIPMENT_SLOT_MAINHAND)
  96. UpdateExpertise(BASE_ATTACK);
  97. else if (slot == EQUIPMENT_SLOT_OFFHAND)
  98. UpdateExpertise(OFF_ATTACK);
  99.  
  100. // equipment visual show
  101. SetVisibleItemSlot(slot, NULL);
  102. }
  103.  
  104. m_items[slot] = NULL;
  105. }
  106. else if (Bag* pBag = GetBagByPos(bag))
  107. pBag->RemoveItem(slot, update);
  108.  
  109. if (IsInWorld() && update)
  110. {
  111. pItem->RemoveFromWorld();
  112. pItem->DestroyForPlayer(this);
  113. }
  114.  
  115. //pItem->SetOwnerGUID(0);
  116. pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, 0);
  117. pItem->SetSlot(NULL_SLOT);
  118. pItem->SetState(ITEM_REMOVED, this);
  119. }
  120. }
  121.  
  122. void Player::DestroyItemCount(uint32 item, uint32 count, bool update, bool unequip_check)
  123. {
  124. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: DestroyItemCount item = %u, count = %u", item, count);
  125. uint32 remcount = 0;
  126.  
  127. // in inventory
  128. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  129. {
  130. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  131. {
  132. if (pItem->GetEntry() == item && !pItem->IsInTrade())
  133. {
  134. if (pItem->GetCount() + remcount <= count)
  135. {
  136. // all items in inventory can unequipped
  137. remcount += pItem->GetCount();
  138. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  139.  
  140. if (remcount >= count)
  141. return;
  142. }
  143. else
  144. {
  145. ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount);
  146. pItem->SetCount(pItem->GetCount() - count + remcount);
  147. if (IsInWorld() && update)
  148. pItem->SendUpdateToPlayer(this);
  149. pItem->SetState(ITEM_CHANGED, this);
  150. return;
  151. }
  152. }
  153. }
  154. }
  155.  
  156. for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
  157. {
  158. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  159. {
  160. if (pItem->GetEntry() == item && !pItem->IsInTrade())
  161. {
  162. if (pItem->GetCount() + remcount <= count)
  163. {
  164. // all keys can be unequipped
  165. remcount += pItem->GetCount();
  166. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  167.  
  168. if (remcount >= count)
  169. return;
  170. }
  171. else
  172. {
  173. ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount);
  174. pItem->SetCount(pItem->GetCount() - count + remcount);
  175. if (IsInWorld() && update)
  176. pItem->SendUpdateToPlayer(this);
  177. pItem->SetState(ITEM_CHANGED, this);
  178. return;
  179. }
  180. }
  181. }
  182. }
  183.  
  184. // in inventory bags
  185. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  186. {
  187. if (Bag* pBag = GetBagByPos(i))
  188. {
  189. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  190. {
  191. if (Item* pItem = pBag->GetItemByPos(j))
  192. {
  193. if (pItem->GetEntry() == item && !pItem->IsInTrade())
  194. {
  195. // all items in bags can be unequipped
  196. if (pItem->GetCount() + remcount <= count)
  197. {
  198. remcount += pItem->GetCount();
  199. DestroyItem(i, j, update);
  200.  
  201. if (remcount >= count)
  202. return;
  203. }
  204. else
  205. {
  206. ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount);
  207. pItem->SetCount(pItem->GetCount() - count + remcount);
  208. if (IsInWorld() && update)
  209. pItem->SendUpdateToPlayer(this);
  210. pItem->SetState(ITEM_CHANGED, this);
  211. return;
  212. }
  213. }
  214. }
  215. }
  216. }
  217. }
  218.  
  219. // in equipment and bag list
  220. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
  221. {
  222. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  223. {
  224. if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade())
  225. {
  226. if (pItem->GetCount() + remcount <= count)
  227. {
  228. if (!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false) == EQUIP_ERR_OK)
  229. {
  230. remcount += pItem->GetCount();
  231. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  232.  
  233. if (remcount >= count)
  234. return;
  235. }
  236. }
  237. else
  238. {
  239. ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount);
  240. pItem->SetCount(pItem->GetCount() - count + remcount);
  241. if (IsInWorld() && update)
  242. pItem->SendUpdateToPlayer(this);
  243. pItem->SetState(ITEM_CHANGED, this);
  244. return;
  245. }
  246. }
  247. }
  248. }
  249. }
  250.  
  251. void Player::DestroyZoneLimitedItem(bool update, uint32 new_zone)
  252. {
  253. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: DestroyZoneLimitedItem in map %u and area %u", GetMapId(), new_zone);
  254.  
  255. // in inventory
  256. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
  257. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  258. if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
  259. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  260.  
  261. for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
  262. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  263. if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
  264. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  265.  
  266. // in inventory bags
  267. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  268. if (Bag* pBag = GetBagByPos(i))
  269. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  270. if (Item* pItem = pBag->GetItemByPos(j))
  271. if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
  272. DestroyItem(i, j, update);
  273.  
  274. // in equipment and bag list
  275. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
  276. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  277. if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
  278. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  279. }
  280.  
  281. void Player::DestroyConjuredItems(bool update)
  282. {
  283. // used when entering arena
  284. // destroys all conjured items
  285. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: DestroyConjuredItems");
  286.  
  287. // in inventory
  288. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
  289. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  290. if (pItem->IsConjuredConsumable())
  291. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  292.  
  293. // in inventory bags
  294. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  295. if (Bag* pBag = GetBagByPos(i))
  296. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  297. if (Item* pItem = pBag->GetItemByPos(j))
  298. if (pItem->IsConjuredConsumable())
  299. DestroyItem(i, j, update);
  300.  
  301. // in equipment and bag list
  302. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
  303. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  304. if (pItem->IsConjuredConsumable())
  305. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  306. }
  307.  
  308. Item* Player::GetItemByEntry(uint32 entry) const
  309. {
  310. // in inventory
  311. for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  312. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  313. if (pItem->GetEntry() == entry)
  314. return pItem;
  315.  
  316. for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
  317. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  318. if (pItem->GetEntry() == entry)
  319. return pItem;
  320.  
  321. for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  322. if (Bag* pBag = GetBagByPos(i))
  323. for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
  324. if (Item* pItem = pBag->GetItemByPos(j))
  325. if (pItem->GetEntry() == entry)
  326. return pItem;
  327.  
  328. for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
  329. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  330. if (pItem->GetEntry() == entry)
  331. return pItem;
  332.  
  333. return NULL;
  334. }
  335.  
  336. void Player::DestroyItemCount(Item* pItem, uint32 &count, bool update)
  337. {
  338. if (!pItem)
  339. return;
  340.  
  341. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: DestroyItemCount item (GUID: %u, Entry: %u) count = %u", pItem->GetGUIDLow(), pItem->GetEntry(), count);
  342.  
  343. if (pItem->GetCount() <= count)
  344. {
  345. count -= pItem->GetCount();
  346.  
  347. DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), update);
  348. }
  349. else
  350. {
  351. ItemRemovedQuestCheck(pItem->GetEntry(), count);
  352. pItem->SetCount(pItem->GetCount() - count);
  353. count = 0;
  354. if (IsInWorld() && update)
  355. pItem->SendUpdateToPlayer(this);
  356. pItem->SetState(ITEM_CHANGED, this);
  357. }
  358. }
  359.  
  360. void Player::SplitItem(uint16 src, uint16 dst, uint32 count)
  361. {
  362. uint8 srcbag = src >> 8;
  363. uint8 srcslot = src & 255;
  364.  
  365. uint8 dstbag = dst >> 8;
  366. uint8 dstslot = dst & 255;
  367.  
  368. Item* pSrcItem = GetItemByPos(srcbag, srcslot);
  369. if (!pSrcItem)
  370. {
  371. SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL);
  372. return;
  373. }
  374.  
  375. if (pSrcItem->m_lootGenerated) // prevent split looting item (item
  376. {
  377. //best error message found for attempting to split while looting
  378. SendEquipError(EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL);
  379. return;
  380. }
  381.  
  382. // not let split all items (can be only at cheating)
  383. if (pSrcItem->GetCount() == count)
  384. {
  385. SendEquipError(EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL);
  386. return;
  387. }
  388.  
  389. // not let split more existed items (can be only at cheating)
  390. if (pSrcItem->GetCount() < count)
  391. {
  392. SendEquipError(EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT, pSrcItem, NULL);
  393. return;
  394. }
  395.  
  396. //! If trading
  397. if (TradeData* tradeData = GetTradeData())
  398. {
  399. //! If current item is in trade window (only possible with packet spoofing - silent return)
  400. if (tradeData->GetTradeSlotForItem(pSrcItem->GetGUID()) != TRADE_SLOT_INVALID)
  401. return;
  402. }
  403.  
  404. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count);
  405. Item* pNewItem = pSrcItem->CloneItem(count, this);
  406. if (!pNewItem)
  407. {
  408. SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL);
  409. return;
  410. }
  411.  
  412. if (IsInventoryPos(dst))
  413. {
  414. // change item amount before check (for unique max count check)
  415. pSrcItem->SetCount(pSrcItem->GetCount() - count);
  416.  
  417. ItemPosCountVec dest;
  418. InventoryResult msg = CanStoreItem(dstbag, dstslot, dest, pNewItem, false);
  419. if (msg != EQUIP_ERR_OK)
  420. {
  421. delete pNewItem;
  422. pSrcItem->SetCount(pSrcItem->GetCount() + count);
  423. SendEquipError(msg, pSrcItem, NULL);
  424. return;
  425. }
  426.  
  427. if (IsInWorld())
  428. pSrcItem->SendUpdateToPlayer(this);
  429. pSrcItem->SetState(ITEM_CHANGED, this);
  430. StoreItem(dest, pNewItem, true);
  431. }
  432. else if (IsBankPos(dst))
  433. {
  434. // change item amount before check (for unique max count check)
  435. pSrcItem->SetCount(pSrcItem->GetCount() - count);
  436.  
  437. ItemPosCountVec dest;
  438. InventoryResult msg = CanBankItem(dstbag, dstslot, dest, pNewItem, false);
  439. if (msg != EQUIP_ERR_OK)
  440. {
  441. delete pNewItem;
  442. pSrcItem->SetCount(pSrcItem->GetCount() + count);
  443. SendEquipError(msg, pSrcItem, NULL);
  444. return;
  445. }
  446.  
  447. if (IsInWorld())
  448. pSrcItem->SendUpdateToPlayer(this);
  449. pSrcItem->SetState(ITEM_CHANGED, this);
  450. BankItem(dest, pNewItem, true);
  451. }
  452. else if (IsEquipmentPos(dst))
  453. {
  454. // change item amount before check (for unique max count check), provide space for splitted items
  455. pSrcItem->SetCount(pSrcItem->GetCount() - count);
  456.  
  457. uint16 dest;
  458. InventoryResult msg = CanEquipItem(dstslot, dest, pNewItem, false);
  459. if (msg != EQUIP_ERR_OK)
  460. {
  461. delete pNewItem;
  462. pSrcItem->SetCount(pSrcItem->GetCount() + count);
  463. SendEquipError(msg, pSrcItem, NULL);
  464. return;
  465. }
  466.  
  467. if (IsInWorld())
  468. pSrcItem->SendUpdateToPlayer(this);
  469. pSrcItem->SetState(ITEM_CHANGED, this);
  470. EquipItem(dest, pNewItem, true);
  471. AutoUnequipOffhandIfNeed();
  472. }
  473. }
  474.  
  475. void Player::SwapItem(uint16 src, uint16 dst)
  476. {
  477. uint8 srcbag = src >> 8;
  478. uint8 srcslot = src & 255;
  479.  
  480. uint8 dstbag = dst >> 8;
  481. uint8 dstslot = dst & 255;
  482.  
  483. Item* pSrcItem = GetItemByPos(srcbag, srcslot);
  484. Item* pDstItem = GetItemByPos(dstbag, dstslot);
  485.  
  486. if (!pSrcItem)
  487. return;
  488.  
  489. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: SwapItem bag = %u, slot = %u, item = %u", dstbag, dstslot, pSrcItem->GetEntry());
  490.  
  491. if (!isAlive())
  492. {
  493. SendEquipError(EQUIP_ERR_YOU_ARE_DEAD, pSrcItem, pDstItem);
  494. return;
  495. }
  496.  
  497. // SRC checks
  498.  
  499. if (pSrcItem->m_lootGenerated) // prevent swap looting item
  500. {
  501. //best error message found for attempting to swap while looting
  502. SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL);
  503. return;
  504. }
  505.  
  506. // check unequip potability for equipped items and bank bags
  507. if (IsEquipmentPos(src) || IsBagPos(src))
  508. {
  509. // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later)
  510. InventoryResult msg = CanUnequipItem(src, !IsBagPos(src) || IsBagPos(dst) || (pDstItem && pDstItem->ToBag() && pDstItem->ToBag()->IsEmpty()));
  511. if (msg != EQUIP_ERR_OK)
  512. {
  513. SendEquipError(msg, pSrcItem, pDstItem);
  514. return;
  515. }
  516. }
  517.  
  518. // prevent put equipped/bank bag in self
  519. if (IsBagPos(src) && srcslot == dstbag)
  520. {
  521. SendEquipError(EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem);
  522. return;
  523. }
  524.  
  525. // prevent equipping bag in the same slot from its inside
  526. if (IsBagPos(dst) && srcbag == dstslot)
  527. {
  528. SendEquipError(EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pSrcItem, pDstItem);
  529. return;
  530. }
  531.  
  532. // DST checks
  533.  
  534. if (pDstItem)
  535. {
  536. if (pDstItem->m_lootGenerated) // prevent swap looting item
  537. {
  538. //best error message found for attempting to swap while looting
  539. SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, pDstItem, NULL);
  540. return;
  541. }
  542.  
  543. // check unequip potability for equipped items and bank bags
  544. if (IsEquipmentPos(dst) || IsBagPos(dst))
  545. {
  546. // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later)
  547. InventoryResult msg = CanUnequipItem(dst, !IsBagPos(dst) || IsBagPos(src) || (pSrcItem->ToBag() && pSrcItem->ToBag()->IsEmpty()));
  548. if (msg != EQUIP_ERR_OK)
  549. {
  550. SendEquipError(msg, pSrcItem, pDstItem);
  551. return;
  552. }
  553. }
  554. }
  555.  
  556. // NOW this is or item move (swap with empty), or swap with another item (including bags in bag possitions)
  557. // or swap empty bag with another empty or not empty bag (with items exchange)
  558.  
  559. // Move case
  560. if (!pDstItem)
  561. {
  562. if (IsInventoryPos(dst))
  563. {
  564. ItemPosCountVec dest;
  565. InventoryResult msg = CanStoreItem(dstbag, dstslot, dest, pSrcItem, false);
  566. if (msg != EQUIP_ERR_OK)
  567. {
  568. SendEquipError(msg, pSrcItem, NULL);
  569. return;
  570. }
  571.  
  572. RemoveItem(srcbag, srcslot, true);
  573. StoreItem(dest, pSrcItem, true);
  574. if (IsBankPos(src))
  575. ItemAddedQuestCheck(pSrcItem->GetEntry(), pSrcItem->GetCount());
  576. }
  577. else if (IsBankPos(dst))
  578. {
  579. ItemPosCountVec dest;
  580. InventoryResult msg = CanBankItem(dstbag, dstslot, dest, pSrcItem, false);
  581. if (msg != EQUIP_ERR_OK)
  582. {
  583. SendEquipError(msg, pSrcItem, NULL);
  584. return;
  585. }
  586.  
  587. RemoveItem(srcbag, srcslot, true);
  588. BankItem(dest, pSrcItem, true);
  589. ItemRemovedQuestCheck(pSrcItem->GetEntry(), pSrcItem->GetCount());
  590. }
  591. else if (IsEquipmentPos(dst))
  592. {
  593. uint16 dest;
  594. InventoryResult msg = CanEquipItem(dstslot, dest, pSrcItem, false);
  595. if (msg != EQUIP_ERR_OK)
  596. {
  597. SendEquipError(msg, pSrcItem, NULL);
  598. return;
  599. }
  600.  
  601. RemoveItem(srcbag, srcslot, true);
  602. EquipItem(dest, pSrcItem, true);
  603. AutoUnequipOffhandIfNeed();
  604. }
  605.  
  606. return;
  607. }
  608.  
  609. // attempt merge to / fill target item
  610. if (!pSrcItem->IsBag() && !pDstItem->IsBag())
  611. {
  612. InventoryResult msg;
  613. ItemPosCountVec sDest;
  614. uint16 eDest = 0;
  615. if (IsInventoryPos(dst))
  616. msg = CanStoreItem(dstbag, dstslot, sDest, pSrcItem, false);
  617. else if (IsBankPos(dst))
  618. msg = CanBankItem(dstbag, dstslot, sDest, pSrcItem, false);
  619. else if (IsEquipmentPos(dst))
  620. msg = CanEquipItem(dstslot, eDest, pSrcItem, false);
  621. else
  622. return;
  623.  
  624. // can be merge/fill
  625. if (msg == EQUIP_ERR_OK)
  626. {
  627. if (pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetTemplate()->GetMaxStackSize())
  628. {
  629. RemoveItem(srcbag, srcslot, true);
  630.  
  631. if (IsInventoryPos(dst))
  632. StoreItem(sDest, pSrcItem, true);
  633. else if (IsBankPos(dst))
  634. BankItem(sDest, pSrcItem, true);
  635. else if (IsEquipmentPos(dst))
  636. {
  637. EquipItem(eDest, pSrcItem, true);
  638. AutoUnequipOffhandIfNeed();
  639. }
  640. }
  641. else
  642. {
  643. pSrcItem->SetCount(pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetTemplate()->GetMaxStackSize());
  644. pDstItem->SetCount(pSrcItem->GetTemplate()->GetMaxStackSize());
  645. pSrcItem->SetState(ITEM_CHANGED, this);
  646. pDstItem->SetState(ITEM_CHANGED, this);
  647. if (IsInWorld())
  648. {
  649. pSrcItem->SendUpdateToPlayer(this);
  650. pDstItem->SendUpdateToPlayer(this);
  651. }
  652. }
  653. SendRefundInfo(pDstItem);
  654. return;
  655. }
  656. }
  657.  
  658. // impossible merge/fill, do real swap
  659. InventoryResult msg = EQUIP_ERR_OK;
  660.  
  661. // check src->dest move possibility
  662. ItemPosCountVec sDest;
  663. uint16 eDest = 0;
  664. if (IsInventoryPos(dst))
  665. msg = CanStoreItem(dstbag, dstslot, sDest, pSrcItem, true);
  666. else if (IsBankPos(dst))
  667. msg = CanBankItem(dstbag, dstslot, sDest, pSrcItem, true);
  668. else if (IsEquipmentPos(dst))
  669. {
  670. msg = CanEquipItem(dstslot, eDest, pSrcItem, true);
  671. if (msg == EQUIP_ERR_OK)
  672. msg = CanUnequipItem(eDest, true);
  673. }
  674.  
  675. if (msg != EQUIP_ERR_OK)
  676. {
  677. SendEquipError(msg, pSrcItem, pDstItem);
  678. return;
  679. }
  680.  
  681. // check dest->src move possibility
  682. ItemPosCountVec sDest2;
  683. uint16 eDest2 = 0;
  684. if (IsInventoryPos(src))
  685. msg = CanStoreItem(srcbag, srcslot, sDest2, pDstItem, true);
  686. else if (IsBankPos(src))
  687. msg = CanBankItem(srcbag, srcslot, sDest2, pDstItem, true);
  688. else if (IsEquipmentPos(src))
  689. {
  690. msg = CanEquipItem(srcslot, eDest2, pDstItem, true);
  691. if (msg == EQUIP_ERR_OK)
  692. msg = CanUnequipItem(eDest2, true);
  693. }
  694.  
  695. if (msg != EQUIP_ERR_OK)
  696. {
  697. SendEquipError(msg, pDstItem, pSrcItem);
  698. return;
  699. }
  700.  
  701. // Check bag swap with item exchange (one from empty in not bag possition (equipped (not possible in fact) or store)
  702. if (Bag* srcBag = pSrcItem->ToBag())
  703. {
  704. if (Bag* dstBag = pDstItem->ToBag())
  705. {
  706. Bag* emptyBag = NULL;
  707. Bag* fullBag = NULL;
  708. if (srcBag->IsEmpty() && !IsBagPos(src))
  709. {
  710. emptyBag = srcBag;
  711. fullBag = dstBag;
  712. }
  713. else if (dstBag->IsEmpty() && !IsBagPos(dst))
  714. {
  715. emptyBag = dstBag;
  716. fullBag = srcBag;
  717. }
  718.  
  719. // bag swap (with items exchange) case
  720. if (emptyBag && fullBag)
  721. {
  722. ItemTemplate const* emptyProto = emptyBag->GetTemplate();
  723.  
  724. uint32 count = 0;
  725.  
  726. for (uint32 i=0; i < fullBag->GetBagSize(); ++i)
  727. {
  728. Item* bagItem = fullBag->GetItemByPos(i);
  729. if (!bagItem)
  730. continue;
  731.  
  732. ItemTemplate const* bagItemProto = bagItem->GetTemplate();
  733. if (!bagItemProto || !ItemCanGoIntoBag(bagItemProto, emptyProto))
  734. {
  735. // one from items not go to empty target bag
  736. SendEquipError(EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem);
  737. return;
  738. }
  739.  
  740. ++count;
  741. }
  742.  
  743. if (count > emptyBag->GetBagSize())
  744. {
  745. // too small targeted bag
  746. SendEquipError(EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pSrcItem, pDstItem);
  747. return;
  748. }
  749.  
  750. // Items swap
  751. count = 0; // will pos in new bag
  752. for (uint32 i = 0; i< fullBag->GetBagSize(); ++i)
  753. {
  754. Item* bagItem = fullBag->GetItemByPos(i);
  755. if (!bagItem)
  756. continue;
  757.  
  758. fullBag->RemoveItem(i, true);
  759. emptyBag->StoreItem(count, bagItem, true);
  760. bagItem->SetState(ITEM_CHANGED, this);
  761.  
  762. ++count;
  763. }
  764. }
  765. }
  766. }
  767.  
  768. // now do moves, remove...
  769. RemoveItem(dstbag, dstslot, false);
  770. RemoveItem(srcbag, srcslot, false);
  771.  
  772. // add to dest
  773. if (IsInventoryPos(dst))
  774. StoreItem(sDest, pSrcItem, true);
  775. else if (IsBankPos(dst))
  776. BankItem(sDest, pSrcItem, true);
  777. else if (IsEquipmentPos(dst))
  778. EquipItem(eDest, pSrcItem, true);
  779.  
  780. // add to src
  781. if (IsInventoryPos(src))
  782. StoreItem(sDest2, pDstItem, true);
  783. else if (IsBankPos(src))
  784. BankItem(sDest2, pDstItem, true);
  785. else if (IsEquipmentPos(src))
  786. EquipItem(eDest2, pDstItem, true);
  787.  
  788. // if player is moving bags and is looting an item inside this bag
  789. // release the loot
  790. if (GetLootGUID())
  791. {
  792. bool released = false;
  793. if (IsBagPos(src))
  794. {
  795. Bag* bag = pSrcItem->ToBag();
  796. for (uint32 i = 0; i < bag->GetBagSize(); ++i)
  797. {
  798. if (Item* bagItem = bag->GetItemByPos(i))
  799. {
  800. if (bagItem->m_lootGenerated)
  801. {
  802. m_session->DoLootRelease(GetLootGUID());
  803. released = true; // so we don't need to look at dstBag
  804. break;
  805. }
  806. }
  807. }
  808. }
  809.  
  810. if (!released && IsBagPos(dst) && pDstItem)
  811. {
  812. Bag* bag = pDstItem->ToBag();
  813. for (uint32 i = 0; i < bag->GetBagSize(); ++i)
  814. {
  815. if (Item* bagItem = bag->GetItemByPos(i))
  816. {
  817. if (bagItem->m_lootGenerated)
  818. {
  819. m_session->DoLootRelease(GetLootGUID());
  820. released = true; // not realy needed here
  821. break;
  822. }
  823. }
  824. }
  825. }
  826. }
  827.  
  828. AutoUnequipOffhandIfNeed();
  829. }
  830.  
  831. void Player::AddItemToBuyBackSlot(Item* pItem)
  832. {
  833. if (pItem)
  834. {
  835. uint32 slot = m_currentBuybackSlot;
  836. // if current back slot non-empty search oldest or free
  837. if (m_items[slot])
  838. {
  839. uint32 oldest_time = GetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1);
  840. uint32 oldest_slot = BUYBACK_SLOT_START;
  841.  
  842. for (uint32 i = BUYBACK_SLOT_START+1; i < BUYBACK_SLOT_END; ++i)
  843. {
  844. // found empty
  845. if (!m_items[i])
  846. {
  847. slot = i;
  848. break;
  849. }
  850.  
  851. uint32 i_time = GetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + i - BUYBACK_SLOT_START);
  852.  
  853. if (oldest_time > i_time)
  854. {
  855. oldest_time = i_time;
  856. oldest_slot = i;
  857. }
  858. }
  859.  
  860. // find oldest
  861. slot = oldest_slot;
  862. }
  863.  
  864. RemoveItemFromBuyBackSlot(slot, true);
  865. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: AddItemToBuyBackSlot item = %u, slot = %u", pItem->GetEntry(), slot);
  866.  
  867. m_items[slot] = pItem;
  868. time_t base = time(NULL);
  869. uint32 etime = uint32(base - m_logintime + (30 * 3600));
  870. uint32 eslot = slot - BUYBACK_SLOT_START;
  871.  
  872. SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), pItem->GetGUID());
  873. if (ItemTemplate const* proto = pItem->GetTemplate())
  874. SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, proto->SellPrice * pItem->GetCount());
  875. else
  876. SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
  877. SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, (uint32)etime);
  878.  
  879. // move to next (for non filled list is move most optimized choice)
  880. if (m_currentBuybackSlot < BUYBACK_SLOT_END - 1)
  881. ++m_currentBuybackSlot;
  882. }
  883. }
  884.  
  885. Item* Player::GetItemFromBuyBackSlot(uint32 slot)
  886. {
  887. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: GetItemFromBuyBackSlot slot = %u", slot);
  888. if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END)
  889. return m_items[slot];
  890. return NULL;
  891. }
  892.  
  893. void Player::RemoveItemFromBuyBackSlot(uint32 slot, bool del)
  894. {
  895. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: RemoveItemFromBuyBackSlot slot = %u", slot);
  896. if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END)
  897. {
  898. Item* pItem = m_items[slot];
  899. if (pItem)
  900. {
  901. pItem->RemoveFromWorld();
  902. if (del)
  903. pItem->SetState(ITEM_REMOVED, this);
  904. }
  905.  
  906. m_items[slot] = NULL;
  907.  
  908. uint32 eslot = slot - BUYBACK_SLOT_START;
  909. SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), 0);
  910. SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
  911. SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0);
  912.  
  913. // if current backslot is filled set to now free slot
  914. if (m_items[m_currentBuybackSlot])
  915. m_currentBuybackSlot = slot;
  916. }
  917. }
  918.  
  919. void Player::SendEquipError(InventoryResult msg, Item* pItem, Item* pItem2, uint32 itemid)
  920. {
  921. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)", msg);
  922. WorldPacket data(SMSG_INVENTORY_CHANGE_FAILURE, (msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I ? 22 : 18));
  923. data << uint8(msg);
  924.  
  925. if (msg != EQUIP_ERR_OK)
  926. {
  927. data << uint64(pItem ? pItem->GetGUID() : 0);
  928. data << uint64(pItem2 ? pItem2->GetGUID() : 0);
  929. data << uint8(0); // bag type subclass, used with EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM and EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG2
  930.  
  931. switch (msg)
  932. {
  933. case EQUIP_ERR_CANT_EQUIP_LEVEL_I:
  934. case EQUIP_ERR_PURCHASE_LEVEL_TOO_LOW:
  935. {
  936. ItemTemplate const* proto = pItem ? pItem->GetTemplate() : sObjectMgr->GetItemTemplate(itemid);
  937. data << uint32(proto ? proto->RequiredLevel : 0);
  938. break;
  939. }
  940. case EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM: // no idea about this one...
  941. {
  942. data << uint64(0); // item guid
  943. data << uint32(0); // slot
  944. data << uint64(0); // container
  945. break;
  946. }
  947. case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED:
  948. case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED:
  949. case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED:
  950. {
  951. ItemTemplate const* proto = pItem ? pItem->GetTemplate() : sObjectMgr->GetItemTemplate(itemid);
  952. data << uint32(proto ? proto->ItemLimitCategory : 0);
  953. break;
  954. }
  955. default:
  956. break;
  957. }
  958. }
  959. GetSession()->SendPacket(&data);
  960. }
  961.  
  962. void Player::SendBuyError(BuyResult msg, Creature* creature, uint32 item, uint32 param)
  963. {
  964. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_BUY_FAILED");
  965. WorldPacket data(SMSG_BUY_FAILED, (8+4+4+1));
  966. data << uint64(creature ? creature->GetGUID() : 0);
  967. data << uint32(item);
  968. if (param > 0)
  969. data << uint32(param);
  970. data << uint8(msg);
  971. GetSession()->SendPacket(&data);
  972. }
  973.  
  974. void Player::SendSellError(SellResult msg, Creature* creature, uint64 guid, uint32 param)
  975. {
  976. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_SELL_ITEM");
  977. WorldPacket data(SMSG_SELL_ITEM, (8+8+(param?4:0)+1)); // last check 2.0.10
  978. data << uint64(creature ? creature->GetGUID() : 0);
  979. data << uint64(guid);
  980. if (param > 0)
  981. data << uint32(param);
  982. data << uint8(msg);
  983. GetSession()->SendPacket(&data);
  984. }
  985.  
  986. void Player::TradeCancel(bool sendback)
  987. {
  988. if (m_trade)
  989. {
  990. Player* trader = m_trade->GetTrader();
  991.  
  992. // send yellow "Trade canceled" message to both traders
  993. if (sendback)
  994. GetSession()->SendCancelTrade();
  995.  
  996. trader->GetSession()->SendCancelTrade();
  997.  
  998. // cleanup
  999. delete m_trade;
  1000. m_trade = NULL;
  1001. delete trader->m_trade;
  1002. trader->m_trade = NULL;
  1003. }
  1004. }
  1005.  
  1006. void Player::UpdateSoulboundTradeItems()
  1007. {
  1008. if (m_itemSoulboundTradeable.empty())
  1009. return;
  1010.  
  1011. // also checks for garbage data
  1012. for (ItemDurationList::iterator itr = m_itemSoulboundTradeable.begin(); itr != m_itemSoulboundTradeable.end();)
  1013. {
  1014. ASSERT(*itr);
  1015. if ((*itr)->GetOwnerGUID() != GetGUID())
  1016. {
  1017. m_itemSoulboundTradeable.erase(itr++);
  1018. continue;
  1019. }
  1020. if ((*itr)->CheckSoulboundTradeExpire())
  1021. {
  1022. m_itemSoulboundTradeable.erase(itr++);
  1023. continue;
  1024. }
  1025. ++itr;
  1026. }
  1027. }
  1028.  
  1029. void Player::AddTradeableItem(Item* item)
  1030. {
  1031. m_itemSoulboundTradeable.push_back(item);
  1032. }
  1033.  
  1034. //TODO: should never allow an item to be added to m_itemSoulboundTradeable twice
  1035. void Player::RemoveTradeableItem(Item* item)
  1036. {
  1037. m_itemSoulboundTradeable.remove(item);
  1038. }
  1039.  
  1040. void Player::UpdateItemDuration(uint32 time, bool realtimeonly)
  1041. {
  1042. if (m_itemDuration.empty())
  1043. return;
  1044.  
  1045. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "Player::UpdateItemDuration(%u, %u)", time, realtimeonly);
  1046.  
  1047. for (ItemDurationList::const_iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end();)
  1048. {
  1049. Item* item = *itr;
  1050. ++itr; // current element can be erased in UpdateDuration
  1051.  
  1052. if (!realtimeonly || item->GetTemplate()->FlagsCu & ITEM_FLAGS_CU_DURATION_REAL_TIME)
  1053. item->UpdateDuration(this, time);
  1054. }
  1055. }
  1056.  
  1057. void Player::UpdateEnchantTime(uint32 time)
  1058. {
  1059. for (EnchantDurationList::iterator itr = m_enchantDuration.begin(), next; itr != m_enchantDuration.end(); itr=next)
  1060. {
  1061. ASSERT(itr->item);
  1062. next = itr;
  1063. if (!itr->item->GetEnchantmentId(itr->slot))
  1064. {
  1065. next = m_enchantDuration.erase(itr);
  1066. }
  1067. else if (itr->leftduration <= time)
  1068. {
  1069. ApplyEnchantment(itr->item, itr->slot, false, false);
  1070. itr->item->ClearEnchantment(itr->slot);
  1071. next = m_enchantDuration.erase(itr);
  1072. }
  1073. else if (itr->leftduration > time)
  1074. {
  1075. itr->leftduration -= time;
  1076. ++next;
  1077. }
  1078. }
  1079. }
  1080.  
  1081. void Player::AddEnchantmentDurations(Item* item)
  1082. {
  1083. for (int x = 0; x < MAX_ENCHANTMENT_SLOT; ++x)
  1084. {
  1085. if (!item->GetEnchantmentId(EnchantmentSlot(x)))
  1086. continue;
  1087.  
  1088. uint32 duration = item->GetEnchantmentDuration(EnchantmentSlot(x));
  1089. if (duration > 0)
  1090. AddEnchantmentDuration(item, EnchantmentSlot(x), duration);
  1091. }
  1092. }
  1093.  
  1094. void Player::RemoveEnchantmentDurations(Item* item)
  1095. {
  1096. for (EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end();)
  1097. {
  1098. if (itr->item == item)
  1099. {
  1100. // save duration in item
  1101. item->SetEnchantmentDuration(EnchantmentSlot(itr->slot), itr->leftduration, this);
  1102. itr = m_enchantDuration.erase(itr);
  1103. }
  1104. else
  1105. ++itr;
  1106. }
  1107. }
  1108.  
  1109. void Player::RemoveArenaEnchantments(EnchantmentSlot slot)
  1110. {
  1111. // remove enchantments from equipped items first to clean up the m_enchantDuration list
  1112. for (EnchantDurationList::iterator itr = m_enchantDuration.begin(), next; itr != m_enchantDuration.end(); itr = next)
  1113. {
  1114. next = itr;
  1115. if (itr->slot == slot)
  1116. {
  1117. if (itr->item && itr->item->GetEnchantmentId(slot))
  1118. {
  1119. // Poisons and DK runes are enchants which are allowed on arenas
  1120. if (sSpellMgr->IsArenaAllowedEnchancment(itr->item->GetEnchantmentId(slot)))
  1121. {
  1122. ++next;
  1123. continue;
  1124. }
  1125. // remove from stats
  1126. ApplyEnchantment(itr->item, slot, false, false);
  1127. // remove visual
  1128. itr->item->ClearEnchantment(slot);
  1129. }
  1130. // remove from update list
  1131. next = m_enchantDuration.erase(itr);
  1132. }
  1133. else
  1134. ++next;
  1135. }
  1136.  
  1137. // remove enchants from inventory items
  1138. // NOTE: no need to remove these from stats, since these aren't equipped
  1139. // in inventory
  1140. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  1141. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  1142. if (pItem->GetEnchantmentId(slot))
  1143. pItem->ClearEnchantment(slot);
  1144.  
  1145. // in inventory bags
  1146. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  1147. if (Bag* pBag = GetBagByPos(i))
  1148. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  1149. if (Item* pItem = pBag->GetItemByPos(j))
  1150. if (pItem->GetEnchantmentId(slot))
  1151. pItem->ClearEnchantment(slot);
  1152. }
  1153.  
  1154. // duration == 0 will remove item enchant
  1155. void Player::AddEnchantmentDuration(Item* item, EnchantmentSlot slot, uint32 duration)
  1156. {
  1157. if (!item)
  1158. return;
  1159.  
  1160. if (slot >= MAX_ENCHANTMENT_SLOT)
  1161. return;
  1162.  
  1163. for (EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr)
  1164. {
  1165. if (itr->item == item && itr->slot == slot)
  1166. {
  1167. itr->item->SetEnchantmentDuration(itr->slot, itr->leftduration, this);
  1168. m_enchantDuration.erase(itr);
  1169. break;
  1170. }
  1171. }
  1172. if (item && duration > 0)
  1173. {
  1174. GetSession()->SendItemEnchantTimeUpdate(GetGUID(), item->GetGUID(), slot, uint32(duration/1000));
  1175. m_enchantDuration.push_back(EnchantDuration(item, slot, duration));
  1176. }
  1177. }
  1178.  
  1179. void Player::ApplyEnchantment(Item* item, bool apply)
  1180. {
  1181. for (uint32 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot)
  1182. ApplyEnchantment(item, EnchantmentSlot(slot), apply);
  1183. }
  1184.  
  1185. void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool apply_dur, bool ignore_condition)
  1186. {
  1187. if (!item || !item->IsEquipped())
  1188. return;
  1189.  
  1190. if (slot >= MAX_ENCHANTMENT_SLOT)
  1191. return;
  1192.  
  1193. uint32 enchant_id = item->GetEnchantmentId(slot);
  1194. if (!enchant_id)
  1195. return;
  1196.  
  1197. SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  1198. if (!pEnchant)
  1199. return;
  1200.  
  1201. if (!ignore_condition && pEnchant->EnchantmentCondition && !EnchantmentFitsRequirements(pEnchant->EnchantmentCondition, -1))
  1202. return;
  1203.  
  1204. if (pEnchant->requiredLevel > getLevel())
  1205. return;
  1206.  
  1207. if (pEnchant->requiredSkill > 0 && pEnchant->requiredSkillValue > GetSkillValue(pEnchant->requiredSkill))
  1208. return;
  1209.  
  1210. // If we're dealing with a gem inside a prismatic socket we need to check the prismatic socket requirements
  1211. // rather than the gem requirements itself. If the socket has no color it is a prismatic socket.
  1212. if ((slot == SOCK_ENCHANTMENT_SLOT || slot == SOCK_ENCHANTMENT_SLOT_2 || slot == SOCK_ENCHANTMENT_SLOT_3)
  1213. && !item->GetTemplate()->Socket[slot-SOCK_ENCHANTMENT_SLOT].Color)
  1214. {
  1215. // Check if the requirements for the prismatic socket are met before applying the gem stats
  1216. SpellItemEnchantmentEntry const* pPrismaticEnchant = sSpellItemEnchantmentStore.LookupEntry(item->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT));
  1217. if (!pPrismaticEnchant || (pPrismaticEnchant->requiredSkill > 0 && pPrismaticEnchant->requiredSkillValue > GetSkillValue(pPrismaticEnchant->requiredSkill)))
  1218. return;
  1219. }
  1220.  
  1221. if (!item->IsBroken())
  1222. {
  1223. for (int s = 0; s < MAX_ITEM_ENCHANTMENT_EFFECTS; ++s)
  1224. {
  1225. uint32 enchant_display_type = pEnchant->type[s];
  1226. uint32 enchant_amount = pEnchant->amount[s];
  1227. uint32 enchant_spell_id = pEnchant->spellid[s];
  1228.  
  1229. switch (enchant_display_type)
  1230. {
  1231. case ITEM_ENCHANTMENT_TYPE_NONE:
  1232. break;
  1233. case ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL:
  1234. // processed in Player::CastItemCombatSpell
  1235. break;
  1236. case ITEM_ENCHANTMENT_TYPE_DAMAGE:
  1237. if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
  1238. HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(enchant_amount), apply);
  1239. else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND)
  1240. HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(enchant_amount), apply);
  1241. else if (item->GetSlot() == EQUIPMENT_SLOT_RANGED)
  1242. HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
  1243. break;
  1244. case ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL:
  1245. if (enchant_spell_id)
  1246. {
  1247. if (apply)
  1248. {
  1249. int32 basepoints = 0;
  1250. // Random Property Exist - try found basepoints for spell (basepoints depends from item suffix factor)
  1251. if (item->GetItemRandomPropertyId())
  1252. {
  1253. ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
  1254. if (item_rand)
  1255. {
  1256. // Search enchant_amount
  1257. for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k)
  1258. {
  1259. if (item_rand->enchant_id[k] == enchant_id)
  1260. {
  1261. basepoints = int32((item_rand->prefix[k] * item->GetItemSuffixFactor()) / 10000);
  1262. break;
  1263. }
  1264. }
  1265. }
  1266. }
  1267. // Cast custom spell vs all equal basepoints got from enchant_amount
  1268. if (basepoints)
  1269. CastCustomSpell(this, enchant_spell_id, &basepoints, &basepoints, &basepoints, true, item);
  1270. else
  1271. CastSpell(this, enchant_spell_id, true, item);
  1272. }
  1273. else
  1274. RemoveAurasDueToItemSpell(item, enchant_spell_id);
  1275. }
  1276. break;
  1277. case ITEM_ENCHANTMENT_TYPE_RESISTANCE:
  1278. if (!enchant_amount)
  1279. {
  1280. ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
  1281. if (item_rand)
  1282. {
  1283. for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k)
  1284. {
  1285. if (item_rand->enchant_id[k] == enchant_id)
  1286. {
  1287. enchant_amount = uint32((item_rand->prefix[k] * item->GetItemSuffixFactor()) / 10000);
  1288. break;
  1289. }
  1290. }
  1291. }
  1292. }
  1293.  
  1294. HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply);
  1295. break;
  1296. case ITEM_ENCHANTMENT_TYPE_STAT:
  1297. {
  1298. if (!enchant_amount)
  1299. {
  1300. ItemRandomSuffixEntry const* item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
  1301. if (item_rand_suffix)
  1302. {
  1303. for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k)
  1304. {
  1305. if (item_rand_suffix->enchant_id[k] == enchant_id)
  1306. {
  1307. enchant_amount = uint32((item_rand_suffix->prefix[k] * item->GetItemSuffixFactor()) / 10000);
  1308. break;
  1309. }
  1310. }
  1311. }
  1312. }
  1313.  
  1314. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "Adding %u to stat nb %u", enchant_amount, enchant_spell_id);
  1315. switch (enchant_spell_id)
  1316. {
  1317. case ITEM_MOD_MANA:
  1318. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u MANA", enchant_amount);
  1319. HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(enchant_amount), apply);
  1320. break;
  1321. case ITEM_MOD_HEALTH:
  1322. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u HEALTH", enchant_amount);
  1323. HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(enchant_amount), apply);
  1324. break;
  1325. case ITEM_MOD_AGILITY:
  1326. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u AGILITY", enchant_amount);
  1327. HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply);
  1328. ApplyStatBuffMod(STAT_AGILITY, (float)enchant_amount, apply);
  1329. break;
  1330. case ITEM_MOD_STRENGTH:
  1331. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u STRENGTH", enchant_amount);
  1332. HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply);
  1333. ApplyStatBuffMod(STAT_STRENGTH, (float)enchant_amount, apply);
  1334. break;
  1335. case ITEM_MOD_INTELLECT:
  1336. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u INTELLECT", enchant_amount);
  1337. HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply);
  1338. ApplyStatBuffMod(STAT_INTELLECT, (float)enchant_amount, apply);
  1339. break;
  1340. case ITEM_MOD_SPIRIT:
  1341. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SPIRIT", enchant_amount);
  1342. HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply);
  1343. ApplyStatBuffMod(STAT_SPIRIT, (float)enchant_amount, apply);
  1344. break;
  1345. case ITEM_MOD_STAMINA:
  1346. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u STAMINA", enchant_amount);
  1347. HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply);
  1348. ApplyStatBuffMod(STAT_STAMINA, (float)enchant_amount, apply);
  1349. break;
  1350. case ITEM_MOD_DEFENSE_SKILL_RATING:
  1351. ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply);
  1352. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u DEFENCE", enchant_amount);
  1353. break;
  1354. case ITEM_MOD_DODGE_RATING:
  1355. ApplyRatingMod(CR_DODGE, enchant_amount, apply);
  1356. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u DODGE", enchant_amount);
  1357. break;
  1358. case ITEM_MOD_PARRY_RATING:
  1359. ApplyRatingMod(CR_PARRY, enchant_amount, apply);
  1360. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u PARRY", enchant_amount);
  1361. break;
  1362. case ITEM_MOD_BLOCK_RATING:
  1363. ApplyRatingMod(CR_BLOCK, enchant_amount, apply);
  1364. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SHIELD_BLOCK", enchant_amount);
  1365. break;
  1366. case ITEM_MOD_HIT_MELEE_RATING:
  1367. ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
  1368. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u MELEE_HIT", enchant_amount);
  1369. break;
  1370. case ITEM_MOD_HIT_RANGED_RATING:
  1371. ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
  1372. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u RANGED_HIT", enchant_amount);
  1373. break;
  1374. case ITEM_MOD_HIT_SPELL_RATING:
  1375. ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
  1376. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SPELL_HIT", enchant_amount);
  1377. break;
  1378. case ITEM_MOD_CRIT_MELEE_RATING:
  1379. ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
  1380. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u MELEE_CRIT", enchant_amount);
  1381. break;
  1382. case ITEM_MOD_CRIT_RANGED_RATING:
  1383. ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
  1384. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u RANGED_CRIT", enchant_amount);
  1385. break;
  1386. case ITEM_MOD_CRIT_SPELL_RATING:
  1387. ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
  1388. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SPELL_CRIT", enchant_amount);
  1389. break;
  1390. // Values from ITEM_STAT_MELEE_HA_RATING to ITEM_MOD_HASTE_RANGED_RATING are never used
  1391. // in Enchantments
  1392. // case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
  1393. // ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
  1394. // break;
  1395. // case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
  1396. // ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
  1397. // break;
  1398. // case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
  1399. // ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
  1400. // break;
  1401. // case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
  1402. // ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
  1403. // break;
  1404. // case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
  1405. // ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
  1406. // break;
  1407. // case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
  1408. // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
  1409. // break;
  1410. // case ITEM_MOD_HASTE_MELEE_RATING:
  1411. // ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
  1412. // break;
  1413. // case ITEM_MOD_HASTE_RANGED_RATING:
  1414. // ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
  1415. // break;
  1416. case ITEM_MOD_HASTE_SPELL_RATING:
  1417. ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
  1418. break;
  1419. case ITEM_MOD_HIT_RATING:
  1420. ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
  1421. ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
  1422. ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
  1423. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u HIT", enchant_amount);
  1424. break;
  1425. case ITEM_MOD_CRIT_RATING:
  1426. ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
  1427. ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
  1428. ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
  1429. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u CRITICAL", enchant_amount);
  1430. break;
  1431. // Values ITEM_MOD_HIT_TAKEN_RATING and ITEM_MOD_CRIT_TAKEN_RATING are never used in Enchantment
  1432. // case ITEM_MOD_HIT_TAKEN_RATING:
  1433. // ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
  1434. // ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
  1435. // ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
  1436. // break;
  1437. // case ITEM_MOD_CRIT_TAKEN_RATING:
  1438. // ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
  1439. // ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
  1440. // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
  1441. // break;
  1442. case ITEM_MOD_RESILIENCE_RATING:
  1443. ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
  1444. ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
  1445. ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
  1446. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u RESILIENCE", enchant_amount);
  1447. break;
  1448. case ITEM_MOD_HASTE_RATING:
  1449. ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
  1450. ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
  1451. ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
  1452. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u HASTE", enchant_amount);
  1453. break;
  1454. case ITEM_MOD_EXPERTISE_RATING:
  1455. ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply);
  1456. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u EXPERTISE", enchant_amount);
  1457. break;
  1458. case ITEM_MOD_ATTACK_POWER:
  1459. HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(enchant_amount), apply);
  1460. HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
  1461. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u ATTACK_POWER", enchant_amount);
  1462. break;
  1463. case ITEM_MOD_RANGED_ATTACK_POWER:
  1464. HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
  1465. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u RANGED_ATTACK_POWER", enchant_amount);
  1466. break;
  1467. // case ITEM_MOD_FERAL_ATTACK_POWER:
  1468. // ApplyFeralAPBonus(enchant_amount, apply);
  1469. // sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u FERAL_ATTACK_POWER", enchant_amount);
  1470. // break;
  1471. case ITEM_MOD_MANA_REGENERATION:
  1472. ApplyManaRegenBonus(enchant_amount, apply);
  1473. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u MANA_REGENERATION", enchant_amount);
  1474. break;
  1475. case ITEM_MOD_ARMOR_PENETRATION_RATING:
  1476. ApplyRatingMod(CR_ARMOR_PENETRATION, enchant_amount, apply);
  1477. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u ARMOR PENETRATION", enchant_amount);
  1478. break;
  1479. case ITEM_MOD_SPELL_POWER:
  1480. ApplySpellPowerBonus(enchant_amount, apply);
  1481. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SPELL_POWER", enchant_amount);
  1482. break;
  1483. case ITEM_MOD_HEALTH_REGEN:
  1484. ApplyHealthRegenBonus(enchant_amount, apply);
  1485. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u HEALTH_REGENERATION", enchant_amount);
  1486. break;
  1487. case ITEM_MOD_SPELL_PENETRATION:
  1488. ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, enchant_amount, apply);
  1489. m_spellPenetrationItemMod += apply ? int32(enchant_amount) : -int32(enchant_amount);
  1490. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SPELL_PENETRATION", enchant_amount);
  1491. break;
  1492. case ITEM_MOD_BLOCK_VALUE:
  1493. HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(enchant_amount), apply);
  1494. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u BLOCK_VALUE", enchant_amount);
  1495. break;
  1496. case ITEM_MOD_SPELL_HEALING_DONE: // deprecated
  1497. case ITEM_MOD_SPELL_DAMAGE_DONE: // deprecated
  1498. default:
  1499. break;
  1500. }
  1501. break;
  1502. }
  1503. case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon
  1504. {
  1505. if (getClass() == CLASS_SHAMAN)
  1506. {
  1507. float addValue = 0.0f;
  1508. if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
  1509. {
  1510. addValue = float(enchant_amount * item->GetTemplate()->Delay / 1000.0f);
  1511. HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, addValue, apply);
  1512. }
  1513. else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND)
  1514. {
  1515. addValue = float(enchant_amount * item->GetTemplate()->Delay / 1000.0f);
  1516. HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, addValue, apply);
  1517. }
  1518. }
  1519. break;
  1520. }
  1521. case ITEM_ENCHANTMENT_TYPE_USE_SPELL:
  1522. // processed in Player::CastItemUseSpell
  1523. break;
  1524. case ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET:
  1525. // nothing do..
  1526. break;
  1527. default:
  1528. sLog->outError(LOG_FILTER_PLAYER, "Unknown item enchantment (id = %d) display type: %d", enchant_id, enchant_display_type);
  1529. break;
  1530. } /*switch (enchant_display_type)*/
  1531. } /*for*/
  1532. }
  1533.  
  1534. // visualize enchantment at player and equipped items
  1535. if (slot == PERM_ENCHANTMENT_SLOT)
  1536. SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 0, apply ? item->GetEnchantmentId(slot) : 0);
  1537.  
  1538. if (slot == TEMP_ENCHANTMENT_SLOT)
  1539. SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 1, apply ? item->GetEnchantmentId(slot) : 0);
  1540.  
  1541. if (apply_dur)
  1542. {
  1543. if (apply)
  1544. {
  1545. // set duration
  1546. uint32 duration = item->GetEnchantmentDuration(slot);
  1547. if (duration > 0)
  1548. AddEnchantmentDuration(item, slot, duration);
  1549. }
  1550. else
  1551. {
  1552. // duration == 0 will remove EnchantDuration
  1553. AddEnchantmentDuration(item, slot, 0);
  1554. }
  1555. }
  1556. }
  1557.  
  1558. void Player::UpdateSkillEnchantments(uint16 skill_id, uint16 curr_value, uint16 new_value)
  1559. {
  1560. for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
  1561. {
  1562. if (m_items[i])
  1563. {
  1564. for (uint8 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot)
  1565. {
  1566. uint32 ench_id = m_items[i]->GetEnchantmentId(EnchantmentSlot(slot));
  1567. if (!ench_id)
  1568. continue;
  1569.  
  1570. SpellItemEnchantmentEntry const* Enchant = sSpellItemEnchantmentStore.LookupEntry(ench_id);
  1571. if (!Enchant)
  1572. return;
  1573.  
  1574. if (Enchant->requiredSkill == skill_id)
  1575. {
  1576. // Checks if the enchantment needs to be applied or removed
  1577. if (curr_value < Enchant->requiredSkillValue && new_value >= Enchant->requiredSkillValue)
  1578. ApplyEnchantment(m_items[i], EnchantmentSlot(slot), true);
  1579. else if (new_value < Enchant->requiredSkillValue && curr_value >= Enchant->requiredSkillValue)
  1580. ApplyEnchantment(m_items[i], EnchantmentSlot(slot), false);
  1581. }
  1582.  
  1583. // If we're dealing with a gem inside a prismatic socket we need to check the prismatic socket requirements
  1584. // rather than the gem requirements itself. If the socket has no color it is a prismatic socket.
  1585. if ((slot == SOCK_ENCHANTMENT_SLOT || slot == SOCK_ENCHANTMENT_SLOT_2 || slot == SOCK_ENCHANTMENT_SLOT_3)
  1586. && !m_items[i]->GetTemplate()->Socket[slot-SOCK_ENCHANTMENT_SLOT].Color)
  1587. {
  1588. SpellItemEnchantmentEntry const* pPrismaticEnchant = sSpellItemEnchantmentStore.LookupEntry(m_items[i]->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT));
  1589.  
  1590. if (pPrismaticEnchant && pPrismaticEnchant->requiredSkill == skill_id)
  1591. {
  1592. if (curr_value < pPrismaticEnchant->requiredSkillValue && new_value >= pPrismaticEnchant->requiredSkillValue)
  1593. ApplyEnchantment(m_items[i], EnchantmentSlot(slot), true);
  1594. else if (new_value < pPrismaticEnchant->requiredSkillValue && curr_value >= pPrismaticEnchant->requiredSkillValue)
  1595. ApplyEnchantment(m_items[i], EnchantmentSlot(slot), false);
  1596. }
  1597. }
  1598. }
  1599. }
  1600. }
  1601. }
  1602.  
  1603. void Player::SendEnchantmentDurations()
  1604. {
  1605. for (EnchantDurationList::const_iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr)
  1606. {
  1607. GetSession()->SendItemEnchantTimeUpdate(GetGUID(), itr->item->GetGUID(), itr->slot, uint32(itr->leftduration) / 1000);
  1608. }
  1609. }
  1610.  
  1611. void Player::SendItemDurations()
  1612. {
  1613. for (ItemDurationList::const_iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end(); ++itr)
  1614. {
  1615. (*itr)->SendTimeUpdate(this);
  1616. }
  1617. }
  1618.  
  1619. void Player::SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast)
  1620. {
  1621. if (!item) // prevent crash
  1622. return;
  1623.  
  1624. // last check 2.0.10
  1625. WorldPacket data(SMSG_ITEM_PUSH_RESULT, (8+4+4+4+1+4+4+4+4+4));
  1626. data << uint64(GetGUID()); // player GUID
  1627. data << uint32(received); // 0=looted, 1=from npc
  1628. data << uint32(created); // 0=received, 1=created
  1629. data << uint32(1); // bool print error to chat
  1630. data << uint8(item->GetBagSlot()); // bagslot
  1631. // item slot, but when added to stack: 0xFFFFFFFF
  1632. data << uint32((item->GetCount() == count) ? item->GetSlot() : -1);
  1633. data << uint32(item->GetEntry()); // item id
  1634. data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
  1635. data << int32(item->GetItemRandomPropertyId()); // random item property id
  1636. data << uint32(count); // count of items
  1637. data << uint32(GetItemCount(item->GetEntry())); // count of items in inventory
  1638.  
  1639. if (broadcast && GetGroup())
  1640. GetGroup()->BroadcastPacket(&data, true);
  1641. else
  1642. GetSession()->SendPacket(&data);
  1643. }
  1644.  
  1645. /*********************************************************/
  1646. /*** GOSSIP SYSTEM ***/
  1647. /*********************************************************/
  1648.  
  1649. void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool showQuests /*= false*/)
  1650. {
  1651. PlayerMenu* menu = PlayerTalkClass;
  1652. menu->ClearMenus();
  1653.  
  1654. menu->GetGossipMenu().SetMenuId(menuId);
  1655.  
  1656. GossipMenuItemsMapBounds menuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(menuId);
  1657.  
  1658. // if default menuId and no menu options exist for this, use options from default options
  1659. if (menuItemBounds.first == menuItemBounds.second && menuId == GetDefaultGossipMenuForSource(source))
  1660. menuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(0);
  1661.  
  1662. uint32 npcflags = 0;
  1663.  
  1664. if (source->GetTypeId() == TYPEID_UNIT)
  1665. {
  1666. npcflags = source->GetUInt32Value(UNIT_NPC_FLAGS);
  1667. if (npcflags & UNIT_NPC_FLAG_QUESTGIVER && showQuests)
  1668. PrepareQuestMenu(source->GetGUID());
  1669. }
  1670.  
  1671. if (source->GetTypeId() == TYPEID_GAMEOBJECT)
  1672. if (source->ToGameObject()->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
  1673. PrepareQuestMenu(source->GetGUID());
  1674.  
  1675. for (GossipMenuItemsContainer::const_iterator itr = menuItemBounds.first; itr != menuItemBounds.second; ++itr)
  1676. {
  1677. bool canTalk = true;
  1678. if (!sConditionMgr->IsObjectMeetToConditions(this, source, itr->second.Conditions))
  1679. continue;
  1680.  
  1681. if (Creature* creature = source->ToCreature())
  1682. {
  1683. if (!(itr->second.OptionNpcflag & npcflags))
  1684. continue;
  1685.  
  1686. switch (itr->second.OptionType)
  1687. {
  1688. case GOSSIP_OPTION_ARMORER:
  1689. canTalk = false; // added in special mode
  1690. break;
  1691. case GOSSIP_OPTION_SPIRITHEALER:
  1692. if (!isDead())
  1693. canTalk = false;
  1694. break;
  1695. case GOSSIP_OPTION_VENDOR:
  1696. {
  1697. VendorItemData const* vendorItems = creature->GetVendorItems();
  1698. if (!vendorItems || vendorItems->Empty())
  1699. {
  1700. sLog->outError(LOG_FILTER_SQL, "Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.", creature->GetGUIDLow(), creature->GetEntry());
  1701. canTalk = false;
  1702. }
  1703. break;
  1704. }
  1705. case GOSSIP_OPTION_TRAINER:
  1706. if (!creature->isCanTrainingOf(this, false))
  1707. canTalk = false;
  1708. break;
  1709. case GOSSIP_OPTION_LEARNDUALSPEC:
  1710. if (!(GetSpecsCount() == 1 && creature->isCanTrainingAndResetTalentsOf(this) && !(getLevel() < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL))))
  1711. canTalk = false;
  1712. break;
  1713. case GOSSIP_OPTION_UNLEARNTALENTS:
  1714. if (!creature->isCanTrainingAndResetTalentsOf(this))
  1715. canTalk = false;
  1716. break;
  1717. case GOSSIP_OPTION_UNLEARNPETTALENTS:
  1718. if (!GetPet() || GetPet()->getPetType() != HUNTER_PET || GetPet()->m_spells.size() <= 1 || creature->GetCreatureTemplate()->trainer_type != TRAINER_TYPE_PETS || creature->GetCreatureTemplate()->trainer_class != CLASS_HUNTER)
  1719. canTalk = false;
  1720. break;
  1721. case GOSSIP_OPTION_TAXIVENDOR:
  1722. if (GetSession()->SendLearnNewTaxiNode(creature))
  1723. return;
  1724. break;
  1725. case GOSSIP_OPTION_BATTLEFIELD:
  1726. if (!creature->isCanInteractWithBattleMaster(this, false))
  1727. canTalk = false;
  1728. break;
  1729. case GOSSIP_OPTION_STABLEPET:
  1730. if (getClass() != CLASS_HUNTER)
  1731. canTalk = false;
  1732. break;
  1733. case GOSSIP_OPTION_QUESTGIVER:
  1734. canTalk = false;
  1735. break;
  1736. case GOSSIP_OPTION_GOSSIP:
  1737. case GOSSIP_OPTION_SPIRITGUIDE:
  1738. case GOSSIP_OPTION_INNKEEPER:
  1739. case GOSSIP_OPTION_BANKER:
  1740. case GOSSIP_OPTION_PETITIONER:
  1741. case GOSSIP_OPTION_TABARDDESIGNER:
  1742. case GOSSIP_OPTION_AUCTIONEER:
  1743. break; // no checks
  1744. case GOSSIP_OPTION_OUTDOORPVP:
  1745. if (!sOutdoorPvPMgr->CanTalkTo(this, creature, itr->second))
  1746. canTalk = false;
  1747. break;
  1748. default:
  1749. sLog->outError(LOG_FILTER_SQL, "Creature entry %u have unknown gossip option %u for menu %u", creature->GetEntry(), itr->second.OptionType, itr->second.MenuId);
  1750. canTalk = false;
  1751. break;
  1752. }
  1753. }
  1754. else if (GameObject* go = source->ToGameObject())
  1755. {
  1756. switch (itr->second.OptionType)
  1757. {
  1758. case GOSSIP_OPTION_GOSSIP:
  1759. if (go->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER && go->GetGoType() != GAMEOBJECT_TYPE_GOOBER)
  1760. canTalk = false;
  1761. break;
  1762. default:
  1763. canTalk = false;
  1764. break;
  1765. }
  1766. }
  1767.  
  1768. if (canTalk)
  1769. {
  1770. std::string strOptionText = itr->second.OptionText;
  1771. std::string strBoxText = itr->second.BoxText;
  1772.  
  1773. int32 locale = GetSession()->GetSessionDbLocaleIndex();
  1774. if (locale >= 0)
  1775. {
  1776. uint32 idxEntry = MAKE_PAIR32(menuId, itr->second.OptionIndex);
  1777. if (GossipMenuItemsLocale const* no = sObjectMgr->GetGossipMenuItemsLocale(idxEntry))
  1778. {
  1779. ObjectMgr::GetLocaleString(no->OptionText, locale, strOptionText);
  1780. ObjectMgr::GetLocaleString(no->BoxText, locale, strBoxText);
  1781. }
  1782. }
  1783.  
  1784. menu->GetGossipMenu().AddMenuItem(itr->second.OptionIndex, itr->second.OptionIcon, strOptionText, 0, itr->second.OptionType, strBoxText, itr->second.BoxMoney, itr->second.BoxCoded);
  1785. menu->GetGossipMenu().AddGossipMenuItemData(itr->second.OptionIndex, itr->second.ActionMenuId, itr->second.ActionPoiId);
  1786. }
  1787. }
  1788. }
  1789.  
  1790. void Player::SendPreparedGossip(WorldObject* source)
  1791. {
  1792. if (!source)
  1793. return;
  1794.  
  1795. if (source->GetTypeId() == TYPEID_UNIT)
  1796. {
  1797. // in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
  1798. if (!source->ToCreature()->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP) && !PlayerTalkClass->GetQuestMenu().Empty())
  1799. {
  1800. SendPreparedQuest(source->GetGUID());
  1801. return;
  1802. }
  1803. }
  1804. else if (source->GetTypeId() == TYPEID_GAMEOBJECT)
  1805. {
  1806. // probably need to find a better way here
  1807. if (!PlayerTalkClass->GetGossipMenu().GetMenuId() && !PlayerTalkClass->GetQuestMenu().Empty())
  1808. {
  1809. SendPreparedQuest(source->GetGUID());
  1810. return;
  1811. }
  1812. }
  1813.  
  1814. // in case non empty gossip menu (that not included quests list size) show it
  1815. // (quest entries from quest menu will be included in list)
  1816.  
  1817. uint32 textId = GetGossipTextId(source);
  1818.  
  1819. if (uint32 menuId = PlayerTalkClass->GetGossipMenu().GetMenuId())
  1820. textId = GetGossipTextId(menuId, source);
  1821.  
  1822. PlayerTalkClass->SendGossipMenu(textId, source->GetGUID());
  1823. }
  1824.  
  1825. void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 menuId)
  1826. {
  1827. GossipMenu& gossipMenu = PlayerTalkClass->GetGossipMenu();
  1828.  
  1829. // if not same, then something funky is going on
  1830. if (menuId != gossipMenu.GetMenuId())
  1831. return;
  1832.  
  1833. GossipMenuItem const* item = gossipMenu.GetItem(gossipListId);
  1834. if (!item)
  1835. return;
  1836.  
  1837. uint32 gossipOptionId = item->OptionType;
  1838. uint64 guid = source->GetGUID();
  1839.  
  1840. if (source->GetTypeId() == TYPEID_GAMEOBJECT)
  1841. {
  1842. if (gossipOptionId > GOSSIP_OPTION_QUESTGIVER)
  1843. {
  1844. sLog->outError(LOG_FILTER_PLAYER, "Player guid %u request invalid gossip option for GameObject entry %u", GetGUIDLow(), source->GetEntry());
  1845. return;
  1846. }
  1847. }
  1848.  
  1849. GossipMenuItemData const* menuItemData = gossipMenu.GetItemData(gossipListId);
  1850. if (!menuItemData)
  1851. return;
  1852.  
  1853. int32 cost = int32(item->BoxMoney);
  1854. if (!HasEnoughMoney(cost))
  1855. {
  1856. SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
  1857. PlayerTalkClass->SendCloseGossip();
  1858. return;
  1859. }
  1860.  
  1861. switch (gossipOptionId)
  1862. {
  1863. case GOSSIP_OPTION_GOSSIP:
  1864. {
  1865. if (menuItemData->GossipActionPoi)
  1866. PlayerTalkClass->SendPointOfInterest(menuItemData->GossipActionPoi);
  1867.  
  1868. if (menuItemData->GossipActionMenuId)
  1869. {
  1870. PrepareGossipMenu(source, menuItemData->GossipActionMenuId);
  1871. SendPreparedGossip(source);
  1872. }
  1873.  
  1874. break;
  1875. }
  1876. case GOSSIP_OPTION_OUTDOORPVP:
  1877. sOutdoorPvPMgr->HandleGossipOption(this, source->GetGUID(), gossipListId);
  1878. break;
  1879. case GOSSIP_OPTION_SPIRITHEALER:
  1880. if (isDead())
  1881. source->ToCreature()->CastSpell(source->ToCreature(), 17251, true, NULL, NULL, GetGUID());
  1882. break;
  1883. case GOSSIP_OPTION_QUESTGIVER:
  1884. PrepareQuestMenu(guid);
  1885. SendPreparedQuest(guid);
  1886. break;
  1887. case GOSSIP_OPTION_VENDOR:
  1888. case GOSSIP_OPTION_ARMORER:
  1889. GetSession()->SendListInventory(guid);
  1890. break;
  1891. case GOSSIP_OPTION_STABLEPET:
  1892. GetSession()->SendStablePet(guid);
  1893. break;
  1894. case GOSSIP_OPTION_TRAINER:
  1895. GetSession()->SendTrainerList(guid);
  1896. break;
  1897. case GOSSIP_OPTION_LEARNDUALSPEC:
  1898. if (GetSpecsCount() == 1 && getLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL))
  1899. {
  1900. // Cast spells that teach dual spec
  1901. // Both are also ImplicitTarget self and must be cast by player
  1902. CastSpell(this, 63680, true, NULL, NULL, GetGUID());
  1903. CastSpell(this, 63624, true, NULL, NULL, GetGUID());
  1904.  
  1905. // Should show another Gossip text with "Congratulations..."
  1906. PlayerTalkClass->SendCloseGossip();
  1907. }
  1908. break;
  1909. case GOSSIP_OPTION_UNLEARNTALENTS:
  1910. PlayerTalkClass->SendCloseGossip();
  1911. SendTalentWipeConfirm(guid);
  1912. break;
  1913. case GOSSIP_OPTION_UNLEARNPETTALENTS:
  1914. PlayerTalkClass->SendCloseGossip();
  1915. ResetPetTalents();
  1916. break;
  1917. case GOSSIP_OPTION_TAXIVENDOR:
  1918. GetSession()->SendTaxiMenu(source->ToCreature());
  1919. break;
  1920. case GOSSIP_OPTION_INNKEEPER:
  1921. PlayerTalkClass->SendCloseGossip();
  1922. SetBindPoint(guid);
  1923. break;
  1924. case GOSSIP_OPTION_BANKER:
  1925. GetSession()->SendShowBank(guid);
  1926. break;
  1927. case GOSSIP_OPTION_PETITIONER:
  1928. PlayerTalkClass->SendCloseGossip();
  1929. GetSession()->SendPetitionShowList(guid);
  1930. break;
  1931. case GOSSIP_OPTION_TABARDDESIGNER:
  1932. PlayerTalkClass->SendCloseGossip();
  1933. GetSession()->SendTabardVendorActivate(guid);
  1934. break;
  1935. case GOSSIP_OPTION_AUCTIONEER:
  1936. GetSession()->SendAuctionHello(guid, source->ToCreature());
  1937. break;
  1938. case GOSSIP_OPTION_SPIRITGUIDE:
  1939. PrepareGossipMenu(source);
  1940. SendPreparedGossip(source);
  1941. break;
  1942. case GOSSIP_OPTION_BATTLEFIELD:
  1943. {
  1944. BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(source->GetEntry());
  1945.  
  1946. if (bgTypeId == BATTLEGROUND_TYPE_NONE)
  1947. {
  1948. sLog->outError(LOG_FILTER_PLAYER, "a user (guid %u) requested battlegroundlist from a npc who is no battlemaster", GetGUIDLow());
  1949. return;
  1950. }
  1951.  
  1952. GetSession()->SendBattleGroundList(guid, bgTypeId);
  1953. break;
  1954. }
  1955. }
  1956.  
  1957. ModifyMoney(-cost);
  1958. }
  1959.  
  1960. uint32 Player::GetGossipTextId(WorldObject* source)
  1961. {
  1962. if (!source)
  1963. return DEFAULT_GOSSIP_MESSAGE;
  1964.  
  1965. return GetGossipTextId(GetDefaultGossipMenuForSource(source), source);
  1966. }
  1967.  
  1968. uint32 Player::GetGossipTextId(uint32 menuId, WorldObject* source)
  1969. {
  1970. uint32 textId = DEFAULT_GOSSIP_MESSAGE;
  1971.  
  1972. if (!menuId)
  1973. return textId;
  1974.  
  1975. GossipMenusMapBounds menuBounds = sObjectMgr->GetGossipMenusMapBounds(menuId);
  1976.  
  1977. for (GossipMenusContainer::const_iterator itr = menuBounds.first; itr != menuBounds.second; ++itr)
  1978. {
  1979. if (sConditionMgr->IsObjectMeetToConditions(this, source, itr->second.conditions))
  1980. textId = itr->second.text_id;
  1981. }
  1982.  
  1983. return textId;
  1984. }
  1985.  
  1986. uint32 Player::GetDefaultGossipMenuForSource(WorldObject* source)
  1987. {
  1988. switch (source->GetTypeId())
  1989. {
  1990. case TYPEID_UNIT:
  1991. return source->ToCreature()->GetCreatureTemplate()->GossipMenuId;
  1992. case TYPEID_GAMEOBJECT:
  1993. return source->ToGameObject()->GetGOInfo()->GetGossipMenuId();
  1994. default:
  1995. break;
  1996. }
  1997.  
  1998. return 0;
  1999. }
  2000.  
  2001. /*********************************************************/
  2002. /*** QUEST SYSTEM ***/
  2003. /*********************************************************/
  2004.  
  2005. void Player::PrepareQuestMenu(uint64 guid)
  2006. {
  2007. QuestRelationBounds objectQR;
  2008. QuestRelationBounds objectQIR;
  2009.  
  2010. // pets also can have quests
  2011. Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid);
  2012. if (creature)
  2013. {
  2014. objectQR = sObjectMgr->GetCreatureQuestRelationBounds(creature->GetEntry());
  2015. objectQIR = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(creature->GetEntry());
  2016. }
  2017. else
  2018. {
  2019. //we should obtain map pointer from GetMap() in 99% of cases. Special case
  2020. //only for quests which cast teleport spells on player
  2021. Map* _map = IsInWorld() ? GetMap() : sMapMgr->FindMap(GetMapId(), GetInstanceId());
  2022. ASSERT(_map);
  2023. GameObject* pGameObject = _map->GetGameObject(guid);
  2024. if (pGameObject)
  2025. {
  2026. objectQR = sObjectMgr->GetGOQuestRelationBounds(pGameObject->GetEntry());
  2027. objectQIR = sObjectMgr->GetGOQuestInvolvedRelationBounds(pGameObject->GetEntry());
  2028. }
  2029. else
  2030. return;
  2031. }
  2032.  
  2033. QuestMenu &qm = PlayerTalkClass->GetQuestMenu();
  2034. qm.ClearMenu();
  2035.  
  2036. for (QuestRelations::const_iterator i = objectQIR.first; i != objectQIR.second; ++i)
  2037. {
  2038. uint32 quest_id = i->second;
  2039. QuestStatus status = GetQuestStatus(quest_id);
  2040. if (status == QUEST_STATUS_COMPLETE)
  2041. qm.AddMenuItem(quest_id, 4);
  2042. else if (status == QUEST_STATUS_INCOMPLETE)
  2043. qm.AddMenuItem(quest_id, 4);
  2044. //else if (status == QUEST_STATUS_AVAILABLE)
  2045. // qm.AddMenuItem(quest_id, 2);
  2046. }
  2047.  
  2048. for (QuestRelations::const_iterator i = objectQR.first; i != objectQR.second; ++i)
  2049. {
  2050. uint32 quest_id = i->second;
  2051. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  2052. if (!quest)
  2053. continue;
  2054.  
  2055. if (!CanTakeQuest(quest, false))
  2056. continue;
  2057.  
  2058. if (quest->IsAutoComplete())
  2059. qm.AddMenuItem(quest_id, 4);
  2060. else if (GetQuestStatus(quest_id) == QUEST_STATUS_NONE)
  2061. qm.AddMenuItem(quest_id, 2);
  2062. }
  2063. }
  2064.  
  2065. void Player::SendPreparedQuest(uint64 guid)
  2066. {
  2067. QuestMenu& questMenu = PlayerTalkClass->GetQuestMenu();
  2068. if (questMenu.Empty())
  2069. return;
  2070.  
  2071. QuestMenuItem const& qmi0 = questMenu.GetItem(0);
  2072.  
  2073. uint32 icon = qmi0.QuestIcon;
  2074.  
  2075. // single element case
  2076. if (questMenu.GetMenuItemCount() == 1)
  2077. {
  2078. // Auto open -- maybe also should verify there is no greeting
  2079. uint32 questId = qmi0.QuestId;
  2080. Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
  2081.  
  2082. if (quest)
  2083. {
  2084. if (icon == 4 && !GetQuestRewardStatus(questId))
  2085. PlayerTalkClass->SendQuestGiverRequestItems(quest, guid, CanRewardQuest(quest, false), true);
  2086. else if (icon == 4)
  2087. PlayerTalkClass->SendQuestGiverRequestItems(quest, guid, CanRewardQuest(quest, false), true);
  2088. // Send completable on repeatable and autoCompletable quest if player don't have quest
  2089. // TODO: verify if check for !quest->IsDaily() is really correct (possibly not)
  2090. else
  2091. {
  2092. Object* object = ObjectAccessor::GetObjectByTypeMask(*this, guid, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM);
  2093. if (!object || (!object->hasQuest(questId) && !object->hasInvolvedQuest(questId)))
  2094. {
  2095. PlayerTalkClass->SendCloseGossip();
  2096. return;
  2097. }
  2098.  
  2099. if (quest->IsAutoAccept() && CanAddQuest(quest, true) && CanTakeQuest(quest, true))
  2100. {
  2101. AddQuest(quest, object);
  2102. if (CanCompleteQuest(questId))
  2103. CompleteQuest(questId);
  2104. }
  2105.  
  2106. if ((quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly()) || quest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE))
  2107. PlayerTalkClass->SendQuestGiverRequestItems(quest, guid, CanCompleteRepeatableQuest(quest), true);
  2108. else
  2109. PlayerTalkClass->SendQuestGiverQuestDetails(quest, guid, true);
  2110. }
  2111. }
  2112. }
  2113. // multiple entries
  2114. else
  2115. {
  2116. QEmote qe;
  2117. qe._Delay = 0;
  2118. qe._Emote = 0;
  2119. std::string title = "";
  2120.  
  2121. // need pet case for some quests
  2122. Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid);
  2123. if (creature)
  2124. {
  2125. uint32 textid = GetGossipTextId(creature);
  2126. GossipText const* gossiptext = sObjectMgr->GetGossipText(textid);
  2127. if (!gossiptext)
  2128. {
  2129. qe._Delay = 0; //TEXTEMOTE_MESSAGE; //zyg: player emote
  2130. qe._Emote = 0; //TEXTEMOTE_HELLO; //zyg: NPC emote
  2131. title = "";
  2132. }
  2133. else
  2134. {
  2135. qe = gossiptext->Options[0].Emotes[0];
  2136.  
  2137. if (!gossiptext->Options[0].Text_0.empty())
  2138. {
  2139. title = gossiptext->Options[0].Text_0;
  2140.  
  2141. int loc_idx = GetSession()->GetSessionDbLocaleIndex();
  2142. if (loc_idx >= 0)
  2143. if (NpcTextLocale const* nl = sObjectMgr->GetNpcTextLocale(textid))
  2144. ObjectMgr::GetLocaleString(nl->Text_0[0], loc_idx, title);
  2145. }
  2146. else
  2147. {
  2148. title = gossiptext->Options[0].Text_1;
  2149.  
  2150. int loc_idx = GetSession()->GetSessionDbLocaleIndex();
  2151. if (loc_idx >= 0)
  2152. if (NpcTextLocale const* nl = sObjectMgr->GetNpcTextLocale(textid))
  2153. ObjectMgr::GetLocaleString(nl->Text_1[0], loc_idx, title);
  2154. }
  2155. }
  2156. }
  2157. PlayerTalkClass->SendQuestGiverQuestList(qe, title, guid);
  2158. }
  2159. }
  2160.  
  2161. bool Player::IsActiveQuest(uint32 quest_id) const
  2162. {
  2163. return m_QuestStatus.find(quest_id) != m_QuestStatus.end();
  2164. }
  2165.  
  2166. Quest const* Player::GetNextQuest(uint64 guid, Quest const* quest)
  2167. {
  2168. QuestRelationBounds objectQR;
  2169.  
  2170. Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid);
  2171. if (creature)
  2172. objectQR = sObjectMgr->GetCreatureQuestRelationBounds(creature->GetEntry());
  2173. else
  2174. {
  2175. //we should obtain map pointer from GetMap() in 99% of cases. Special case
  2176. //only for quests which cast teleport spells on player
  2177. Map* _map = IsInWorld() ? GetMap() : sMapMgr->FindMap(GetMapId(), GetInstanceId());
  2178. ASSERT(_map);
  2179. GameObject* pGameObject = _map->GetGameObject(guid);
  2180. if (pGameObject)
  2181. objectQR = sObjectMgr->GetGOQuestRelationBounds(pGameObject->GetEntry());
  2182. else
  2183. return NULL;
  2184. }
  2185.  
  2186. uint32 nextQuestID = quest->GetNextQuestInChain();
  2187. for (QuestRelations::const_iterator itr = objectQR.first; itr != objectQR.second; ++itr)
  2188. {
  2189. if (itr->second == nextQuestID)
  2190. return sObjectMgr->GetQuestTemplate(nextQuestID);
  2191. }
  2192.  
  2193. return NULL;
  2194. }
  2195.  
  2196. bool Player::CanSeeStartQuest(Quest const* quest)
  2197. {
  2198. if (SatisfyQuestClass(quest, false) && SatisfyQuestRace(quest, false) && SatisfyQuestSkill(quest, false) &&
  2199. SatisfyQuestExclusiveGroup(quest, false) && SatisfyQuestReputation(quest, false) &&
  2200. SatisfyQuestPreviousQuest(quest, false) && SatisfyQuestNextChain(quest, false) &&
  2201. SatisfyQuestPrevChain(quest, false) && SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) &&
  2202. SatisfyQuestSeasonal(quest, false) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this))
  2203. {
  2204. return getLevel() + sWorld->getIntConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF) >= quest->GetMinLevel();
  2205. }
  2206.  
  2207. return false;
  2208. }
  2209.  
  2210. bool Player::CanTakeQuest(Quest const* quest, bool msg)
  2211. {
  2212. return SatisfyQuestStatus(quest, msg) && SatisfyQuestExclusiveGroup(quest, msg)
  2213. && SatisfyQuestClass(quest, msg) && SatisfyQuestRace(quest, msg) && SatisfyQuestLevel(quest, msg)
  2214. && SatisfyQuestSkill(quest, msg) && SatisfyQuestReputation(quest, msg)
  2215. && SatisfyQuestPreviousQuest(quest, msg) && SatisfyQuestTimed(quest, msg)
  2216. && SatisfyQuestNextChain(quest, msg) && SatisfyQuestPrevChain(quest, msg)
  2217. && SatisfyQuestDay(quest, msg) && SatisfyQuestWeek(quest, msg)
  2218. && SatisfyQuestSeasonal(quest,msg) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this)
  2219. && SatisfyQuestConditions(quest, msg);
  2220. }
  2221.  
  2222. bool Player::CanAddQuest(Quest const* quest, bool msg)
  2223. {
  2224. if (!SatisfyQuestLog(msg))
  2225. return false;
  2226.  
  2227. uint32 srcitem = quest->GetSrcItemId();
  2228. if (srcitem > 0)
  2229. {
  2230. uint32 count = quest->GetSrcItemCount();
  2231. ItemPosCountVec dest;
  2232. InventoryResult msg2 = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, srcitem, count);
  2233.  
  2234. // player already have max number (in most case 1) source item, no additional item needed and quest can be added.
  2235. if (msg2 == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
  2236. return true;
  2237. else if (msg2 != EQUIP_ERR_OK)
  2238. {
  2239. SendEquipError(msg2, NULL, NULL, srcitem);
  2240. return false;
  2241. }
  2242. }
  2243. return true;
  2244. }
  2245.  
  2246. bool Player::CanCompleteQuest(uint32 quest_id)
  2247. {
  2248. if (quest_id)
  2249. {
  2250. Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
  2251. if (!qInfo)
  2252. return false;
  2253.  
  2254. if (!qInfo->IsRepeatable() && m_RewardedQuests.find(quest_id) != m_RewardedQuests.end())
  2255. return false; // not allow re-complete quest
  2256.  
  2257. // auto complete quest
  2258. if ((qInfo->IsAutoComplete() || qInfo->GetFlags() & QUEST_FLAGS_AUTOCOMPLETE) && CanTakeQuest(qInfo, false))
  2259. return true;
  2260.  
  2261. QuestStatusMap::iterator itr = m_QuestStatus.find(quest_id);
  2262. if (itr == m_QuestStatus.end())
  2263. return false;
  2264.  
  2265. QuestStatusData &q_status = itr->second;
  2266.  
  2267. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  2268. {
  2269. if (qInfo->HasFlag(QUEST_TRINITY_FLAGS_DELIVER))
  2270. {
  2271. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
  2272. {
  2273. if (qInfo->RequiredItemCount[i]!= 0 && q_status.ItemCount[i] < qInfo->RequiredItemCount[i])
  2274. return false;
  2275. }
  2276. }
  2277.  
  2278. if (qInfo->HasFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST | QUEST_TRINITY_FLAGS_SPEAKTO))
  2279. {
  2280. for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
  2281. {
  2282. if (qInfo->RequiredNpcOrGo[i] == 0)
  2283. continue;
  2284.  
  2285. if (qInfo->RequiredNpcOrGoCount[i] != 0 && q_status.CreatureOrGOCount[i] < qInfo->RequiredNpcOrGoCount[i])
  2286. return false;
  2287. }
  2288. }
  2289.  
  2290. if (qInfo->HasFlag(QUEST_TRINITY_FLAGS_PLAYER_KILL))
  2291. if (qInfo->GetPlayersSlain() != 0 && q_status.PlayerCount < qInfo->GetPlayersSlain())
  2292. return false;
  2293.  
  2294. if (qInfo->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT) && !q_status.Explored)
  2295. return false;
  2296.  
  2297. if (qInfo->HasFlag(QUEST_TRINITY_FLAGS_TIMED) && q_status.Timer == 0)
  2298. return false;
  2299.  
  2300. if (qInfo->GetRewOrReqMoney() < 0)
  2301. {
  2302. if (!HasEnoughMoney(-qInfo->GetRewOrReqMoney()))
  2303. return false;
  2304. }
  2305.  
  2306. uint32 repFacId = qInfo->GetRepObjectiveFaction();
  2307. if (repFacId && GetReputationMgr().GetReputation(repFacId) < qInfo->GetRepObjectiveValue())
  2308. return false;
  2309.  
  2310. return true;
  2311. }
  2312. }
  2313. return false;
  2314. }
  2315.  
  2316. bool Player::CanCompleteRepeatableQuest(Quest const* quest)
  2317. {
  2318. // Solve problem that player don't have the quest and try complete it.
  2319. // if repeatable she must be able to complete event if player don't have it.
  2320. // Seem that all repeatable quest are DELIVER Flag so, no need to add more.
  2321. if (!CanTakeQuest(quest, false))
  2322. return false;
  2323.  
  2324. if (quest->HasFlag(QUEST_TRINITY_FLAGS_DELIVER))
  2325. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
  2326. if (quest->RequiredItemId[i] && quest->RequiredItemCount[i] && !HasItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i]))
  2327. return false;
  2328.  
  2329. if (!CanRewardQuest(quest, false))
  2330. return false;
  2331.  
  2332. return true;
  2333. }
  2334.  
  2335. bool Player::CanRewardQuest(Quest const* quest, bool msg)
  2336. {
  2337. // not auto complete quest and not completed quest (only cheating case, then ignore without message)
  2338. if (!quest->IsDFQuest() && !quest->IsAutoComplete() && !(quest->GetFlags() & QUEST_FLAGS_AUTOCOMPLETE) && GetQuestStatus(quest->GetQuestId()) != QUEST_STATUS_COMPLETE)
  2339. return false;
  2340.  
  2341. // daily quest can't be rewarded (25 daily quest already completed)
  2342. if (!SatisfyQuestDay(quest, true) || !SatisfyQuestWeek(quest, true) || !SatisfyQuestSeasonal(quest,true))
  2343. return false;
  2344.  
  2345. // rewarded and not repeatable quest (only cheating case, then ignore without message)
  2346. if (GetQuestRewardStatus(quest->GetQuestId()))
  2347. return false;
  2348.  
  2349. // prevent receive reward with quest items in bank
  2350. if (quest->HasFlag(QUEST_TRINITY_FLAGS_DELIVER))
  2351. {
  2352. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
  2353. {
  2354. if (quest->RequiredItemCount[i]!= 0 &&
  2355. GetItemCount(quest->RequiredItemId[i]) < quest->RequiredItemCount[i])
  2356. {
  2357. if (msg)
  2358. SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL, quest->RequiredItemId[i]);
  2359. return false;
  2360. }
  2361. }
  2362. }
  2363.  
  2364. // prevent receive reward with low money and GetRewOrReqMoney() < 0
  2365. if (quest->GetRewOrReqMoney() < 0 && !HasEnoughMoney(-quest->GetRewOrReqMoney()))
  2366. return false;
  2367.  
  2368. return true;
  2369. }
  2370.  
  2371. bool Player::CanRewardQuest(Quest const* quest, uint32 reward, bool msg)
  2372. {
  2373. // prevent receive reward with quest items in bank or for not completed quest
  2374. if (!CanRewardQuest(quest, msg))
  2375. return false;
  2376.  
  2377. if (quest->GetRewChoiceItemsCount() > 0)
  2378. {
  2379. if (quest->RewardChoiceItemId[reward])
  2380. {
  2381. ItemPosCountVec dest;
  2382. InventoryResult res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, quest->RewardChoiceItemId[reward], quest->RewardChoiceItemCount[reward]);
  2383. if (res != EQUIP_ERR_OK)
  2384. {
  2385. SendEquipError(res, NULL, NULL, quest->RewardChoiceItemId[reward]);
  2386. return false;
  2387. }
  2388. }
  2389. }
  2390.  
  2391. if (quest->GetRewItemsCount() > 0)
  2392. {
  2393. for (uint32 i = 0; i < quest->GetRewItemsCount(); ++i)
  2394. {
  2395. if (quest->RewardItemId[i])
  2396. {
  2397. ItemPosCountVec dest;
  2398. InventoryResult res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, quest->RewardItemId[i], quest->RewardItemIdCount[i]);
  2399. if (res != EQUIP_ERR_OK)
  2400. {
  2401. SendEquipError(res, NULL, NULL, quest->RewardItemId[i]);
  2402. return false;
  2403. }
  2404. }
  2405. }
  2406. }
  2407.  
  2408. return true;
  2409. }
  2410.  
  2411. void Player::AddQuest(Quest const* quest, Object* questGiver)
  2412. {
  2413. uint16 log_slot = FindQuestSlot(0);
  2414.  
  2415. if (log_slot >= MAX_QUEST_LOG_SIZE) // Player does not have any free slot in the quest log
  2416. return;
  2417.  
  2418. uint32 quest_id = quest->GetQuestId();
  2419.  
  2420. // if not exist then created with set uState == NEW and rewarded=false
  2421. QuestStatusData& questStatusData = m_QuestStatus[quest_id];
  2422.  
  2423. // check for repeatable quests status reset
  2424. questStatusData.Status = QUEST_STATUS_INCOMPLETE;
  2425. questStatusData.Explored = false;
  2426.  
  2427. if (quest->HasFlag(QUEST_TRINITY_FLAGS_DELIVER))
  2428. {
  2429. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
  2430. questStatusData.ItemCount[i] = 0;
  2431. }
  2432.  
  2433. if (quest->HasFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST | QUEST_TRINITY_FLAGS_SPEAKTO))
  2434. {
  2435. for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
  2436. questStatusData.CreatureOrGOCount[i] = 0;
  2437. }
  2438.  
  2439. if (quest->HasFlag(QUEST_TRINITY_FLAGS_PLAYER_KILL))
  2440. questStatusData.PlayerCount = 0;
  2441.  
  2442. GiveQuestSourceItem(quest);
  2443. AdjustQuestReqItemCount(quest, questStatusData);
  2444.  
  2445. if (quest->GetRepObjectiveFaction())
  2446. if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(quest->GetRepObjectiveFaction()))
  2447. GetReputationMgr().SetVisible(factionEntry);
  2448.  
  2449. if (quest->GetRepObjectiveFaction2())
  2450. if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(quest->GetRepObjectiveFaction2()))
  2451. GetReputationMgr().SetVisible(factionEntry);
  2452.  
  2453. uint32 qtime = 0;
  2454. if (quest->HasFlag(QUEST_TRINITY_FLAGS_TIMED))
  2455. {
  2456. uint32 limittime = quest->GetLimitTime();
  2457.  
  2458. // shared timed quest
  2459. if (questGiver && questGiver->GetTypeId() == TYPEID_PLAYER)
  2460. limittime = questGiver->ToPlayer()->getQuestStatusMap()[quest_id].Timer / IN_MILLISECONDS;
  2461.  
  2462. AddTimedQuest(quest_id);
  2463. questStatusData.Timer = limittime * IN_MILLISECONDS;
  2464. qtime = static_cast<uint32>(time(NULL)) + limittime;
  2465. }
  2466. else
  2467. questStatusData.Timer = 0;
  2468.  
  2469. SetQuestSlot(log_slot, quest_id, qtime);
  2470.  
  2471. m_QuestStatusSave[quest_id] = true;
  2472.  
  2473. GetAchievementMgr().StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, quest_id);
  2474.  
  2475. //starting initial quest script
  2476. if (questGiver && quest->GetQuestStartScript() != 0)
  2477. GetMap()->ScriptsStart(sQuestStartScripts, quest->GetQuestStartScript(), questGiver, this);
  2478.  
  2479. // Some spells applied at quest activation
  2480. SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForQuestMapBounds(quest_id, true);
  2481. if (saBounds.first != saBounds.second)
  2482. {
  2483. uint32 zone, area;
  2484. GetZoneAndAreaId(zone, area);
  2485.  
  2486. for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
  2487. if (itr->second->autocast && itr->second->IsFitToRequirements(this, zone, area))
  2488. if (!HasAura(itr->second->spellId))
  2489. CastSpell(this, itr->second->spellId, true);
  2490. }
  2491.  
  2492. UpdateForQuestWorldObjects();
  2493. }
  2494.  
  2495. void Player::CompleteQuest(uint32 quest_id)
  2496. {
  2497. if (quest_id)
  2498. {
  2499. SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE);
  2500.  
  2501. uint16 log_slot = FindQuestSlot(quest_id);
  2502. if (log_slot < MAX_QUEST_LOG_SIZE)
  2503. SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
  2504.  
  2505. if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id))
  2506. {
  2507. if (qInfo->HasFlag(QUEST_FLAGS_AUTO_REWARDED))
  2508. RewardQuest(qInfo, 0, this, false);
  2509. else
  2510. SendQuestComplete(quest_id);
  2511. }
  2512. }
  2513. }
  2514.  
  2515. void Player::IncompleteQuest(uint32 quest_id)
  2516. {
  2517. if (quest_id)
  2518. {
  2519. SetQuestStatus(quest_id, QUEST_STATUS_INCOMPLETE);
  2520.  
  2521. uint16 log_slot = FindQuestSlot(quest_id);
  2522. if (log_slot < MAX_QUEST_LOG_SIZE)
  2523. RemoveQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
  2524. }
  2525. }
  2526.  
  2527. void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce)
  2528. {
  2529. //this THING should be here to protect code from quest, which cast on player far teleport as a reward
  2530. //should work fine, cause far teleport will be executed in Player::Update()
  2531. SetCanDelayTeleport(true);
  2532.  
  2533. uint32 quest_id = quest->GetQuestId();
  2534.  
  2535. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
  2536. if (quest->RequiredItemId[i])
  2537. DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true);
  2538.  
  2539. for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
  2540. {
  2541. if (quest->RequiredSourceItemId[i])
  2542. {
  2543. uint32 count = quest->RequiredSourceItemCount[i];
  2544. DestroyItemCount(quest->RequiredSourceItemId[i], count ? count : 9999, true);
  2545. }
  2546. }
  2547.  
  2548. RemoveTimedQuest(quest_id);
  2549.  
  2550. if (quest->GetRewChoiceItemsCount() > 0)
  2551. {
  2552. if (uint32 itemId = quest->RewardChoiceItemId[reward])
  2553. {
  2554. ItemPosCountVec dest;
  2555. if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, quest->RewardChoiceItemCount[reward]) == EQUIP_ERR_OK)
  2556. {
  2557. Item* item = StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
  2558. SendNewItem(item, quest->RewardChoiceItemCount[reward], true, false);
  2559. }
  2560. }
  2561. }
  2562.  
  2563. if (quest->GetRewItemsCount() > 0)
  2564. {
  2565. for (uint32 i = 0; i < quest->GetRewItemsCount(); ++i)
  2566. {
  2567. if (uint32 itemId = quest->RewardItemId[i])
  2568. {
  2569. ItemPosCountVec dest;
  2570. if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, quest->RewardItemIdCount[i]) == EQUIP_ERR_OK)
  2571. {
  2572. Item* item = StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
  2573. SendNewItem(item, quest->RewardItemIdCount[i], true, false);
  2574. }
  2575. }
  2576. }
  2577. }
  2578.  
  2579. RewardReputation(quest);
  2580.  
  2581. uint16 log_slot = FindQuestSlot(quest_id);
  2582. if (log_slot < MAX_QUEST_LOG_SIZE)
  2583. SetQuestSlot(log_slot, 0);
  2584.  
  2585. bool rewarded = (m_RewardedQuests.find(quest_id) != m_RewardedQuests.end());
  2586.  
  2587. // Not give XP in case already completed once repeatable quest
  2588. uint32 XP = rewarded ? 0 : uint32(quest->XPValue(this)*sWorld->getRate(RATE_XP_QUEST));
  2589.  
  2590. // handle SPELL_AURA_MOD_XP_QUEST_PCT auras
  2591. Unit::AuraEffectList const& ModXPPctAuras = GetAuraEffectsByType(SPELL_AURA_MOD_XP_QUEST_PCT);
  2592. for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i)
  2593. AddPctN(XP, (*i)->GetAmount());
  2594.  
  2595. int32 moneyRew = 0;
  2596. if (getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  2597. GiveXP(XP, NULL);
  2598. else
  2599. moneyRew = int32(quest->GetRewMoneyMaxLevel() * sWorld->getRate(RATE_DROP_MONEY));
  2600.  
  2601. // Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
  2602. if (quest->GetRewOrReqMoney())
  2603. moneyRew += quest->GetRewOrReqMoney();
  2604.  
  2605. if (moneyRew)
  2606. {
  2607. ModifyMoney(moneyRew);
  2608.  
  2609. if (moneyRew > 0)
  2610. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, uint32(moneyRew));
  2611. }
  2612.  
  2613. // honor reward
  2614. if (uint32 honor = quest->CalculateHonorGain(getLevel()))
  2615. RewardHonor(NULL, 0, honor);
  2616.  
  2617. // title reward
  2618. if (quest->GetCharTitleId())
  2619. {
  2620. if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(quest->GetCharTitleId()))
  2621. SetTitle(titleEntry);
  2622. }
  2623.  
  2624. if (quest->GetBonusTalents())
  2625. {
  2626. m_questRewardTalentCount+=quest->GetBonusTalents();
  2627. InitTalentForLevel();
  2628. }
  2629.  
  2630. if (quest->GetRewArenaPoints())
  2631. ModifyArenaPoints(quest->GetRewArenaPoints());
  2632.  
  2633. // Send reward mail
  2634. if (uint32 mail_template_id = quest->GetRewMailTemplateId())
  2635. {
  2636. //- TODO: Poor design of mail system
  2637. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  2638. MailDraft(mail_template_id).SendMailTo(trans, this, questGiver, MAIL_CHECK_MASK_HAS_BODY, quest->GetRewMailDelaySecs());
  2639. CharacterDatabase.CommitTransaction(trans);
  2640. }
  2641.  
  2642. if (quest->IsDaily() || quest->IsDFQuest())
  2643. {
  2644. SetDailyQuestStatus(quest_id);
  2645. if (quest->IsDaily())
  2646. {
  2647. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, quest_id);
  2648. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY, quest_id);
  2649. }
  2650. }
  2651. else if (quest->IsWeekly())
  2652. SetWeeklyQuestStatus(quest_id);
  2653. else if (quest->IsSeasonal())
  2654. SetSeasonalQuestStatus(quest_id);
  2655.  
  2656. RemoveActiveQuest(quest_id);
  2657. m_RewardedQuests.insert(quest_id);
  2658. m_RewardedQuestsSave[quest_id] = true;
  2659.  
  2660. // StoreNewItem, mail reward, etc. save data directly to the database
  2661. // to prevent exploitable data desynchronisation we save the quest status to the database too
  2662. // (to prevent rewarding this quest another time while rewards were already given out)
  2663. SQLTransaction trans = SQLTransaction(NULL);
  2664. _SaveQuestStatus(trans);
  2665.  
  2666. if (announce)
  2667. SendQuestReward(quest, XP, questGiver);
  2668.  
  2669. // cast spells after mark quest complete (some spells have quest completed state requirements in spell_area data)
  2670. if (quest->GetRewSpellCast() > 0)
  2671. CastSpell(this, quest->GetRewSpellCast(), true);
  2672. else if (quest->GetRewSpell() > 0)
  2673. CastSpell(this, quest->GetRewSpell(), true);
  2674.  
  2675. if (quest->GetZoneOrSort() > 0)
  2676. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE, quest->GetZoneOrSort());
  2677. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT);
  2678. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, quest->GetQuestId());
  2679.  
  2680. uint32 zone = 0;
  2681. uint32 area = 0;
  2682.  
  2683. // remove auras from spells with quest reward state limitations
  2684. SpellAreaForQuestMapBounds saEndBounds = sSpellMgr->GetSpellAreaForQuestEndMapBounds(quest_id);
  2685. if (saEndBounds.first != saEndBounds.second)
  2686. {
  2687. GetZoneAndAreaId(zone, area);
  2688.  
  2689. for (SpellAreaForAreaMap::const_iterator itr = saEndBounds.first; itr != saEndBounds.second; ++itr)
  2690. if (!itr->second->IsFitToRequirements(this, zone, area))
  2691. RemoveAurasDueToSpell(itr->second->spellId);
  2692. }
  2693.  
  2694. // Some spells applied at quest reward
  2695. SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForQuestMapBounds(quest_id, false);
  2696. if (saBounds.first != saBounds.second)
  2697. {
  2698. if (!zone || !area)
  2699. GetZoneAndAreaId(zone, area);
  2700.  
  2701. for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
  2702. if (itr->second->autocast && itr->second->IsFitToRequirements(this, zone, area))
  2703. if (!HasAura(itr->second->spellId))
  2704. CastSpell(this, itr->second->spellId, true);
  2705. }
  2706.  
  2707. //lets remove flag for delayed teleports
  2708. SetCanDelayTeleport(false);
  2709. }
  2710.  
  2711. void Player::FailQuest(uint32 questId)
  2712. {
  2713. if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
  2714. {
  2715. SetQuestStatus(questId, QUEST_STATUS_FAILED);
  2716.  
  2717. uint16 log_slot = FindQuestSlot(questId);
  2718.  
  2719. if (log_slot < MAX_QUEST_LOG_SIZE)
  2720. {
  2721. SetQuestSlotTimer(log_slot, 1);
  2722. SetQuestSlotState(log_slot, QUEST_STATE_FAIL);
  2723. }
  2724.  
  2725. if (quest->HasFlag(QUEST_TRINITY_FLAGS_TIMED))
  2726. {
  2727. QuestStatusData& q_status = m_QuestStatus[questId];
  2728.  
  2729. RemoveTimedQuest(questId);
  2730. q_status.Timer = 0;
  2731.  
  2732. SendQuestTimerFailed(questId);
  2733. }
  2734. else
  2735. SendQuestFailed(questId);
  2736.  
  2737. // Destroy quest items on quest failure.
  2738. for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
  2739. if (quest->RequiredItemId[i] > 0 && quest->RequiredItemCount[i] > 0)
  2740. // Destroy items received on starting the quest.
  2741. DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true, true);
  2742. for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
  2743. if (quest->RequiredSourceItemId[i] > 0 && quest->RequiredSourceItemCount[i] > 0)
  2744. // Destroy items received during the quest.
  2745. DestroyItemCount(quest->RequiredSourceItemId[i], quest->RequiredSourceItemCount[i], true, true);
  2746. }
  2747. }
  2748.  
  2749. bool Player::SatisfyQuestSkill(Quest const* qInfo, bool msg) const
  2750. {
  2751. uint32 skill = qInfo->GetRequiredSkill();
  2752.  
  2753. // skip 0 case RequiredSkill
  2754. if (skill == 0)
  2755. return true;
  2756.  
  2757. // check skill value
  2758. if (GetSkillValue(skill) < qInfo->GetRequiredSkillValue())
  2759. {
  2760. if (msg)
  2761. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  2762.  
  2763. return false;
  2764. }
  2765.  
  2766. return true;
  2767. }
  2768.  
  2769. bool Player::SatisfyQuestLevel(Quest const* qInfo, bool msg)
  2770. {
  2771. if (getLevel() < qInfo->GetMinLevel())
  2772. {
  2773. if (msg)
  2774. SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_LOW_LEVEL);
  2775. return false;
  2776. }
  2777. else if (qInfo->GetMaxLevel() > 0 && getLevel() > qInfo->GetMaxLevel())
  2778. {
  2779. if (msg)
  2780. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); // There doesn't seem to be a specific response for too high player level
  2781. return false;
  2782. }
  2783. return true;
  2784. }
  2785.  
  2786. bool Player::SatisfyQuestLog(bool msg)
  2787. {
  2788. // exist free slot
  2789. if (FindQuestSlot(0) < MAX_QUEST_LOG_SIZE)
  2790. return true;
  2791.  
  2792. if (msg)
  2793. {
  2794. WorldPacket data(SMSG_QUESTLOG_FULL, 0);
  2795. GetSession()->SendPacket(&data);
  2796. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTLOG_FULL");
  2797. }
  2798. return false;
  2799. }
  2800.  
  2801. bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg)
  2802. {
  2803. // No previous quest (might be first quest in a series)
  2804. if (qInfo->prevQuests.empty())
  2805. return true;
  2806.  
  2807. for (Quest::PrevQuests::const_iterator iter = qInfo->prevQuests.begin(); iter != qInfo->prevQuests.end(); ++iter)
  2808. {
  2809. uint32 prevId = abs(*iter);
  2810.  
  2811. Quest const* qPrevInfo = sObjectMgr->GetQuestTemplate(prevId);
  2812.  
  2813. if (qPrevInfo)
  2814. {
  2815. // If any of the positive previous quests completed, return true
  2816. if (*iter > 0 && m_RewardedQuests.find(prevId) != m_RewardedQuests.end())
  2817. {
  2818. // skip one-from-all exclusive group
  2819. if (qPrevInfo->GetExclusiveGroup() >= 0)
  2820. return true;
  2821.  
  2822. // each-from-all exclusive group (< 0)
  2823. // can be start if only all quests in prev quest exclusive group completed and rewarded
  2824. ObjectMgr::ExclusiveQuestGroups::iterator iter2 = sObjectMgr->mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
  2825. ObjectMgr::ExclusiveQuestGroups::iterator end = sObjectMgr->mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
  2826.  
  2827. ASSERT(iter2 != end); // always must be found if qPrevInfo->ExclusiveGroup != 0
  2828.  
  2829. for (; iter2 != end; ++iter2)
  2830. {
  2831. uint32 exclude_Id = iter2->second;
  2832.  
  2833. // skip checked quest id, only state of other quests in group is interesting
  2834. if (exclude_Id == prevId)
  2835. continue;
  2836.  
  2837. // alternative quest from group also must be completed and rewarded(reported)
  2838. if (m_RewardedQuests.find(exclude_Id) == m_RewardedQuests.end())
  2839. {
  2840. if (msg)
  2841. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  2842. return false;
  2843. }
  2844. }
  2845. return true;
  2846. }
  2847.  
  2848. // If any of the negative previous quests active, return true
  2849. if (*iter < 0 && GetQuestStatus(prevId) != QUEST_STATUS_NONE)
  2850. {
  2851. // skip one-from-all exclusive group
  2852. if (qPrevInfo->GetExclusiveGroup() >= 0)
  2853. return true;
  2854.  
  2855. // each-from-all exclusive group (< 0)
  2856. // can be start if only all quests in prev quest exclusive group active
  2857. ObjectMgr::ExclusiveQuestGroups::iterator iter2 = sObjectMgr->mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
  2858. ObjectMgr::ExclusiveQuestGroups::iterator end = sObjectMgr->mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
  2859.  
  2860. ASSERT(iter2 != end); // always must be found if qPrevInfo->ExclusiveGroup != 0
  2861.  
  2862. for (; iter2 != end; ++iter2)
  2863. {
  2864. uint32 exclude_Id = iter2->second;
  2865.  
  2866. // skip checked quest id, only state of other quests in group is interesting
  2867. if (exclude_Id == prevId)
  2868. continue;
  2869.  
  2870. // alternative quest from group also must be active
  2871. if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE)
  2872. {
  2873. if (msg)
  2874. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  2875. return false;
  2876. }
  2877. }
  2878. return true;
  2879. }
  2880. }
  2881. }
  2882.  
  2883. // Has only positive prev. quests in non-rewarded state
  2884. // and negative prev. quests in non-active state
  2885. if (msg)
  2886. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  2887.  
  2888. return false;
  2889. }
  2890.  
  2891. bool Player::SatisfyQuestClass(Quest const* qInfo, bool msg) const
  2892. {
  2893. uint32 reqClass = qInfo->GetRequiredClasses();
  2894.  
  2895. if (reqClass == 0)
  2896. return true;
  2897.  
  2898. if ((reqClass & getClassMask()) == 0)
  2899. {
  2900. if (msg)
  2901. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  2902.  
  2903. return false;
  2904. }
  2905.  
  2906. return true;
  2907. }
  2908.  
  2909. bool Player::SatisfyQuestRace(Quest const* qInfo, bool msg)
  2910. {
  2911. uint32 reqraces = qInfo->GetRequiredRaces();
  2912. if (reqraces == 0)
  2913. return true;
  2914. if ((reqraces & getRaceMask()) == 0)
  2915. {
  2916. if (msg)
  2917. SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_WRONG_RACE);
  2918. return false;
  2919. }
  2920. return true;
  2921. }
  2922.  
  2923. bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg)
  2924. {
  2925. uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); //Min required rep
  2926. if (fIdMin && GetReputationMgr().GetReputation(fIdMin) < qInfo->GetRequiredMinRepValue())
  2927. {
  2928. if (msg)
  2929. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  2930. return false;
  2931. }
  2932.  
  2933. uint32 fIdMax = qInfo->GetRequiredMaxRepFaction(); //Max required rep
  2934. if (fIdMax && GetReputationMgr().GetReputation(fIdMax) >= qInfo->GetRequiredMaxRepValue())
  2935. {
  2936. if (msg)
  2937. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  2938. return false;
  2939. }
  2940.  
  2941. // ReputationObjective2 does not seem to be an objective requirement but a requirement
  2942. // to be able to accept the quest
  2943. uint32 fIdObj = qInfo->GetRepObjectiveFaction2();
  2944. if (fIdObj && GetReputationMgr().GetReputation(fIdObj) >= qInfo->GetRepObjectiveValue2())
  2945. {
  2946. if (msg)
  2947. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  2948. return false;
  2949. }
  2950.  
  2951. return true;
  2952. }
  2953.  
  2954. bool Player::SatisfyQuestStatus(Quest const* qInfo, bool msg)
  2955. {
  2956. if (GetQuestStatus(qInfo->GetQuestId()) != QUEST_STATUS_NONE)
  2957. {
  2958. if (msg)
  2959. SendCanTakeQuestResponse(INVALIDREASON_QUEST_ALREADY_ON);
  2960. return false;
  2961. }
  2962. return true;
  2963. }
  2964.  
  2965. bool Player::SatisfyQuestConditions(Quest const* qInfo, bool msg)
  2966. {
  2967. ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_ACCEPT, qInfo->GetQuestId());
  2968. if (!sConditionMgr->IsObjectMeetToConditions(this, conditions))
  2969. {
  2970. if (msg)
  2971. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  2972. sLog->outDebug(LOG_FILTER_CONDITIONSYS, "Player::SatisfyQuestConditions: conditions not met for quest %u", qInfo->GetQuestId());
  2973. return false;
  2974. }
  2975. return true;
  2976. }
  2977.  
  2978. bool Player::SatisfyQuestTimed(Quest const* qInfo, bool msg)
  2979. {
  2980. if (!m_timedquests.empty() && qInfo->HasFlag(QUEST_TRINITY_FLAGS_TIMED))
  2981. {
  2982. if (msg)
  2983. SendCanTakeQuestResponse(INVALIDREASON_QUEST_ONLY_ONE_TIMED);
  2984. return false;
  2985. }
  2986. return true;
  2987. }
  2988.  
  2989. bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg)
  2990. {
  2991. // non positive exclusive group, if > 0 then can be start if any other quest in exclusive group already started/completed
  2992. if (qInfo->GetExclusiveGroup() <= 0)
  2993. return true;
  2994.  
  2995. ObjectMgr::ExclusiveQuestGroups::iterator iter = sObjectMgr->mExclusiveQuestGroups.lower_bound(qInfo->GetExclusiveGroup());
  2996. ObjectMgr::ExclusiveQuestGroups::iterator end = sObjectMgr->mExclusiveQuestGroups.upper_bound(qInfo->GetExclusiveGroup());
  2997.  
  2998. ASSERT(iter != end); // always must be found if qInfo->ExclusiveGroup != 0
  2999.  
  3000. for (; iter != end; ++iter)
  3001. {
  3002. uint32 exclude_Id = iter->second;
  3003.  
  3004. // skip checked quest id, only state of other quests in group is interesting
  3005. if (exclude_Id == qInfo->GetQuestId())
  3006. continue;
  3007.  
  3008. // not allow have daily quest if daily quest from exclusive group already recently completed
  3009. Quest const* Nquest = sObjectMgr->GetQuestTemplate(exclude_Id);
  3010. if (!SatisfyQuestDay(Nquest, false) || !SatisfyQuestWeek(Nquest, false) || !SatisfyQuestSeasonal(Nquest,false))
  3011. {
  3012. if (msg)
  3013. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  3014.  
  3015. return false;
  3016. }
  3017.  
  3018. // alternative quest already started or completed - but don't check rewarded states if both are repeatable
  3019. if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE || (!(qInfo->IsRepeatable() && Nquest->IsRepeatable()) && (m_RewardedQuests.find(exclude_Id) != m_RewardedQuests.end())))
  3020. {
  3021. if (msg)
  3022. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  3023. return false;
  3024. }
  3025. }
  3026. return true;
  3027. }
  3028.  
  3029. bool Player::SatisfyQuestNextChain(Quest const* qInfo, bool msg)
  3030. {
  3031. uint32 nextQuest = qInfo->GetNextQuestInChain();
  3032. if (!nextQuest)
  3033. return true;
  3034.  
  3035. // next quest in chain already started or completed
  3036. if (GetQuestStatus(nextQuest) != QUEST_STATUS_NONE) // GetQuestStatus returns QUEST_STATUS_COMPLETED for rewarded quests
  3037. {
  3038. if (msg)
  3039. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  3040. return false;
  3041. }
  3042.  
  3043. // check for all quests further up the chain
  3044. // only necessary if there are quest chains with more than one quest that can be skipped
  3045. //return SatisfyQuestNextChain(qInfo->GetNextQuestInChain(), msg);
  3046. return true;
  3047. }
  3048.  
  3049. bool Player::SatisfyQuestPrevChain(Quest const* qInfo, bool msg)
  3050. {
  3051. // No previous quest in chain
  3052. if (qInfo->prevChainQuests.empty())
  3053. return true;
  3054.  
  3055. for (Quest::PrevChainQuests::const_iterator iter = qInfo->prevChainQuests.begin(); iter != qInfo->prevChainQuests.end(); ++iter)
  3056. {
  3057. QuestStatusMap::const_iterator itr = m_QuestStatus.find(*iter);
  3058.  
  3059. // If any of the previous quests in chain active, return false
  3060. if (itr != m_QuestStatus.end() && itr->second.Status != QUEST_STATUS_NONE)
  3061. {
  3062. if (msg)
  3063. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  3064. return false;
  3065. }
  3066.  
  3067. // check for all quests further down the chain
  3068. // only necessary if there are quest chains with more than one quest that can be skipped
  3069. //if (!SatisfyQuestPrevChain(prevId, msg))
  3070. // return false;
  3071. }
  3072.  
  3073. // No previous quest in chain active
  3074. return true;
  3075. }
  3076.  
  3077. bool Player::SatisfyQuestDay(Quest const* qInfo, bool msg)
  3078. {
  3079. if (!qInfo->IsDaily() && !qInfo->IsDFQuest())
  3080. return true;
  3081.  
  3082. if (qInfo->IsDFQuest())
  3083. {
  3084. if (!m_DFQuests.empty())
  3085. return false;
  3086.  
  3087. return true;
  3088. }
  3089.  
  3090. bool have_slot = false;
  3091. for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
  3092. {
  3093. uint32 id = GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx);
  3094. if (qInfo->GetQuestId() == id)
  3095. return false;
  3096.  
  3097. if (!id)
  3098. have_slot = true;
  3099. }
  3100.  
  3101. if (!have_slot)
  3102. {
  3103. if (msg)
  3104. SendCanTakeQuestResponse(INVALIDREASON_DAILY_QUESTS_REMAINING);
  3105. return false;
  3106. }
  3107.  
  3108. return true;
  3109. }
  3110.  
  3111. bool Player::SatisfyQuestWeek(Quest const* qInfo, bool /*msg*/)
  3112. {
  3113. if (!qInfo->IsWeekly() || m_weeklyquests.empty())
  3114. return true;
  3115.  
  3116. // if not found in cooldown list
  3117. return m_weeklyquests.find(qInfo->GetQuestId()) == m_weeklyquests.end();
  3118. }
  3119.  
  3120. bool Player::SatisfyQuestSeasonal(Quest const* qInfo, bool /*msg*/)
  3121. {
  3122. if (!qInfo->IsSeasonal() || m_seasonalquests.empty())
  3123. return true;
  3124.  
  3125. uint16 eventId = sGameEventMgr->GetEventIdForQuest(qInfo);
  3126. if (m_seasonalquests.find(eventId) == m_seasonalquests.end() || m_seasonalquests[eventId].empty())
  3127. return true;
  3128.  
  3129. // if not found in cooldown list
  3130. return m_seasonalquests[eventId].find(qInfo->GetQuestId()) == m_seasonalquests[eventId].end();
  3131. }
  3132.  
  3133. bool Player::GiveQuestSourceItem(Quest const* quest)
  3134. {
  3135. uint32 srcitem = quest->GetSrcItemId();
  3136. if (srcitem > 0)
  3137. {
  3138. uint32 count = quest->GetSrcItemCount();
  3139. if (count <= 0)
  3140. count = 1;
  3141.  
  3142. ItemPosCountVec dest;
  3143. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, srcitem, count);
  3144. if (msg == EQUIP_ERR_OK)
  3145. {
  3146. Item* item = StoreNewItem(dest, srcitem, true);
  3147. SendNewItem(item, count, true, false);
  3148. return true;
  3149. }
  3150. // player already have max amount required item, just report success
  3151. else if (msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
  3152. return true;
  3153. else
  3154. SendEquipError(msg, NULL, NULL, srcitem);
  3155. return false;
  3156. }
  3157.  
  3158. return true;
  3159. }
  3160.  
  3161. bool Player::TakeQuestSourceItem(uint32 questId, bool msg)
  3162. {
  3163. Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
  3164. if (quest)
  3165. {
  3166. uint32 srcItemId = quest->GetSrcItemId();
  3167. ItemTemplate const* item = sObjectMgr->GetItemTemplate(srcItemId);
  3168. bool destroyItem = true;
  3169.  
  3170. if (srcItemId > 0)
  3171. {
  3172. uint32 count = quest->GetSrcItemCount();
  3173. if (count <= 0)
  3174. count = 1;
  3175.  
  3176. // exist two cases when destroy source quest item not possible:
  3177. // a) non un-equippable item (equipped non-empty bag, for example)
  3178. // b) when quest is started from an item and item also is needed in
  3179. // the end as RequiredItemId
  3180. InventoryResult res = CanUnequipItems(srcItemId, count);
  3181. if (res != EQUIP_ERR_OK)
  3182. {
  3183. if (msg)
  3184. SendEquipError(res, NULL, NULL, srcItemId);
  3185. return false;
  3186. }
  3187.  
  3188. for (uint8 n = 0; n < QUEST_ITEM_OBJECTIVES_COUNT; ++n)
  3189. if (item->StartQuest == questId && srcItemId == quest->RequiredItemId[n])
  3190. destroyItem = false;
  3191.  
  3192. if (destroyItem)
  3193. DestroyItemCount(srcItemId, count, true, true);
  3194. }
  3195. }
  3196.  
  3197. return true;
  3198. }
  3199.  
  3200. bool Player::GetQuestRewardStatus(uint32 quest_id) const
  3201. {
  3202. Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
  3203. if (qInfo)
  3204. {
  3205. // for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
  3206. if (!qInfo->IsRepeatable())
  3207. return m_RewardedQuests.find(quest_id) != m_RewardedQuests.end();
  3208.  
  3209. return false;
  3210. }
  3211. return false;
  3212. }
  3213.  
  3214. QuestStatus Player::GetQuestStatus(uint32 quest_id) const
  3215. {
  3216. if (quest_id)
  3217. {
  3218. QuestStatusMap::const_iterator itr = m_QuestStatus.find(quest_id);
  3219. if (itr != m_QuestStatus.end())
  3220. return itr->second.Status;
  3221.  
  3222. if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id))
  3223. if (!qInfo->IsRepeatable() && m_RewardedQuests.find(quest_id) != m_RewardedQuests.end())
  3224. return QUEST_STATUS_REWARDED;
  3225. }
  3226. return QUEST_STATUS_NONE;
  3227. }
  3228.  
  3229. bool Player::CanShareQuest(uint32 quest_id) const
  3230. {
  3231. Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
  3232. if (qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE))
  3233. {
  3234. QuestStatusMap::const_iterator itr = m_QuestStatus.find(quest_id);
  3235. if (itr != m_QuestStatus.end())
  3236. return itr->second.Status == QUEST_STATUS_INCOMPLETE;
  3237. }
  3238. return false;
  3239. }
  3240.  
  3241. void Player::SetQuestStatus(uint32 quest_id, QuestStatus status)
  3242. {
  3243. if (sObjectMgr->GetQuestTemplate(quest_id))
  3244. {
  3245. m_QuestStatus[quest_id].Status = status;
  3246. m_QuestStatusSave[quest_id] = true;
  3247. }
  3248.  
  3249. UpdateForQuestWorldObjects();
  3250. }
  3251.  
  3252. void Player::RemoveActiveQuest(uint32 quest_id)
  3253. {
  3254. QuestStatusMap::iterator itr = m_QuestStatus.find(quest_id);
  3255. if (itr != m_QuestStatus.end())
  3256. {
  3257. m_QuestStatus.erase(itr);
  3258. m_QuestStatusSave[quest_id] = false;
  3259. return;
  3260. }
  3261. }
  3262.  
  3263. void Player::RemoveRewardedQuest(uint32 quest_id)
  3264. {
  3265. RewardedQuestSet::iterator rewItr = m_RewardedQuests.find(quest_id);
  3266. if (rewItr != m_RewardedQuests.end())
  3267. {
  3268. m_RewardedQuests.erase(rewItr);
  3269. m_RewardedQuestsSave[quest_id] = false;
  3270. }
  3271. }
  3272.  
  3273. // not used in Trinity, but used in scripting code
  3274. uint16 Player::GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry)
  3275. {
  3276. Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
  3277. if (!qInfo)
  3278. return 0;
  3279.  
  3280. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  3281. if (qInfo->RequiredNpcOrGo[j] == entry)
  3282. return m_QuestStatus[quest_id].CreatureOrGOCount[j];
  3283.  
  3284. return 0;
  3285. }
  3286.  
  3287. void Player::AdjustQuestReqItemCount(Quest const* quest, QuestStatusData& questStatusData)
  3288. {
  3289. if (quest->HasFlag(QUEST_TRINITY_FLAGS_DELIVER))
  3290. {
  3291. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
  3292. {
  3293. uint32 reqitemcount = quest->RequiredItemCount[i];
  3294. if (reqitemcount != 0)
  3295. {
  3296. uint32 curitemcount = GetItemCount(quest->RequiredItemId[i], true);
  3297.  
  3298. questStatusData.ItemCount[i] = std::min(curitemcount, reqitemcount);
  3299. m_QuestStatusSave[quest->GetQuestId()] = true;
  3300. }
  3301. }
  3302. }
  3303. }
  3304.  
  3305. uint16 Player::FindQuestSlot(uint32 quest_id) const
  3306. {
  3307. for (uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3308. if (GetQuestSlotQuestId(i) == quest_id)
  3309. return i;
  3310.  
  3311. return MAX_QUEST_LOG_SIZE;
  3312. }
  3313.  
  3314. void Player::AreaExploredOrEventHappens(uint32 questId)
  3315. {
  3316. if (questId)
  3317. {
  3318. uint16 log_slot = FindQuestSlot(questId);
  3319. if (log_slot < MAX_QUEST_LOG_SIZE)
  3320. {
  3321. QuestStatusData& q_status = m_QuestStatus[questId];
  3322.  
  3323. if (!q_status.Explored)
  3324. {
  3325. q_status.Explored = true;
  3326. m_QuestStatusSave[questId] = true;
  3327. }
  3328. }
  3329. if (CanCompleteQuest(questId))
  3330. CompleteQuest(questId);
  3331. }
  3332. }
  3333.  
  3334. //not used in Trinityd, function for external script library
  3335. void Player::GroupEventHappens(uint32 questId, WorldObject const* pEventObject)
  3336. {
  3337. if (Group* group = GetGroup())
  3338. {
  3339. for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  3340. {
  3341. Player* player = itr->getSource();
  3342.  
  3343. // for any leave or dead (with not released body) group member at appropriate distance
  3344. if (player && player->IsAtGroupRewardDistance(pEventObject) && !player->GetCorpse())
  3345. player->AreaExploredOrEventHappens(questId);
  3346. }
  3347. }
  3348. else
  3349. AreaExploredOrEventHappens(questId);
  3350. }
  3351.  
  3352. void Player::ItemAddedQuestCheck(uint32 entry, uint32 count)
  3353. {
  3354. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3355. {
  3356. uint32 questid = GetQuestSlotQuestId(i);
  3357. if (questid == 0)
  3358. continue;
  3359.  
  3360. QuestStatusData& q_status = m_QuestStatus[questid];
  3361.  
  3362. if (q_status.Status != QUEST_STATUS_INCOMPLETE)
  3363. continue;
  3364.  
  3365. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  3366. if (!qInfo || !qInfo->HasFlag(QUEST_TRINITY_FLAGS_DELIVER))
  3367. continue;
  3368.  
  3369. for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
  3370. {
  3371. uint32 reqitem = qInfo->RequiredItemId[j];
  3372. if (reqitem == entry)
  3373. {
  3374. uint32 reqitemcount = qInfo->RequiredItemCount[j];
  3375. uint16 curitemcount = q_status.ItemCount[j];
  3376. if (curitemcount < reqitemcount)
  3377. {
  3378. uint16 additemcount = curitemcount + count <= reqitemcount ? count : reqitemcount - curitemcount;
  3379. q_status.ItemCount[j] += additemcount;
  3380.  
  3381. m_QuestStatusSave[questid] = true;
  3382.  
  3383. SendQuestUpdateAddItem(qInfo, j, additemcount);
  3384. }
  3385. if (CanCompleteQuest(questid))
  3386. CompleteQuest(questid);
  3387. return;
  3388. }
  3389. }
  3390. }
  3391. UpdateForQuestWorldObjects();
  3392. }
  3393.  
  3394. void Player::ItemRemovedQuestCheck(uint32 entry, uint32 count)
  3395. {
  3396. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3397. {
  3398. uint32 questid = GetQuestSlotQuestId(i);
  3399. if (!questid)
  3400. continue;
  3401. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  3402. if (!qInfo)
  3403. continue;
  3404. if (!qInfo->HasFlag(QUEST_TRINITY_FLAGS_DELIVER))
  3405. continue;
  3406.  
  3407. for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
  3408. {
  3409. uint32 reqitem = qInfo->RequiredItemId[j];
  3410. if (reqitem == entry)
  3411. {
  3412. QuestStatusData& q_status = m_QuestStatus[questid];
  3413.  
  3414. uint32 reqitemcount = qInfo->RequiredItemCount[j];
  3415. uint16 curitemcount;
  3416. if (q_status.Status != QUEST_STATUS_COMPLETE)
  3417. curitemcount = q_status.ItemCount[j];
  3418. else
  3419. curitemcount = GetItemCount(entry, true);
  3420. if (curitemcount < reqitemcount + count)
  3421. {
  3422. uint16 remitemcount = curitemcount <= reqitemcount ? count : count + reqitemcount - curitemcount;
  3423. q_status.ItemCount[j] = (curitemcount <= remitemcount) ? 0 : curitemcount - remitemcount;
  3424.  
  3425. m_QuestStatusSave[questid] = true;
  3426.  
  3427. IncompleteQuest(questid);
  3428. }
  3429. return;
  3430. }
  3431. }
  3432. }
  3433. UpdateForQuestWorldObjects();
  3434. }
  3435.  
  3436. void Player::KilledMonster(CreatureTemplate const* cInfo, uint64 guid)
  3437. {
  3438. if (cInfo->Entry)
  3439. KilledMonsterCredit(cInfo->Entry, guid);
  3440.  
  3441. for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
  3442. if (cInfo->KillCredit[i])
  3443. KilledMonsterCredit(cInfo->KillCredit[i], 0);
  3444. }
  3445.  
  3446. void Player::KilledMonsterCredit(uint32 entry, uint64 guid)
  3447. {
  3448. uint16 addkillcount = 1;
  3449. uint32 real_entry = entry;
  3450. if (guid)
  3451. {
  3452. Creature* killed = GetMap()->GetCreature(guid);
  3453. if (killed && killed->GetEntry())
  3454. real_entry = killed->GetEntry();
  3455. }
  3456.  
  3457. GetAchievementMgr().StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_CREATURE, real_entry); // MUST BE CALLED FIRST
  3458. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, real_entry, addkillcount, guid ? GetMap()->GetCreature(guid) : NULL);
  3459.  
  3460. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3461. {
  3462. uint32 questid = GetQuestSlotQuestId(i);
  3463. if (!questid)
  3464. continue;
  3465.  
  3466. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  3467. if (!qInfo)
  3468. continue;
  3469. // just if !ingroup || !noraidgroup || raidgroup
  3470. QuestStatusData& q_status = m_QuestStatus[questid];
  3471. if (q_status.Status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->IsAllowedInRaid()))
  3472. {
  3473. if (qInfo->HasFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST))
  3474. {
  3475. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  3476. {
  3477. // skip GO activate objective or none
  3478. if (qInfo->RequiredNpcOrGo[j] <= 0)
  3479. continue;
  3480.  
  3481. // skip Cast at creature objective
  3482. if (qInfo->RequiredSpellCast[j] != 0)
  3483. continue;
  3484.  
  3485. uint32 reqkill = qInfo->RequiredNpcOrGo[j];
  3486.  
  3487. if (reqkill == real_entry)
  3488. {
  3489. uint32 reqkillcount = qInfo->RequiredNpcOrGoCount[j];
  3490. uint16 curkillcount = q_status.CreatureOrGOCount[j];
  3491. if (curkillcount < reqkillcount)
  3492. {
  3493. q_status.CreatureOrGOCount[j] = curkillcount + addkillcount;
  3494.  
  3495. m_QuestStatusSave[questid] = true;
  3496.  
  3497. SendQuestUpdateAddCreatureOrGo(qInfo, guid, j, curkillcount, addkillcount);
  3498. }
  3499. if (CanCompleteQuest(questid))
  3500. CompleteQuest(questid);
  3501.  
  3502. // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
  3503. break;
  3504. }
  3505. }
  3506. }
  3507. }
  3508. }
  3509. }
  3510.  
  3511. void Player::KilledPlayerCredit()
  3512. {
  3513. uint16 addkillcount = 1;
  3514.  
  3515. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3516. {
  3517. uint32 questid = GetQuestSlotQuestId(i);
  3518. if (!questid)
  3519. continue;
  3520.  
  3521. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  3522. if (!qInfo)
  3523. continue;
  3524. // just if !ingroup || !noraidgroup || raidgroup
  3525. QuestStatusData& q_status = m_QuestStatus[questid];
  3526. if (q_status.Status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->IsAllowedInRaid()))
  3527. {
  3528. if (qInfo->HasFlag(QUEST_TRINITY_FLAGS_PLAYER_KILL))
  3529. {
  3530. uint32 reqkill = qInfo->GetPlayersSlain();
  3531. uint16 curkill = q_status.PlayerCount;
  3532.  
  3533. if (curkill < reqkill)
  3534. {
  3535. q_status.PlayerCount = curkill + addkillcount;
  3536.  
  3537. m_QuestStatusSave[questid] = true;
  3538.  
  3539. SendQuestUpdateAddPlayer(qInfo, curkill, addkillcount);
  3540. }
  3541.  
  3542. if (CanCompleteQuest(questid))
  3543. CompleteQuest(questid);
  3544.  
  3545. break;
  3546. }
  3547. }
  3548. }
  3549. }
  3550.  
  3551. void Player::CastedCreatureOrGO(uint32 entry, uint64 guid, uint32 spell_id)
  3552. {
  3553. bool isCreature = IS_CRE_OR_VEH_GUID(guid);
  3554.  
  3555. uint16 addCastCount = 1;
  3556. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3557. {
  3558. uint32 questid = GetQuestSlotQuestId(i);
  3559. if (!questid)
  3560. continue;
  3561.  
  3562. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  3563. if (!qInfo)
  3564. continue;
  3565.  
  3566. QuestStatusData& q_status = m_QuestStatus[questid];
  3567.  
  3568. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  3569. {
  3570. if (qInfo->HasFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST))
  3571. {
  3572. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  3573. {
  3574. // skip kill creature objective (0) or wrong spell casts
  3575. if (qInfo->RequiredSpellCast[j] != spell_id)
  3576. continue;
  3577.  
  3578. uint32 reqTarget = 0;
  3579.  
  3580. if (isCreature)
  3581. {
  3582. // creature activate objectives
  3583. if (qInfo->RequiredNpcOrGo[j] > 0)
  3584. {
  3585. // checked at quest_template loading
  3586. reqTarget = qInfo->RequiredNpcOrGo[j];
  3587. if (reqTarget != entry) // if entry doesn't match, check for killcredits referenced in template
  3588. {
  3589. CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry);
  3590. for (uint8 k = 0; k < MAX_KILL_CREDIT; ++k)
  3591. if (cinfo->KillCredit[k] == reqTarget)
  3592. entry = cinfo->KillCredit[k];
  3593. }
  3594. }
  3595. }
  3596. else
  3597. {
  3598. // GO activate objective
  3599. if (qInfo->RequiredNpcOrGo[j] < 0)
  3600. // checked at quest_template loading
  3601. reqTarget = - qInfo->RequiredNpcOrGo[j];
  3602. }
  3603.  
  3604. // other not this creature/GO related objectives
  3605. if (reqTarget != entry)
  3606. continue;
  3607.  
  3608. uint32 reqCastCount = qInfo->RequiredNpcOrGoCount[j];
  3609. uint16 curCastCount = q_status.CreatureOrGOCount[j];
  3610. if (curCastCount < reqCastCount)
  3611. {
  3612. q_status.CreatureOrGOCount[j] = curCastCount + addCastCount;
  3613.  
  3614. m_QuestStatusSave[questid] = true;
  3615.  
  3616. SendQuestUpdateAddCreatureOrGo(qInfo, guid, j, curCastCount, addCastCount);
  3617. }
  3618.  
  3619. if (CanCompleteQuest(questid))
  3620. CompleteQuest(questid);
  3621.  
  3622. // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
  3623. break;
  3624. }
  3625. }
  3626. }
  3627. }
  3628. }
  3629.  
  3630. void Player::TalkedToCreature(uint32 entry, uint64 guid)
  3631. {
  3632. uint16 addTalkCount = 1;
  3633. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3634. {
  3635. uint32 questid = GetQuestSlotQuestId(i);
  3636. if (!questid)
  3637. continue;
  3638.  
  3639. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  3640. if (!qInfo)
  3641. continue;
  3642.  
  3643. QuestStatusData& q_status = m_QuestStatus[questid];
  3644.  
  3645. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  3646. {
  3647. if (qInfo->HasFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST | QUEST_TRINITY_FLAGS_SPEAKTO))
  3648. {
  3649. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  3650. {
  3651. // skip spell casts and Gameobject objectives
  3652. if (qInfo->RequiredSpellCast[j] > 0 || qInfo->RequiredNpcOrGo[j] < 0)
  3653. continue;
  3654.  
  3655. uint32 reqTarget = 0;
  3656.  
  3657. if (qInfo->RequiredNpcOrGo[j] > 0) // creature activate objectives
  3658. // checked at quest_template loading
  3659. reqTarget = qInfo->RequiredNpcOrGo[j];
  3660. else
  3661. continue;
  3662.  
  3663. if (reqTarget == entry)
  3664. {
  3665. uint32 reqTalkCount = qInfo->RequiredNpcOrGoCount[j];
  3666. uint16 curTalkCount = q_status.CreatureOrGOCount[j];
  3667. if (curTalkCount < reqTalkCount)
  3668. {
  3669. q_status.CreatureOrGOCount[j] = curTalkCount + addTalkCount;
  3670.  
  3671. m_QuestStatusSave[questid] = true;
  3672.  
  3673. SendQuestUpdateAddCreatureOrGo(qInfo, guid, j, curTalkCount, addTalkCount);
  3674. }
  3675. if (CanCompleteQuest(questid))
  3676. CompleteQuest(questid);
  3677.  
  3678. // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
  3679. continue;
  3680. }
  3681. }
  3682. }
  3683. }
  3684. }
  3685. }
  3686.  
  3687. void Player::MoneyChanged(uint32 count)
  3688. {
  3689. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3690. {
  3691. uint32 questid = GetQuestSlotQuestId(i);
  3692. if (!questid)
  3693. continue;
  3694.  
  3695. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  3696. if (qInfo && qInfo->GetRewOrReqMoney() < 0)
  3697. {
  3698. QuestStatusData& q_status = m_QuestStatus[questid];
  3699.  
  3700. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  3701. {
  3702. if (int32(count) >= -qInfo->GetRewOrReqMoney())
  3703. {
  3704. if (CanCompleteQuest(questid))
  3705. CompleteQuest(questid);
  3706. }
  3707. }
  3708. else if (q_status.Status == QUEST_STATUS_COMPLETE)
  3709. {
  3710. if (int32(count) < -qInfo->GetRewOrReqMoney())
  3711. IncompleteQuest(questid);
  3712. }
  3713. }
  3714. }
  3715. }
  3716.  
  3717. void Player::ReputationChanged(FactionEntry const* factionEntry)
  3718. {
  3719. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3720. {
  3721. if (uint32 questid = GetQuestSlotQuestId(i))
  3722. {
  3723. if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid))
  3724. {
  3725. if (qInfo->GetRepObjectiveFaction() == factionEntry->ID)
  3726. {
  3727. QuestStatusData& q_status = m_QuestStatus[questid];
  3728. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  3729. {
  3730. if (GetReputationMgr().GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue())
  3731. if (CanCompleteQuest(questid))
  3732. CompleteQuest(questid);
  3733. }
  3734. else if (q_status.Status == QUEST_STATUS_COMPLETE)
  3735. {
  3736. if (GetReputationMgr().GetReputation(factionEntry) < qInfo->GetRepObjectiveValue())
  3737. IncompleteQuest(questid);
  3738. }
  3739. }
  3740. }
  3741. }
  3742. }
  3743. }
  3744.  
  3745. void Player::ReputationChanged2(FactionEntry const* factionEntry)
  3746. {
  3747. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3748. {
  3749. if (uint32 questid = GetQuestSlotQuestId(i))
  3750. {
  3751. if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid))
  3752. {
  3753. if (qInfo->GetRepObjectiveFaction2() == factionEntry->ID)
  3754. {
  3755. QuestStatusData& q_status = m_QuestStatus[questid];
  3756. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  3757. {
  3758. if (GetReputationMgr().GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue2())
  3759. if (CanCompleteQuest(questid))
  3760. CompleteQuest(questid);
  3761. }
  3762. else if (q_status.Status == QUEST_STATUS_COMPLETE)
  3763. {
  3764. if (GetReputationMgr().GetReputation(factionEntry) < qInfo->GetRepObjectiveValue2())
  3765. IncompleteQuest(questid);
  3766. }
  3767. }
  3768. }
  3769. }
  3770. }
  3771. }
  3772.  
  3773. bool Player::HasQuestForItem(uint32 itemid) const
  3774. {
  3775. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  3776. {
  3777. uint32 questid = GetQuestSlotQuestId(i);
  3778. if (questid == 0)
  3779. continue;
  3780.  
  3781. QuestStatusMap::const_iterator qs_itr = m_QuestStatus.find(questid);
  3782. if (qs_itr == m_QuestStatus.end())
  3783. continue;
  3784.  
  3785. QuestStatusData const& q_status = qs_itr->second;
  3786.  
  3787. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  3788. {
  3789. Quest const* qinfo = sObjectMgr->GetQuestTemplate(questid);
  3790. if (!qinfo)
  3791. continue;
  3792.  
  3793. // hide quest if player is in raid-group and quest is no raid quest
  3794. if (GetGroup() && GetGroup()->isRaidGroup() && !qinfo->IsAllowedInRaid())
  3795. if (!InBattleground()) //there are two ways.. we can make every bg-quest a raidquest, or add this code here.. i don't know if this can be exploited by other quests, but i think all other quests depend on a specific area.. but keep this in mind, if something strange happens later
  3796. continue;
  3797.  
  3798. // There should be no mixed ReqItem/ReqSource drop
  3799. // This part for ReqItem drop
  3800. for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
  3801. {
  3802. if (itemid == qinfo->RequiredItemId[j] && q_status.ItemCount[j] < qinfo->RequiredItemCount[j])
  3803. return true;
  3804. }
  3805. // This part - for ReqSource
  3806. for (uint8 j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
  3807. {
  3808. // examined item is a source item
  3809. if (qinfo->RequiredSourceItemId[j] == itemid)
  3810. {
  3811. ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid);
  3812.  
  3813. // 'unique' item
  3814. if (pProto->MaxCount && int32(GetItemCount(itemid, true)) < pProto->MaxCount)
  3815. return true;
  3816.  
  3817. // allows custom amount drop when not 0
  3818. if (qinfo->RequiredSourceItemCount[j])
  3819. {
  3820. if (GetItemCount(itemid, true) < qinfo->RequiredSourceItemCount[j])
  3821. return true;
  3822. } else if (GetItemCount(itemid, true) < pProto->GetMaxStackSize())
  3823. return true;
  3824. }
  3825. }
  3826. }
  3827. }
  3828. return false;
  3829. }
  3830.  
  3831. void Player::SendQuestComplete(uint32 quest_id)
  3832. {
  3833. if (quest_id)
  3834. {
  3835. WorldPacket data(SMSG_QUESTUPDATE_COMPLETE, 4);
  3836. data << uint32(quest_id);
  3837. GetSession()->SendPacket(&data);
  3838. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = %u", quest_id);
  3839. }
  3840. }
  3841.  
  3842. void Player::SendQuestReward(Quest const* quest, uint32 XP, Object* questGiver)
  3843. {
  3844. uint32 questid = quest->GetQuestId();
  3845. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questid);
  3846. sGameEventMgr->HandleQuestComplete(questid);
  3847. WorldPacket data(SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4));
  3848. data << uint32(questid);
  3849.  
  3850. if (getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  3851. {
  3852. data << uint32(XP);
  3853. data << uint32(quest->GetRewOrReqMoney());
  3854. }
  3855. else
  3856. {
  3857. data << uint32(0);
  3858. data << uint32(quest->GetRewOrReqMoney() + int32(quest->GetRewMoneyMaxLevel() * sWorld->getRate(RATE_DROP_MONEY)));
  3859. }
  3860.  
  3861. data << 10 * Trinity::Honor::hk_honor_at_level(getLevel(), quest->GetRewHonorMultiplier());
  3862. data << uint32(quest->GetBonusTalents()); // bonus talents
  3863. data << uint32(quest->GetRewArenaPoints());
  3864. GetSession()->SendPacket(&data);
  3865.  
  3866. if (quest->GetQuestCompleteScript() != 0)
  3867. GetMap()->ScriptsStart(sQuestEndScripts, quest->GetQuestCompleteScript(), questGiver, this);
  3868. }
  3869.  
  3870. void Player::SendQuestFailed(uint32 questId, InventoryResult reason)
  3871. {
  3872. if (questId)
  3873. {
  3874. WorldPacket data(SMSG_QUESTGIVER_QUEST_FAILED, 4 + 4);
  3875. data << uint32(questId);
  3876. data << uint32(reason); // failed reason (valid reasons: 4, 16, 50, 17, 74, other values show default message)
  3877. GetSession()->SendPacket(&data);
  3878. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED");
  3879. }
  3880. }
  3881.  
  3882. void Player::SendQuestTimerFailed(uint32 quest_id)
  3883. {
  3884. if (quest_id)
  3885. {
  3886. WorldPacket data(SMSG_QUESTUPDATE_FAILEDTIMER, 4);
  3887. data << uint32(quest_id);
  3888. GetSession()->SendPacket(&data);
  3889. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
  3890. }
  3891. }
  3892.  
  3893. void Player::SendCanTakeQuestResponse(uint32 msg) const
  3894. {
  3895. WorldPacket data(SMSG_QUESTGIVER_QUEST_INVALID, 4);
  3896. data << uint32(msg);
  3897. GetSession()->SendPacket(&data);
  3898. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID");
  3899. }
  3900.  
  3901. void Player::SendQuestConfirmAccept(const Quest* quest, Player* pReceiver)
  3902. {
  3903. if (pReceiver)
  3904. {
  3905. std::string strTitle = quest->GetTitle();
  3906.  
  3907. int loc_idx = pReceiver->GetSession()->GetSessionDbLocaleIndex();
  3908. if (loc_idx >= 0)
  3909. if (const QuestLocale* pLocale = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
  3910. ObjectMgr::GetLocaleString(pLocale->Title, loc_idx, strTitle);
  3911.  
  3912. WorldPacket data(SMSG_QUEST_CONFIRM_ACCEPT, (4 + strTitle.size() + 8));
  3913. data << uint32(quest->GetQuestId());
  3914. data << strTitle;
  3915. data << uint64(GetGUID());
  3916. pReceiver->GetSession()->SendPacket(&data);
  3917.  
  3918. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUEST_CONFIRM_ACCEPT");
  3919. }
  3920. }
  3921.  
  3922. void Player::SendPushToPartyResponse(Player* player, uint32 msg)
  3923. {
  3924. if (player)
  3925. {
  3926. WorldPacket data(MSG_QUEST_PUSH_RESULT, (8+1));
  3927. data << uint64(player->GetGUID());
  3928. data << uint8(msg); // valid values: 0-8
  3929. GetSession()->SendPacket(&data);
  3930. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent MSG_QUEST_PUSH_RESULT");
  3931. }
  3932. }
  3933.  
  3934. void Player::SendQuestUpdateAddItem(Quest const* /*quest*/, uint32 /*item_idx*/, uint16 /*count*/)
  3935. {
  3936. WorldPacket data(SMSG_QUESTUPDATE_ADD_ITEM, 0);
  3937. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTUPDATE_ADD_ITEM");
  3938. //data << quest->RequiredItemId[item_idx];
  3939. //data << count;
  3940. GetSession()->SendPacket(&data);
  3941. }
  3942.  
  3943. void Player::SendQuestUpdateAddCreatureOrGo(Quest const* quest, uint64 guid, uint32 creatureOrGO_idx, uint16 old_count, uint16 add_count)
  3944. {
  3945. ASSERT(old_count + add_count < 65536 && "mob/GO count store in 16 bits 2^16 = 65536 (0..65536)");
  3946.  
  3947. int32 entry = quest->RequiredNpcOrGo[ creatureOrGO_idx ];
  3948. if (entry < 0)
  3949. // client expected gameobject template id in form (id|0x80000000)
  3950. entry = (-entry) | 0x80000000;
  3951.  
  3952. WorldPacket data(SMSG_QUESTUPDATE_ADD_KILL, (4*4+8));
  3953. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTUPDATE_ADD_KILL");
  3954. data << uint32(quest->GetQuestId());
  3955. data << uint32(entry);
  3956. data << uint32(old_count + add_count);
  3957. data << uint32(quest->RequiredNpcOrGoCount[ creatureOrGO_idx ]);
  3958. data << uint64(guid);
  3959. GetSession()->SendPacket(&data);
  3960.  
  3961. uint16 log_slot = FindQuestSlot(quest->GetQuestId());
  3962. if (log_slot < MAX_QUEST_LOG_SIZE)
  3963. SetQuestSlotCounter(log_slot, creatureOrGO_idx, GetQuestSlotCounter(log_slot, creatureOrGO_idx)+add_count);
  3964. }
  3965.  
  3966. void Player::SendQuestUpdateAddPlayer(Quest const* quest, uint16 old_count, uint16 add_count)
  3967. {
  3968. ASSERT(old_count + add_count < 65536 && "player count store in 16 bits");
  3969.  
  3970. WorldPacket data(SMSG_QUESTUPDATE_ADD_PVP_KILL, (3*4));
  3971. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTUPDATE_ADD_PVP_KILL");
  3972. data << uint32(quest->GetQuestId());
  3973. data << uint32(old_count + add_count);
  3974. data << uint32(quest->GetPlayersSlain());
  3975. GetSession()->SendPacket(&data);
  3976.  
  3977. uint16 log_slot = FindQuestSlot(quest->GetQuestId());
  3978. if (log_slot < MAX_QUEST_LOG_SIZE)
  3979. SetQuestSlotCounter(log_slot, QUEST_PVP_KILL_SLOT, GetQuestSlotCounter(log_slot, QUEST_PVP_KILL_SLOT) + add_count);
  3980. }
  3981.  
  3982. /*********************************************************/
  3983. /*** LOAD SYSTEM ***/
  3984. /*********************************************************/
  3985.  
  3986. void Player::Initialize(uint32 guid)
  3987. {
  3988. Object::_Create(guid, 0, HIGHGUID_PLAYER);
  3989. }
  3990.  
  3991. void Player::_LoadDeclinedNames(PreparedQueryResult result)
  3992. {
  3993. if (!result)
  3994. return;
  3995.  
  3996. delete m_declinedname;
  3997. m_declinedname = new DeclinedName;
  3998. for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
  3999. m_declinedname->name[i] = (*result)[i].GetString();
  4000. }
  4001.  
  4002. void Player::_LoadArenaTeamInfo(PreparedQueryResult result)
  4003. {
  4004. // arenateamid, played_week, played_season, personal_rating
  4005. memset((void*)&m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1], 0, sizeof(uint32) * MAX_ARENA_SLOT * ARENA_TEAM_END);
  4006.  
  4007. uint16 personalRatingCache[] = {0, 0, 0};
  4008.  
  4009. if (result)
  4010. {
  4011. do
  4012. {
  4013. Field* fields = result->Fetch();
  4014.  
  4015. uint32 arenaTeamId = fields[0].GetUInt32();
  4016.  
  4017. ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
  4018. if (!arenaTeam)
  4019. {
  4020. sLog->outError(LOG_FILTER_PLAYER, "Player::_LoadArenaTeamInfo: couldn't load arenateam %u", arenaTeamId);
  4021. continue;
  4022. }
  4023.  
  4024. uint8 arenaSlot = arenaTeam->GetSlot();
  4025.  
  4026. personalRatingCache[arenaSlot] = fields[4].GetUInt16();
  4027.  
  4028. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_ID, arenaTeamId);
  4029. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_TYPE, arenaTeam->GetType());
  4030. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_MEMBER, (arenaTeam->GetCaptain() == GetGUID()) ? 0 : 1);
  4031. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_WEEK, uint32(fields[1].GetUInt16()));
  4032. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_SEASON, uint32(fields[2].GetUInt16()));
  4033. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_WINS_SEASON, uint32(fields[3].GetUInt16()));
  4034. }
  4035. while (result->NextRow());
  4036. }
  4037.  
  4038. for (uint8 slot = 0; slot <= 2; ++slot)
  4039. {
  4040. SetArenaTeamInfoField(slot, ARENA_TEAM_PERSONAL_RATING, uint32(personalRatingCache[slot]));
  4041. }
  4042. }
  4043.  
  4044. void Player::_LoadEquipmentSets(PreparedQueryResult result)
  4045. {
  4046. // SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = '%u' ORDER BY setindex", GUID_LOPART(m_guid));
  4047. if (!result)
  4048. return;
  4049.  
  4050. uint32 count = 0;
  4051. do
  4052. {
  4053. Field* fields = result->Fetch();
  4054. EquipmentSet eqSet;
  4055.  
  4056. eqSet.Guid = fields[0].GetUInt64();
  4057. uint8 index = fields[1].GetUInt8();
  4058. eqSet.Name = fields[2].GetString();
  4059. eqSet.IconName = fields[3].GetString();
  4060. eqSet.IgnoreMask = fields[4].GetUInt32();
  4061. eqSet.state = EQUIPMENT_SET_UNCHANGED;
  4062.  
  4063. for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
  4064. eqSet.Items[i] = fields[5+i].GetUInt32();
  4065.  
  4066. m_EquipmentSets[index] = eqSet;
  4067.  
  4068. ++count;
  4069.  
  4070. if (count >= MAX_EQUIPMENT_SET_INDEX) // client limit
  4071. break;
  4072. }
  4073. while (result->NextRow());
  4074. }
  4075.  
  4076. void Player::_LoadBGData(PreparedQueryResult result)
  4077. {
  4078. if (!result)
  4079. return;
  4080.  
  4081. Field* fields = result->Fetch();
  4082. // Expecting only one row
  4083. // 0 1 2 3 4 5 6 7 8 9
  4084. // SELECT instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell FROM character_battleground_data WHERE guid = ?
  4085.  
  4086. m_bgData.bgInstanceID = fields[0].GetUInt32();
  4087. m_bgData.bgTeam = fields[1].GetUInt16();
  4088. m_bgData.joinPos = WorldLocation(fields[6].GetUInt16(), // Map
  4089. fields[2].GetFloat(), // X
  4090. fields[3].GetFloat(), // Y
  4091. fields[4].GetFloat(), // Z
  4092. fields[5].GetFloat()); // Orientation
  4093. m_bgData.taxiPath[0] = fields[7].GetUInt32();
  4094. m_bgData.taxiPath[1] = fields[8].GetUInt32();
  4095. m_bgData.mountSpell = fields[9].GetUInt32();
  4096. }
  4097.  
  4098. bool Player::LoadPositionFromDB(uint32& mapid, float& x, float& y, float& z, float& o, bool& in_flight, uint64 guid)
  4099. {
  4100. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_POSITION);
  4101. stmt->setUInt32(0, GUID_LOPART(guid));
  4102. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  4103.  
  4104. if (!result)
  4105. return false;
  4106.  
  4107. Field* fields = result->Fetch();
  4108.  
  4109. x = fields[0].GetFloat();
  4110. y = fields[1].GetFloat();
  4111. z = fields[2].GetFloat();
  4112. o = fields[3].GetFloat();
  4113. mapid = fields[4].GetUInt16();
  4114. in_flight = !fields[5].GetString().empty();
  4115.  
  4116. return true;
  4117. }
  4118.  
  4119. void Player::SetHomebind(WorldLocation const& /*loc*/, uint32 /*area_id*/)
  4120. {
  4121. m_homebindMapId = GetMapId();
  4122. m_homebindAreaId = GetAreaId();
  4123. m_homebindX = GetPositionX();
  4124. m_homebindY = GetPositionY();
  4125. m_homebindZ = GetPositionZ();
  4126.  
  4127. // update sql homebind
  4128. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PLAYER_HOMEBIND);
  4129. stmt->setUInt16(0, m_homebindMapId);
  4130. stmt->setUInt16(1, m_homebindAreaId);
  4131. stmt->setFloat (2, m_homebindX);
  4132. stmt->setFloat (3, m_homebindY);
  4133. stmt->setFloat (4, m_homebindZ);
  4134. stmt->setUInt32(5, GetGUIDLow());
  4135. CharacterDatabase.Execute(stmt);
  4136. }
  4137.  
  4138. uint32 Player::GetUInt32ValueFromArray(Tokens const& data, uint16 index)
  4139. {
  4140. if (index >= data.size())
  4141. return 0;
  4142.  
  4143. return (uint32)atoi(data[index]);
  4144. }
  4145.  
  4146. float Player::GetFloatValueFromArray(Tokens const& data, uint16 index)
  4147. {
  4148. float result;
  4149. uint32 temp = Player::GetUInt32ValueFromArray(data, index);
  4150. memcpy(&result, &temp, sizeof(result));
  4151.  
  4152. return result;
  4153. }
  4154.  
  4155. bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder)
  4156. {
  4157. //// 0 1 2 3 4 5 6 7 8 9 10 11
  4158. //QueryResult* result = CharacterDatabase.PQuery("SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, "
  4159. // 12 13 14 15 16 17 18 19 20 21 22 23 24
  4160. //"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, "
  4161. // 25 26 27 28 29 30 31 32 33 34 35 36 37 38
  4162. //"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, "
  4163. // 39 40 41 42 43 44 45 46 47 48 49
  4164. //"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
  4165. // 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
  4166. //"health, power1, power2, power3, power4, power5, power6, power7, instance_id, speccount, activespec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels FROM characters WHERE guid = '%u'", guid);
  4167. PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADFROM);
  4168.  
  4169. if (!result)
  4170. {
  4171. sLog->outError(LOG_FILTER_PLAYER, "Player (GUID: %u) not found in table `characters`, can't load. ", guid);
  4172. return false;
  4173. }
  4174.  
  4175. Field* fields = result->Fetch();
  4176.  
  4177. uint32 dbAccountId = fields[1].GetUInt32();
  4178.  
  4179. // check if the character's account in the db and the logged in account match.
  4180. // player should be able to load/delete character only with correct account!
  4181. if (dbAccountId != GetSession()->GetAccountId())
  4182. {
  4183. sLog->outError(LOG_FILTER_PLAYER, "Player (GUID: %u) loading from wrong account (is: %u, should be: %u)", guid, GetSession()->GetAccountId(), dbAccountId);
  4184. return false;
  4185. }
  4186.  
  4187. if (holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBANNED))
  4188. {
  4189. sLog->outError(LOG_FILTER_PLAYER, "Player (GUID: %u) is banned, can't load.", guid);
  4190. return false;
  4191. }
  4192.  
  4193. Object::_Create(guid, 0, HIGHGUID_PLAYER);
  4194.  
  4195. m_name = fields[2].GetString();
  4196.  
  4197. // check name limitations
  4198. if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS ||
  4199. (AccountMgr::IsPlayerAccount(GetSession()->GetSecurity()) && sObjectMgr->IsReservedName(m_name)))
  4200. {
  4201. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
  4202.  
  4203. stmt->setUInt16(0, uint16(AT_LOGIN_RENAME));
  4204. stmt->setUInt32(1, guid);
  4205.  
  4206. CharacterDatabase.Execute(stmt);
  4207.  
  4208. return false;
  4209. }
  4210. // Cleanup old Wowarmory feeds
  4211. InitWowarmoryFeeds();
  4212. // overwrite possible wrong/corrupted guid
  4213. SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
  4214.  
  4215. uint8 Gender = fields[5].GetUInt8();
  4216. if (!IsValidGender(Gender))
  4217. {
  4218. sLog->outError(LOG_FILTER_PLAYER, "Player (GUID: %u) has wrong gender (%hu), can't be loaded.", guid, Gender);
  4219. return false;
  4220. }
  4221.  
  4222. // overwrite some data fields
  4223. uint32 bytes0 = 0;
  4224. bytes0 |= fields[3].GetUInt8(); // race
  4225. bytes0 |= fields[4].GetUInt8() << 8; // class
  4226. bytes0 |= Gender << 16; // gender
  4227. SetUInt32Value(UNIT_FIELD_BYTES_0, bytes0);
  4228.  
  4229. SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8());
  4230. SetUInt32Value(PLAYER_XP, fields[7].GetUInt32());
  4231.  
  4232. _LoadIntoDataField(fields[61].GetCString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE);
  4233. _LoadIntoDataField(fields[64].GetCString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2);
  4234.  
  4235. SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE);
  4236. SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f);
  4237. SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f);
  4238.  
  4239. // load achievements before anything else to prevent multiple gains for the same achievement/criteria on every loading (as loading does call UpdateAchievementCriteria)
  4240. m_achievementMgr.LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS));
  4241.  
  4242. uint32 money = fields[8].GetUInt32();
  4243. if (money > MAX_MONEY_AMOUNT)
  4244. money = MAX_MONEY_AMOUNT;
  4245. SetMoney(money);
  4246.  
  4247. SetUInt32Value(PLAYER_BYTES, fields[9].GetUInt32());
  4248. SetUInt32Value(PLAYER_BYTES_2, fields[10].GetUInt32());
  4249. SetByteValue(PLAYER_BYTES_3, 0, fields[5].GetUInt8());
  4250. SetByteValue(PLAYER_BYTES_3, 1, fields[49].GetUInt8());
  4251. SetUInt32Value(PLAYER_FLAGS, fields[11].GetUInt32());
  4252. SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[48].GetUInt32());
  4253.  
  4254. SetUInt64Value(PLAYER_FIELD_KNOWN_CURRENCIES, fields[47].GetUInt64());
  4255.  
  4256. SetUInt32Value(PLAYER_AMMO_ID, fields[63].GetUInt32());
  4257.  
  4258. // set which actionbars the client has active - DO NOT REMOVE EVER AGAIN (can be changed though, if it does change fieldwise)
  4259. SetByteValue(PLAYER_FIELD_BYTES, 2, fields[65].GetUInt8());
  4260.  
  4261. InitDisplayIds();
  4262.  
  4263. // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
  4264. for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
  4265. {
  4266. SetUInt64Value(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), 0);
  4267. SetVisibleItemSlot(slot, NULL);
  4268.  
  4269. delete m_items[slot];
  4270. m_items[slot] = NULL;
  4271. }
  4272.  
  4273. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Load Basic value of player %s is: ", m_name.c_str());
  4274. outDebugValues();
  4275.  
  4276. //Need to call it to initialize m_team (m_team can be calculated from race)
  4277. //Other way is to saves m_team into characters table.
  4278. setFactionForRace(getRace());
  4279.  
  4280. // load home bind and check in same time class/race pair, it used later for restore broken positions
  4281. if (!_LoadHomeBind(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND)))
  4282. return false;
  4283.  
  4284. InitPrimaryProfessions(); // to max set before any spell loaded
  4285.  
  4286. // init saved position, and fix it later if problematic
  4287. uint32 transGUID = fields[30].GetUInt32();
  4288. Relocate(fields[12].GetFloat(), fields[13].GetFloat(), fields[14].GetFloat(), fields[16].GetFloat());
  4289. uint32 mapId = fields[15].GetUInt16();
  4290. uint32 instanceId = fields[58].GetUInt32();
  4291.  
  4292. uint32 dungeonDiff = fields[38].GetUInt8() & 0x0F;
  4293. if (dungeonDiff >= MAX_DUNGEON_DIFFICULTY)
  4294. dungeonDiff = DUNGEON_DIFFICULTY_NORMAL;
  4295. uint32 raidDiff = (fields[38].GetUInt8() >> 4) & 0x0F;
  4296. if (raidDiff >= MAX_RAID_DIFFICULTY)
  4297. raidDiff = RAID_DIFFICULTY_10MAN_NORMAL;
  4298. SetDungeonDifficulty(Difficulty(dungeonDiff)); // may be changed in _LoadGroup
  4299. SetRaidDifficulty(Difficulty(raidDiff)); // may be changed in _LoadGroup
  4300.  
  4301. std::string taxi_nodes = fields[37].GetString();
  4302.  
  4303. #define RelocateToHomebind(){ mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ); }
  4304.  
  4305. _LoadGroup(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADGROUP));
  4306.  
  4307. _LoadArenaTeamInfo(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADARENAINFO));
  4308.  
  4309. SetArenaPoints(fields[39].GetUInt32());
  4310.  
  4311. // check arena teams integrity
  4312. for (uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot)
  4313. {
  4314. uint32 arena_team_id = GetArenaTeamId(arena_slot);
  4315. if (!arena_team_id)
  4316. continue;
  4317.  
  4318. if (ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(arena_team_id))
  4319. if (at->IsMember(GetGUID()))
  4320. continue;
  4321.  
  4322. // arena team not exist or not member, cleanup fields
  4323. for (int j = 0; j < 6; ++j)
  4324. SetArenaTeamInfoField(arena_slot, ArenaTeamInfoType(j), 0);
  4325. }
  4326.  
  4327. SetHonorPoints(fields[40].GetUInt32());
  4328. SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, fields[41].GetUInt32());
  4329. SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, fields[42].GetUInt32());
  4330. SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, fields[43].GetUInt32());
  4331. SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[44].GetUInt16());
  4332. SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[45].GetUInt16());
  4333.  
  4334. _LoadBoundInstances(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES));
  4335. _LoadInstanceTimeRestrictions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADINSTANCELOCKTIMES));
  4336. _LoadBGData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBGDATA));
  4337.  
  4338. GetSession()->SetPlayer(this);
  4339. MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
  4340. if (!mapEntry || !IsPositionValid())
  4341. {
  4342. sLog->outError(LOG_FILTER_PLAYER, "Player (guidlow %d) have invalid coordinates (MapId: %u X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.", guid, mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  4343. RelocateToHomebind();
  4344. }
  4345. // Player was saved in Arena or Bg
  4346. else if (mapEntry && mapEntry->IsBattlegroundOrArena())
  4347. {
  4348. Battleground* currentBg = NULL;
  4349. if (m_bgData.bgInstanceID) //saved in Battleground
  4350. currentBg = sBattlegroundMgr->GetBattleground(m_bgData.bgInstanceID, BATTLEGROUND_TYPE_NONE);
  4351.  
  4352. bool player_at_bg = currentBg && currentBg->IsPlayerInBattleground(GetGUID());
  4353.  
  4354. if (player_at_bg && currentBg->GetStatus() != STATUS_WAIT_LEAVE)
  4355. {
  4356. BattlegroundQueueTypeId bgQueueTypeId = sBattlegroundMgr->BGQueueTypeId(currentBg->GetTypeID(), currentBg->GetArenaType());
  4357. AddBattlegroundQueueId(bgQueueTypeId);
  4358.  
  4359. m_bgData.bgTypeID = currentBg->GetTypeID();
  4360.  
  4361. //join player to battleground group
  4362. currentBg->EventPlayerLoggedIn(this);
  4363. currentBg->AddOrSetPlayerToCorrectBgGroup(this, m_bgData.bgTeam);
  4364.  
  4365. SetInviteForBattlegroundQueueType(bgQueueTypeId, currentBg->GetInstanceID());
  4366. }
  4367. // Bg was not found - go to Entry Point
  4368. else
  4369. {
  4370. // leave bg
  4371. if (player_at_bg)
  4372. currentBg->RemovePlayerAtLeave(GetGUID(), false, true);
  4373.  
  4374. // Do not look for instance if bg not found
  4375. const WorldLocation& _loc = GetBattlegroundEntryPoint();
  4376. mapId = _loc.GetMapId(); instanceId = 0;
  4377.  
  4378. if (mapId == MAPID_INVALID) // Battleground Entry Point not found (???)
  4379. {
  4380. sLog->outError(LOG_FILTER_PLAYER, "Player (guidlow %d) was in BG in database, but BG was not found, and entry point was invalid! Teleport to default race/class locations.", guid);
  4381. RelocateToHomebind();
  4382. }
  4383. else
  4384. Relocate(&_loc);
  4385.  
  4386. // We are not in BG anymore
  4387. m_bgData.bgInstanceID = 0;
  4388. }
  4389. }
  4390. // currently we do not support transport in bg
  4391. else if (transGUID)
  4392. {
  4393. m_movementInfo.t_guid = MAKE_NEW_GUID(transGUID, 0, HIGHGUID_MO_TRANSPORT);
  4394. m_movementInfo.t_pos.Relocate(fields[26].GetFloat(), fields[27].GetFloat(), fields[28].GetFloat(), fields[29].GetFloat());
  4395.  
  4396. if (!Trinity::IsValidMapCoord(
  4397. GetPositionX()+m_movementInfo.t_pos.m_positionX, GetPositionY()+m_movementInfo.t_pos.m_positionY,
  4398. GetPositionZ()+m_movementInfo.t_pos.m_positionZ, GetOrientation()+m_movementInfo.t_pos.m_orientation) ||
  4399. // transport size limited
  4400. m_movementInfo.t_pos.m_positionX > 250 || m_movementInfo.t_pos.m_positionY > 250 || m_movementInfo.t_pos.m_positionZ > 250)
  4401. {
  4402. sLog->outError(LOG_FILTER_PLAYER, "Player (guidlow %d) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.",
  4403. guid, GetPositionX()+m_movementInfo.t_pos.m_positionX, GetPositionY()+m_movementInfo.t_pos.m_positionY,
  4404. GetPositionZ()+m_movementInfo.t_pos.m_positionZ, GetOrientation()+m_movementInfo.t_pos.m_orientation);
  4405.  
  4406. RelocateToHomebind();
  4407. }
  4408. else
  4409. {
  4410. for (MapManager::TransportSet::iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter)
  4411. {
  4412. if ((*iter)->GetGUIDLow() == transGUID)
  4413. {
  4414. m_transport = *iter;
  4415. m_transport->AddPassenger(this);
  4416. mapId = (m_transport->GetMapId());
  4417. break;
  4418. }
  4419. }
  4420. if (!m_transport)
  4421. {
  4422. sLog->outError(LOG_FILTER_PLAYER, "Player (guidlow %d) have problems with transport guid (%u). Teleport to bind location.",
  4423. guid, transGUID);
  4424.  
  4425. RelocateToHomebind();
  4426. }
  4427. }
  4428. }
  4429. // currently we do not support taxi in instance
  4430. else if (!taxi_nodes.empty())
  4431. {
  4432. instanceId = 0;
  4433.  
  4434. // Not finish taxi flight path
  4435. if (m_bgData.HasTaxiPath())
  4436. {
  4437. for (int i = 0; i < 2; ++i)
  4438. m_taxi.AddTaxiDestination(m_bgData.taxiPath[i]);
  4439. }
  4440. else if (!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes, GetTeam()))
  4441. {
  4442. // problems with taxi path loading
  4443. TaxiNodesEntry const* nodeEntry = NULL;
  4444. if (uint32 node_id = m_taxi.GetTaxiSource())
  4445. nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
  4446.  
  4447. if (!nodeEntry) // don't know taxi start node, to homebind
  4448. {
  4449. sLog->outError(LOG_FILTER_PLAYER, "Character %u have wrong data in taxi destination list, teleport to homebind.", GetGUIDLow());
  4450. RelocateToHomebind();
  4451. }
  4452. else // have start node, to it
  4453. {
  4454. sLog->outError(LOG_FILTER_PLAYER, "Character %u have too short taxi destination list, teleport to original node.", GetGUIDLow());
  4455. mapId = nodeEntry->map_id;
  4456. Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z, 0.0f);
  4457. }
  4458. m_taxi.ClearTaxiDestinations();
  4459. }
  4460.  
  4461. if (uint32 node_id = m_taxi.GetTaxiSource())
  4462. {
  4463. // save source node as recall coord to prevent recall and fall from sky
  4464. TaxiNodesEntry const* nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
  4465. if (nodeEntry && nodeEntry->map_id == GetMapId())
  4466. {
  4467. ASSERT(nodeEntry); // checked in m_taxi.LoadTaxiDestinationsFromString
  4468. mapId = nodeEntry->map_id;
  4469. Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z, 0.0f);
  4470. }
  4471.  
  4472. // flight will started later
  4473. }
  4474. }
  4475.  
  4476. // Map could be changed before
  4477. mapEntry = sMapStore.LookupEntry(mapId);
  4478. // client without expansion support
  4479. if (mapEntry)
  4480. {
  4481. if (GetSession()->Expansion() < mapEntry->Expansion())
  4482. {
  4483. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player %s using client without required expansion tried login at non accessible map %u", GetName(), mapId);
  4484. RelocateToHomebind();
  4485. }
  4486.  
  4487. // fix crash (because of if (Map* map = _FindMap(instanceId)) in MapInstanced::CreateInstance)
  4488. if (instanceId)
  4489. if (InstanceSave* save = GetInstanceSave(mapId, mapEntry->IsRaid()))
  4490. if (save->GetInstanceId() != instanceId)
  4491. instanceId = 0;
  4492. }
  4493.  
  4494. // NOW player must have valid map
  4495. // load the player's map here if it's not already loaded
  4496. Map* map = sMapMgr->CreateMap(mapId, this);
  4497.  
  4498. if (!map)
  4499. {
  4500. instanceId = 0;
  4501. AreaTrigger const* at = sObjectMgr->GetGoBackTrigger(mapId);
  4502. if (at)
  4503. {
  4504. sLog->outError(LOG_FILTER_PLAYER, "Player (guidlow %d) is teleported to gobacktrigger (Map: %u X: %f Y: %f Z: %f O: %f).", guid, mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  4505. Relocate(at->target_X, at->target_Y, at->target_Z, GetOrientation());
  4506. mapId = at->target_mapId;
  4507. }
  4508. else
  4509. {
  4510. sLog->outError(LOG_FILTER_PLAYER, "Player (guidlow %d) is teleported to home (Map: %u X: %f Y: %f Z: %f O: %f).", guid, mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  4511. RelocateToHomebind();
  4512. }
  4513.  
  4514. map = sMapMgr->CreateMap(mapId, this);
  4515. if (!map)
  4516. {
  4517. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  4518. mapId = info->mapId;
  4519. Relocate(info->positionX, info->positionY, info->positionZ, 0.0f);
  4520. sLog->outError(LOG_FILTER_PLAYER, "Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.", guid, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  4521. map = sMapMgr->CreateMap(mapId, this);
  4522. if (!map)
  4523. {
  4524. sLog->outError(LOG_FILTER_PLAYER, "Player (guidlow %d) has invalid default map coordinates (X: %f Y: %f Z: %f O: %f). or instance couldn't be created", guid, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  4525. return false;
  4526. }
  4527. }
  4528. }
  4529.  
  4530. // if the player is in an instance and it has been reset in the meantime teleport him to the entrance
  4531. if (instanceId && !sInstanceSaveMgr->GetInstanceSave(instanceId) && !map->IsBattlegroundOrArena())
  4532. {
  4533. AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(mapId);
  4534. if (at)
  4535. Relocate(at->target_X, at->target_Y, at->target_Z, at->target_Orientation);
  4536. else
  4537. {
  4538. sLog->outError(LOG_FILTER_PLAYER, "Player %s(GUID: %u) logged in to a reset instance (map: %u) and there is no area-trigger leading to this map. Thus he can't be ported back to the entrance. This _might_ be an exploit attempt.", GetName(), GetGUIDLow(), mapId);
  4539. RelocateToHomebind();
  4540. }
  4541. }
  4542.  
  4543. SetMap(map);
  4544. StoreRaidMapDifficulty();
  4545.  
  4546. // randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE]
  4547. // this must help in case next save after mass player load after server startup
  4548. m_nextSave = urand(m_nextSave/2, m_nextSave*3/2);
  4549.  
  4550. SaveRecallPosition();
  4551.  
  4552. time_t now = time(NULL);
  4553. time_t logoutTime = time_t(fields[22].GetUInt32());
  4554.  
  4555. // since last logout (in seconds)
  4556. uint32 time_diff = uint32(now - logoutTime); //uint64 is excessive for a time_diff in seconds.. uint32 allows for 136~ year difference.
  4557.  
  4558. // set value, including drunk invisibility detection
  4559. // calculate sobering. after 15 minutes logged out, the player will be sober again
  4560. uint8 newDrunkValue = 0;
  4561. if (time_diff < uint32(GetDrunkValue()) * 9)
  4562. newDrunkValue = GetDrunkValue() - time_diff / 9;
  4563.  
  4564. SetDrunkValue(newDrunkValue);
  4565.  
  4566. m_cinematic = fields[18].GetUInt8();
  4567. m_Played_time[PLAYED_TIME_TOTAL]= fields[19].GetUInt32();
  4568. m_Played_time[PLAYED_TIME_LEVEL]= fields[20].GetUInt32();
  4569.  
  4570. m_resetTalentsCost = fields[24].GetUInt32();
  4571. m_resetTalentsTime = time_t(fields[25].GetUInt32());
  4572.  
  4573. m_taxi.LoadTaxiMask(fields[17].GetCString()); // must be before InitTaxiNodesForLevel
  4574.  
  4575. uint32 extraflags = fields[31].GetUInt16();
  4576.  
  4577. m_stableSlots = fields[32].GetUInt8();
  4578. if (m_stableSlots > MAX_PET_STABLES)
  4579. {
  4580. sLog->outError(LOG_FILTER_PLAYER, "Player can have not more %u stable slots, but have in DB %u", MAX_PET_STABLES, uint32(m_stableSlots));
  4581. m_stableSlots = MAX_PET_STABLES;
  4582. }
  4583.  
  4584. m_atLoginFlags = fields[33].GetUInt16();
  4585.  
  4586. // Honor system
  4587. // Update Honor kills data
  4588. m_lastHonorUpdateTime = logoutTime;
  4589. UpdateHonorFields();
  4590.  
  4591. m_deathExpireTime = time_t(fields[36].GetUInt32());
  4592. if (m_deathExpireTime > now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP)
  4593. m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP-1;
  4594.  
  4595. // clear channel spell data (if saved at channel spell casting)
  4596. SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, 0);
  4597. SetUInt32Value(UNIT_CHANNEL_SPELL, 0);
  4598.  
  4599. // clear charm/summon related fields
  4600. SetOwnerGUID(0);
  4601. SetUInt64Value(UNIT_FIELD_CHARMEDBY, 0);
  4602. SetUInt64Value(UNIT_FIELD_CHARM, 0);
  4603. SetUInt64Value(UNIT_FIELD_SUMMON, 0);
  4604. SetUInt64Value(PLAYER_FARSIGHT, 0);
  4605. SetCreatorGUID(0);
  4606.  
  4607. RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE);
  4608.  
  4609. // reset some aura modifiers before aura apply
  4610. SetUInt32Value(PLAYER_TRACK_CREATURES, 0);
  4611. SetUInt32Value(PLAYER_TRACK_RESOURCES, 0);
  4612.  
  4613. // make sure the unit is considered out of combat for proper loading
  4614. ClearInCombat();
  4615.  
  4616. // make sure the unit is considered not in duel for proper loading
  4617. SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
  4618. SetUInt32Value(PLAYER_DUEL_TEAM, 0);
  4619.  
  4620. // reset stats before loading any modifiers
  4621. InitStatsForLevel();
  4622. InitGlyphsForLevel();
  4623. InitTaxiNodesForLevel();
  4624. InitRunes();
  4625.  
  4626. // rest bonus can only be calculated after InitStatsForLevel()
  4627. m_rest_bonus = fields[21].GetFloat();
  4628.  
  4629. if (time_diff > 0)
  4630. {
  4631. //speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour)
  4632. float bubble0 = 0.031f;
  4633. //speed collect rest bonus in offline, in logout, in tavern, city (section/in hour)
  4634. float bubble1 = 0.125f;
  4635. float bubble = fields[23].GetUInt8() > 0
  4636. ? bubble1*sWorld->getRate(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY)
  4637. : bubble0*sWorld->getRate(RATE_REST_OFFLINE_IN_WILDERNESS);
  4638.  
  4639. SetRestBonus(GetRestBonus()+ time_diff*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble);
  4640. }
  4641.  
  4642. // load skills after InitStatsForLevel because it triggering aura apply also
  4643. _LoadSkills(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSKILLS));
  4644. UpdateSkillsForLevel(); //update skills after load, to make sure they are correctly update at player load
  4645.  
  4646. // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
  4647.  
  4648. //mails are loaded only when needed ;-) - when player in game click on mailbox.
  4649. //_LoadMail();
  4650.  
  4651. m_specsCount = fields[59].GetUInt8();
  4652. m_activeSpec = fields[60].GetUInt8();
  4653.  
  4654. // sanity check
  4655. if (m_specsCount > MAX_TALENT_SPECS || m_activeSpec > MAX_TALENT_SPEC || m_specsCount < MIN_TALENT_SPECS)
  4656. {
  4657. m_activeSpec = 0;
  4658. sLog->outError(LOG_FILTER_PLAYER, "Player %s(GUID: %u) has SpecCount = %u and ActiveSpec = %u.", GetName(), GetGUIDLow(), m_specsCount, m_activeSpec);
  4659. }
  4660.  
  4661. _LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADTALENTS));
  4662. _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSPELLS));
  4663.  
  4664. _LoadGlyphs(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADGLYPHS));
  4665. _LoadAuras(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff);
  4666. _LoadGlyphAuras();
  4667. // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
  4668. if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
  4669. m_deathState = DEAD;
  4670.  
  4671. // after spell load, learn rewarded spell if need also
  4672. _LoadQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
  4673. _LoadQuestStatusRewarded(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUSREW));
  4674. _LoadDailyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS));
  4675. _LoadWeeklyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS));
  4676. _LoadSeasonalQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSEASONALQUESTSTATUS));
  4677. _LoadRandomBGStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADRANDOMBG));
  4678.  
  4679. // after spell and quest load
  4680. InitTalentForLevel();
  4681. learnDefaultSpells();
  4682.  
  4683. // must be before inventory (some items required reputation check)
  4684. m_reputationMgr.LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADREPUTATION));
  4685.  
  4686. _LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff);
  4687.  
  4688. // update items with duration and realtime
  4689. UpdateItemDuration(time_diff, true);
  4690.  
  4691. _LoadActions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADACTIONS));
  4692.  
  4693. // unread mails and next delivery time, actual mails not loaded
  4694. _LoadMailInit(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADMAILCOUNT), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADMAILDATE));
  4695.  
  4696. m_social = sSocialMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSOCIALLIST), GetGUIDLow());
  4697.  
  4698. // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES
  4699. // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded
  4700. uint32 curTitle = fields[46].GetUInt32();
  4701. if (curTitle && !HasTitle(curTitle))
  4702. curTitle = 0;
  4703.  
  4704. SetUInt32Value(PLAYER_CHOSEN_TITLE, curTitle);
  4705.  
  4706. // has to be called after last Relocate() in Player::LoadFromDB
  4707. SetFallInformation(0, GetPositionZ());
  4708.  
  4709. _LoadSpellCooldowns(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS));
  4710.  
  4711. // Spell code allow apply any auras to dead character in load time in aura/spell/item loading
  4712. // Do now before stats re-calculation cleanup for ghost state unexpected auras
  4713. if (!isAlive())
  4714. RemoveAllAurasOnDeath();
  4715. else
  4716. RemoveAllAurasRequiringDeadTarget();
  4717.  
  4718. //apply all stat bonuses from items and auras
  4719. SetCanModifyStats(true);
  4720. UpdateAllStats();
  4721.  
  4722. // restore remembered power/health values (but not more max values)
  4723. uint32 savedHealth = fields[50].GetUInt32();
  4724. SetHealth(savedHealth > GetMaxHealth() ? GetMaxHealth() : savedHealth);
  4725. for (uint8 i = 0; i < MAX_POWERS; ++i)
  4726. {
  4727. uint32 savedPower = fields[51+i].GetUInt32();
  4728. SetPower(Powers(i), savedPower > GetMaxPower(Powers(i)) ? GetMaxPower(Powers(i)) : savedPower);
  4729. }
  4730.  
  4731. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "The value of player %s after load item and aura is: ", m_name.c_str());
  4732. outDebugValues();
  4733.  
  4734. // GM state
  4735. if (!AccountMgr::IsPlayerAccount(GetSession()->GetSecurity()))
  4736. {
  4737. switch (sWorld->getIntConfig(CONFIG_GM_LOGIN_STATE))
  4738. {
  4739. default:
  4740. case 0: break; // disable
  4741. case 1: SetGameMaster(true); break; // enable
  4742. case 2: // save state
  4743. if (extraflags & PLAYER_EXTRA_GM_ON)
  4744. SetGameMaster(true);
  4745. break;
  4746. }
  4747.  
  4748. switch (sWorld->getIntConfig(CONFIG_GM_VISIBLE_STATE))
  4749. {
  4750. default:
  4751. case 0: SetGMVisible(false); break; // invisible
  4752. case 1: break; // visible
  4753. case 2: // save state
  4754. if (extraflags & PLAYER_EXTRA_GM_INVISIBLE)
  4755. SetGMVisible(false);
  4756. break;
  4757. }
  4758.  
  4759. switch (sWorld->getIntConfig(CONFIG_GM_CHAT))
  4760. {
  4761. default:
  4762. case 0: break; // disable
  4763. case 1: SetGMChat(true); break; // enable
  4764. case 2: // save state
  4765. if (extraflags & PLAYER_EXTRA_GM_CHAT)
  4766. SetGMChat(true);
  4767. break;
  4768. }
  4769.  
  4770. switch (sWorld->getIntConfig(CONFIG_GM_WHISPERING_TO))
  4771. {
  4772. default:
  4773. case 0: break; // disable
  4774. case 1: SetAcceptWhispers(true); break; // enable
  4775. case 2: // save state
  4776. if (extraflags & PLAYER_EXTRA_ACCEPT_WHISPERS)
  4777. SetAcceptWhispers(true);
  4778. break;
  4779. }
  4780. }
  4781.  
  4782. // RaF stuff.
  4783. m_grantableLevels = fields[66].GetUInt8();
  4784. if (GetSession()->IsARecruiter() || (GetSession()->GetRecruiterId() != 0))
  4785. SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_REFER_A_FRIEND);
  4786.  
  4787. if (m_grantableLevels > 0)
  4788. SetByteValue(PLAYER_FIELD_BYTES, 1, 0x01);
  4789.  
  4790. _LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES));
  4791.  
  4792. m_achievementMgr.CheckAllAchievementCriteria();
  4793.  
  4794. _LoadEquipmentSets(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS));
  4795.  
  4796. return true;
  4797. }
  4798.  
  4799. bool Player::isAllowedToLoot(const Creature* creature)
  4800. {
  4801. if (!creature->isDead() || !creature->IsDamageEnoughForLootingAndReward())
  4802. return false;
  4803.  
  4804. if (HasPendingBind())
  4805. return false;
  4806.  
  4807. const Loot* loot = &creature->loot;
  4808. if (loot->isLooted()) // nothing to loot or everything looted.
  4809. return false;
  4810.  
  4811. Group* thisGroup = GetGroup();
  4812. if (!thisGroup)
  4813. return this == creature->GetLootRecipient();
  4814. else if (thisGroup != creature->GetLootRecipientGroup())
  4815. return false;
  4816.  
  4817. switch (thisGroup->GetLootMethod())
  4818. {
  4819. case FREE_FOR_ALL:
  4820. return true;
  4821. case ROUND_ROBIN:
  4822. case MASTER_LOOT:
  4823. // may only loot if the player is the loot roundrobin player
  4824. // or if there are free/quest/conditional item for the player
  4825. if (loot->roundRobinPlayer == 0 || loot->roundRobinPlayer == GetGUID())
  4826. return true;
  4827.  
  4828. return loot->hasItemFor(this);
  4829. case GROUP_LOOT:
  4830. case NEED_BEFORE_GREED:
  4831. // may only loot if the player is the loot roundrobin player
  4832. // or item over threshold (so roll(s) can be launched)
  4833. // or if there are free/quest/conditional item for the player
  4834. if (loot->roundRobinPlayer == 0 || loot->roundRobinPlayer == GetGUID())
  4835. return true;
  4836.  
  4837. if (loot->hasOverThresholdItem())
  4838. return true;
  4839.  
  4840. return loot->hasItemFor(this);
  4841. }
  4842.  
  4843. return false;
  4844. }
  4845.  
  4846. void Player::_LoadActions(PreparedQueryResult result)
  4847. {
  4848. m_actionButtons.clear();
  4849.  
  4850. if (result)
  4851. {
  4852. do
  4853. {
  4854. Field* fields = result->Fetch();
  4855. uint8 button = fields[0].GetUInt8();
  4856. uint32 action = fields[1].GetUInt32();
  4857. uint8 type = fields[2].GetUInt8();
  4858.  
  4859. if (ActionButton* ab = addActionButton(button, action, type))
  4860. ab->uState = ACTIONBUTTON_UNCHANGED;
  4861. else
  4862. {
  4863. sLog->outError(LOG_FILTER_PLAYER, " ...at loading, and will deleted in DB also");
  4864.  
  4865. // Will deleted in DB at next save (it can create data until save but marked as deleted)
  4866. m_actionButtons[button].uState = ACTIONBUTTON_DELETED;
  4867. }
  4868. } while (result->NextRow());
  4869. }
  4870. }
  4871.  
  4872. void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff)
  4873. {
  4874. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Loading auras for player %u", GetGUIDLow());
  4875.  
  4876. /* 0 1 2 3 4 5 6 7 8 9 10
  4877. QueryResult* result = CharacterDatabase.PQuery("SELECT caster_guid, spell, effect_mask, recalculate_mask, stackcount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2,
  4878. 11 12 13
  4879. maxduration, remaintime, remaincharges FROM character_aura WHERE guid = '%u'", GetGUIDLow());
  4880. */
  4881.  
  4882. if (result)
  4883. {
  4884. do
  4885. {
  4886. Field* fields = result->Fetch();
  4887. int32 damage[3];
  4888. int32 baseDamage[3];
  4889. uint64 caster_guid = fields[0].GetUInt64();
  4890. uint32 spellid = fields[1].GetUInt32();
  4891. uint8 effmask = fields[2].GetUInt8();
  4892. uint8 recalculatemask = fields[3].GetUInt8();
  4893. uint8 stackcount = fields[4].GetUInt8();
  4894. damage[0] = fields[5].GetInt32();
  4895. damage[1] = fields[6].GetInt32();
  4896. damage[2] = fields[7].GetInt32();
  4897. baseDamage[0] = fields[8].GetInt32();
  4898. baseDamage[1] = fields[9].GetInt32();
  4899. baseDamage[2] = fields[10].GetInt32();
  4900. int32 maxduration = fields[11].GetInt32();
  4901. int32 remaintime = fields[12].GetInt32();
  4902. uint8 remaincharges = fields[13].GetUInt8();
  4903.  
  4904. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
  4905. if (!spellInfo)
  4906. {
  4907. sLog->outError(LOG_FILTER_PLAYER, "Unknown aura (spellid %u), ignore.", spellid);
  4908. continue;
  4909. }
  4910.  
  4911. // negative effects should continue counting down after logout
  4912. if (remaintime != -1 && !spellInfo->IsPositive())
  4913. {
  4914. if (remaintime/IN_MILLISECONDS <= int32(timediff))
  4915. continue;
  4916.  
  4917. remaintime -= timediff*IN_MILLISECONDS;
  4918. }
  4919.  
  4920. // prevent wrong values of remaincharges
  4921. if (spellInfo->ProcCharges)
  4922. {
  4923. // we have no control over the order of applying auras and modifiers allow auras
  4924. // to have more charges than value in SpellInfo
  4925. if (remaincharges <= 0/* || remaincharges > spellproto->procCharges*/)
  4926. remaincharges = spellInfo->ProcCharges;
  4927. }
  4928. else
  4929. remaincharges = 0;
  4930.  
  4931. if (Aura* aura = Aura::TryCreate(spellInfo, effmask, this, NULL, &baseDamage[0], NULL, caster_guid))
  4932. {
  4933. if (!aura->CanBeSaved())
  4934. {
  4935. aura->Remove();
  4936. continue;
  4937. }
  4938.  
  4939. aura->SetLoadedState(maxduration, remaintime, remaincharges, stackcount, recalculatemask, &damage[0]);
  4940. aura->ApplyForTargets();
  4941. sLog->outInfo(LOG_FILTER_PLAYER, "Added aura spellid %u, effectmask %u", spellInfo->Id, effmask);
  4942. }
  4943. }
  4944. while (result->NextRow());
  4945. }
  4946. }
  4947.  
  4948. void Player::_LoadGlyphAuras()
  4949. {
  4950. for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
  4951. {
  4952. if (uint32 glyph = GetGlyph(i))
  4953. {
  4954. if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph))
  4955. {
  4956. if (GlyphSlotEntry const* gs = sGlyphSlotStore.LookupEntry(GetGlyphSlot(i)))
  4957. {
  4958. if (gp->TypeFlags == gs->TypeFlags)
  4959. {
  4960. CastSpell(this, gp->SpellId, true);
  4961. continue;
  4962. }
  4963. else
  4964. sLog->outError(LOG_FILTER_PLAYER, "Player %s has glyph with typeflags %u in slot with typeflags %u, removing.", m_name.c_str(), gp->TypeFlags, gs->TypeFlags);
  4965. }
  4966. else
  4967. sLog->outError(LOG_FILTER_PLAYER, "Player %s has not existing glyph slot entry %u on index %u", m_name.c_str(), GetGlyphSlot(i), i);
  4968. }
  4969. else
  4970. sLog->outError(LOG_FILTER_PLAYER, "Player %s has not existing glyph entry %u on index %u", m_name.c_str(), glyph, i);
  4971.  
  4972. // On any error remove glyph
  4973. SetGlyph(i, 0);
  4974. }
  4975. }
  4976. }
  4977.  
  4978. void Player::LoadCorpse()
  4979. {
  4980. if (isAlive())
  4981. sObjectAccessor->ConvertCorpseForPlayer(GetGUID());
  4982. else
  4983. {
  4984. if (Corpse* corpse = GetCorpse())
  4985. ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, corpse && !sMapStore.LookupEntry(corpse->GetMapId())->Instanceable());
  4986. else
  4987. //Prevent Dead Player login without corpse
  4988. ResurrectPlayer(0.5f);
  4989. }
  4990. }
  4991.  
  4992. void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
  4993. {
  4994. //QueryResult* result = CharacterDatabase.PQuery("SELECT data, text, bag, slot, item, item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag, slot", GetGUIDLow());
  4995. //NOTE: the "order by `bag`" is important because it makes sure
  4996. //the bagMap is filled before items in the bags are loaded
  4997. //NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?)
  4998. //expected to be equipped before offhand items (TODO: fixme)
  4999.  
  5000. if (result)
  5001. {
  5002. uint32 zoneId = GetZoneId();
  5003.  
  5004. std::map<uint64, Bag*> bagMap; // fast guid lookup for bags
  5005. std::map<uint64, Item*> invalidBagMap; // fast guid lookup for bags
  5006. std::list<Item*> problematicItems;
  5007. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  5008.  
  5009. // Prevent items from being added to the queue while loading
  5010. m_itemUpdateQueueBlocked = true;
  5011. do
  5012. {
  5013. Field* fields = result->Fetch();
  5014. if (Item* item = _LoadItem(trans, zoneId, timeDiff, fields))
  5015. {
  5016. uint32 bagGuid = fields[11].GetUInt32();
  5017. uint8 slot = fields[12].GetUInt8();
  5018.  
  5019. uint8 err = EQUIP_ERR_OK;
  5020. // Item is not in bag
  5021. if (!bagGuid)
  5022. {
  5023. item->SetContainer(NULL);
  5024. item->SetSlot(slot);
  5025.  
  5026. if (IsInventoryPos(INVENTORY_SLOT_BAG_0, slot))
  5027. {
  5028. ItemPosCountVec dest;
  5029. err = CanStoreItem(INVENTORY_SLOT_BAG_0, slot, dest, item, false);
  5030. if (err == EQUIP_ERR_OK)
  5031. item = StoreItem(dest, item, true);
  5032. }
  5033. else if (IsEquipmentPos(INVENTORY_SLOT_BAG_0, slot))
  5034. {
  5035. uint16 dest;
  5036. err = CanEquipItem(slot, dest, item, false, false);
  5037. if (err == EQUIP_ERR_OK)
  5038. QuickEquipItem(dest, item);
  5039. }
  5040. else if (IsBankPos(INVENTORY_SLOT_BAG_0, slot))
  5041. {
  5042. ItemPosCountVec dest;
  5043. err = CanBankItem(INVENTORY_SLOT_BAG_0, slot, dest, item, false, false);
  5044. if (err == EQUIP_ERR_OK)
  5045. item = BankItem(dest, item, true);
  5046. }
  5047.  
  5048. // Remember bags that may contain items in them
  5049. if (err == EQUIP_ERR_OK)
  5050. {
  5051. if (IsBagPos(item->GetPos()))
  5052. if (Bag* pBag = item->ToBag())
  5053. bagMap[item->GetGUIDLow()] = pBag;
  5054. }
  5055. else
  5056. if (IsBagPos(item->GetPos()))
  5057. if (item->IsBag())
  5058. invalidBagMap[item->GetGUIDLow()] = item;
  5059. }
  5060. else
  5061. {
  5062. item->SetSlot(NULL_SLOT);
  5063. // Item is in the bag, find the bag
  5064. std::map<uint64, Bag*>::iterator itr = bagMap.find(bagGuid);
  5065. if (itr != bagMap.end())
  5066. {
  5067. ItemPosCountVec dest;
  5068. err = CanStoreItem(itr->second->GetSlot(), slot, dest, item);
  5069. if (err == EQUIP_ERR_OK)
  5070. item = StoreItem(dest, item, true);
  5071. }
  5072. else if (invalidBagMap.find(bagGuid) != invalidBagMap.end())
  5073. {
  5074. std::map<uint64, Item*>::iterator itr = invalidBagMap.find(bagGuid);
  5075. if (std::find(problematicItems.begin(),problematicItems.end(),itr->second) != problematicItems.end())
  5076. err = EQUIP_ERR_INT_BAG_ERROR;
  5077. }
  5078. else
  5079. {
  5080. sLog->outError(LOG_FILTER_PLAYER, "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) which doesnt have a valid bag (Bag GUID: %u, slot: %u). Possible cheat?",
  5081. GetGUIDLow(), GetName(), item->GetGUIDLow(), item->GetEntry(), bagGuid, slot);
  5082. item->DeleteFromInventoryDB(trans);
  5083. delete item;
  5084. continue;
  5085. }
  5086.  
  5087. }
  5088.  
  5089. // Item's state may have changed after storing
  5090. if (err == EQUIP_ERR_OK)
  5091. item->SetState(ITEM_UNCHANGED, this);
  5092. else
  5093. {
  5094. sLog->outError(LOG_FILTER_PLAYER, "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) which can't be loaded into inventory (Bag GUID: %u, slot: %u) by reason %u. Item will be sent by mail.",
  5095. GetGUIDLow(), GetName(), item->GetGUIDLow(), item->GetEntry(), bagGuid, slot, err);
  5096. item->DeleteFromInventoryDB(trans);
  5097. problematicItems.push_back(item);
  5098. }
  5099. }
  5100. } while (result->NextRow());
  5101.  
  5102. m_itemUpdateQueueBlocked = false;
  5103.  
  5104. // Send problematic items by mail
  5105. while (!problematicItems.empty())
  5106. {
  5107. std::string subject = GetSession()->GetTrinityString(LANG_NOT_EQUIPPED_ITEM);
  5108.  
  5109. MailDraft draft(subject, "There were problems with equipping item(s).");
  5110. for (uint8 i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i)
  5111. {
  5112. draft.AddItem(problematicItems.front());
  5113. problematicItems.pop_front();
  5114. }
  5115. draft.SendMailTo(trans, this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED);
  5116. }
  5117. CharacterDatabase.CommitTransaction(trans);
  5118. }
  5119. //if (isAlive())
  5120. _ApplyAllItemMods();
  5121. }
  5122.  
  5123. Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields)
  5124. {
  5125. PreparedStatement* stmt = NULL;
  5126. Item* item = NULL;
  5127. uint32 itemGuid = fields[13].GetUInt32();
  5128. uint32 itemEntry = fields[14].GetUInt32();
  5129. if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry))
  5130. {
  5131. bool remove = false;
  5132. item = NewItemOrBag(proto);
  5133. if (item->LoadFromDB(itemGuid, GetGUID(), fields, itemEntry))
  5134. {
  5135. // Do not allow to have item limited to another map/zone in alive state
  5136. if (isAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(), zoneId))
  5137. {
  5138. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::_LoadInventory: player (GUID: %u, name: '%s', map: %u) has item (GUID: %u, entry: %u) limited to another map (%u). Deleting item.",
  5139. GetGUIDLow(), GetName(), GetMapId(), item->GetGUIDLow(), item->GetEntry(), zoneId);
  5140. remove = true;
  5141. }
  5142. // "Conjured items disappear if you are logged out for more than 15 minutes"
  5143. else if (timeDiff > 15 * MINUTE && proto->Flags & ITEM_PROTO_FLAG_CONJURED)
  5144. {
  5145. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::_LoadInventory: player (GUID: %u, name: '%s', diff: %u) has conjured item (GUID: %u, entry: %u) with expired lifetime (15 minutes). Deleting item.",
  5146. GetGUIDLow(), GetName(), timeDiff, item->GetGUIDLow(), item->GetEntry());
  5147. remove = true;
  5148. }
  5149. else if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE))
  5150. {
  5151. if (item->GetPlayedTime() > (2 * HOUR))
  5152. {
  5153. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with expired refund time (%u). Deleting refund data and removing refundable flag.",
  5154. GetGUIDLow(), GetName(), item->GetGUIDLow(), item->GetEntry(), item->GetPlayedTime());
  5155.  
  5156. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
  5157. stmt->setUInt32(0, item->GetGUIDLow());
  5158. trans->Append(stmt);
  5159.  
  5160. item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE);
  5161. }
  5162. else
  5163. {
  5164. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_REFUNDS);
  5165. stmt->setUInt32(0, item->GetGUIDLow());
  5166. stmt->setUInt32(1, GetGUIDLow());
  5167. if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
  5168. {
  5169. item->SetRefundRecipient((*result)[0].GetUInt32());
  5170. item->SetPaidMoney((*result)[1].GetUInt32());
  5171. item->SetPaidExtendedCost((*result)[2].GetUInt16());
  5172. AddRefundReference(item->GetGUIDLow());
  5173. }
  5174. else
  5175. {
  5176. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with refundable flags, but without data in item_refund_instance. Removing flag.",
  5177. GetGUIDLow(), GetName(), item->GetGUIDLow(), item->GetEntry());
  5178. item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE);
  5179. }
  5180. }
  5181. }
  5182. else if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
  5183. {
  5184. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_BOP_TRADE);
  5185. stmt->setUInt32(0, item->GetGUIDLow());
  5186. if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
  5187. {
  5188. std::string strGUID = (*result)[0].GetString();
  5189. Tokens GUIDlist(strGUID, ' ');
  5190. AllowedLooterSet looters;
  5191. for (Tokens::iterator itr = GUIDlist.begin(); itr != GUIDlist.end(); ++itr)
  5192. looters.insert(atol(*itr));
  5193. item->SetSoulboundTradeable(looters);
  5194. AddTradeableItem(item);
  5195. }
  5196. else
  5197. {
  5198. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with ITEM_FLAG_BOP_TRADEABLE flag, but without data in item_soulbound_trade_data. Removing flag.",
  5199. GetGUIDLow(), GetName(), item->GetGUIDLow(), item->GetEntry());
  5200. item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE);
  5201. }
  5202. }
  5203. else if (proto->HolidayId)
  5204. {
  5205. remove = true;
  5206. GameEventMgr::GameEventDataMap const& events = sGameEventMgr->GetEventMap();
  5207. GameEventMgr::ActiveEvents const& activeEventsList = sGameEventMgr->GetActiveEventList();
  5208. for (GameEventMgr::ActiveEvents::const_iterator itr = activeEventsList.begin(); itr != activeEventsList.end(); ++itr)
  5209. {
  5210. if (uint32(events[*itr].holiday_id) == proto->HolidayId)
  5211. {
  5212. remove = false;
  5213. break;
  5214. }
  5215. }
  5216. }
  5217. }
  5218. else
  5219. {
  5220. sLog->outError(LOG_FILTER_PLAYER, "Player::_LoadInventory: player (GUID: %u, name: '%s') has broken item (GUID: %u, entry: %u) in inventory. Deleting item.",
  5221. GetGUIDLow(), GetName(), itemGuid, itemEntry);
  5222. remove = true;
  5223. }
  5224. // Remove item from inventory if necessary
  5225. if (remove)
  5226. {
  5227. Item::DeleteFromInventoryDB(trans, itemGuid);
  5228. item->FSetState(ITEM_REMOVED);
  5229. item->SaveToDB(trans); // it also deletes item object!
  5230. item = NULL;
  5231. }
  5232. }
  5233. else
  5234. {
  5235. sLog->outError(LOG_FILTER_PLAYER, "Player::_LoadInventory: player (GUID: %u, name: '%s') has unknown item (entry: %u) in inventory. Deleting item.",
  5236. GetGUIDLow(), GetName(), itemEntry);
  5237. Item::DeleteFromInventoryDB(trans, itemGuid);
  5238. Item::DeleteFromDB(trans, itemGuid);
  5239. }
  5240. return item;
  5241. }
  5242.  
  5243. // load mailed item which should receive current player
  5244. void Player::_LoadMailedItems(Mail* mail)
  5245. {
  5246. // data needs to be at first place for Item::LoadFromDB
  5247. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS);
  5248. stmt->setUInt32(0, mail->messageID);
  5249. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  5250. if (!result)
  5251. return;
  5252.  
  5253. do
  5254. {
  5255. Field* fields = result->Fetch();
  5256.  
  5257. uint32 itemGuid = fields[11].GetUInt32();
  5258. uint32 itemTemplate = fields[12].GetUInt32();
  5259.  
  5260. mail->AddItem(itemGuid, itemTemplate);
  5261.  
  5262. ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemTemplate);
  5263.  
  5264. if (!proto)
  5265. {
  5266. sLog->outError(LOG_FILTER_PLAYER, "Player %u has unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), itemGuid, itemTemplate, mail->messageID);
  5267.  
  5268. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM);
  5269. stmt->setUInt32(0, itemGuid);
  5270. CharacterDatabase.Execute(stmt);
  5271.  
  5272. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  5273. stmt->setUInt32(0, itemGuid);
  5274. CharacterDatabase.Execute(stmt);
  5275. continue;
  5276. }
  5277.  
  5278. Item* item = NewItemOrBag(proto);
  5279.  
  5280. if (!item->LoadFromDB(itemGuid, MAKE_NEW_GUID(fields[13].GetUInt32(), 0, HIGHGUID_PLAYER), fields, itemTemplate))
  5281. {
  5282. sLog->outError(LOG_FILTER_PLAYER, "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, itemGuid);
  5283.  
  5284. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM);
  5285. stmt->setUInt32(0, itemGuid);
  5286. CharacterDatabase.Execute(stmt);
  5287.  
  5288. item->FSetState(ITEM_REMOVED);
  5289.  
  5290. SQLTransaction temp = SQLTransaction(NULL);
  5291. item->SaveToDB(temp); // it also deletes item object !
  5292. continue;
  5293. }
  5294.  
  5295. AddMItem(item);
  5296. }
  5297. while (result->NextRow());
  5298. }
  5299.  
  5300. void Player::_LoadMailInit(PreparedQueryResult resultUnread, PreparedQueryResult resultDelivery)
  5301. {
  5302. //set a count of unread mails
  5303. //QueryResult* resultMails = CharacterDatabase.PQuery("SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" UI64FMTD "'", GUID_LOPART(playerGuid), (uint64)cTime);
  5304. if (resultUnread)
  5305. unReadMails = uint8((*resultUnread)[0].GetUInt64());
  5306.  
  5307. // store nearest delivery time (it > 0 and if it < current then at next player update SendNewMaill will be called)
  5308. //resultMails = CharacterDatabase.PQuery("SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(playerGuid));
  5309. if (resultDelivery)
  5310. m_nextMailDelivereTime = time_t((*resultDelivery)[0].GetUInt32());
  5311. }
  5312.  
  5313. void Player::_LoadMail()
  5314. {
  5315. m_mail.clear();
  5316.  
  5317. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL);
  5318. stmt->setUInt32(0, GetGUIDLow());
  5319. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  5320.  
  5321. if (result)
  5322. {
  5323. do
  5324. {
  5325. Field* fields = result->Fetch();
  5326. Mail* m = new Mail;
  5327.  
  5328. m->messageID = fields[0].GetUInt32();
  5329. m->messageType = fields[1].GetUInt8();
  5330. m->sender = fields[2].GetUInt32();
  5331. m->receiver = fields[3].GetUInt32();
  5332. m->subject = fields[4].GetString();
  5333. m->body = fields[5].GetString();
  5334. bool has_items = fields[6].GetBool();
  5335. m->expire_time = time_t(fields[7].GetUInt32());
  5336. m->deliver_time = time_t(fields[8].GetUInt32());
  5337. m->money = fields[9].GetUInt32();
  5338. m->COD = fields[10].GetUInt32();
  5339. m->checked = fields[11].GetUInt8();
  5340. m->stationery = fields[12].GetUInt8();
  5341. m->mailTemplateId = fields[13].GetInt16();
  5342.  
  5343. if (m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId))
  5344. {
  5345. sLog->outError(LOG_FILTER_PLAYER, "Player::_LoadMail - Mail (%u) have not existed MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId);
  5346. m->mailTemplateId = 0;
  5347. }
  5348.  
  5349. m->state = MAIL_STATE_UNCHANGED;
  5350.  
  5351. if (has_items)
  5352. _LoadMailedItems(m);
  5353.  
  5354. m_mail.push_back(m);
  5355. }
  5356. while (result->NextRow());
  5357. }
  5358. m_mailsLoaded = true;
  5359. }
  5360.  
  5361. void Player::LoadPet()
  5362. {
  5363. //fixme: the pet should still be loaded if the player is not in world
  5364. // just not added to the map
  5365. if (IsInWorld())
  5366. {
  5367. Pet* pet = new Pet(this);
  5368. if (!pet->LoadPetFromDB(this, 0, 0, true))
  5369. delete pet;
  5370. }
  5371. }
  5372.  
  5373. void Player::_LoadQuestStatus(PreparedQueryResult result)
  5374. {
  5375. uint16 slot = 0;
  5376.  
  5377. //// 0 1 2 3 4 5 6 7 8 9 10
  5378. //QueryResult* result = CharacterDatabase.PQuery("SELECT quest, status, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3,
  5379. // 11 12
  5380. // itemcount4, playercount FROM character_queststatus WHERE guid = '%u'", GetGUIDLow());
  5381.  
  5382. if (result)
  5383. {
  5384. do
  5385. {
  5386. Field* fields = result->Fetch();
  5387.  
  5388. uint32 quest_id = fields[0].GetUInt32();
  5389. // used to be new, no delete?
  5390. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  5391. if (quest)
  5392. {
  5393. // find or create
  5394. QuestStatusData& questStatusData = m_QuestStatus[quest_id];
  5395.  
  5396. uint8 qstatus = fields[1].GetUInt8();
  5397. if (qstatus < MAX_QUEST_STATUS)
  5398. questStatusData.Status = QuestStatus(qstatus);
  5399. else
  5400. {
  5401. questStatusData.Status = QUEST_STATUS_INCOMPLETE;
  5402. sLog->outError(LOG_FILTER_PLAYER, "Player %s (GUID: %u) has invalid quest %d status (%u), replaced by QUEST_STATUS_INCOMPLETE(3).",
  5403. GetName(), GetGUIDLow(), quest_id, qstatus);
  5404. }
  5405.  
  5406. questStatusData.Explored = (fields[2].GetUInt8() > 0);
  5407.  
  5408. time_t quest_time = time_t(fields[3].GetUInt32());
  5409.  
  5410. if (quest->HasFlag(QUEST_TRINITY_FLAGS_TIMED) && !GetQuestRewardStatus(quest_id))
  5411. {
  5412. AddTimedQuest(quest_id);
  5413.  
  5414. if (quest_time <= sWorld->GetGameTime())
  5415. questStatusData.Timer = 1;
  5416. else
  5417. questStatusData.Timer = uint32((quest_time - sWorld->GetGameTime()) * IN_MILLISECONDS);
  5418. }
  5419. else
  5420. quest_time = 0;
  5421.  
  5422. questStatusData.CreatureOrGOCount[0] = fields[4].GetUInt16();
  5423. questStatusData.CreatureOrGOCount[1] = fields[5].GetUInt16();
  5424. questStatusData.CreatureOrGOCount[2] = fields[6].GetUInt16();
  5425. questStatusData.CreatureOrGOCount[3] = fields[7].GetUInt16();
  5426. questStatusData.ItemCount[0] = fields[8].GetUInt16();
  5427. questStatusData.ItemCount[1] = fields[9].GetUInt16();
  5428. questStatusData.ItemCount[2] = fields[10].GetUInt16();
  5429. questStatusData.ItemCount[3] = fields[11].GetUInt16();
  5430. questStatusData.PlayerCount = fields[12].GetUInt16();
  5431.  
  5432. // add to quest log
  5433. if (slot < MAX_QUEST_LOG_SIZE && questStatusData.Status != QUEST_STATUS_NONE)
  5434. {
  5435. SetQuestSlot(slot, quest_id, uint32(quest_time)); // cast can't be helped
  5436.  
  5437. if (questStatusData.Status == QUEST_STATUS_COMPLETE)
  5438. SetQuestSlotState(slot, QUEST_STATE_COMPLETE);
  5439. else if (questStatusData.Status == QUEST_STATUS_FAILED)
  5440. SetQuestSlotState(slot, QUEST_STATE_FAIL);
  5441.  
  5442. for (uint8 idx = 0; idx < QUEST_OBJECTIVES_COUNT; ++idx)
  5443. if (questStatusData.CreatureOrGOCount[idx])
  5444. SetQuestSlotCounter(slot, idx, questStatusData.CreatureOrGOCount[idx]);
  5445.  
  5446. if (questStatusData.PlayerCount)
  5447. SetQuestSlotCounter(slot, QUEST_PVP_KILL_SLOT, questStatusData.PlayerCount);
  5448.  
  5449. ++slot;
  5450. }
  5451.  
  5452. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Quest status is {%u} for quest {%u} for player (GUID: %u)", questStatusData.Status, quest_id, GetGUIDLow());
  5453. }
  5454. }
  5455. while (result->NextRow());
  5456. }
  5457.  
  5458. // clear quest log tail
  5459. for (uint16 i = slot; i < MAX_QUEST_LOG_SIZE; ++i)
  5460. SetQuestSlot(i, 0);
  5461. }
  5462.  
  5463. void Player::_LoadQuestStatusRewarded(PreparedQueryResult result)
  5464. {
  5465. // SELECT quest FROM character_queststatus_rewarded WHERE guid = ?
  5466.  
  5467. if (result)
  5468. {
  5469. do
  5470. {
  5471. Field* fields = result->Fetch();
  5472.  
  5473. uint32 quest_id = fields[0].GetUInt32();
  5474. // used to be new, no delete?
  5475. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  5476. if (quest)
  5477. {
  5478. // learn rewarded spell if unknown
  5479. learnQuestRewardedSpells(quest);
  5480.  
  5481. // set rewarded title if any
  5482. if (quest->GetCharTitleId())
  5483. {
  5484. if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(quest->GetCharTitleId()))
  5485. SetTitle(titleEntry);
  5486. }
  5487.  
  5488. if (quest->GetBonusTalents())
  5489. m_questRewardTalentCount += quest->GetBonusTalents();
  5490. }
  5491.  
  5492. m_RewardedQuests.insert(quest_id);
  5493. }
  5494. while (result->NextRow());
  5495. }
  5496. }
  5497.  
  5498. void Player::_LoadDailyQuestStatus(PreparedQueryResult result)
  5499. {
  5500. for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
  5501. SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx, 0);
  5502.  
  5503. m_DFQuests.clear();
  5504.  
  5505. //QueryResult* result = CharacterDatabase.PQuery("SELECT quest, time FROM character_queststatus_daily WHERE guid = '%u'", GetGUIDLow());
  5506.  
  5507. if (result)
  5508. {
  5509. uint32 quest_daily_idx = 0;
  5510.  
  5511. do
  5512. {
  5513. Field* fields = result->Fetch();
  5514. if (Quest const* qQuest = sObjectMgr->GetQuestTemplate(fields[0].GetUInt32()))
  5515. {
  5516. if (qQuest->IsDFQuest())
  5517. {
  5518. m_DFQuests.insert(qQuest->GetQuestId());
  5519. m_lastDailyQuestTime = time_t(fields[1].GetUInt32());
  5520. continue;
  5521. }
  5522. }
  5523.  
  5524. if (quest_daily_idx >= PLAYER_MAX_DAILY_QUESTS) // max amount with exist data in query
  5525. {
  5526. sLog->outError(LOG_FILTER_PLAYER, "Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`", GetGUIDLow());
  5527. break;
  5528. }
  5529.  
  5530. uint32 quest_id = fields[0].GetUInt32();
  5531.  
  5532. // save _any_ from daily quest times (it must be after last reset anyway)
  5533. m_lastDailyQuestTime = time_t(fields[1].GetUInt32());
  5534.  
  5535. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  5536. if (!quest)
  5537. continue;
  5538.  
  5539. SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx, quest_id);
  5540. ++quest_daily_idx;
  5541.  
  5542. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Daily quest (%u) cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
  5543. }
  5544. while (result->NextRow());
  5545. }
  5546.  
  5547. m_DailyQuestChanged = false;
  5548. }
  5549.  
  5550. void Player::_LoadWeeklyQuestStatus(PreparedQueryResult result)
  5551. {
  5552. m_weeklyquests.clear();
  5553.  
  5554. if (result)
  5555. {
  5556. do
  5557. {
  5558. Field* fields = result->Fetch();
  5559. uint32 quest_id = fields[0].GetUInt32();
  5560. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  5561. if (!quest)
  5562. continue;
  5563.  
  5564. m_weeklyquests.insert(quest_id);
  5565. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Weekly quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
  5566. }
  5567. while (result->NextRow());
  5568. }
  5569.  
  5570. m_WeeklyQuestChanged = false;
  5571. }
  5572.  
  5573. void Player::_LoadSeasonalQuestStatus(PreparedQueryResult result)
  5574. {
  5575. m_seasonalquests.clear();
  5576.  
  5577. if (result)
  5578. {
  5579. do
  5580. {
  5581. Field* fields = result->Fetch();
  5582. uint32 quest_id = fields[0].GetUInt32();
  5583. uint32 event_id = fields[1].GetUInt32();
  5584. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  5585. if (!quest)
  5586. continue;
  5587.  
  5588. m_seasonalquests[event_id].insert(quest_id);
  5589. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Seasonal quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
  5590. }
  5591. while (result->NextRow());
  5592. }
  5593.  
  5594. m_SeasonalQuestChanged = false;
  5595. }
  5596.  
  5597. void Player::_LoadSpells(PreparedQueryResult result)
  5598. {
  5599. //QueryResult* result = CharacterDatabase.PQuery("SELECT spell, active, disabled FROM character_spell WHERE guid = '%u'", GetGUIDLow());
  5600.  
  5601. if (result)
  5602. {
  5603. do
  5604. addSpell((*result)[0].GetUInt32(), (*result)[1].GetBool(), false, false, (*result)[2].GetBool(), true);
  5605. while (result->NextRow());
  5606. }
  5607. }
  5608.  
  5609. void Player::_LoadGroup(PreparedQueryResult result)
  5610. {
  5611. //QueryResult* result = CharacterDatabase.PQuery("SELECT guid FROM group_member WHERE memberGuid=%u", GetGUIDLow());
  5612. if (result)
  5613. {
  5614. if (Group* group = sGroupMgr->GetGroupByDbStoreId((*result)[0].GetUInt32()))
  5615. {
  5616. uint8 subgroup = group->GetMemberGroup(GetGUID());
  5617. SetGroup(group, subgroup);
  5618. if (getLevel() >= LEVELREQUIREMENT_HEROIC)
  5619. {
  5620. // the group leader may change the instance difficulty while the player is offline
  5621. SetDungeonDifficulty(group->GetDungeonDifficulty());
  5622. SetRaidDifficulty(group->GetRaidDifficulty());
  5623. }
  5624. }
  5625. }
  5626. }
  5627.  
  5628. void Player::_LoadBoundInstances(PreparedQueryResult result)
  5629. {
  5630. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  5631. m_boundInstances[i].clear();
  5632.  
  5633. Group* group = GetGroup();
  5634.  
  5635. //QueryResult* result = CharacterDatabase.PQuery("SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
  5636. if (result)
  5637. {
  5638. do
  5639. {
  5640. Field* fields = result->Fetch();
  5641.  
  5642. bool perm = fields[1].GetBool();
  5643. uint32 mapId = fields[2].GetUInt16();
  5644. uint32 instanceId = fields[0].GetUInt32();
  5645. uint8 difficulty = fields[3].GetUInt8();
  5646.  
  5647. time_t resetTime = time_t(fields[4].GetUInt32());
  5648. // the resettime for normal instances is only saved when the InstanceSave is unloaded
  5649. // so the value read from the DB may be wrong here but only if the InstanceSave is loaded
  5650. // and in that case it is not used
  5651.  
  5652. bool deleteInstance = false;
  5653.  
  5654. MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
  5655. if (!mapEntry || !mapEntry->IsDungeon())
  5656. {
  5657. sLog->outError(LOG_FILTER_PLAYER, "_LoadBoundInstances: player %s(%d) has bind to not existed or not dungeon map %d", GetName(), GetGUIDLow(), mapId);
  5658. deleteInstance = true;
  5659. }
  5660. else if (difficulty >= MAX_DIFFICULTY)
  5661. {
  5662. sLog->outError(LOG_FILTER_PLAYER, "_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u", GetName(), GetGUIDLow(), difficulty, mapId);
  5663. deleteInstance = true;
  5664. }
  5665. else
  5666. {
  5667. MapDifficulty const* mapDiff = GetMapDifficultyData(mapId, Difficulty(difficulty));
  5668. if (!mapDiff)
  5669. {
  5670. sLog->outError(LOG_FILTER_PLAYER, "_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u", GetName(), GetGUIDLow(), difficulty, mapId);
  5671. deleteInstance = true;
  5672. }
  5673. else if (!perm && group)
  5674. {
  5675. sLog->outError(LOG_FILTER_PLAYER, "_LoadBoundInstances: player %s(%d) is in group %d but has a non-permanent character bind to map %d, %d, %d", GetName(), GetGUIDLow(), GUID_LOPART(group->GetGUID()), mapId, instanceId, difficulty);
  5676. deleteInstance = true;
  5677. }
  5678. }
  5679.  
  5680. if (deleteInstance)
  5681. {
  5682. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID);
  5683.  
  5684. stmt->setUInt32(0, GetGUIDLow());
  5685. stmt->setUInt32(1, instanceId);
  5686.  
  5687. CharacterDatabase.Execute(stmt);
  5688.  
  5689. continue;
  5690. }
  5691.  
  5692. // since non permanent binds are always solo bind, they can always be reset
  5693. if (InstanceSave* save = sInstanceSaveMgr->AddInstanceSave(mapId, instanceId, Difficulty(difficulty), resetTime, !perm, true))
  5694. BindToInstance(save, perm, true);
  5695. }
  5696. while (result->NextRow());
  5697. }
  5698. }
  5699.  
  5700. InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty)
  5701. {
  5702. // some instances only have one difficulty
  5703. MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(mapid, difficulty);
  5704. if (!mapDiff)
  5705. return NULL;
  5706.  
  5707. BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
  5708. if (itr != m_boundInstances[difficulty].end())
  5709. return &itr->second;
  5710. else
  5711. return NULL;
  5712. }
  5713.  
  5714. InstanceSave* Player::GetInstanceSave(uint32 mapid, bool raid)
  5715. {
  5716. InstancePlayerBind* pBind = GetBoundInstance(mapid, GetDifficulty(raid));
  5717. InstanceSave* pSave = pBind ? pBind->save : NULL;
  5718. if (!pBind || !pBind->perm)
  5719. if (Group* group = GetGroup())
  5720. if (InstanceGroupBind* groupBind = group->GetBoundInstance(this))
  5721. pSave = groupBind->save;
  5722.  
  5723. return pSave;
  5724. }
  5725.  
  5726. void Player::UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload)
  5727. {
  5728. BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
  5729. UnbindInstance(itr, difficulty, unload);
  5730. }
  5731.  
  5732. void Player::UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficulty, bool unload)
  5733. {
  5734. if (itr != m_boundInstances[difficulty].end())
  5735. {
  5736. if (!unload)
  5737. {
  5738. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID);
  5739.  
  5740. stmt->setUInt32(0, GetGUIDLow());
  5741. stmt->setUInt32(1, itr->second.save->GetInstanceId());
  5742.  
  5743. CharacterDatabase.Execute(stmt);
  5744. }
  5745.  
  5746. if (itr->second.perm)
  5747. GetSession()->SendCalendarRaidLockout(itr->second.save, false);
  5748.  
  5749. itr->second.save->RemovePlayer(this); // save can become invalid
  5750. m_boundInstances[difficulty].erase(itr++);
  5751. }
  5752. }
  5753.  
  5754. InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, bool load)
  5755. {
  5756. if (save)
  5757. {
  5758. InstancePlayerBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
  5759. if (bind.save)
  5760. {
  5761. // update the save when the group kills a boss
  5762. if (permanent != bind.perm || save != bind.save)
  5763. if (!load)
  5764. {
  5765. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE);
  5766.  
  5767. stmt->setUInt32(0, save->GetInstanceId());
  5768. stmt->setBool(1, permanent);
  5769. stmt->setUInt32(2, GetGUIDLow());
  5770. stmt->setUInt32(3, bind.save->GetInstanceId());
  5771.  
  5772. CharacterDatabase.Execute(stmt);
  5773. }
  5774. }
  5775. else
  5776. if (!load)
  5777. {
  5778. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_INSTANCE);
  5779.  
  5780. stmt->setUInt32(0, GetGUIDLow());
  5781. stmt->setUInt32(1, save->GetInstanceId());
  5782. stmt->setBool(2, permanent);
  5783.  
  5784. CharacterDatabase.Execute(stmt);
  5785. }
  5786.  
  5787. if (bind.save != save)
  5788. {
  5789. if (bind.save)
  5790. bind.save->RemovePlayer(this);
  5791. save->AddPlayer(this);
  5792. }
  5793.  
  5794. if (permanent)
  5795. save->SetCanReset(false);
  5796.  
  5797. bind.save = save;
  5798. bind.perm = permanent;
  5799. if (!load)
  5800. sLog->outDebug(LOG_FILTER_MAPS, "Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", GetName(), GetGUIDLow(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
  5801. sScriptMgr->OnPlayerBindToInstance(this, save->GetDifficulty(), save->GetMapId(), permanent);
  5802. return &bind;
  5803. }
  5804. else
  5805. return NULL;
  5806. }
  5807.  
  5808. void Player::BindToInstance()
  5809. {
  5810. InstanceSave* mapSave = sInstanceSaveMgr->GetInstanceSave(_pendingBindId);
  5811. if (!mapSave) //it seems sometimes mapSave is NULL, but I did not check why
  5812. return;
  5813.  
  5814. WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
  5815. data << uint32(0);
  5816. GetSession()->SendPacket(&data);
  5817. BindToInstance(mapSave, true);
  5818.  
  5819. GetSession()->SendCalendarRaidLockout(mapSave, true);
  5820. }
  5821.  
  5822. void Player::SendRaidInfo()
  5823. {
  5824. uint32 counter = 0;
  5825.  
  5826. WorldPacket data(SMSG_RAID_INSTANCE_INFO, 4);
  5827.  
  5828. size_t p_counter = data.wpos();
  5829. data << uint32(counter); // placeholder
  5830.  
  5831. time_t now = time(NULL);
  5832.  
  5833. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  5834. {
  5835. for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
  5836. {
  5837. if (itr->second.perm)
  5838. {
  5839. InstanceSave* save = itr->second.save;
  5840. data << uint32(save->GetMapId()); // map id
  5841. data << uint32(save->GetDifficulty()); // difficulty
  5842. data << uint64(save->GetInstanceId()); // instance id
  5843. data << uint8(1); // expired = 0
  5844. data << uint8(0); // extended = 1
  5845. data << uint32(save->GetResetTime() - now); // reset time
  5846. ++counter;
  5847. }
  5848. }
  5849. }
  5850. data.put<uint32>(p_counter, counter);
  5851. GetSession()->SendPacket(&data);
  5852. }
  5853.  
  5854. /*
  5855. - called on every successful teleportation to a map
  5856. */
  5857. void Player::SendSavedInstances()
  5858. {
  5859. bool hasBeenSaved = false;
  5860. WorldPacket data;
  5861.  
  5862. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  5863. {
  5864. for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
  5865. {
  5866. if (itr->second.perm) // only permanent binds are sent
  5867. {
  5868. hasBeenSaved = true;
  5869. break;
  5870. }
  5871. }
  5872. }
  5873.  
  5874. //Send opcode 811. true or false means, whether you have current raid/heroic instances
  5875. data.Initialize(SMSG_UPDATE_INSTANCE_OWNERSHIP);
  5876. data << uint32(hasBeenSaved);
  5877. GetSession()->SendPacket(&data);
  5878.  
  5879. if (!hasBeenSaved)
  5880. return;
  5881.  
  5882. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  5883. {
  5884. for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
  5885. {
  5886. if (itr->second.perm)
  5887. {
  5888. data.Initialize(SMSG_UPDATE_LAST_INSTANCE);
  5889. data << uint32(itr->second.save->GetMapId());
  5890. GetSession()->SendPacket(&data);
  5891. }
  5892. }
  5893. }
  5894. }
  5895.  
  5896. /// convert the player's binds to the group
  5897. void Player::ConvertInstancesToGroup(Player* player, Group* group, bool switchLeader)
  5898. {
  5899. // copy all binds to the group, when changing leader it's assumed the character
  5900. // will not have any solo binds
  5901.  
  5902. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  5903. {
  5904. for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();)
  5905. {
  5906. group->BindToInstance(itr->second.save, itr->second.perm, false);
  5907. // permanent binds are not removed
  5908. if (switchLeader && !itr->second.perm)
  5909. {
  5910. // increments itr in call
  5911. player->UnbindInstance(itr, Difficulty(i), false);
  5912. }
  5913. else
  5914. ++itr;
  5915. }
  5916. }
  5917. }
  5918.  
  5919. bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report)
  5920. {
  5921. if (!isGameMaster() && ar)
  5922. {
  5923. uint8 LevelMin = 0;
  5924. uint8 LevelMax = 0;
  5925.  
  5926. MapEntry const* mapEntry = sMapStore.LookupEntry(target_map);
  5927. if (!mapEntry)
  5928. return false;
  5929.  
  5930. if (!sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_LEVEL))
  5931. {
  5932. if (ar->levelMin && getLevel() < ar->levelMin)
  5933. LevelMin = ar->levelMin;
  5934. if (ar->levelMax && getLevel() > ar->levelMax)
  5935. LevelMax = ar->levelMax;
  5936. }
  5937.  
  5938. uint32 missingItem = 0;
  5939. if (ar->item)
  5940. {
  5941. if (!HasItemCount(ar->item, 1) &&
  5942. (!ar->item2 || !HasItemCount(ar->item2, 1)))
  5943. missingItem = ar->item;
  5944. }
  5945. else if (ar->item2 && !HasItemCount(ar->item2, 1))
  5946. missingItem = ar->item2;
  5947.  
  5948. if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, target_map, this))
  5949. {
  5950. GetSession()->SendAreaTriggerMessage("%s", GetSession()->GetTrinityString(LANG_INSTANCE_CLOSED));
  5951. return false;
  5952. }
  5953.  
  5954. uint32 missingQuest = 0;
  5955. if (GetTeam() == ALLIANCE && ar->quest_A && !GetQuestRewardStatus(ar->quest_A))
  5956. missingQuest = ar->quest_A;
  5957. else if (GetTeam() == HORDE && ar->quest_H && !GetQuestRewardStatus(ar->quest_H))
  5958. missingQuest = ar->quest_H;
  5959.  
  5960. uint32 missingAchievement = 0;
  5961. Player* leader = this;
  5962. uint64 leaderGuid = GetGroup() ? GetGroup()->GetLeaderGUID() : GetGUID();
  5963. if (leaderGuid != GetGUID())
  5964. leader = ObjectAccessor::FindPlayer(leaderGuid);
  5965.  
  5966. if (ar->achievement)
  5967. if (!leader || !leader->GetAchievementMgr().HasAchieved(ar->achievement))
  5968. missingAchievement = ar->achievement;
  5969.  
  5970. Difficulty target_difficulty = GetDifficulty(mapEntry->IsRaid());
  5971. MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(target_map, target_difficulty);
  5972. if (LevelMin || LevelMax || missingItem || missingQuest || missingAchievement)
  5973. {
  5974. if (report)
  5975. {
  5976. if (missingQuest && !ar->questFailedText.empty())
  5977. ChatHandler(GetSession()).PSendSysMessage("%s", ar->questFailedText.c_str());
  5978. else if (mapDiff->hasErrorMessage) // if (missingAchievement) covered by this case
  5979. SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY, target_difficulty);
  5980. else if (missingItem)
  5981. GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, sObjectMgr->GetItemTemplate(missingItem)->Name1.c_str());
  5982. else if (LevelMin)
  5983. GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED), LevelMin);
  5984. }
  5985. return false;
  5986. }
  5987. }
  5988. return true;
  5989. }
  5990.  
  5991. bool Player::CheckInstanceLoginValid()
  5992. {
  5993. if (!GetMap())
  5994. return false;
  5995.  
  5996. if (!GetMap()->IsDungeon() || isGameMaster())
  5997. return true;
  5998.  
  5999. if (GetMap()->IsRaid())
  6000. {
  6001. // cannot be in raid instance without a group
  6002. if (!GetGroup())
  6003. return false;
  6004. }
  6005. else
  6006. {
  6007. // cannot be in normal instance without a group and more players than 1 in instance
  6008. if (!GetGroup() && GetMap()->GetPlayersCountExceptGMs() > 1)
  6009. return false;
  6010. }
  6011.  
  6012. // do checks for satisfy accessreqs, instance full, encounter in progress (raid), perm bind group != perm bind player
  6013. return sMapMgr->CanPlayerEnter(GetMap()->GetId(), this, true);
  6014. }
  6015.  
  6016. bool Player::_LoadHomeBind(PreparedQueryResult result)
  6017. {
  6018. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  6019. if (!info)
  6020. {
  6021. sLog->outError(LOG_FILTER_PLAYER, "Player (Name %s) has incorrect race/class pair. Can't be loaded.", GetName());
  6022. return false;
  6023. }
  6024.  
  6025. bool ok = false;
  6026. // SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?
  6027. if (result)
  6028. {
  6029. Field* fields = result->Fetch();
  6030.  
  6031. m_homebindMapId = fields[0].GetUInt16();
  6032. m_homebindAreaId = fields[1].GetUInt16();
  6033. m_homebindX = fields[2].GetFloat();
  6034. m_homebindY = fields[3].GetFloat();
  6035. m_homebindZ = fields[4].GetFloat();
  6036.  
  6037. MapEntry const* bindMapEntry = sMapStore.LookupEntry(m_homebindMapId);
  6038.  
  6039. // accept saved data only for valid position (and non instanceable), and accessable
  6040. if (MapManager::IsValidMapCoord(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ) &&
  6041. !bindMapEntry->Instanceable() && GetSession()->Expansion() >= bindMapEntry->Expansion())
  6042. ok = true;
  6043. else
  6044. {
  6045. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_HOMEBIND);
  6046. stmt->setUInt32(0, GetGUIDLow());
  6047. CharacterDatabase.Execute(stmt);
  6048. }
  6049. }
  6050.  
  6051. if (!ok)
  6052. {
  6053. m_homebindMapId = info->mapId;
  6054. m_homebindAreaId = info->areaId;
  6055. m_homebindX = info->positionX;
  6056. m_homebindY = info->positionY;
  6057. m_homebindZ = info->positionZ;
  6058.  
  6059. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_HOMEBIND);
  6060. stmt->setUInt32(0, GetGUIDLow());
  6061. stmt->setUInt16(1, m_homebindMapId);
  6062. stmt->setUInt16(2, m_homebindAreaId);
  6063. stmt->setFloat (3, m_homebindX);
  6064. stmt->setFloat (4, m_homebindY);
  6065. stmt->setFloat (5, m_homebindZ);
  6066. CharacterDatabase.Execute(stmt);
  6067. }
  6068.  
  6069. sLog->outDebug(LOG_FILTER_PLAYER, "Setting player home position - mapid: %u, areaid: %u, X: %f, Y: %f, Z: %f",
  6070. m_homebindMapId, m_homebindAreaId, m_homebindX, m_homebindY, m_homebindZ);
  6071.  
  6072. return true;
  6073. }
  6074.  
  6075. /*********************************************************/
  6076. /*** SAVE SYSTEM ***/
  6077. /*********************************************************/
  6078.  
  6079. void Player::SaveToDB(bool create /*=false*/)
  6080. {
  6081. // delay auto save at any saves (manual, in code, or autosave)
  6082. m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE);
  6083.  
  6084. //lets allow only players in world to be saved
  6085. if (IsBeingTeleportedFar())
  6086. {
  6087. ScheduleDelayedOperation(DELAYED_SAVE_PLAYER);
  6088. return;
  6089. }
  6090.  
  6091. // first save/honor gain after midnight will also update the player's honor fields
  6092. UpdateHonorFields();
  6093.  
  6094. sLog->outDebug(LOG_FILTER_UNITS, "The value of player %s at save: ", m_name.c_str());
  6095. outDebugValues();
  6096.  
  6097. PreparedStatement* stmt = NULL;
  6098. uint8 index = 0;
  6099.  
  6100. if (create)
  6101. {
  6102. //! Insert query
  6103. //! TO DO: Filter out more redundant fields that can take their default value at player create
  6104. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER);
  6105. stmt->setUInt32(index++, GetGUIDLow());
  6106. stmt->setUInt32(index++, GetSession()->GetAccountId());
  6107. stmt->setString(index++, GetName());
  6108. stmt->setUInt8(index++, getRace());
  6109. stmt->setUInt8(index++, getClass());
  6110. stmt->setUInt8(index++, getGender());
  6111. stmt->setUInt8(index++, getLevel());
  6112. stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP));
  6113. stmt->setUInt32(index++, GetMoney());
  6114. stmt->setUInt32(index++, GetUInt32Value(PLAYER_BYTES));
  6115. stmt->setUInt32(index++, GetUInt32Value(PLAYER_BYTES_2));
  6116. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FLAGS));
  6117. stmt->setUInt16(index++, (uint16)GetMapId());
  6118. stmt->setUInt32(index++, (uint32)GetInstanceId());
  6119. stmt->setUInt8(index++, (uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4));
  6120. stmt->setFloat(index++, finiteAlways(GetPositionX()));
  6121. stmt->setFloat(index++, finiteAlways(GetPositionY()));
  6122. stmt->setFloat(index++, finiteAlways(GetPositionZ()));
  6123. stmt->setFloat(index++, finiteAlways(GetOrientation()));
  6124.  
  6125. std::ostringstream ss;
  6126. ss << m_taxi;
  6127. stmt->setString(index++, ss.str());
  6128. stmt->setUInt8(index++, m_cinematic);
  6129. stmt->setUInt32(index++, m_Played_time[PLAYED_TIME_TOTAL]);
  6130. stmt->setUInt32(index++, m_Played_time[PLAYED_TIME_LEVEL]);
  6131. stmt->setFloat(index++, finiteAlways(m_rest_bonus));
  6132. stmt->setUInt32(index++, uint32(time(NULL)));
  6133. stmt->setUInt8(index++, (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0));
  6134. //save, far from tavern/city
  6135. //save, but in tavern/city
  6136. stmt->setUInt32(index++, m_resetTalentsCost);
  6137. stmt->setUInt32(index++, uint32(m_resetTalentsTime));
  6138. stmt->setUInt16(index++, (uint16)m_ExtraFlags);
  6139. stmt->setUInt8(index++, m_stableSlots);
  6140. stmt->setUInt16(index++, (uint16)m_atLoginFlags);
  6141. stmt->setUInt16(index++, GetZoneId());
  6142. stmt->setUInt32(index++, uint32(m_deathExpireTime));
  6143.  
  6144. ss.str("");
  6145. ss << m_taxi.SaveTaxiDestinationsToString();
  6146.  
  6147. stmt->setString(index++, ss.str());
  6148. stmt->setUInt32(index++, GetArenaPoints());
  6149. stmt->setUInt32(index++, GetHonorPoints());
  6150. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));
  6151. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION));
  6152. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS));
  6153. stmt->setUInt16(index++, GetUInt16Value(PLAYER_FIELD_KILLS, 0));
  6154. stmt->setUInt16(index++, GetUInt16Value(PLAYER_FIELD_KILLS, 1));
  6155. stmt->setUInt32(index++, GetUInt32Value(PLAYER_CHOSEN_TITLE));
  6156. stmt->setUInt64(index++, GetUInt64Value(PLAYER_FIELD_KNOWN_CURRENCIES));
  6157. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX));
  6158. stmt->setUInt8(index++, GetDrunkValue());
  6159. stmt->setUInt32(index++, GetHealth());
  6160.  
  6161. for (uint32 i = 0; i < MAX_POWERS; ++i)
  6162. stmt->setUInt32(index++, GetPower(Powers(i)));
  6163.  
  6164. stmt->setUInt32(index++, GetSession()->GetLatency());
  6165.  
  6166. stmt->setUInt8(index++, m_specsCount);
  6167. stmt->setUInt8(index++, m_activeSpec);
  6168.  
  6169. ss.str("");
  6170. for (uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i)
  6171. ss << GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + i) << ' ';
  6172. stmt->setString(index++, ss.str());
  6173.  
  6174. ss.str("");
  6175. // cache equipment...
  6176. for (uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i)
  6177. ss << GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + i) << ' ';
  6178.  
  6179. // ...and bags for enum opcode
  6180. for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  6181. {
  6182. if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  6183. ss << item->GetEntry();
  6184. else
  6185. ss << '0';
  6186. ss << " 0 ";
  6187. }
  6188.  
  6189. stmt->setString(index++, ss.str());
  6190. stmt->setUInt32(index++, GetUInt32Value(PLAYER_AMMO_ID));
  6191.  
  6192. ss.str("");
  6193. for (uint32 i = 0; i < KNOWN_TITLES_SIZE*2; ++i)
  6194. ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << ' ';
  6195.  
  6196. stmt->setString(index++, ss.str());
  6197. stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, 2));
  6198. stmt->setUInt32(index++, m_grantableLevels);
  6199. }
  6200. else
  6201. {
  6202. // Update query
  6203. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER);
  6204. stmt->setString(index++, GetName());
  6205. stmt->setUInt8(index++, getRace());
  6206. stmt->setUInt8(index++, getClass());
  6207. stmt->setUInt8(index++, getGender());
  6208. stmt->setUInt8(index++, getLevel());
  6209. stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP));
  6210. stmt->setUInt32(index++, GetMoney());
  6211. stmt->setUInt32(index++, GetUInt32Value(PLAYER_BYTES));
  6212. stmt->setUInt32(index++, GetUInt32Value(PLAYER_BYTES_2));
  6213. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FLAGS));
  6214.  
  6215. if (!IsBeingTeleported())
  6216. {
  6217. stmt->setUInt16(index++, (uint16)GetMapId());
  6218. stmt->setUInt32(index++, (uint32)GetInstanceId());
  6219. stmt->setUInt8(index++, (uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4));
  6220. stmt->setFloat(index++, finiteAlways(GetPositionX()));
  6221. stmt->setFloat(index++, finiteAlways(GetPositionY()));
  6222. stmt->setFloat(index++, finiteAlways(GetPositionZ()));
  6223. stmt->setFloat(index++, finiteAlways(GetOrientation()));
  6224. }
  6225. else
  6226. {
  6227. stmt->setUInt16(index++, (uint16)GetTeleportDest().GetMapId());
  6228. stmt->setUInt32(index++, (uint32)0);
  6229. stmt->setUInt8(index++, (uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4));
  6230. stmt->setFloat(index++, finiteAlways(GetTeleportDest().GetPositionX()));
  6231. stmt->setFloat(index++, finiteAlways(GetTeleportDest().GetPositionY()));
  6232. stmt->setFloat(index++, finiteAlways(GetTeleportDest().GetPositionZ()));
  6233. stmt->setFloat(index++, finiteAlways(GetTeleportDest().GetOrientation()));
  6234. }
  6235.  
  6236. std::ostringstream ss;
  6237. ss << m_taxi;
  6238. stmt->setString(index++, ss.str());
  6239. stmt->setUInt8(index++, m_cinematic);
  6240. stmt->setUInt32(index++, m_Played_time[PLAYED_TIME_TOTAL]);
  6241. stmt->setUInt32(index++, m_Played_time[PLAYED_TIME_LEVEL]);
  6242. stmt->setFloat(index++, finiteAlways(m_rest_bonus));
  6243. stmt->setUInt32(index++, uint32(time(NULL)));
  6244. stmt->setUInt8(index++, (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0));
  6245. //save, far from tavern/city
  6246. //save, but in tavern/city
  6247. stmt->setUInt32(index++, m_resetTalentsCost);
  6248. stmt->setUInt32(index++, uint32(m_resetTalentsTime));
  6249. stmt->setUInt16(index++, (uint16)m_ExtraFlags);
  6250. stmt->setUInt8(index++, m_stableSlots);
  6251. stmt->setUInt16(index++, (uint16)m_atLoginFlags);
  6252. stmt->setUInt16(index++, GetZoneId());
  6253. stmt->setUInt32(index++, uint32(m_deathExpireTime));
  6254.  
  6255. ss.str("");
  6256. ss << m_taxi.SaveTaxiDestinationsToString();
  6257.  
  6258. stmt->setString(index++, ss.str());
  6259. stmt->setUInt32(index++, GetArenaPoints());
  6260. stmt->setUInt32(index++, GetHonorPoints());
  6261. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));
  6262. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION));
  6263. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS));
  6264. stmt->setUInt16(index++, GetUInt16Value(PLAYER_FIELD_KILLS, 0));
  6265. stmt->setUInt16(index++, GetUInt16Value(PLAYER_FIELD_KILLS, 1));
  6266. stmt->setUInt32(index++, GetUInt32Value(PLAYER_CHOSEN_TITLE));
  6267. stmt->setUInt64(index++, GetUInt64Value(PLAYER_FIELD_KNOWN_CURRENCIES));
  6268. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX));
  6269. stmt->setUInt8(index++, GetDrunkValue());
  6270. stmt->setUInt32(index++, GetHealth());
  6271.  
  6272. for (uint32 i = 0; i < MAX_POWERS; ++i)
  6273. stmt->setUInt32(index++, GetPower(Powers(i)));
  6274.  
  6275. stmt->setUInt32(index++, GetSession()->GetLatency());
  6276.  
  6277. stmt->setUInt8(index++, m_specsCount);
  6278. stmt->setUInt8(index++, m_activeSpec);
  6279.  
  6280. ss.str("");
  6281. for (uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i)
  6282. ss << GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + i) << ' ';
  6283. stmt->setString(index++, ss.str());
  6284.  
  6285. ss.str("");
  6286. // cache equipment...
  6287. for (uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i)
  6288. ss << GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + i) << ' ';
  6289.  
  6290. // ...and bags for enum opcode
  6291. for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  6292. {
  6293. if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  6294. ss << item->GetEntry();
  6295. else
  6296. ss << '0';
  6297. ss << " 0 ";
  6298. }
  6299.  
  6300. stmt->setString(index++, ss.str());
  6301. stmt->setUInt32(index++, GetUInt32Value(PLAYER_AMMO_ID));
  6302.  
  6303. ss.str("");
  6304. for (uint32 i = 0; i < KNOWN_TITLES_SIZE*2; ++i)
  6305. ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << ' ';
  6306.  
  6307. stmt->setString(index++, ss.str());
  6308. stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, 2));
  6309. stmt->setUInt32(index++, m_grantableLevels);
  6310.  
  6311. stmt->setUInt8(index++, IsInWorld() ? 1 : 0);
  6312. // Index
  6313. stmt->setUInt32(index++, GetGUIDLow());
  6314. }
  6315.  
  6316. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  6317.  
  6318. trans->Append(stmt);
  6319.  
  6320. if (m_mailsUpdated) //save mails only when needed
  6321. _SaveMail(trans);
  6322.  
  6323. _SaveBGData(trans);
  6324. _SaveInventory(trans);
  6325. _SaveQuestStatus(trans);
  6326. _SaveDailyQuestStatus(trans);
  6327. _SaveWeeklyQuestStatus(trans);
  6328. _SaveSeasonalQuestStatus(trans);
  6329. _SaveTalents(trans);
  6330. _SaveSpells(trans);
  6331. _SaveSpellCooldowns(trans);
  6332. _SaveActions(trans);
  6333. _SaveAuras(trans);
  6334. _SaveSkills(trans);
  6335. m_achievementMgr.SaveToDB(trans);
  6336. m_reputationMgr.SaveToDB(trans);
  6337. _SaveEquipmentSets(trans);
  6338. GetSession()->SaveTutorialsData(trans); // changed only while character in game
  6339. _SaveGlyphs(trans);
  6340. _SaveInstanceTimeRestrictions(trans);
  6341.  
  6342. // check if stats should only be saved on logout
  6343. // save stats can be out of transaction
  6344. if (m_session->isLogingOut() || !sWorld->getBoolConfig(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT))
  6345. _SaveStats(trans);
  6346.  
  6347. CharacterDatabase.CommitTransaction(trans);
  6348. /* World of Warcraft Armory */
  6349. // Place this code AFTER CharacterDatabase.CommitTransaction(); to avoid some character saving errors.
  6350. // Wowarmory feeds
  6351. if (sWorld->getBoolConfig(CONFIG_ARMORY_ENABLE))
  6352. {
  6353. std::ostringstream sWowarmory;
  6354. for (WowarmoryFeeds::iterator iter = m_wowarmory_feeds.begin(); iter < m_wowarmory_feeds.end(); ++iter) {
  6355. sWowarmory << "INSERT IGNORE INTO character_feed_log (guid,type,data,date,counter,difficulty,item_guid,item_quality) VALUES ";
  6356. // guid type data date counter difficulty item_guid item_quality
  6357. sWowarmory << "(" << (*iter).guid << ", " << (*iter).type << ", " << (*iter).data << ", " << uint64((*iter).date) << ", " << (*iter).counter << ", " << uint32((*iter).difficulty) << ", " << (*iter).item_guid << ", " << (*iter).item_quality << ");";
  6358. CharacterDatabase.PExecute(sWowarmory.str().c_str());
  6359. sWowarmory.str("");
  6360. }
  6361. // Clear old saved feeds from storage - they are not required for server core.
  6362. InitWowarmoryFeeds();
  6363. // Character stats
  6364. std::ostringstream ps;
  6365. time_t t = time(NULL);
  6366. CharacterDatabase.PExecute("DELETE FROM armory_character_stats WHERE guid = %u", GetGUIDLow());
  6367. ps << "INSERT INTO armory_character_stats (guid, data, save_date) VALUES (" << GetGUIDLow() << ", '";
  6368. for (uint16 i = 0; i < m_valuesCount; ++i)
  6369. ps << GetUInt32Value(i) << " ";
  6370. ps << "', " << uint64(t) << ");";
  6371. CharacterDatabase.PExecute(ps.str().c_str());
  6372. }
  6373. /* World of Warcraft Armory */
  6374.  
  6375. // save pet (hunter pet level and experience and all type pets health/mana).
  6376. if (Pet* pet = GetPet())
  6377. pet->SavePetToDB(PET_SAVE_AS_CURRENT);
  6378. }
  6379.  
  6380. // fast save function for item/money cheating preventing - save only inventory and money state
  6381. void Player::SaveInventoryAndGoldToDB(SQLTransaction& trans)
  6382. {
  6383. _SaveInventory(trans);
  6384. SaveGoldToDB(trans);
  6385. }
  6386.  
  6387. void Player::SaveGoldToDB(SQLTransaction& trans)
  6388. {
  6389. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_MONEY);
  6390. stmt->setUInt32(0, GetMoney());
  6391. stmt->setUInt32(1, GetGUIDLow());
  6392. trans->Append(stmt);
  6393. }
  6394.  
  6395. void Player::_SaveActions(SQLTransaction& trans)
  6396. {
  6397. PreparedStatement* stmt = NULL;
  6398.  
  6399. for (ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end();)
  6400. {
  6401. switch (itr->second.uState)
  6402. {
  6403. case ACTIONBUTTON_NEW:
  6404. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACTION);
  6405. stmt->setUInt32(0, GetGUIDLow());
  6406. stmt->setUInt8(1, m_activeSpec);
  6407. stmt->setUInt8(2, itr->first);
  6408. stmt->setUInt32(3, itr->second.GetAction());
  6409. stmt->setUInt8(4, uint8(itr->second.GetType()));
  6410. trans->Append(stmt);
  6411.  
  6412. itr->second.uState = ACTIONBUTTON_UNCHANGED;
  6413. ++itr;
  6414. break;
  6415. case ACTIONBUTTON_CHANGED:
  6416. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_ACTION);
  6417. stmt->setUInt32(0, itr->second.GetAction());
  6418. stmt->setUInt8(1, uint8(itr->second.GetType()));
  6419. stmt->setUInt32(2, GetGUIDLow());
  6420. stmt->setUInt8(3, itr->first);
  6421. stmt->setUInt8(4, m_activeSpec);
  6422. trans->Append(stmt);
  6423.  
  6424. itr->second.uState = ACTIONBUTTON_UNCHANGED;
  6425. ++itr;
  6426. break;
  6427. case ACTIONBUTTON_DELETED:
  6428. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACTION_BY_BUTTON_SPEC);
  6429. stmt->setUInt32(0, GetGUIDLow());
  6430. stmt->setUInt8(1, itr->first);
  6431. stmt->setUInt8(2, m_activeSpec);
  6432. trans->Append(stmt);
  6433.  
  6434. m_actionButtons.erase(itr++);
  6435. break;
  6436. default:
  6437. ++itr;
  6438. break;
  6439. }
  6440. }
  6441. }
  6442.  
  6443. void Player::_SaveAuras(SQLTransaction& trans)
  6444. {
  6445. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_AURA);
  6446. stmt->setUInt32(0, GetGUIDLow());
  6447. trans->Append(stmt);
  6448.  
  6449. for (AuraMap::const_iterator itr = m_ownedAuras.begin(); itr != m_ownedAuras.end(); ++itr)
  6450. {
  6451. if (!itr->second->CanBeSaved())
  6452. continue;
  6453.  
  6454. Aura* aura = itr->second;
  6455.  
  6456. int32 damage[MAX_SPELL_EFFECTS];
  6457. int32 baseDamage[MAX_SPELL_EFFECTS];
  6458. uint8 effMask = 0;
  6459. uint8 recalculateMask = 0;
  6460. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  6461. {
  6462. if (AuraEffect const* effect = aura->GetEffect(i))
  6463. {
  6464. baseDamage[i] = effect->GetBaseAmount();
  6465. damage[i] = effect->GetAmount();
  6466. effMask |= 1 << i;
  6467. if (effect->CanBeRecalculated())
  6468. recalculateMask |= 1 << i;
  6469. }
  6470. else
  6471. {
  6472. baseDamage[i] = 0;
  6473. damage[i] = 0;
  6474. }
  6475. }
  6476.  
  6477. uint8 index = 0;
  6478. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AURA);
  6479. stmt->setUInt32(index++, GetGUIDLow());
  6480. stmt->setUInt64(index++, itr->second->GetCasterGUID());
  6481. stmt->setUInt64(index++, itr->second->GetCastItemGUID());
  6482. stmt->setUInt32(index++, itr->second->GetId());
  6483. stmt->setUInt8(index++, effMask);
  6484. stmt->setUInt8(index++, recalculateMask);
  6485. stmt->setUInt8(index++, itr->second->GetStackAmount());
  6486. stmt->setInt32(index++, damage[0]);
  6487. stmt->setInt32(index++, damage[1]);
  6488. stmt->setInt32(index++, damage[2]);
  6489. stmt->setInt32(index++, baseDamage[0]);
  6490. stmt->setInt32(index++, baseDamage[1]);
  6491. stmt->setInt32(index++, baseDamage[2]);
  6492. stmt->setInt32(index++, itr->second->GetMaxDuration());
  6493. stmt->setInt32(index++, itr->second->GetDuration());
  6494. stmt->setUInt8(index, itr->second->GetCharges());
  6495. trans->Append(stmt);
  6496. }
  6497. }
  6498.  
  6499. void Player::_SaveInventory(SQLTransaction& trans)
  6500. {
  6501. PreparedStatement* stmt = NULL;
  6502. // force items in buyback slots to new state
  6503. // and remove those that aren't already
  6504. for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; ++i)
  6505. {
  6506. Item* item = m_items[i];
  6507. if (!item || item->GetState() == ITEM_NEW)
  6508. continue;
  6509.  
  6510. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
  6511. stmt->setUInt32(0, item->GetGUIDLow());
  6512. trans->Append(stmt);
  6513.  
  6514. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  6515. stmt->setUInt32(0, item->GetGUIDLow());
  6516. trans->Append(stmt);
  6517. m_items[i]->FSetState(ITEM_NEW);
  6518. }
  6519.  
  6520. // Updated played time for refundable items. We don't do this in Player::Update because there's simply no need for it,
  6521. // the client auto counts down in real time after having received the initial played time on the first
  6522. // SMSG_ITEM_REFUND_INFO_RESPONSE packet.
  6523. // Item::UpdatePlayedTime is only called when needed, which is in DB saves, and item refund info requests.
  6524. std::set<uint32>::iterator i_next;
  6525. for (std::set<uint32>::iterator itr = m_refundableItems.begin(); itr!= m_refundableItems.end(); itr = i_next)
  6526. {
  6527. // use copy iterator because itr may be invalid after operations in this loop
  6528. i_next = itr;
  6529. ++i_next;
  6530.  
  6531. Item* iPtr = GetItemByGuid(MAKE_NEW_GUID(*itr, 0, HIGHGUID_ITEM));
  6532. if (iPtr)
  6533. {
  6534. iPtr->UpdatePlayedTime(this);
  6535. continue;
  6536. }
  6537. else
  6538. {
  6539. sLog->outError(LOG_FILTER_PLAYER, "Can't find item guid %u but is in refundable storage for player %u ! Removing.", *itr, GetGUIDLow());
  6540. m_refundableItems.erase(itr);
  6541. }
  6542. }
  6543.  
  6544. // update enchantment durations
  6545. for (EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr)
  6546. itr->item->SetEnchantmentDuration(itr->slot, itr->leftduration, this);
  6547.  
  6548. // if no changes
  6549. if (m_itemUpdateQueue.empty())
  6550. return;
  6551.  
  6552. uint32 lowGuid = GetGUIDLow();
  6553. for (size_t i = 0; i < m_itemUpdateQueue.size(); ++i)
  6554. {
  6555. Item* item = m_itemUpdateQueue[i];
  6556. if (!item)
  6557. continue;
  6558.  
  6559. Bag* container = item->GetContainer();
  6560. uint32 bag_guid = container ? container->GetGUIDLow() : 0;
  6561.  
  6562. if (item->GetState() != ITEM_REMOVED)
  6563. {
  6564. Item* test = GetItemByPos(item->GetBagSlot(), item->GetSlot());
  6565. if (test == NULL)
  6566. {
  6567. uint32 bagTestGUID = 0;
  6568. if (Item* test2 = GetItemByPos(INVENTORY_SLOT_BAG_0, item->GetBagSlot()))
  6569. bagTestGUID = test2->GetGUIDLow();
  6570. sLog->outError(LOG_FILTER_PLAYER, "Player(GUID: %u Name: %s)::_SaveInventory - the bag(%u) and slot(%u) values for the item with guid %u (state %d) are incorrect, the player doesn't have an item at that position!", lowGuid, GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), (int32)item->GetState());
  6571. // according to the test that was just performed nothing should be in this slot, delete
  6572. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_BAG_SLOT);
  6573. stmt->setUInt32(0, bagTestGUID);
  6574. stmt->setUInt8(1, item->GetSlot());
  6575. stmt->setUInt32(2, lowGuid);
  6576. trans->Append(stmt);
  6577.  
  6578. // also THIS item should be somewhere else, cheat attempt
  6579. item->FSetState(ITEM_REMOVED); // we are IN updateQueue right now, can't use SetState which modifies the queue
  6580. DeleteRefundReference(item->GetGUIDLow());
  6581. // don't skip, let the switch delete it
  6582. //continue;
  6583. }
  6584. else if (test != item)
  6585. {
  6586. sLog->outError(LOG_FILTER_PLAYER, "Player(GUID: %u Name: %s)::_SaveInventory - the bag(%u) and slot(%u) values for the item with guid %u are incorrect, the item with guid %u is there instead!", lowGuid, GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), test->GetGUIDLow());
  6587. // save all changes to the item...
  6588. if (item->GetState() != ITEM_NEW) // only for existing items, no dupes
  6589. item->SaveToDB(trans);
  6590. // ...but do not save position in invntory
  6591. continue;
  6592. }
  6593. }
  6594.  
  6595. PreparedStatement* stmt = NULL;
  6596. switch (item->GetState())
  6597. {
  6598. case ITEM_NEW:
  6599. case ITEM_CHANGED:
  6600. stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_INVENTORY_ITEM);
  6601. stmt->setUInt32(0, lowGuid);
  6602. stmt->setUInt32(1, bag_guid);
  6603. stmt->setUInt8 (2, item->GetSlot());
  6604. stmt->setUInt32(3, item->GetGUIDLow());
  6605. trans->Append(stmt);
  6606. break;
  6607. case ITEM_REMOVED:
  6608. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
  6609. stmt->setUInt32(0, item->GetGUIDLow());
  6610. trans->Append(stmt);
  6611. case ITEM_UNCHANGED:
  6612. break;
  6613. }
  6614.  
  6615. item->SaveToDB(trans); // item have unchanged inventory record and can be save standalone
  6616. }
  6617. m_itemUpdateQueue.clear();
  6618. }
  6619.  
  6620. void Player::_SaveMail(SQLTransaction& trans)
  6621. {
  6622. if (!m_mailsLoaded)
  6623. return;
  6624.  
  6625. PreparedStatement* stmt = NULL;
  6626.  
  6627. for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
  6628. {
  6629. Mail* m = (*itr);
  6630. if (m->state == MAIL_STATE_CHANGED)
  6631. {
  6632. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL);
  6633. stmt->setUInt8(0, uint8(m->HasItems() ? 1 : 0));
  6634. stmt->setUInt32(1, uint32(m->expire_time));
  6635. stmt->setUInt32(2, uint32(m->deliver_time));
  6636. stmt->setUInt32(3, m->money);
  6637. stmt->setUInt32(4, m->COD);
  6638. stmt->setUInt8(5, uint8(m->checked));
  6639. stmt->setUInt32(6, m->messageID);
  6640.  
  6641. trans->Append(stmt);
  6642.  
  6643. if (!m->removedItems.empty())
  6644. {
  6645. for (std::vector<uint32>::iterator itr2 = m->removedItems.begin(); itr2 != m->removedItems.end(); ++itr2)
  6646. {
  6647. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM);
  6648. stmt->setUInt32(0, *itr2);
  6649. trans->Append(stmt);
  6650. }
  6651. m->removedItems.clear();
  6652. }
  6653. m->state = MAIL_STATE_UNCHANGED;
  6654. }
  6655. else if (m->state == MAIL_STATE_DELETED)
  6656. {
  6657. if (m->HasItems())
  6658. {
  6659. PreparedStatement* stmt = NULL;
  6660. for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
  6661. {
  6662. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  6663. stmt->setUInt32(0, itr2->item_guid);
  6664. trans->Append(stmt);
  6665. }
  6666. }
  6667. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
  6668. stmt->setUInt32(0, m->messageID);
  6669. trans->Append(stmt);
  6670.  
  6671. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
  6672. stmt->setUInt32(0, m->messageID);
  6673. trans->Append(stmt);
  6674. }
  6675. }
  6676.  
  6677. //deallocate deleted mails...
  6678. for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();)
  6679. {
  6680. if ((*itr)->state == MAIL_STATE_DELETED)
  6681. {
  6682. Mail* m = *itr;
  6683. m_mail.erase(itr);
  6684. delete m;
  6685. itr = m_mail.begin();
  6686. }
  6687. else
  6688. ++itr;
  6689. }
  6690.  
  6691. m_mailsUpdated = false;
  6692. }
  6693.  
  6694. void Player::_SaveQuestStatus(SQLTransaction& trans)
  6695. {
  6696. bool isTransaction = !trans.null();
  6697. if (!isTransaction)
  6698. trans = CharacterDatabase.BeginTransaction();
  6699.  
  6700. QuestStatusSaveMap::iterator saveItr;
  6701. QuestStatusMap::iterator statusItr;
  6702. PreparedStatement* stmt = NULL;
  6703.  
  6704. bool keepAbandoned = !(sWorld->GetCleaningFlags() & CharacterDatabaseCleaner::CLEANING_FLAG_QUESTSTATUS);
  6705.  
  6706. for (saveItr = m_QuestStatusSave.begin(); saveItr != m_QuestStatusSave.end(); ++saveItr)
  6707. {
  6708. if (saveItr->second)
  6709. {
  6710. statusItr = m_QuestStatus.find(saveItr->first);
  6711. if (statusItr != m_QuestStatus.end() && (keepAbandoned || statusItr->second.Status != QUEST_STATUS_NONE))
  6712. {
  6713. uint8 index = 0;
  6714. stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_QUESTSTATUS);
  6715.  
  6716. stmt->setUInt32(index++, GetGUIDLow());
  6717. stmt->setUInt32(index++, statusItr->first);
  6718. stmt->setUInt8(index++, uint8(statusItr->second.Status));
  6719. stmt->setBool(index++, statusItr->second.Explored);
  6720. stmt->setUInt32(index++, uint32(statusItr->second.Timer / IN_MILLISECONDS+ sWorld->GetGameTime()));
  6721.  
  6722. for (uint8 i = 0; i < 4; i++)
  6723. stmt->setUInt16(index++, statusItr->second.CreatureOrGOCount[i]);
  6724.  
  6725. for (uint8 i = 0; i < 4; i++)
  6726. stmt->setUInt16(index++, statusItr->second.ItemCount[i]);
  6727.  
  6728. stmt->setUInt16(index, statusItr->second.PlayerCount);
  6729. trans->Append(stmt);
  6730. }
  6731. }
  6732. else
  6733. {
  6734. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_QUESTSTATUS_BY_QUEST);
  6735. stmt->setUInt32(0, GetGUIDLow());
  6736. stmt->setUInt32(1, saveItr->first);
  6737. trans->Append(stmt);
  6738. }
  6739. }
  6740.  
  6741. m_QuestStatusSave.clear();
  6742.  
  6743. for (saveItr = m_RewardedQuestsSave.begin(); saveItr != m_RewardedQuestsSave.end(); ++saveItr)
  6744. {
  6745. if (saveItr->second)
  6746. {
  6747. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_QUESTSTATUS);
  6748. stmt->setUInt32(0, GetGUIDLow());
  6749. stmt->setUInt32(1, saveItr->first);
  6750. trans->Append(stmt);
  6751.  
  6752. }
  6753. else if (!keepAbandoned)
  6754. {
  6755. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_QUESTSTATUS_REWARDED_BY_QUEST);
  6756. stmt->setUInt32(0, GetGUIDLow());
  6757. stmt->setUInt32(1, saveItr->first);
  6758. trans->Append(stmt);
  6759. }
  6760. }
  6761.  
  6762. m_RewardedQuestsSave.clear();
  6763.  
  6764. if (!isTransaction)
  6765. CharacterDatabase.CommitTransaction(trans);
  6766. }
  6767.  
  6768. void Player::_SaveDailyQuestStatus(SQLTransaction& trans)
  6769. {
  6770. if (!m_DailyQuestChanged)
  6771. return;
  6772.  
  6773. m_DailyQuestChanged = false;
  6774.  
  6775. // save last daily quest time for all quests: we need only mostly reset time for reset check anyway
  6776.  
  6777. // we don't need transactions here.
  6778. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_DAILY_CHAR);
  6779. stmt->setUInt32(0, GetGUIDLow());
  6780. trans->Append(stmt);
  6781. for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
  6782. {
  6783. if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
  6784. {
  6785. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_DAILYQUESTSTATUS);
  6786. stmt->setUInt32(0, GetGUIDLow());
  6787. stmt->setUInt32(1, GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx));
  6788. stmt->setUInt64(2, uint64(m_lastDailyQuestTime));
  6789. trans->Append(stmt);
  6790. }
  6791. }
  6792.  
  6793. if (!m_DFQuests.empty())
  6794. {
  6795. for (DFQuestsDoneList::iterator itr = m_DFQuests.begin(); itr != m_DFQuests.end(); ++itr)
  6796. {
  6797. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_DAILYQUESTSTATUS);
  6798. stmt->setUInt32(0, GetGUIDLow());
  6799. stmt->setUInt32(1, (*itr));
  6800. stmt->setUInt64(2, uint64(m_lastDailyQuestTime));
  6801. trans->Append(stmt);
  6802. }
  6803. }
  6804. }
  6805.  
  6806. void Player::_SaveWeeklyQuestStatus(SQLTransaction& trans)
  6807. {
  6808. if (!m_WeeklyQuestChanged || m_weeklyquests.empty())
  6809. return;
  6810.  
  6811. // we don't need transactions here.
  6812. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR);
  6813. stmt->setUInt32(0, GetGUIDLow());
  6814. trans->Append(stmt);
  6815.  
  6816. for (QuestSet::const_iterator iter = m_weeklyquests.begin(); iter != m_weeklyquests.end(); ++iter)
  6817. {
  6818. uint32 quest_id = *iter;
  6819.  
  6820. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_WEEKLYQUESTSTATUS);
  6821. stmt->setUInt32(0, GetGUIDLow());
  6822. stmt->setUInt32(1, quest_id);
  6823. trans->Append(stmt);
  6824. }
  6825.  
  6826. m_WeeklyQuestChanged = false;
  6827. }
  6828.  
  6829. void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans)
  6830. {
  6831. if (!m_SeasonalQuestChanged || m_seasonalquests.empty())
  6832. return;
  6833.  
  6834. // we don't need transactions here.
  6835. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR);
  6836. stmt->setUInt32(0, GetGUIDLow());
  6837. trans->Append(stmt);
  6838.  
  6839. for (SeasonalEventQuestMap::const_iterator iter = m_seasonalquests.begin(); iter != m_seasonalquests.end(); ++iter)
  6840. {
  6841. uint16 event_id = iter->first;
  6842. for (SeasonalQuestSet::const_iterator itr = iter->second.begin(); itr != iter->second.end(); ++itr)
  6843. {
  6844. uint32 quest_id = (*itr);
  6845.  
  6846. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_SEASONALQUESTSTATUS);
  6847. stmt->setUInt32(0, GetGUIDLow());
  6848. stmt->setUInt32(1, quest_id);
  6849. stmt->setUInt32(2, event_id);
  6850. trans->Append(stmt);
  6851. }
  6852. }
  6853.  
  6854. m_SeasonalQuestChanged = false;
  6855. }
  6856.  
  6857. void Player::_SaveSkills(SQLTransaction& trans)
  6858. {
  6859. PreparedStatement* stmt = NULL;
  6860. // we don't need transactions here.
  6861. for (SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end();)
  6862. {
  6863. if (itr->second.uState == SKILL_UNCHANGED)
  6864. {
  6865. ++itr;
  6866. continue;
  6867. }
  6868.  
  6869. if (itr->second.uState == SKILL_DELETED)
  6870. {
  6871. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SKILL_BY_SKILL);
  6872. stmt->setUInt32(0, GetGUIDLow());
  6873. stmt->setUInt32(1, itr->first);
  6874. trans->Append(stmt);
  6875.  
  6876. mSkillStatus.erase(itr++);
  6877. continue;
  6878. }
  6879.  
  6880. uint32 valueData = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos));
  6881. uint16 value = SKILL_VALUE(valueData);
  6882. uint16 max = SKILL_MAX(valueData);
  6883.  
  6884. switch (itr->second.uState)
  6885. {
  6886. case SKILL_NEW:
  6887. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_SKILLS);
  6888. stmt->setUInt32(0, GetGUIDLow());
  6889. stmt->setUInt16(1, uint16(itr->first));
  6890. stmt->setUInt16(2, value);
  6891. stmt->setUInt16(3, max);
  6892. trans->Append(stmt);
  6893.  
  6894. break;
  6895. case SKILL_CHANGED:
  6896. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_SKILLS);
  6897. stmt->setUInt16(0, value);
  6898. stmt->setUInt16(1, max);
  6899. stmt->setUInt32(2, GetGUIDLow());
  6900. stmt->setUInt16(3, uint16(itr->first));
  6901. trans->Append(stmt);
  6902.  
  6903. break;
  6904. default:
  6905. break;
  6906. }
  6907. itr->second.uState = SKILL_UNCHANGED;
  6908.  
  6909. ++itr;
  6910. }
  6911. }
  6912.  
  6913. void Player::_SaveSpells(SQLTransaction& trans)
  6914. {
  6915. PreparedStatement* stmt = NULL;
  6916.  
  6917. for (PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end();)
  6918. {
  6919. if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
  6920. {
  6921. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_BY_SPELL);
  6922. stmt->setUInt32(0, itr->first);
  6923. stmt->setUInt32(1, GetGUIDLow());
  6924. trans->Append(stmt);
  6925. }
  6926.  
  6927. // add only changed/new not dependent spells
  6928. if (!itr->second->dependent && (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED))
  6929. {
  6930. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_SPELL);
  6931. stmt->setUInt32(0, GetGUIDLow());
  6932. stmt->setUInt32(1, itr->first);
  6933. stmt->setBool(2, itr->second->active);
  6934. stmt->setBool(3, itr->second->disabled);
  6935. trans->Append(stmt);
  6936. }
  6937.  
  6938. if (itr->second->state == PLAYERSPELL_REMOVED)
  6939. {
  6940. delete itr->second;
  6941. m_spells.erase(itr++);
  6942. }
  6943. else
  6944. {
  6945. itr->second->state = PLAYERSPELL_UNCHANGED;
  6946. ++itr;
  6947. }
  6948. }
  6949. }
  6950.  
  6951. // save player stats -- only for external usage
  6952. // real stats will be recalculated on player login
  6953. void Player::_SaveStats(SQLTransaction& trans)
  6954. {
  6955. // check if stat saving is enabled and if char level is high enough
  6956. if (!sWorld->getIntConfig(CONFIG_MIN_LEVEL_STAT_SAVE) || getLevel() < sWorld->getIntConfig(CONFIG_MIN_LEVEL_STAT_SAVE))
  6957. return;
  6958.  
  6959. PreparedStatement* stmt = NULL;
  6960.  
  6961. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_STATS);
  6962. stmt->setUInt32(0, GetGUIDLow());
  6963. trans->Append(stmt);
  6964.  
  6965. uint8 index = 0;
  6966.  
  6967. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_STATS);
  6968. stmt->setUInt32(index++, GetGUIDLow());
  6969. stmt->setUInt32(index++, GetMaxHealth());
  6970.  
  6971. for (uint8 i = 0; i < MAX_POWERS; ++i)
  6972. stmt->setUInt32(index++, GetMaxPower(Powers(i)));
  6973.  
  6974. for (uint8 i = 0; i < MAX_STATS; ++i)
  6975. stmt->setUInt32(index++, GetStat(Stats(i)));
  6976.  
  6977. for (int i = 0; i < MAX_SPELL_SCHOOL; ++i)
  6978. stmt->setUInt32(index++, GetResistance(SpellSchools(i)));
  6979.  
  6980. stmt->setFloat(index++, GetFloatValue(PLAYER_BLOCK_PERCENTAGE));
  6981. stmt->setFloat(index++, GetFloatValue(PLAYER_DODGE_PERCENTAGE));
  6982. stmt->setFloat(index++, GetFloatValue(PLAYER_PARRY_PERCENTAGE));
  6983. stmt->setFloat(index++, GetFloatValue(PLAYER_CRIT_PERCENTAGE));
  6984. stmt->setFloat(index++, GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE));
  6985. stmt->setFloat(index++, GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1));
  6986. stmt->setUInt32(index++, GetUInt32Value(UNIT_FIELD_ATTACK_POWER));
  6987. stmt->setUInt32(index++, GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER));
  6988. stmt->setUInt32(index++, GetBaseSpellPowerBonus());
  6989. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_CRIT_TAKEN_SPELL));
  6990.  
  6991. trans->Append(stmt);
  6992. }
  6993.  
  6994. void Player::outDebugValues() const
  6995. {
  6996. if (!sLog->ShouldLog(LOG_FILTER_UNITS, LOG_LEVEL_DEBUG))
  6997. return;
  6998.  
  6999. sLog->outDebug(LOG_FILTER_UNITS, "HP is: \t\t\t%u\t\tMP is: \t\t\t%u", GetMaxHealth(), GetMaxPower(POWER_MANA));
  7000. sLog->outDebug(LOG_FILTER_UNITS, "AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f", GetStat(STAT_AGILITY), GetStat(STAT_STRENGTH));
  7001. sLog->outDebug(LOG_FILTER_UNITS, "INTELLECT is: \t\t%f\t\tSPIRIT is: \t\t%f", GetStat(STAT_INTELLECT), GetStat(STAT_SPIRIT));
  7002. sLog->outDebug(LOG_FILTER_UNITS, "STAMINA is: \t\t%f", GetStat(STAT_STAMINA));
  7003. sLog->outDebug(LOG_FILTER_UNITS, "Armor is: \t\t%u\t\tBlock is: \t\t%f", GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE));
  7004. sLog->outDebug(LOG_FILTER_UNITS, "HolyRes is: \t\t%u\t\tFireRes is: \t\t%u", GetResistance(SPELL_SCHOOL_HOLY), GetResistance(SPELL_SCHOOL_FIRE));
  7005. sLog->outDebug(LOG_FILTER_UNITS, "NatureRes is: \t\t%u\t\tFrostRes is: \t\t%u", GetResistance(SPELL_SCHOOL_NATURE), GetResistance(SPELL_SCHOOL_FROST));
  7006. sLog->outDebug(LOG_FILTER_UNITS, "ShadowRes is: \t\t%u\t\tArcaneRes is: \t\t%u", GetResistance(SPELL_SCHOOL_SHADOW), GetResistance(SPELL_SCHOOL_ARCANE));
  7007. sLog->outDebug(LOG_FILTER_UNITS, "MIN_DAMAGE is: \t\t%f\tMAX_DAMAGE is: \t\t%f", GetFloatValue(UNIT_FIELD_MINDAMAGE), GetFloatValue(UNIT_FIELD_MAXDAMAGE));
  7008. sLog->outDebug(LOG_FILTER_UNITS, "MIN_OFFHAND_DAMAGE is: \t%f\tMAX_OFFHAND_DAMAGE is: \t%f", GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE), GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE));
  7009. sLog->outDebug(LOG_FILTER_UNITS, "MIN_RANGED_DAMAGE is: \t%f\tMAX_RANGED_DAMAGE is: \t%f", GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE), GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE));
  7010. sLog->outDebug(LOG_FILTER_UNITS, "ATTACK_TIME is: \t%u\t\tRANGE_ATTACK_TIME is: \t%u", GetAttackTime(BASE_ATTACK), GetAttackTime(RANGED_ATTACK));
  7011. }
  7012.  
  7013. /*********************************************************/
  7014. /*** FLOOD FILTER SYSTEM ***/
  7015. /*********************************************************/
  7016.  
  7017. void Player::UpdateSpeakTime()
  7018. {
  7019. // ignore chat spam protection for GMs in any mode
  7020. if (!AccountMgr::IsPlayerAccount(GetSession()->GetSecurity()))
  7021. return;
  7022.  
  7023. time_t current = time (NULL);
  7024. if (m_speakTime > current)
  7025. {
  7026. uint32 max_count = sWorld->getIntConfig(CONFIG_CHATFLOOD_MESSAGE_COUNT);
  7027. if (!max_count)
  7028. return;
  7029.  
  7030. ++m_speakCount;
  7031. if (m_speakCount >= max_count)
  7032. {
  7033. // prevent overwrite mute time, if message send just before mutes set, for example.
  7034. time_t new_mute = current + sWorld->getIntConfig(CONFIG_CHATFLOOD_MUTE_TIME);
  7035. if (GetSession()->m_muteTime < new_mute)
  7036. GetSession()->m_muteTime = new_mute;
  7037.  
  7038. m_speakCount = 0;
  7039. }
  7040. }
  7041. else
  7042. m_speakCount = 0;
  7043.  
  7044. m_speakTime = current + sWorld->getIntConfig(CONFIG_CHATFLOOD_MESSAGE_DELAY);
  7045. }
  7046.  
  7047. bool Player::CanSpeak() const
  7048. {
  7049. return GetSession()->m_muteTime <= time (NULL);
  7050. }
  7051.  
  7052. /*********************************************************/
  7053. /*** LOW LEVEL FUNCTIONS:Notifiers ***/
  7054. /*********************************************************/
  7055.  
  7056. void Player::SendAttackSwingNotInRange()
  7057. {
  7058. WorldPacket data(SMSG_ATTACKSWING_NOTINRANGE, 0);
  7059. GetSession()->SendPacket(&data);
  7060. }
  7061.  
  7062. void Player::SavePositionInDB(uint32 mapid, float x, float y, float z, float o, uint32 zone, uint64 guid)
  7063. {
  7064. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_POSITION);
  7065.  
  7066. stmt->setFloat(0, x);
  7067. stmt->setFloat(1, y);
  7068. stmt->setFloat(2, z);
  7069. stmt->setFloat(3, o);
  7070. stmt->setUInt16(4, uint16(mapid));
  7071. stmt->setUInt16(5, uint16(zone));
  7072. stmt->setUInt32(6, GUID_LOPART(guid));
  7073.  
  7074. CharacterDatabase.Execute(stmt);
  7075. }
  7076.  
  7077. void Player::SetUInt32ValueInArray(Tokens& tokens, uint16 index, uint32 value)
  7078. {
  7079. char buf[11];
  7080. snprintf(buf, 11, "%u", value);
  7081.  
  7082. if (index >= tokens.size())
  7083. return;
  7084.  
  7085. tokens[index] = buf;
  7086. }
  7087.  
  7088. void Player::Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair)
  7089. {
  7090. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PLAYERBYTES2);
  7091. stmt->setUInt32(0, GUID_LOPART(guid));
  7092. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  7093.  
  7094. if (!result)
  7095. return;
  7096.  
  7097. Field* fields = result->Fetch();
  7098.  
  7099. uint32 playerBytes2 = fields[0].GetUInt32();
  7100. playerBytes2 &= ~0xFF;
  7101. playerBytes2 |= facialHair;
  7102.  
  7103. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GENDER_PLAYERBYTES);
  7104.  
  7105. stmt->setUInt8(0, gender);
  7106. stmt->setUInt32(1, skin | (face << 8) | (hairStyle << 16) | (hairColor << 24));
  7107. stmt->setUInt32(2, playerBytes2);
  7108. stmt->setUInt32(3, GUID_LOPART(guid));
  7109.  
  7110. CharacterDatabase.Execute(stmt);
  7111. }
  7112.  
  7113. void Player::SendAttackSwingDeadTarget()
  7114. {
  7115. WorldPacket data(SMSG_ATTACKSWING_DEADTARGET, 0);
  7116. GetSession()->SendPacket(&data);
  7117. }
  7118.  
  7119. void Player::SendAttackSwingCantAttack()
  7120. {
  7121. WorldPacket data(SMSG_ATTACKSWING_CANT_ATTACK, 0);
  7122. GetSession()->SendPacket(&data);
  7123. }
  7124.  
  7125. void Player::SendAttackSwingCancelAttack()
  7126. {
  7127. WorldPacket data(SMSG_CANCEL_COMBAT, 0);
  7128. GetSession()->SendPacket(&data);
  7129. }
  7130.  
  7131. void Player::SendAttackSwingBadFacingAttack()
  7132. {
  7133. WorldPacket data(SMSG_ATTACKSWING_BADFACING, 0);
  7134. GetSession()->SendPacket(&data);
  7135. }
  7136.  
  7137. void Player::SendAutoRepeatCancel(Unit* target)
  7138. {
  7139. WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, target->GetPackGUID().size());
  7140. data.append(target->GetPackGUID()); // may be it's target guid
  7141. GetSession()->SendPacket(&data);
  7142. }
  7143.  
  7144. void Player::SendExplorationExperience(uint32 Area, uint32 Experience)
  7145. {
  7146. WorldPacket data(SMSG_EXPLORATION_EXPERIENCE, 8);
  7147. data << uint32(Area);
  7148. data << uint32(Experience);
  7149. GetSession()->SendPacket(&data);
  7150. }
  7151.  
  7152. void Player::SendDungeonDifficulty(bool IsInGroup)
  7153. {
  7154. uint8 val = 0x00000001;
  7155. WorldPacket data(MSG_SET_DUNGEON_DIFFICULTY, 12);
  7156. data << (uint32)GetDungeonDifficulty();
  7157. data << uint32(val);
  7158. data << uint32(IsInGroup);
  7159. GetSession()->SendPacket(&data);
  7160. }
  7161.  
  7162. void Player::SendRaidDifficulty(bool IsInGroup, int32 forcedDifficulty)
  7163. {
  7164. uint8 val = 0x00000001;
  7165. WorldPacket data(MSG_SET_RAID_DIFFICULTY, 12);
  7166. data << uint32(forcedDifficulty == -1 ? GetRaidDifficulty() : forcedDifficulty);
  7167. data << uint32(val);
  7168. data << uint32(IsInGroup);
  7169. GetSession()->SendPacket(&data);
  7170. }
  7171.  
  7172. void Player::SendResetFailedNotify(uint32 mapid)
  7173. {
  7174. WorldPacket data(SMSG_RESET_FAILED_NOTIFY, 4);
  7175. data << uint32(mapid);
  7176. GetSession()->SendPacket(&data);
  7177. }
  7178.  
  7179. /// Reset all solo instances and optionally send a message on success for each
  7180. void Player::ResetInstances(uint8 method, bool isRaid)
  7181. {
  7182. // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN
  7183.  
  7184. // we assume that when the difficulty changes, all instances that can be reset will be
  7185. Difficulty diff = GetDifficulty(isRaid);
  7186.  
  7187. for (BoundInstancesMap::iterator itr = m_boundInstances[diff].begin(); itr != m_boundInstances[diff].end();)
  7188. {
  7189. InstanceSave* p = itr->second.save;
  7190. const MapEntry* entry = sMapStore.LookupEntry(itr->first);
  7191. if (!entry || entry->IsRaid() != isRaid || !p->CanReset())
  7192. {
  7193. ++itr;
  7194. continue;
  7195. }
  7196.  
  7197. if (method == INSTANCE_RESET_ALL)
  7198. {
  7199. // the "reset all instances" method can only reset normal maps
  7200. if (entry->map_type == MAP_RAID || diff == DUNGEON_DIFFICULTY_HEROIC)
  7201. {
  7202. ++itr;
  7203. continue;
  7204. }
  7205. }
  7206.  
  7207. // if the map is loaded, reset it
  7208. Map* map = sMapMgr->FindMap(p->GetMapId(), p->GetInstanceId());
  7209. if (map && map->IsDungeon())
  7210. if (!((InstanceMap*)map)->Reset(method))
  7211. {
  7212. ++itr;
  7213. continue;
  7214. }
  7215.  
  7216. // since this is a solo instance there should not be any players inside
  7217. if (method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
  7218. SendResetInstanceSuccess(p->GetMapId());
  7219.  
  7220. p->DeleteFromDB();
  7221. m_boundInstances[diff].erase(itr++);
  7222.  
  7223. // the following should remove the instance save from the manager and delete it as well
  7224. p->RemovePlayer(this);
  7225. }
  7226. }
  7227.  
  7228. void Player::SendResetInstanceSuccess(uint32 MapId)
  7229. {
  7230. WorldPacket data(SMSG_INSTANCE_RESET, 4);
  7231. data << uint32(MapId);
  7232. GetSession()->SendPacket(&data);
  7233. }
  7234.  
  7235. void Player::SendResetInstanceFailed(uint32 reason, uint32 MapId)
  7236. {
  7237. /*reasons for instance reset failure:
  7238. // 0: There are players inside the instance.
  7239. // 1: There are players offline in your party.
  7240. // 2>: There are players in your party attempting to zone into an instance.
  7241. */
  7242. WorldPacket data(SMSG_INSTANCE_RESET_FAILED, 4);
  7243. data << uint32(reason);
  7244. data << uint32(MapId);
  7245. GetSession()->SendPacket(&data);
  7246. }
  7247.  
  7248. /*********************************************************/
  7249. /*** Update timers ***/
  7250. /*********************************************************/
  7251.  
  7252. ///checks the 15 afk reports per 5 minutes limit
  7253. void Player::UpdateAfkReport(time_t currTime)
  7254. {
  7255. if (m_bgData.bgAfkReportedTimer <= currTime)
  7256. {
  7257. m_bgData.bgAfkReportedCount = 0;
  7258. m_bgData.bgAfkReportedTimer = currTime+5*MINUTE;
  7259. }
  7260. }
  7261.  
  7262. void Player::UpdateContestedPvP(uint32 diff)
  7263. {
  7264. if (!m_contestedPvPTimer||isInCombat())
  7265. return;
  7266. if (m_contestedPvPTimer <= diff)
  7267. {
  7268. ResetContestedPvP();
  7269. }
  7270. else
  7271. m_contestedPvPTimer -= diff;
  7272. }
  7273.  
  7274. void Player::UpdatePvPFlag(time_t currTime)
  7275. {
  7276. if (!IsPvP())
  7277. return;
  7278. if (pvpInfo.endTimer == 0 || currTime < (pvpInfo.endTimer + 300) || pvpInfo.inHostileArea)
  7279. return;
  7280.  
  7281. UpdatePvP(false);
  7282. }
  7283.  
  7284. void Player::UpdateDuelFlag(time_t currTime)
  7285. {
  7286. if (!duel || duel->startTimer == 0 ||currTime < duel->startTimer + 3)
  7287. return;
  7288.  
  7289. sScriptMgr->OnPlayerDuelStart(this, duel->opponent);
  7290.  
  7291. SetUInt32Value(PLAYER_DUEL_TEAM, 1);
  7292. duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 2);
  7293.  
  7294. duel->startTimer = 0;
  7295. duel->startTime = currTime;
  7296. duel->opponent->duel->startTimer = 0;
  7297. duel->opponent->duel->startTime = currTime;
  7298. }
  7299.  
  7300. Pet* Player::GetPet() const
  7301. {
  7302. if (uint64 pet_guid = GetPetGUID())
  7303. {
  7304. if (!IS_PET_GUID(pet_guid))
  7305. return NULL;
  7306.  
  7307. Pet* pet = ObjectAccessor::GetPet(*this, pet_guid);
  7308.  
  7309. if (!pet)
  7310. return NULL;
  7311.  
  7312. if (IsInWorld() && pet)
  7313. return pet;
  7314.  
  7315. //there may be a guardian in slot
  7316. //sLog->outError(LOG_FILTER_PLAYER, "Player::GetPet: Pet %u not exist.", GUID_LOPART(pet_guid));
  7317. //const_cast<Player*>(this)->SetPetGUID(0);
  7318. }
  7319.  
  7320. return NULL;
  7321. }
  7322.  
  7323. void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
  7324. {
  7325. if (!pet)
  7326. pet = GetPet();
  7327.  
  7328. if (pet)
  7329. {
  7330. sLog->outDebug(LOG_FILTER_PETS, "RemovePet %u, %u, %u", pet->GetEntry(), mode, returnreagent);
  7331.  
  7332. if (pet->m_removed)
  7333. return;
  7334. }
  7335.  
  7336. if (returnreagent && (pet || m_temporaryUnsummonedPetNumber) && !InBattleground())
  7337. {
  7338. //returning of reagents only for players, so best done here
  7339. uint32 spellId = pet ? pet->GetUInt32Value(UNIT_CREATED_BY_SPELL) : m_oldpetspell;
  7340. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  7341.  
  7342. if (spellInfo)
  7343. {
  7344. for (uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i)
  7345. {
  7346. if (spellInfo->Reagent[i] > 0)
  7347. {
  7348. ItemPosCountVec dest; //for succubus, voidwalker, felhunter and felguard credit soulshard when despawn reason other than death (out of range, logout)
  7349. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, spellInfo->Reagent[i], spellInfo->ReagentCount[i]);
  7350. if (msg == EQUIP_ERR_OK)
  7351. {
  7352. Item* item = StoreNewItem(dest, spellInfo->Reagent[i], true);
  7353. if (IsInWorld())
  7354. SendNewItem(item, spellInfo->ReagentCount[i], true, false);
  7355. }
  7356. }
  7357. }
  7358. }
  7359. m_temporaryUnsummonedPetNumber = 0;
  7360. }
  7361.  
  7362. if (!pet || pet->GetOwnerGUID() != GetGUID())
  7363. return;
  7364.  
  7365. pet->CombatStop();
  7366.  
  7367. if (returnreagent)
  7368. {
  7369. switch (pet->GetEntry())
  7370. {
  7371. //warlock pets except imp are removed(?) when logging out
  7372. case 1860:
  7373. case 1863:
  7374. case 417:
  7375. case 17252:
  7376. mode = PET_SAVE_NOT_IN_SLOT;
  7377. break;
  7378. }
  7379. }
  7380.  
  7381. // only if current pet in slot
  7382. pet->SavePetToDB(mode);
  7383.  
  7384. SetMinion(pet, false);
  7385.  
  7386. pet->AddObjectToRemoveList();
  7387. pet->m_removed = true;
  7388.  
  7389. if (pet->isControlled())
  7390. {
  7391. WorldPacket data(SMSG_PET_SPELLS, 8);
  7392. data << uint64(0);
  7393. GetSession()->SendPacket(&data);
  7394.  
  7395. if (GetGroup())
  7396. SetGroupUpdateFlag(GROUP_UPDATE_PET);
  7397. }
  7398. }
  7399.  
  7400. void Player::StopCastingCharm()
  7401. {
  7402. Unit* charm = GetCharm();
  7403. if (!charm)
  7404. return;
  7405.  
  7406. if (charm->GetTypeId() == TYPEID_UNIT)
  7407. {
  7408. if (charm->ToCreature()->HasUnitTypeMask(UNIT_MASK_PUPPET))
  7409. ((Puppet*)charm)->UnSummon();
  7410. else if (charm->IsVehicle())
  7411. ExitVehicle();
  7412. }
  7413. if (GetCharmGUID())
  7414. charm->RemoveCharmAuras();
  7415.  
  7416. if (GetCharmGUID())
  7417. {
  7418. sLog->outFatal(LOG_FILTER_PLAYER, "Player %s (GUID: " UI64FMTD " is not able to uncharm unit (GUID: " UI64FMTD " Entry: %u, Type: %u)", GetName(), GetGUID(), GetCharmGUID(), charm->GetEntry(), charm->GetTypeId());
  7419. if (charm->GetCharmerGUID())
  7420. {
  7421. sLog->outFatal(LOG_FILTER_PLAYER, "Charmed unit has charmer guid " UI64FMTD, charm->GetCharmerGUID());
  7422. ASSERT(false);
  7423. }
  7424. else
  7425. SetCharm(charm, false);
  7426. }
  7427. }
  7428.  
  7429. inline void Player::BuildPlayerChat(WorldPacket* data, uint8 msgtype, const std::string& text, uint32 language) const
  7430. {
  7431. *data << uint8(msgtype);
  7432. *data << uint32(language);
  7433. *data << uint64(GetGUID());
  7434. *data << uint32(0); // constant unknown time
  7435. *data << uint64(GetGUID());
  7436. *data << uint32(text.length() + 1);
  7437. *data << text;
  7438. *data << uint8(GetChatTag());
  7439. }
  7440.  
  7441. void Player::Say(const std::string& text, const uint32 language)
  7442. {
  7443. std::string _text(text);
  7444. sScriptMgr->OnPlayerChat(this, CHAT_MSG_SAY, language, _text);
  7445.  
  7446. WorldPacket data(SMSG_MESSAGECHAT, 200);
  7447. BuildPlayerChat(&data, CHAT_MSG_SAY, _text, language);
  7448. SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true);
  7449. }
  7450.  
  7451. void Player::Yell(const std::string& text, const uint32 language)
  7452. {
  7453. std::string _text(text);
  7454. sScriptMgr->OnPlayerChat(this, CHAT_MSG_YELL, language, _text);
  7455.  
  7456. WorldPacket data(SMSG_MESSAGECHAT, 200);
  7457. BuildPlayerChat(&data, CHAT_MSG_YELL, _text, language);
  7458. SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true);
  7459. }
  7460.  
  7461. void Player::TextEmote(const std::string& text)
  7462. {
  7463. std::string _text(text);
  7464. sScriptMgr->OnPlayerChat(this, CHAT_MSG_EMOTE, LANG_UNIVERSAL, _text);
  7465.  
  7466. WorldPacket data(SMSG_MESSAGECHAT, 200);
  7467. BuildPlayerChat(&data, CHAT_MSG_EMOTE, _text, LANG_UNIVERSAL);
  7468. SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT));
  7469. }
  7470.  
  7471. void Player::Whisper(const std::string& text, uint32 language, uint64 receiver)
  7472. {
  7473. bool isAddonMessage = language == LANG_ADDON;
  7474.  
  7475. if (!isAddonMessage) // if not addon data
  7476. language = LANG_UNIVERSAL; // whispers should always be readable
  7477.  
  7478. Player* rPlayer = ObjectAccessor::FindPlayer(receiver);
  7479.  
  7480. std::string _text(text);
  7481. sScriptMgr->OnPlayerChat(this, CHAT_MSG_WHISPER, language, _text, rPlayer);
  7482.  
  7483. // when player you are whispering to is dnd, he cannot receive your message, unless you are in gm mode
  7484. if (!rPlayer->isDND() || isGameMaster())
  7485. {
  7486. WorldPacket data(SMSG_MESSAGECHAT, 200);
  7487. BuildPlayerChat(&data, CHAT_MSG_WHISPER, _text, language);
  7488. rPlayer->GetSession()->SendPacket(&data);
  7489.  
  7490. // not send confirmation for addon messages
  7491. if (!isAddonMessage)
  7492. {
  7493. data.Initialize(SMSG_MESSAGECHAT, 200);
  7494. rPlayer->BuildPlayerChat(&data, CHAT_MSG_WHISPER_INFORM, _text, language);
  7495. GetSession()->SendPacket(&data);
  7496. }
  7497. }
  7498. else if (!isAddonMessage)
  7499. // announce to player that player he is whispering to is dnd and cannot receive his message
  7500. ChatHandler(this).PSendSysMessage(LANG_PLAYER_DND, rPlayer->GetName(), rPlayer->dndMsg.c_str());
  7501.  
  7502. // rest stuff shouldn't happen in case of addon message
  7503. if (isAddonMessage)
  7504. return;
  7505.  
  7506. if (!isAcceptWhispers() && !isGameMaster() && !rPlayer->isGameMaster())
  7507. {
  7508. SetAcceptWhispers(true);
  7509. ChatHandler(this).SendSysMessage(LANG_COMMAND_WHISPERON);
  7510. }
  7511.  
  7512. // announce to player that player he is whispering to is afk
  7513. if (rPlayer->isAFK())
  7514. ChatHandler(this).PSendSysMessage(LANG_PLAYER_AFK, rPlayer->GetName(), rPlayer->afkMsg.c_str());
  7515.  
  7516. // if player whisper someone, auto turn of dnd to be able to receive an answer
  7517. if (isDND() && !rPlayer->isGameMaster())
  7518. ToggleDND();
  7519. }
  7520.  
  7521. void Player::PetSpellInitialize()
  7522. {
  7523. Pet* pet = GetPet();
  7524.  
  7525. if (!pet)
  7526. return;
  7527.  
  7528. sLog->outDebug(LOG_FILTER_PETS, "Pet Spells Groups");
  7529.  
  7530. CharmInfo* charmInfo = pet->GetCharmInfo();
  7531.  
  7532. WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1);
  7533. data << uint64(pet->GetGUID());
  7534. data << uint16(pet->GetCreatureTemplate()->family); // creature family (required for pet talents)
  7535. data << uint32(pet->GetDuration());
  7536. data << uint8(pet->GetReactState());
  7537. data << uint8(charmInfo->GetCommandState());
  7538. data << uint16(0); // Flags, mostly unknown
  7539.  
  7540. // action bar loop
  7541. charmInfo->BuildActionBar(&data);
  7542.  
  7543. size_t spellsCountPos = data.wpos();
  7544.  
  7545. // spells count
  7546. uint8 addlist = 0;
  7547. data << uint8(addlist); // placeholder
  7548.  
  7549. if (pet->IsPermanentPetFor(this))
  7550. {
  7551. // spells loop
  7552. for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
  7553. {
  7554. if (itr->second.state == PETSPELL_REMOVED)
  7555. continue;
  7556.  
  7557. data << uint32(MAKE_UNIT_ACTION_BUTTON(itr->first, itr->second.active));
  7558. ++addlist;
  7559. }
  7560. }
  7561.  
  7562. data.put<uint8>(spellsCountPos, addlist);
  7563.  
  7564. uint8 cooldownsCount = pet->m_CreatureSpellCooldowns.size() + pet->m_CreatureCategoryCooldowns.size();
  7565. data << uint8(cooldownsCount);
  7566.  
  7567. time_t curTime = time(NULL);
  7568.  
  7569. for (CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureSpellCooldowns.begin(); itr != pet->m_CreatureSpellCooldowns.end(); ++itr)
  7570. {
  7571. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
  7572. if (!spellInfo)
  7573. {
  7574. data << uint32(0);
  7575. data << uint16(0);
  7576. data << uint32(0);
  7577. data << uint32(0);
  7578. continue;
  7579. }
  7580.  
  7581. time_t cooldown = (itr->second > curTime) ? (itr->second - curTime) * IN_MILLISECONDS : 0;
  7582. data << uint32(itr->first); // spell ID
  7583.  
  7584. CreatureSpellCooldowns::const_iterator categoryitr = pet->m_CreatureCategoryCooldowns.find(spellInfo->Category);
  7585. if (categoryitr != pet->m_CreatureCategoryCooldowns.end())
  7586. {
  7587. time_t categoryCooldown = (categoryitr->second > curTime) ? (categoryitr->second - curTime) * IN_MILLISECONDS : 0;
  7588. data << uint16(spellInfo->Category); // spell category
  7589. data << uint32(cooldown); // spell cooldown
  7590. data << uint32(categoryCooldown); // category cooldown
  7591. }
  7592. else
  7593. {
  7594. data << uint16(0);
  7595. data << uint32(cooldown);
  7596. data << uint32(0);
  7597. }
  7598. }
  7599.  
  7600. GetSession()->SendPacket(&data);
  7601. }
  7602.  
  7603. void Player::PossessSpellInitialize()
  7604. {
  7605. Unit* charm = GetCharm();
  7606. if (!charm)
  7607. return;
  7608.  
  7609. CharmInfo* charmInfo = charm->GetCharmInfo();
  7610.  
  7611. if (!charmInfo)
  7612. {
  7613. sLog->outError(LOG_FILTER_PLAYER, "Player::PossessSpellInitialize(): charm ("UI64FMTD") has no charminfo!", charm->GetGUID());
  7614. return;
  7615. }
  7616.  
  7617. WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1);
  7618. data << uint64(charm->GetGUID());
  7619. data << uint16(0);
  7620. data << uint32(0);
  7621. data << uint32(0);
  7622.  
  7623. charmInfo->BuildActionBar(&data);
  7624.  
  7625. data << uint8(0); // spells count
  7626. data << uint8(0); // cooldowns count
  7627.  
  7628. GetSession()->SendPacket(&data);
  7629. }
  7630.  
  7631. void Player::VehicleSpellInitialize()
  7632. {
  7633. Creature* vehicle = GetVehicleCreatureBase();
  7634. if (!vehicle)
  7635. return;
  7636.  
  7637. uint8 cooldownCount = vehicle->m_CreatureSpellCooldowns.size();
  7638.  
  7639. WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * 10 + 1 + 1 + cooldownCount * (4 + 2 + 4 + 4));
  7640. data << uint64(vehicle->GetGUID()); // Guid
  7641. data << uint16(0); // Pet Family (0 for all vehicles)
  7642. data << uint32(vehicle->isSummon() ? vehicle->ToTempSummon()->GetTimer() : 0); // Duration
  7643. // The following three segments are read by the client as one uint32
  7644. data << uint8(vehicle->GetReactState()); // React State
  7645. data << uint8(0); // Command State
  7646. data << uint16(0x800); // DisableActions (set for all vehicles)
  7647.  
  7648. for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
  7649. {
  7650. uint32 spellId = vehicle->m_spells[i];
  7651. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  7652. if (!spellInfo)
  7653. {
  7654. data << uint16(0) << uint8(0) << uint8(i+8);
  7655. continue;
  7656. }
  7657.  
  7658. ConditionList conditions = sConditionMgr->GetConditionsForVehicleSpell(vehicle->GetEntry(), spellId);
  7659. if (!sConditionMgr->IsObjectMeetToConditions(this, vehicle, conditions))
  7660. {
  7661. sLog->outDebug(LOG_FILTER_CONDITIONSYS, "VehicleSpellInitialize: conditions not met for Vehicle entry %u spell %u", vehicle->ToCreature()->GetEntry(), spellId);
  7662. data << uint16(0) << uint8(0) << uint8(i+8);
  7663. continue;
  7664. }
  7665.  
  7666. if (spellInfo->IsPassive())
  7667. vehicle->CastSpell(vehicle, spellId, true);
  7668.  
  7669. data << uint32(MAKE_UNIT_ACTION_BUTTON(spellId, i+8));
  7670. }
  7671.  
  7672. for (uint32 i = CREATURE_MAX_SPELLS; i < MAX_SPELL_CONTROL_BAR; ++i)
  7673. data << uint32(0);
  7674.  
  7675. data << uint8(0); // Auras?
  7676.  
  7677. // Cooldowns
  7678. data << uint8(cooldownCount);
  7679.  
  7680. time_t now = sWorld->GetGameTime();
  7681.  
  7682. for (CreatureSpellCooldowns::const_iterator itr = vehicle->m_CreatureSpellCooldowns.begin(); itr != vehicle->m_CreatureSpellCooldowns.end(); ++itr)
  7683. {
  7684. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
  7685. if (!spellInfo)
  7686. {
  7687. data << uint32(0);
  7688. data << uint16(0);
  7689. data << uint32(0);
  7690. data << uint32(0);
  7691. continue;
  7692. }
  7693.  
  7694. time_t cooldown = (itr->second > now) ? (itr->second - now) * IN_MILLISECONDS : 0;
  7695. data << uint32(itr->first); // spell ID
  7696.  
  7697. CreatureSpellCooldowns::const_iterator categoryitr = vehicle->m_CreatureCategoryCooldowns.find(spellInfo->Category);
  7698. if (categoryitr != vehicle->m_CreatureCategoryCooldowns.end())
  7699. {
  7700. time_t categoryCooldown = (categoryitr->second > now) ? (categoryitr->second - now) * IN_MILLISECONDS : 0;
  7701. data << uint16(spellInfo->Category); // spell category
  7702. data << uint32(cooldown); // spell cooldown
  7703. data << uint32(categoryCooldown); // category cooldown
  7704. }
  7705. else
  7706. {
  7707. data << uint16(0);
  7708. data << uint32(cooldown);
  7709. data << uint32(0);
  7710. }
  7711. }
  7712.  
  7713. GetSession()->SendPacket(&data);
  7714. }
  7715.  
  7716. void Player::CharmSpellInitialize()
  7717. {
  7718. Unit* charm = GetFirstControlled();
  7719. if (!charm)
  7720. return;
  7721.  
  7722. CharmInfo* charmInfo = charm->GetCharmInfo();
  7723. if (!charmInfo)
  7724. {
  7725. sLog->outError(LOG_FILTER_PLAYER, "Player::CharmSpellInitialize(): the player's charm ("UI64FMTD") has no charminfo!", charm->GetGUID());
  7726. return;
  7727. }
  7728.  
  7729. uint8 addlist = 0;
  7730. if (charm->GetTypeId() != TYPEID_PLAYER)
  7731. {
  7732. //CreatureInfo const* cinfo = charm->ToCreature()->GetCreatureTemplate();
  7733. //if (cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK)
  7734. {
  7735. for (uint32 i = 0; i < MAX_SPELL_CHARM; ++i)
  7736. if (charmInfo->GetCharmSpell(i)->GetAction())
  7737. ++addlist;
  7738. }
  7739. }
  7740.  
  7741. WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+4*addlist+1);
  7742. data << uint64(charm->GetGUID());
  7743. data << uint16(0);
  7744. data << uint32(0);
  7745.  
  7746. if (charm->GetTypeId() != TYPEID_PLAYER)
  7747. data << uint8(charm->ToCreature()->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0);
  7748. else
  7749. data << uint32(0);
  7750.  
  7751. charmInfo->BuildActionBar(&data);
  7752.  
  7753. data << uint8(addlist);
  7754.  
  7755. if (addlist)
  7756. {
  7757. for (uint32 i = 0; i < MAX_SPELL_CHARM; ++i)
  7758. {
  7759. CharmSpellInfo* cspell = charmInfo->GetCharmSpell(i);
  7760. if (cspell->GetAction())
  7761. data << uint32(cspell->packedData);
  7762. }
  7763. }
  7764.  
  7765. data << uint8(0); // cooldowns count
  7766.  
  7767. GetSession()->SendPacket(&data);
  7768. }
  7769.  
  7770. void Player::SendRemoveControlBar()
  7771. {
  7772. WorldPacket data(SMSG_PET_SPELLS, 8);
  7773. data << uint64(0);
  7774. GetSession()->SendPacket(&data);
  7775. }
  7776.  
  7777. bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell)
  7778. {
  7779. if (!mod || !spellInfo)
  7780. return false;
  7781.  
  7782. // Mod out of charges
  7783. if (spell && mod->charges == -1 && spell->m_appliedMods.find(mod->ownerAura) == spell->m_appliedMods.end())
  7784. return false;
  7785.  
  7786. // +duration to infinite duration spells making them limited
  7787. if (mod->op == SPELLMOD_DURATION && spellInfo->GetDuration() == -1)
  7788. return false;
  7789.  
  7790. return spellInfo->IsAffectedBySpellMod(mod);
  7791. }
  7792.  
  7793. void Player::AddSpellMod(SpellModifier* mod, bool apply)
  7794. {
  7795. sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Player::AddSpellMod %d", mod->spellId);
  7796. uint16 Opcode = (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER;
  7797.  
  7798. int i = 0;
  7799. flag96 _mask = 0;
  7800. for (int eff = 0; eff < 96; ++eff)
  7801. {
  7802. if (eff != 0 && eff%32 == 0)
  7803. _mask[i++] = 0;
  7804.  
  7805. _mask[i] = uint32(1) << (eff-(32*i));
  7806. if (mod->mask & _mask)
  7807. {
  7808. int32 val = 0;
  7809. for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr)
  7810. {
  7811. if ((*itr)->type == mod->type && (*itr)->mask & _mask)
  7812. val += (*itr)->value;
  7813. }
  7814. val += apply ? mod->value : -(mod->value);
  7815. WorldPacket data(Opcode, (1+1+4));
  7816. data << uint8(eff);
  7817. data << uint8(mod->op);
  7818. data << int32(val);
  7819. SendDirectMessage(&data);
  7820. }
  7821. }
  7822.  
  7823. if (apply)
  7824. m_spellMods[mod->op].push_back(mod);
  7825. else
  7826. {
  7827. m_spellMods[mod->op].remove(mod);
  7828. // mods bound to aura will be removed in AuraEffect::~AuraEffect
  7829. if (!mod->ownerAura)
  7830. delete mod;
  7831. }
  7832. }
  7833.  
  7834. // Restore spellmods in case of failed cast
  7835. void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId, Aura* aura)
  7836. {
  7837. if (!spell || spell->m_appliedMods.empty())
  7838. return;
  7839.  
  7840. for (uint8 i=0; i<MAX_SPELLMOD; ++i)
  7841. {
  7842. for (SpellModList::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end(); ++itr)
  7843. {
  7844. SpellModifier* mod = *itr;
  7845.  
  7846. // spellmods without aura set cannot be charged
  7847. if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges())
  7848. continue;
  7849.  
  7850. // Restore only specific owner aura mods
  7851. if (ownerAuraId && (ownerAuraId != mod->ownerAura->GetSpellInfo()->Id))
  7852. continue;
  7853.  
  7854. if (aura && mod->ownerAura != aura)
  7855. continue;
  7856.  
  7857. // check if mod affected this spell
  7858. // first, check if the mod aura applied at least one spellmod to this spell
  7859. Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(mod->ownerAura);
  7860. if (iterMod == spell->m_appliedMods.end())
  7861. continue;
  7862. // secondly, check if the current mod is one of the spellmods applied by the mod aura
  7863. if (!(mod->mask & spell->m_spellInfo->SpellFamilyFlags))
  7864. continue;
  7865.  
  7866. // remove from list
  7867. spell->m_appliedMods.erase(iterMod);
  7868.  
  7869. // add mod charges back to mod
  7870. if (mod->charges == -1)
  7871. mod->charges = 1;
  7872. else
  7873. mod->charges++;
  7874.  
  7875. // Do not set more spellmods than avalible
  7876. if (mod->ownerAura->GetCharges() < mod->charges)
  7877. mod->charges = mod->ownerAura->GetCharges();
  7878.  
  7879. // Skip this check for now - aura charges may change due to various reason
  7880. // TODO: trac these changes correctly
  7881. //ASSERT (mod->ownerAura->GetCharges() <= mod->charges);
  7882. }
  7883. }
  7884. }
  7885.  
  7886. void Player::RestoreAllSpellMods(uint32 ownerAuraId, Aura* aura)
  7887. {
  7888. for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i)
  7889. if (m_currentSpells[i])
  7890. RestoreSpellMods(m_currentSpells[i], ownerAuraId, aura);
  7891. }
  7892.  
  7893. void Player::RemoveSpellMods(Spell* spell)
  7894. {
  7895. if (!spell)
  7896. return;
  7897.  
  7898. if (spell->m_appliedMods.empty())
  7899. return;
  7900.  
  7901. for (uint8 i=0; i<MAX_SPELLMOD; ++i)
  7902. {
  7903. for (SpellModList::const_iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();)
  7904. {
  7905. SpellModifier* mod = *itr;
  7906. ++itr;
  7907.  
  7908. // spellmods without aura set cannot be charged
  7909. if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges())
  7910. continue;
  7911.  
  7912. // check if mod affected this spell
  7913. Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(mod->ownerAura);
  7914. if (iterMod == spell->m_appliedMods.end())
  7915. continue;
  7916.  
  7917. // remove from list
  7918. spell->m_appliedMods.erase(iterMod);
  7919.  
  7920. if (mod->ownerAura->DropCharge(AURA_REMOVE_BY_EXPIRE))
  7921. itr = m_spellMods[i].begin();
  7922. }
  7923. }
  7924. }
  7925.  
  7926. void Player::DropModCharge(SpellModifier* mod, Spell* spell)
  7927. {
  7928. // don't handle spells with proc_event entry defined
  7929. // this is a temporary workaround, because all spellmods should be handled like that
  7930. if (sSpellMgr->GetSpellProcEvent(mod->spellId))
  7931. return;
  7932.  
  7933. if (spell && mod->ownerAura && mod->charges > 0)
  7934. {
  7935. if (--mod->charges == 0)
  7936. mod->charges = -1;
  7937.  
  7938. spell->m_appliedMods.insert(mod->ownerAura);
  7939. }
  7940. }
  7941.  
  7942. void Player::SetSpellModTakingSpell(Spell* spell, bool apply)
  7943. {
  7944. if (!spell || (m_spellModTakingSpell && m_spellModTakingSpell != spell))
  7945. return;
  7946.  
  7947. if (apply && spell->getState() == SPELL_STATE_FINISHED)
  7948. return;
  7949.  
  7950. m_spellModTakingSpell = apply ? spell : NULL;
  7951. }
  7952.  
  7953. // send Proficiency
  7954. void Player::SendProficiency(ItemClass itemClass, uint32 itemSubclassMask)
  7955. {
  7956. WorldPacket data(SMSG_SET_PROFICIENCY, 1 + 4);
  7957. data << uint8(itemClass) << uint32(itemSubclassMask);
  7958. GetSession()->SendPacket(&data);
  7959. }
  7960.  
  7961. void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type)
  7962. {
  7963. PreparedStatement* stmt;
  7964.  
  7965. if (type == 10)
  7966. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PETITION_SIG_BY_GUID);
  7967. else
  7968. {
  7969. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PETITION_SIG_BY_GUID_TYPE);
  7970. stmt->setUInt8(1, uint8(type));
  7971. }
  7972.  
  7973. stmt->setUInt32(0, GUID_LOPART(guid));
  7974. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  7975.  
  7976. if (result)
  7977. {
  7978. do // this part effectively does nothing, since the deletion / modification only takes place _after_ the PetitionQuery. Though I don't know if the result remains intact if I execute the delete query beforehand.
  7979. { // and SendPetitionQueryOpcode reads data from the DB
  7980. Field* fields = result->Fetch();
  7981. uint64 ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
  7982. uint64 petitionguid = MAKE_NEW_GUID(fields[1].GetUInt32(), 0, HIGHGUID_ITEM);
  7983.  
  7984. // send update if charter owner in game
  7985. Player* owner = ObjectAccessor::FindPlayer(ownerguid);
  7986. if (owner)
  7987. owner->GetSession()->SendPetitionQueryOpcode(petitionguid);
  7988. } while (result->NextRow());
  7989.  
  7990. if (type == 10)
  7991. {
  7992. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_PETITION_SIGNATURES);
  7993.  
  7994. stmt->setUInt32(0, GUID_LOPART(guid));
  7995.  
  7996. CharacterDatabase.Execute(stmt);
  7997. }
  7998. else
  7999. {
  8000. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE);
  8001.  
  8002. stmt->setUInt32(0, GUID_LOPART(guid));
  8003. stmt->setUInt8(1, uint8(type));
  8004.  
  8005. CharacterDatabase.Execute(stmt);
  8006. }
  8007. }
  8008.  
  8009. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  8010. if (type == 10)
  8011. {
  8012. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER);
  8013. stmt->setUInt32(0, GUID_LOPART(guid));
  8014. trans->Append(stmt);
  8015.  
  8016. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER);
  8017. stmt->setUInt32(0, GUID_LOPART(guid));
  8018. trans->Append(stmt);
  8019. }
  8020. else
  8021. {
  8022. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER_AND_TYPE);
  8023. stmt->setUInt32(0, GUID_LOPART(guid));
  8024. stmt->setUInt8(1, uint8(type));
  8025. trans->Append(stmt);
  8026.  
  8027. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER_AND_TYPE);
  8028. stmt->setUInt32(0, GUID_LOPART(guid));
  8029. stmt->setUInt8(1, uint8(type));
  8030. trans->Append(stmt);
  8031. }
  8032. CharacterDatabase.CommitTransaction(trans);
  8033. }
  8034.  
  8035. void Player::LeaveAllArenaTeams(uint64 guid)
  8036. {
  8037. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYER_ARENA_TEAMS);
  8038. stmt->setUInt32(0, GUID_LOPART(guid));
  8039. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  8040.  
  8041. if (!result)
  8042. return;
  8043.  
  8044. do
  8045. {
  8046. Field* fields = result->Fetch();
  8047. uint32 arenaTeamId = fields[0].GetUInt32();
  8048. if (arenaTeamId != 0)
  8049. {
  8050. ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
  8051. if (arenaTeam)
  8052. arenaTeam->DelMember(guid, true);
  8053. }
  8054. }
  8055. while (result->NextRow());
  8056. }
  8057.  
  8058. void Player::SetRestBonus (float rest_bonus_new)
  8059. {
  8060. // Prevent resting on max level
  8061. if (getLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  8062. rest_bonus_new = 0;
  8063.  
  8064. if (rest_bonus_new < 0)
  8065. rest_bonus_new = 0;
  8066.  
  8067. float rest_bonus_max = (float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)*1.5f/2;
  8068.  
  8069. if (rest_bonus_new > rest_bonus_max)
  8070. m_rest_bonus = rest_bonus_max;
  8071. else
  8072. m_rest_bonus = rest_bonus_new;
  8073.  
  8074. // update data for client
  8075. if (GetSession()->IsARecruiter() || (GetSession()->GetRecruiterId() != 0))
  8076. SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RAF_LINKED);
  8077. else if (m_rest_bonus > 10)
  8078. SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RESTED); // Set Reststate = Rested
  8079. else if (m_rest_bonus <= 1)
  8080. SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_NOT_RAF_LINKED); // Set Reststate = Normal
  8081.  
  8082. //RestTickUpdate
  8083. SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus));
  8084. }
  8085.  
  8086. bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc /*= NULL*/, uint32 spellid /*= 0*/)
  8087. {
  8088. if (nodes.size() < 2)
  8089. return false;
  8090.  
  8091. // not let cheating with start flight in time of logout process || while in combat || has type state: stunned || has type state: root
  8092. if (GetSession()->isLogingOut() || isInCombat() || HasUnitState(UNIT_STATE_STUNNED) || HasUnitState(UNIT_STATE_ROOT))
  8093. {
  8094. GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERBUSY);
  8095. return false;
  8096. }
  8097.  
  8098. if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
  8099. return false;
  8100.  
  8101. // taximaster case
  8102. if (npc)
  8103. {
  8104. // not let cheating with start flight mounted
  8105. if (IsMounted())
  8106. {
  8107. GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERALREADYMOUNTED);
  8108. return false;
  8109. }
  8110.  
  8111. if (IsInDisallowedMountForm())
  8112. {
  8113. GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERSHAPESHIFTED);
  8114. return false;
  8115. }
  8116.  
  8117. // not let cheating with start flight in time of logout process || if casting not finished || while in combat || if not use Spell's with EffectSendTaxi
  8118. if (IsNonMeleeSpellCasted(false))
  8119. {
  8120. GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERBUSY);
  8121. return false;
  8122. }
  8123. }
  8124. // cast case or scripted call case
  8125. else
  8126. {
  8127. RemoveAurasByType(SPELL_AURA_MOUNTED);
  8128.  
  8129. if (IsInDisallowedMountForm())
  8130. RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
  8131.  
  8132. if (Spell* spell = GetCurrentSpell(CURRENT_GENERIC_SPELL))
  8133. if (spell->m_spellInfo->Id != spellid)
  8134. InterruptSpell(CURRENT_GENERIC_SPELL, false);
  8135.  
  8136. InterruptSpell(CURRENT_AUTOREPEAT_SPELL, false);
  8137.  
  8138. if (Spell* spell = GetCurrentSpell(CURRENT_CHANNELED_SPELL))
  8139. if (spell->m_spellInfo->Id != spellid)
  8140. InterruptSpell(CURRENT_CHANNELED_SPELL, true);
  8141. }
  8142.  
  8143. uint32 sourcenode = nodes[0];
  8144.  
  8145. // starting node too far away (cheat?)
  8146. TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(sourcenode);
  8147. if (!node)
  8148. {
  8149. GetSession()->SendActivateTaxiReply(ERR_TAXINOSUCHPATH);
  8150. return false;
  8151. }
  8152.  
  8153. // check node starting pos data set case if provided
  8154. if (node->x != 0.0f || node->y != 0.0f || node->z != 0.0f)
  8155. {
  8156. if (node->map_id != GetMapId() ||
  8157. (node->x - GetPositionX())*(node->x - GetPositionX())+
  8158. (node->y - GetPositionY())*(node->y - GetPositionY())+
  8159. (node->z - GetPositionZ())*(node->z - GetPositionZ()) >
  8160. (2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE))
  8161. {
  8162. GetSession()->SendActivateTaxiReply(ERR_TAXITOOFARAWAY);
  8163. return false;
  8164. }
  8165. }
  8166. // node must have pos if taxi master case (npc != NULL)
  8167. else if (npc)
  8168. {
  8169. GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR);
  8170. return false;
  8171. }
  8172.  
  8173. // Prepare to flight start now
  8174.  
  8175. // stop combat at start taxi flight if any
  8176. CombatStop();
  8177.  
  8178. StopCastingCharm();
  8179. StopCastingBindSight();
  8180. ExitVehicle();
  8181.  
  8182. // stop trade (client cancel trade at taxi map open but cheating tools can be used for reopen it)
  8183. TradeCancel(true);
  8184.  
  8185. // clean not finished taxi path if any
  8186. m_taxi.ClearTaxiDestinations();
  8187.  
  8188. // 0 element current node
  8189. m_taxi.AddTaxiDestination(sourcenode);
  8190.  
  8191. // fill destinations path tail
  8192. uint32 sourcepath = 0;
  8193. uint32 totalcost = 0;
  8194.  
  8195. uint32 prevnode = sourcenode;
  8196. uint32 lastnode = 0;
  8197.  
  8198. for (uint32 i = 1; i < nodes.size(); ++i)
  8199. {
  8200. uint32 path, cost;
  8201.  
  8202. lastnode = nodes[i];
  8203. sObjectMgr->GetTaxiPath(prevnode, lastnode, path, cost);
  8204.  
  8205. if (!path)
  8206. {
  8207. m_taxi.ClearTaxiDestinations();
  8208. return false;
  8209. }
  8210.  
  8211. totalcost += cost;
  8212.  
  8213. if (prevnode == sourcenode)
  8214. sourcepath = path;
  8215.  
  8216. m_taxi.AddTaxiDestination(lastnode);
  8217.  
  8218. prevnode = lastnode;
  8219. }
  8220.  
  8221. // get mount model (in case non taximaster (npc == NULL) allow more wide lookup)
  8222. //
  8223. // Hack-Fix for Alliance not being able to use Acherus taxi. There is
  8224. // only one mount ID for both sides. Probably not good to use 315 in case DBC nodes
  8225. // change but I couldn't find a suitable alternative. OK to use class because only DK
  8226. // can use this taxi.
  8227. uint32 mount_display_id = sObjectMgr->GetTaxiMountDisplayId(sourcenode, GetTeam(), npc == NULL || (sourcenode == 315 && getClass() == CLASS_DEATH_KNIGHT));
  8228.  
  8229. // in spell case allow 0 model
  8230. if ((mount_display_id == 0 && spellid == 0) || sourcepath == 0)
  8231. {
  8232. GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR);
  8233. m_taxi.ClearTaxiDestinations();
  8234. return false;
  8235. }
  8236.  
  8237. uint32 money = GetMoney();
  8238.  
  8239. if (npc)
  8240. totalcost = (uint32)ceil(totalcost*GetReputationPriceDiscount(npc));
  8241.  
  8242. if (money < totalcost)
  8243. {
  8244. GetSession()->SendActivateTaxiReply(ERR_TAXINOTENOUGHMONEY);
  8245. m_taxi.ClearTaxiDestinations();
  8246. return false;
  8247. }
  8248.  
  8249. //Checks and preparations done, DO FLIGHT
  8250. ModifyMoney(-(int32)totalcost);
  8251. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost);
  8252. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN, 1);
  8253.  
  8254. // prevent stealth flight
  8255. //RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);
  8256.  
  8257. if (sWorld->getBoolConfig(CONFIG_INSTANT_TAXI))
  8258. {
  8259. TaxiNodesEntry const* lastPathNode = sTaxiNodesStore.LookupEntry(nodes[nodes.size()-1]);
  8260. m_taxi.ClearTaxiDestinations();
  8261. TeleportTo(lastPathNode->map_id, lastPathNode->x, lastPathNode->y, lastPathNode->z, GetOrientation());
  8262. return false;
  8263. }
  8264. else
  8265. {
  8266. GetSession()->SendActivateTaxiReply(ERR_TAXIOK);
  8267. GetSession()->SendDoFlight(mount_display_id, sourcepath);
  8268. }
  8269. return true;
  8270. }
  8271.  
  8272. bool Player::ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid /*= 0*/)
  8273. {
  8274. TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(taxi_path_id);
  8275. if (!entry)
  8276. return false;
  8277.  
  8278. std::vector<uint32> nodes;
  8279.  
  8280. nodes.resize(2);
  8281. nodes[0] = entry->from;
  8282. nodes[1] = entry->to;
  8283.  
  8284. return ActivateTaxiPathTo(nodes, NULL, spellid);
  8285. }
  8286.  
  8287. void Player::CleanupAfterTaxiFlight()
  8288. {
  8289. m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
  8290. Dismount();
  8291. RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
  8292. getHostileRefManager().setOnlineOfflineState(true);
  8293. }
  8294.  
  8295. void Player::ContinueTaxiFlight()
  8296. {
  8297. uint32 sourceNode = m_taxi.GetTaxiSource();
  8298. if (!sourceNode)
  8299. return;
  8300.  
  8301. sLog->outDebug(LOG_FILTER_UNITS, "WORLD: Restart character %u taxi flight", GetGUIDLow());
  8302.  
  8303. uint32 mountDisplayId = sObjectMgr->GetTaxiMountDisplayId(sourceNode, GetTeam(), true);
  8304. uint32 path = m_taxi.GetCurrentTaxiPath();
  8305.  
  8306. // search appropriate start path node
  8307. uint32 startNode = 0;
  8308.  
  8309. TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path];
  8310.  
  8311. float distPrev = MAP_SIZE*MAP_SIZE;
  8312. float distNext =
  8313. (nodeList[0].x-GetPositionX())*(nodeList[0].x-GetPositionX())+
  8314. (nodeList[0].y-GetPositionY())*(nodeList[0].y-GetPositionY())+
  8315. (nodeList[0].z-GetPositionZ())*(nodeList[0].z-GetPositionZ());
  8316.  
  8317. for (uint32 i = 1; i < nodeList.size(); ++i)
  8318. {
  8319. TaxiPathNodeEntry const& node = nodeList[i];
  8320. TaxiPathNodeEntry const& prevNode = nodeList[i-1];
  8321.  
  8322. // skip nodes at another map
  8323. if (node.mapid != GetMapId())
  8324. continue;
  8325.  
  8326. distPrev = distNext;
  8327.  
  8328. distNext =
  8329. (node.x-GetPositionX())*(node.x-GetPositionX())+
  8330. (node.y-GetPositionY())*(node.y-GetPositionY())+
  8331. (node.z-GetPositionZ())*(node.z-GetPositionZ());
  8332.  
  8333. float distNodes =
  8334. (node.x-prevNode.x)*(node.x-prevNode.x)+
  8335. (node.y-prevNode.y)*(node.y-prevNode.y)+
  8336. (node.z-prevNode.z)*(node.z-prevNode.z);
  8337.  
  8338. if (distNext + distPrev < distNodes)
  8339. {
  8340. startNode = i;
  8341. break;
  8342. }
  8343. }
  8344.  
  8345. GetSession()->SendDoFlight(mountDisplayId, path, startNode);
  8346. }
  8347.  
  8348. void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
  8349. {
  8350. // last check 2.0.10
  8351. WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+m_spells.size()*8);
  8352. data << uint64(GetGUID());
  8353. data << uint8(0x0); // flags (0x1, 0x2)
  8354. time_t curTime = time(NULL);
  8355. for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
  8356. {
  8357. if (itr->second->state == PLAYERSPELL_REMOVED)
  8358. continue;
  8359. uint32 unSpellId = itr->first;
  8360. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(unSpellId);
  8361. if (!spellInfo)
  8362. {
  8363. ASSERT(spellInfo);
  8364. continue;
  8365. }
  8366.  
  8367. // Not send cooldown for this spells
  8368. if (spellInfo->Attributes & SPELL_ATTR0_DISABLED_WHILE_ACTIVE)
  8369. continue;
  8370.  
  8371. if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE)
  8372. continue;
  8373.  
  8374. if ((idSchoolMask & spellInfo->GetSchoolMask()) && GetSpellCooldownDelay(unSpellId) < unTimeMs)
  8375. {
  8376. data << uint32(unSpellId);
  8377. data << uint32(unTimeMs); // in m.secs
  8378. AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/IN_MILLISECONDS);
  8379. }
  8380. }
  8381. GetSession()->SendPacket(&data);
  8382. }
  8383.  
  8384. void Player::InitDataForForm(bool reapplyMods)
  8385. {
  8386. ShapeshiftForm form = GetShapeshiftForm();
  8387.  
  8388. SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(form);
  8389. if (ssEntry && ssEntry->attackSpeed)
  8390. {
  8391. SetAttackTime(BASE_ATTACK, ssEntry->attackSpeed);
  8392. SetAttackTime(OFF_ATTACK, ssEntry->attackSpeed);
  8393. SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
  8394. }
  8395. else
  8396. SetRegularAttackTime();
  8397.  
  8398. switch (form)
  8399. {
  8400. case FORM_GHOUL:
  8401. case FORM_CAT:
  8402. {
  8403. if (getPowerType() != POWER_ENERGY)
  8404. setPowerType(POWER_ENERGY);
  8405. break;
  8406. }
  8407. case FORM_BEAR:
  8408. case FORM_DIREBEAR:
  8409. {
  8410. if (getPowerType() != POWER_RAGE)
  8411. setPowerType(POWER_RAGE);
  8412. break;
  8413. }
  8414. default: // 0, for example
  8415. {
  8416. ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(getClass());
  8417. if (cEntry && cEntry->powerType < MAX_POWERS && uint32(getPowerType()) != cEntry->powerType)
  8418. setPowerType(Powers(cEntry->powerType));
  8419. break;
  8420. }
  8421. }
  8422.  
  8423. // update auras at form change, ignore this at mods reapply (.reset stats/etc) when form not change.
  8424. if (!reapplyMods)
  8425. UpdateEquipSpellsAtFormChange();
  8426.  
  8427. UpdateAttackPowerAndDamage();
  8428. UpdateAttackPowerAndDamage(true);
  8429. }
  8430.  
  8431. void Player::InitDisplayIds()
  8432. {
  8433. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  8434. if (!info)
  8435. {
  8436. sLog->outError(LOG_FILTER_PLAYER, "Player %u has incorrect race/class pair. Can't init display ids.", GetGUIDLow());
  8437. return;
  8438. }
  8439.  
  8440. uint8 gender = getGender();
  8441. switch (gender)
  8442. {
  8443. case GENDER_FEMALE:
  8444. SetDisplayId(info->displayId_f);
  8445. SetNativeDisplayId(info->displayId_f);
  8446. break;
  8447. case GENDER_MALE:
  8448. SetDisplayId(info->displayId_m);
  8449. SetNativeDisplayId(info->displayId_m);
  8450. break;
  8451. default:
  8452. sLog->outError(LOG_FILTER_PLAYER, "Invalid gender %u for player", gender);
  8453. return;
  8454. }
  8455. }
  8456.  
  8457. inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot, int32 price, ItemTemplate const* pProto, Creature* pVendor, VendorItem const* crItem, bool bStore)
  8458. {
  8459. ItemPosCountVec vDest;
  8460. uint16 uiDest = 0;
  8461. InventoryResult msg = bStore ?
  8462. CanStoreNewItem(bag, slot, vDest, item, pProto->BuyCount * count) :
  8463. CanEquipNewItem(slot, uiDest, item, false);
  8464. if (msg != EQUIP_ERR_OK)
  8465. {
  8466. SendEquipError(msg, NULL, NULL, item);
  8467. return false;
  8468. }
  8469.  
  8470. ModifyMoney(-price);
  8471.  
  8472. if (crItem->ExtendedCost) // case for new honor system
  8473. {
  8474. ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
  8475. if (iece->reqhonorpoints)
  8476. ModifyHonorPoints(- int32(iece->reqhonorpoints * count));
  8477.  
  8478. if (iece->reqarenapoints)
  8479. ModifyArenaPoints(- int32(iece->reqarenapoints * count));
  8480.  
  8481. for (uint8 i = 0; i < MAX_ITEM_EXTENDED_COST_REQUIREMENTS; ++i)
  8482. {
  8483. if (iece->reqitem[i])
  8484. DestroyItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count), true);
  8485. }
  8486. }
  8487.  
  8488. Item* it = bStore ?
  8489. StoreNewItem(vDest, item, true) :
  8490. EquipNewItem(uiDest, item, true);
  8491. if (it)
  8492. {
  8493. uint32 new_count = pVendor->UpdateVendorItemCurrentCount(crItem, pProto->BuyCount * count);
  8494.  
  8495. WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
  8496. data << uint64(pVendor->GetGUID());
  8497. data << uint32(vendorslot + 1); // numbered from 1 at client
  8498. data << int32(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
  8499. data << uint32(count);
  8500. GetSession()->SendPacket(&data);
  8501. SendNewItem(it, pProto->BuyCount * count, true, false, false);
  8502.  
  8503. if (!bStore)
  8504. AutoUnequipOffhandIfNeed();
  8505.  
  8506. if (pProto->Flags & ITEM_PROTO_FLAG_REFUNDABLE && crItem->ExtendedCost && pProto->GetMaxStackSize() == 1)
  8507. {
  8508. it->SetFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE);
  8509. it->SetRefundRecipient(GetGUIDLow());
  8510. it->SetPaidMoney(price);
  8511. it->SetPaidExtendedCost(crItem->ExtendedCost);
  8512. it->SaveRefundDataToDB();
  8513. AddRefundReference(it->GetGUIDLow());
  8514. }
  8515. }
  8516. return true;
  8517. }
  8518.  
  8519. // Return true is the bought item has a max count to force refresh of window by caller
  8520. bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot)
  8521. {
  8522. // cheating attempt
  8523. if (count < 1) count = 1;
  8524.  
  8525. // cheating attempt
  8526. if (slot > MAX_BAG_SIZE && slot !=NULL_SLOT)
  8527. return false;
  8528.  
  8529. if (!isAlive())
  8530. return false;
  8531.  
  8532. ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(item);
  8533. if (!pProto)
  8534. {
  8535. SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, item, 0);
  8536. return false;
  8537. }
  8538.  
  8539. Creature* creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
  8540. if (!creature)
  8541. {
  8542. sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: BuyItemFromVendor - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)));
  8543. SendBuyError(BUY_ERR_DISTANCE_TOO_FAR, NULL, item, 0);
  8544. return false;
  8545. }
  8546.  
  8547. VendorItemData const* vItems = creature->GetVendorItems();
  8548. if (!vItems || vItems->Empty())
  8549. {
  8550. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
  8551. return false;
  8552. }
  8553.  
  8554. if (vendorslot >= vItems->GetItemCount())
  8555. {
  8556. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
  8557. return false;
  8558. }
  8559.  
  8560. VendorItem const* crItem = vItems->GetItem(vendorslot);
  8561. // store diff item (cheating)
  8562. if (!crItem || crItem->item != item)
  8563. {
  8564. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
  8565. return false;
  8566. }
  8567.  
  8568. // check current item amount if it limited
  8569. if (crItem->maxcount != 0)
  8570. {
  8571. if (creature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count)
  8572. {
  8573. SendBuyError(BUY_ERR_ITEM_ALREADY_SOLD, creature, item, 0);
  8574. return false;
  8575. }
  8576. }
  8577.  
  8578. if (pProto->RequiredReputationFaction && (uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank))
  8579. {
  8580. SendBuyError(BUY_ERR_REPUTATION_REQUIRE, creature, item, 0);
  8581. return false;
  8582. }
  8583.  
  8584. if (crItem->ExtendedCost)
  8585. {
  8586. ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
  8587. if (!iece)
  8588. {
  8589. sLog->outError(LOG_FILTER_PLAYER, "Item %u have wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost);
  8590. return false;
  8591. }
  8592.  
  8593. // honor points price
  8594. if (GetHonorPoints() < (iece->reqhonorpoints * count))
  8595. {
  8596. SendEquipError(EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS, NULL, NULL);
  8597. return false;
  8598. }
  8599.  
  8600. // arena points price
  8601. if (GetArenaPoints() < (iece->reqarenapoints * count))
  8602. {
  8603. SendEquipError(EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS, NULL, NULL);
  8604. return false;
  8605. }
  8606.  
  8607. // item base price
  8608. for (uint8 i = 0; i < MAX_ITEM_EXTENDED_COST_REQUIREMENTS; ++i)
  8609. {
  8610. if (iece->reqitem[i] && !HasItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count)))
  8611. {
  8612. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
  8613. return false;
  8614. }
  8615. }
  8616.  
  8617. // check for personal arena rating requirement
  8618. if (GetMaxPersonalArenaRatingRequirement(iece->reqarenaslot) < iece->reqpersonalarenarating)
  8619. {
  8620. // probably not the proper equip err
  8621. SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK, NULL, NULL);
  8622. return false;
  8623. }
  8624. }
  8625.  
  8626. uint32 price = 0;
  8627. if (crItem->IsGoldRequired(pProto) && pProto->BuyPrice > 0) //Assume price cannot be negative (do not know why it is int32)
  8628. {
  8629. uint32 maxCount = MAX_MONEY_AMOUNT / pProto->BuyPrice;
  8630. if ((uint32)count > maxCount)
  8631. {
  8632. sLog->outError(LOG_FILTER_PLAYER, "Player %s tried to buy %u item id %u, causing overflow", GetName(), (uint32)count, pProto->ItemId);
  8633. count = (uint8)maxCount;
  8634. }
  8635. price = pProto->BuyPrice * count; //it should not exceed MAX_MONEY_AMOUNT
  8636.  
  8637. // reputation discount
  8638. price = uint32(floor(price * GetReputationPriceDiscount(creature)));
  8639.  
  8640. if (!HasEnoughMoney(price))
  8641. {
  8642. SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, creature, item, 0);
  8643. return false;
  8644. }
  8645. }
  8646.  
  8647. if ((bag == NULL_BAG && slot == NULL_SLOT) || IsInventoryPos(bag, slot))
  8648. {
  8649. if (!_StoreOrEquipNewItem(vendorslot, item, count, bag, slot, price, pProto, creature, crItem, true))
  8650. return false;
  8651. }
  8652. else if (IsEquipmentPos(bag, slot))
  8653. {
  8654. if (pProto->BuyCount * count != 1)
  8655. {
  8656. SendEquipError(EQUIP_ERR_ITEM_CANT_BE_EQUIPPED, NULL, NULL);
  8657. return false;
  8658. }
  8659. if (!_StoreOrEquipNewItem(vendorslot, item, count, bag, slot, price, pProto, creature, crItem, false))
  8660. return false;
  8661. }
  8662. else
  8663. {
  8664. SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL);
  8665. return false;
  8666. }
  8667.  
  8668. return crItem->maxcount != 0;
  8669. }
  8670.  
  8671. uint32 Player::GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot) const
  8672. {
  8673. // returns the maximal personal arena rating that can be used to purchase items requiring this condition
  8674. // the personal rating of the arena team must match the required limit as well
  8675. // so return max[in arenateams](min(personalrating[teamtype], teamrating[teamtype]))
  8676. uint32 max_personal_rating = 0;
  8677. for (uint8 i = minarenaslot; i < MAX_ARENA_SLOT; ++i)
  8678. {
  8679. if (ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamId(i)))
  8680. {
  8681. uint32 p_rating = GetArenaPersonalRating(i);
  8682. uint32 t_rating = at->GetRating();
  8683. p_rating = p_rating < t_rating ? p_rating : t_rating;
  8684. if (max_personal_rating < p_rating)
  8685. max_personal_rating = p_rating;
  8686. }
  8687. }
  8688. return max_personal_rating;
  8689. }
  8690.  
  8691. void Player::UpdateHomebindTime(uint32 time)
  8692. {
  8693. // GMs never get homebind timer online
  8694. if (m_InstanceValid || isGameMaster())
  8695. {
  8696. if (m_HomebindTimer) // instance valid, but timer not reset
  8697. {
  8698. // hide reminder
  8699. WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
  8700. data << uint32(0);
  8701. data << uint32(0);
  8702. GetSession()->SendPacket(&data);
  8703. }
  8704. // instance is valid, reset homebind timer
  8705. m_HomebindTimer = 0;
  8706. }
  8707. else if (m_HomebindTimer > 0)
  8708. {
  8709. if (time >= m_HomebindTimer)
  8710. {
  8711. // teleport to nearest graveyard
  8712. RepopAtGraveyard();
  8713. }
  8714. else
  8715. m_HomebindTimer -= time;
  8716. }
  8717. else
  8718. {
  8719. // instance is invalid, start homebind timer
  8720. m_HomebindTimer = 60000;
  8721. // send message to player
  8722. WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
  8723. data << uint32(m_HomebindTimer);
  8724. data << uint32(1);
  8725. GetSession()->SendPacket(&data);
  8726. sLog->outDebug(LOG_FILTER_MAPS, "PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName(), GetGUIDLow());
  8727. }
  8728. }
  8729.  
  8730. void Player::UpdatePvPState(bool onlyFFA)
  8731. {
  8732. // TODO: should we always synchronize UNIT_FIELD_BYTES_2, 1 of controller and controlled?
  8733. // no, we shouldn't, those are checked for affecting player by client
  8734. if (!pvpInfo.inNoPvPArea && !isGameMaster()
  8735. && (pvpInfo.inFFAPvPArea || sWorld->IsFFAPvPRealm()))
  8736. {
  8737. if (!HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP))
  8738. {
  8739. SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
  8740. for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
  8741. (*itr)->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
  8742. }
  8743. }
  8744. else if (HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP))
  8745. {
  8746. RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
  8747. for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
  8748. (*itr)->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
  8749. }
  8750.  
  8751. if (onlyFFA)
  8752. return;
  8753.  
  8754. if (pvpInfo.inHostileArea) // in hostile area
  8755. {
  8756. if (!IsPvP() || pvpInfo.endTimer != 0)
  8757. UpdatePvP(true, true);
  8758. }
  8759. else // in friendly area
  8760. {
  8761. if (IsPvP() && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP) && pvpInfo.endTimer == 0)
  8762. pvpInfo.endTimer = time(0); // start toggle-off
  8763. }
  8764. }
  8765.  
  8766. void Player::UpdatePvP(bool state, bool override)
  8767. {
  8768. if (!state || override)
  8769. {
  8770. SetPvP(state);
  8771. pvpInfo.endTimer = 0;
  8772. }
  8773. else
  8774. {
  8775. pvpInfo.endTimer = time(NULL);
  8776. SetPvP(state);
  8777. }
  8778. }
  8779.  
  8780. void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown)
  8781. {
  8782. // init cooldown values
  8783. uint32 cat = 0;
  8784. int32 rec = -1;
  8785. int32 catrec = -1;
  8786.  
  8787. // some special item spells without correct cooldown in SpellInfo
  8788. // cooldown information stored in item prototype
  8789. // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client.
  8790.  
  8791. if (itemId)
  8792. {
  8793. if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
  8794. {
  8795. for (uint8 idx = 0; idx < MAX_ITEM_SPELLS; ++idx)
  8796. {
  8797. if (uint32(proto->Spells[idx].SpellId) == spellInfo->Id)
  8798. {
  8799. cat = proto->Spells[idx].SpellCategory;
  8800. rec = proto->Spells[idx].SpellCooldown;
  8801. catrec = proto->Spells[idx].SpellCategoryCooldown;
  8802. break;
  8803. }
  8804. }
  8805. }
  8806. }
  8807.  
  8808. // if no cooldown found above then base at DBC data
  8809. if (rec < 0 && catrec < 0)
  8810. {
  8811. cat = spellInfo->Category;
  8812. rec = spellInfo->RecoveryTime;
  8813. catrec = spellInfo->CategoryRecoveryTime;
  8814. }
  8815.  
  8816. time_t curTime = time(NULL);
  8817.  
  8818. time_t catrecTime;
  8819. time_t recTime;
  8820.  
  8821. // overwrite time for selected category
  8822. if (infinityCooldown)
  8823. {
  8824. // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped)
  8825. // but not allow ignore until reset or re-login
  8826. catrecTime = catrec > 0 ? curTime+infinityCooldownDelay : 0;
  8827. recTime = rec > 0 ? curTime+infinityCooldownDelay : catrecTime;
  8828. }
  8829. else
  8830. {
  8831. // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
  8832. // prevent 0 cooldowns set by another way
  8833. if (rec <= 0 && catrec <= 0 && (cat == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75)))
  8834. rec = GetAttackTime(RANGED_ATTACK);
  8835.  
  8836. // Now we have cooldown data (if found any), time to apply mods
  8837. if (rec > 0)
  8838. ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell);
  8839.  
  8840. if (catrec > 0)
  8841. ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell);
  8842.  
  8843. // replace negative cooldowns by 0
  8844. if (rec < 0) rec = 0;
  8845. if (catrec < 0) catrec = 0;
  8846.  
  8847. // no cooldown after applying spell mods
  8848. if (rec == 0 && catrec == 0)
  8849. return;
  8850.  
  8851. catrecTime = catrec ? curTime+catrec/IN_MILLISECONDS : 0;
  8852. recTime = rec ? curTime+rec/IN_MILLISECONDS : catrecTime;
  8853. }
  8854.  
  8855. // self spell cooldown
  8856. if (recTime > 0)
  8857. AddSpellCooldown(spellInfo->Id, itemId, recTime);
  8858.  
  8859. // category spells
  8860. if (cat && catrec > 0)
  8861. {
  8862. SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat);
  8863. if (i_scstore != sSpellCategoryStore.end())
  8864. {
  8865. for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
  8866. {
  8867. if (*i_scset == spellInfo->Id) // skip main spell, already handled above
  8868. continue;
  8869.  
  8870. AddSpellCooldown(*i_scset, itemId, catrecTime);
  8871. }
  8872. }
  8873. }
  8874. }
  8875.  
  8876. void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
  8877. {
  8878. SpellCooldown sc;
  8879. sc.end = end_time;
  8880. sc.itemid = itemid;
  8881. m_spellCooldowns[spellid] = sc;
  8882. }
  8883.  
  8884. void Player::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= NULL*/, bool setCooldown /*= true*/)
  8885. {
  8886. // start cooldowns at server side, if any
  8887. if (setCooldown)
  8888. AddSpellAndCategoryCooldowns(spellInfo, itemId, spell);
  8889.  
  8890. // Send activate cooldown timer (possible 0) at client side
  8891. WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
  8892. data << uint32(spellInfo->Id);
  8893. data << uint64(GetGUID());
  8894. SendDirectMessage(&data);
  8895. }
  8896.  
  8897. void Player::UpdatePotionCooldown(Spell* spell)
  8898. {
  8899. // no potion used i combat or still in combat
  8900. if (!m_lastPotionId || isInCombat())
  8901. return;
  8902.  
  8903. // Call not from spell cast, send cooldown event for item spells if no in combat
  8904. if (!spell)
  8905. {
  8906. // spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions)
  8907. if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(m_lastPotionId))
  8908. for (uint8 idx = 0; idx < MAX_ITEM_SPELLS; ++idx)
  8909. if (proto->Spells[idx].SpellId && proto->Spells[idx].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
  8910. if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[idx].SpellId))
  8911. SendCooldownEvent(spellInfo, m_lastPotionId);
  8912. }
  8913. // from spell cases (m_lastPotionId set in Spell::SendSpellCooldown)
  8914. else
  8915. SendCooldownEvent(spell->m_spellInfo, m_lastPotionId, spell);
  8916.  
  8917. m_lastPotionId = 0;
  8918. }
  8919.  
  8920. //slot to be excluded while counting
  8921. bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot)
  8922. {
  8923. if (!enchantmentcondition)
  8924. return true;
  8925.  
  8926. SpellItemEnchantmentConditionEntry const* Condition = sSpellItemEnchantmentConditionStore.LookupEntry(enchantmentcondition);
  8927.  
  8928. if (!Condition)
  8929. return true;
  8930.  
  8931. uint8 curcount[4] = {0, 0, 0, 0};
  8932.  
  8933. //counting current equipped gem colors
  8934. for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
  8935. {
  8936. if (i == slot)
  8937. continue;
  8938. Item* pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
  8939. if (pItem2 && !pItem2->IsBroken() && pItem2->GetTemplate()->Socket[0].Color)
  8940. {
  8941. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
  8942. {
  8943. uint32 enchant_id = pItem2->GetEnchantmentId(EnchantmentSlot(enchant_slot));
  8944. if (!enchant_id)
  8945. continue;
  8946.  
  8947. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  8948. if (!enchantEntry)
  8949. continue;
  8950.  
  8951. uint32 gemid = enchantEntry->GemID;
  8952. if (!gemid)
  8953. continue;
  8954.  
  8955. ItemTemplate const* gemProto = sObjectMgr->GetItemTemplate(gemid);
  8956. if (!gemProto)
  8957. continue;
  8958.  
  8959. GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties);
  8960. if (!gemProperty)
  8961. continue;
  8962.  
  8963. uint8 GemColor = gemProperty->color;
  8964.  
  8965. for (uint8 b = 0, tmpcolormask = 1; b < 4; b++, tmpcolormask <<= 1)
  8966. {
  8967. if (tmpcolormask & GemColor)
  8968. ++curcount[b];
  8969. }
  8970. }
  8971. }
  8972. }
  8973.  
  8974. bool activate = true;
  8975.  
  8976. for (uint8 i = 0; i < 5; i++)
  8977. {
  8978. if (!Condition->Color[i])
  8979. continue;
  8980.  
  8981. uint32 _cur_gem = curcount[Condition->Color[i] - 1];
  8982.  
  8983. // if have <CompareColor> use them as count, else use <value> from Condition
  8984. uint32 _cmp_gem = Condition->CompareColor[i] ? curcount[Condition->CompareColor[i] - 1]: Condition->Value[i];
  8985.  
  8986. switch (Condition->Comparator[i])
  8987. {
  8988. case 2: // requires less <color> than (<value> || <comparecolor>) gems
  8989. activate &= (_cur_gem < _cmp_gem) ? true : false;
  8990. break;
  8991. case 3: // requires more <color> than (<value> || <comparecolor>) gems
  8992. activate &= (_cur_gem > _cmp_gem) ? true : false;
  8993. break;
  8994. case 5: // requires at least <color> than (<value> || <comparecolor>) gems
  8995. activate &= (_cur_gem >= _cmp_gem) ? true : false;
  8996. break;
  8997. }
  8998. }
  8999.  
  9000. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "Checking Condition %u, there are %u Meta Gems, %u Red Gems, %u Yellow Gems and %u Blue Gems, Activate:%s", enchantmentcondition, curcount[0], curcount[1], curcount[2], curcount[3], activate ? "yes" : "no");
  9001.  
  9002. return activate;
  9003. }
  9004.  
  9005. void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply)
  9006. {
  9007. //cycle all equipped items
  9008. for (uint32 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
  9009. {
  9010. //enchants for the slot being socketed are handled by Player::ApplyItemMods
  9011. if (slot == exceptslot)
  9012. continue;
  9013.  
  9014. Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
  9015.  
  9016. if (!pItem || !pItem->GetTemplate()->Socket[0].Color)
  9017. continue;
  9018.  
  9019. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
  9020. {
  9021. uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
  9022. if (!enchant_id)
  9023. continue;
  9024.  
  9025. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  9026. if (!enchantEntry)
  9027. continue;
  9028.  
  9029. uint32 condition = enchantEntry->EnchantmentCondition;
  9030. if (condition)
  9031. {
  9032. //was enchant active with/without item?
  9033. bool wasactive = EnchantmentFitsRequirements(condition, apply ? exceptslot : -1);
  9034. //should it now be?
  9035. if (wasactive ^ EnchantmentFitsRequirements(condition, apply ? -1 : exceptslot))
  9036. {
  9037. // ignore item gem conditions
  9038. //if state changed, (dis)apply enchant
  9039. ApplyEnchantment(pItem, EnchantmentSlot(enchant_slot), !wasactive, true, true);
  9040. }
  9041. }
  9042. }
  9043. }
  9044. }
  9045.  
  9046. //if false -> then toggled off if was on| if true -> toggled on if was off AND meets requirements
  9047. void Player::ToggleMetaGemsActive(uint8 exceptslot, bool apply)
  9048. {
  9049. //cycle all equipped items
  9050. for (int slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
  9051. {
  9052. //enchants for the slot being socketed are handled by WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
  9053. if (slot == exceptslot)
  9054. continue;
  9055.  
  9056. Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
  9057.  
  9058. if (!pItem || !pItem->GetTemplate()->Socket[0].Color) //if item has no sockets or no item is equipped go to next item
  9059. continue;
  9060.  
  9061. //cycle all (gem)enchants
  9062. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
  9063. {
  9064. uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
  9065. if (!enchant_id) //if no enchant go to next enchant(slot)
  9066. continue;
  9067.  
  9068. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  9069. if (!enchantEntry)
  9070. continue;
  9071.  
  9072. //only metagems to be (de)activated, so only enchants with condition
  9073. uint32 condition = enchantEntry->EnchantmentCondition;
  9074. if (condition)
  9075. ApplyEnchantment(pItem, EnchantmentSlot(enchant_slot), apply);
  9076. }
  9077. }
  9078. }
  9079.  
  9080. void Player::SetBattlegroundEntryPoint()
  9081. {
  9082. // Taxi path store
  9083. if (!m_taxi.empty())
  9084. {
  9085. m_bgData.mountSpell = 0;
  9086. m_bgData.taxiPath[0] = m_taxi.GetTaxiSource();
  9087. m_bgData.taxiPath[1] = m_taxi.GetTaxiDestination();
  9088.  
  9089. // On taxi we don't need check for dungeon
  9090. m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  9091. }
  9092. else
  9093. {
  9094. m_bgData.ClearTaxiPath();
  9095.  
  9096. // Mount spell id storing
  9097. if (IsMounted())
  9098. {
  9099. AuraEffectList const& auras = GetAuraEffectsByType(SPELL_AURA_MOUNTED);
  9100. if (!auras.empty())
  9101. m_bgData.mountSpell = (*auras.begin())->GetId();
  9102. }
  9103. else
  9104. m_bgData.mountSpell = 0;
  9105.  
  9106. // If map is dungeon find linked graveyard
  9107. if (GetMap()->IsDungeon())
  9108. {
  9109. if (const WorldSafeLocsEntry* entry = sObjectMgr->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam()))
  9110. m_bgData.joinPos = WorldLocation(entry->map_id, entry->x, entry->y, entry->z, 0.0f);
  9111. else
  9112. sLog->outError(LOG_FILTER_PLAYER, "SetBattlegroundEntryPoint: Dungeon map %u has no linked graveyard, setting home location as entry point.", GetMapId());
  9113. }
  9114. // If new entry point is not BG or arena set it
  9115. else if (!GetMap()->IsBattlegroundOrArena())
  9116. m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  9117. }
  9118.  
  9119. if (m_bgData.joinPos.m_mapId == MAPID_INVALID) // In error cases use homebind position
  9120. m_bgData.joinPos = WorldLocation(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, 0.0f);
  9121. }
  9122.  
  9123. void Player::LeaveBattleground(bool teleportToEntryPoint)
  9124. {
  9125. if (Battleground* bg = GetBattleground())
  9126. {
  9127. bg->RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true);
  9128.  
  9129. // call after remove to be sure that player resurrected for correct cast
  9130. if (bg->isBattleground() && !isGameMaster() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_CAST_DESERTER))
  9131. {
  9132. if (bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN)
  9133. {
  9134. //lets check if player was teleported from BG and schedule delayed Deserter spell cast
  9135. if (IsBeingTeleportedFar())
  9136. {
  9137. ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER);
  9138. return;
  9139. }
  9140.  
  9141. CastSpell(this, 26013, true); // Deserter
  9142. }
  9143. }
  9144. }
  9145. }
  9146.  
  9147. bool Player::CanJoinToBattleground() const
  9148. {
  9149. // check Deserter debuff
  9150. if (HasAura(26013))
  9151. return false;
  9152.  
  9153. return true;
  9154. }
  9155.  
  9156. bool Player::CanReportAfkDueToLimit()
  9157. {
  9158. // a player can complain about 15 people per 5 minutes
  9159. if (m_bgData.bgAfkReportedCount++ >= 15)
  9160. return false;
  9161.  
  9162. return true;
  9163. }
  9164.  
  9165. ///This player has been blamed to be inactive in a battleground
  9166. void Player::ReportedAfkBy(Player* reporter)
  9167. {
  9168. Battleground* bg = GetBattleground();
  9169. // Battleground also must be in progress!
  9170. if (!bg || bg != reporter->GetBattleground() || GetTeam() != reporter->GetTeam() || bg->GetStatus() != STATUS_IN_PROGRESS)
  9171. return;
  9172.  
  9173. // check if player has 'Idle' or 'Inactive' debuff
  9174. if (m_bgData.bgAfkReporter.find(reporter->GetGUIDLow()) == m_bgData.bgAfkReporter.end() && !HasAura(43680) && !HasAura(43681) && reporter->CanReportAfkDueToLimit())
  9175. {
  9176. m_bgData.bgAfkReporter.insert(reporter->GetGUIDLow());
  9177. // 3 players have to complain to apply debuff
  9178. if (m_bgData.bgAfkReporter.size() >= 3)
  9179. {
  9180. // cast 'Idle' spell
  9181. CastSpell(this, 43680, true);
  9182. m_bgData.bgAfkReporter.clear();
  9183. }
  9184. }
  9185. }
  9186.  
  9187. WorldLocation Player::GetStartPosition() const
  9188. {
  9189. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  9190. uint32 mapId = info->mapId;
  9191. if (getClass() == CLASS_DEATH_KNIGHT && HasSpell(50977))
  9192. mapId = 0;
  9193. return WorldLocation(mapId, info->positionX, info->positionY, info->positionZ, 0);
  9194. }
  9195.  
  9196. bool Player::IsNeverVisible() const
  9197. {
  9198. if (Unit::IsNeverVisible())
  9199. return true;
  9200.  
  9201. if (GetSession()->PlayerLogout() || GetSession()->PlayerLoading())
  9202. return true;
  9203.  
  9204. return false;
  9205. }
  9206.  
  9207. bool Player::CanAlwaysSee(WorldObject const* obj) const
  9208. {
  9209. // Always can see self
  9210. if (m_mover == obj)
  9211. return true;
  9212.  
  9213. if (uint64 guid = GetUInt64Value(PLAYER_FARSIGHT))
  9214. if (obj->GetGUID() == guid)
  9215. return true;
  9216.  
  9217. return false;
  9218. }
  9219.  
  9220. bool Player::IsAlwaysDetectableFor(WorldObject const* seer) const
  9221. {
  9222. if (Unit::IsAlwaysDetectableFor(seer))
  9223. return true;
  9224.  
  9225. if (const Player* seerPlayer = seer->ToPlayer())
  9226. if (IsGroupVisibleFor(seerPlayer))
  9227. return true;
  9228.  
  9229. return false;
  9230. }
  9231.  
  9232. bool Player::IsVisibleGloballyFor(Player* u) const
  9233. {
  9234. if (!u)
  9235. return false;
  9236.  
  9237. // Always can see self
  9238. if (u == this)
  9239. return true;
  9240.  
  9241. // Visible units, always are visible for all players
  9242. if (IsVisible())
  9243. return true;
  9244.  
  9245. // GMs are visible for higher gms (or players are visible for gms)
  9246. if (!AccountMgr::IsPlayerAccount(u->GetSession()->GetSecurity()))
  9247. return GetSession()->GetSecurity() <= u->GetSession()->GetSecurity();
  9248.  
  9249. // non faction visibility non-breakable for non-GMs
  9250. if (!IsVisible())
  9251. return false;
  9252.  
  9253. // non-gm stealth/invisibility not hide from global player lists
  9254. return true;
  9255. }
  9256.  
  9257. template<class T>
  9258. inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, T* target, std::set<Unit*>& /*v*/)
  9259. {
  9260. s64.insert(target->GetGUID());
  9261. }
  9262.  
  9263. template<>
  9264. inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, Creature* target, std::set<Unit*>& v)
  9265. {
  9266. s64.insert(target->GetGUID());
  9267. v.insert(target);
  9268. }
  9269.  
  9270. template<>
  9271. inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, Player* target, std::set<Unit*>& v)
  9272. {
  9273. s64.insert(target->GetGUID());
  9274. v.insert(target);
  9275. }
  9276.  
  9277. template<class T>
  9278. inline void BeforeVisibilityDestroy(T* /*t*/, Player* /*p*/)
  9279. {
  9280. }
  9281.  
  9282. template<>
  9283. inline void BeforeVisibilityDestroy<Creature>(Creature* t, Player* p)
  9284. {
  9285. if (p->GetPetGUID() == t->GetGUID() && t->ToCreature()->isPet())
  9286. ((Pet*)t)->Remove(PET_SAVE_NOT_IN_SLOT, true);
  9287. }
  9288.  
  9289. void Player::UpdateVisibilityOf(WorldObject* target)
  9290. {
  9291. if (HaveAtClient(target))
  9292. {
  9293. if (!canSeeOrDetect(target, false, true))
  9294. {
  9295. if (target->GetTypeId() == TYPEID_UNIT)
  9296. BeforeVisibilityDestroy<Creature>(target->ToCreature(), this);
  9297.  
  9298. target->DestroyForPlayer(this);
  9299. m_clientGUIDs.erase(target->GetGUID());
  9300.  
  9301. #ifdef TRINITY_DEBUG
  9302. sLog->outDebug(LOG_FILTER_MAPS, "Object %u (Type: %u) out of range for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), GetGUIDLow(), GetDistance(target));
  9303. #endif
  9304. }
  9305. }
  9306. else
  9307. {
  9308. if (canSeeOrDetect(target, false, true))
  9309. {
  9310. //if (target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle)
  9311. // UpdateVisibilityOf(((Unit*)target)->m_Vehicle);
  9312.  
  9313. target->SendUpdateToPlayer(this);
  9314. m_clientGUIDs.insert(target->GetGUID());
  9315.  
  9316. #ifdef TRINITY_DEBUG
  9317. sLog->outDebug(LOG_FILTER_MAPS, "Object %u (Type: %u) is visible now for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), GetGUIDLow(), GetDistance(target));
  9318. #endif
  9319.  
  9320. // target aura duration for caster show only if target exist at caster client
  9321. // send data at target visibility change (adding to client)
  9322. if (target->isType(TYPEMASK_UNIT))
  9323. SendInitialVisiblePackets((Unit*)target);
  9324. }
  9325. }
  9326. }
  9327.  
  9328. void Player::UpdateTriggerVisibility()
  9329. {
  9330. if (m_clientGUIDs.empty())
  9331. return;
  9332.  
  9333. if (!IsInWorld())
  9334. return;
  9335.  
  9336. UpdateData udata;
  9337. WorldPacket packet;
  9338. for (ClientGUIDs::iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
  9339. {
  9340. if (IS_CREATURE_GUID(*itr))
  9341. {
  9342. Creature* obj = GetMap()->GetCreature(*itr);
  9343. if (!obj || !(obj->isTrigger() || obj->HasAuraType(SPELL_AURA_TRANSFORM))) // can transform into triggers
  9344. continue;
  9345.  
  9346. obj->BuildCreateUpdateBlockForPlayer(&udata, this);
  9347. }
  9348. }
  9349.  
  9350. udata.BuildPacket(&packet);
  9351. GetSession()->SendPacket(&packet);
  9352. }
  9353.  
  9354. void Player::SendInitialVisiblePackets(Unit* target)
  9355. {
  9356. SendAurasForTarget(target);
  9357. if (target->isAlive())
  9358. {
  9359. if (target->HasUnitState(UNIT_STATE_MELEE_ATTACKING) && target->getVictim())
  9360. target->SendMeleeAttackStart(target->getVictim());
  9361. }
  9362. }
  9363.  
  9364. template<class T>
  9365. void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<Unit*>& visibleNow)
  9366. {
  9367. if (HaveAtClient(target))
  9368. {
  9369. if (!canSeeOrDetect(target, false, true))
  9370. {
  9371. BeforeVisibilityDestroy<T>(target, this);
  9372.  
  9373. target->BuildOutOfRangeUpdateBlock(&data);
  9374. m_clientGUIDs.erase(target->GetGUID());
  9375.  
  9376. #ifdef TRINITY_DEBUG
  9377. sLog->outDebug(LOG_FILTER_MAPS, "Object %u (Type: %u, Entry: %u) is out of range for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), target->GetEntry(), GetGUIDLow(), GetDistance(target));
  9378. #endif
  9379. }
  9380. }
  9381. else //if (visibleNow.size() < 30 || target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->IsVehicle())
  9382. {
  9383. if (canSeeOrDetect(target, false, true))
  9384. {
  9385. //if (target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle)
  9386. // UpdateVisibilityOf(((Unit*)target)->m_Vehicle, data, visibleNow);
  9387.  
  9388. target->BuildCreateUpdateBlockForPlayer(&data, this);
  9389. UpdateVisibilityOf_helper(m_clientGUIDs, target, visibleNow);
  9390.  
  9391. #ifdef TRINITY_DEBUG
  9392. sLog->outDebug(LOG_FILTER_MAPS, "Object %u (Type: %u, Entry: %u) is visible now for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), target->GetEntry(), GetGUIDLow(), GetDistance(target));
  9393. #endif
  9394. }
  9395. }
  9396. }
  9397.  
  9398. template void Player::UpdateVisibilityOf(Player* target, UpdateData& data, std::set<Unit*>& visibleNow);
  9399. template void Player::UpdateVisibilityOf(Creature* target, UpdateData& data, std::set<Unit*>& visibleNow);
  9400. template void Player::UpdateVisibilityOf(Corpse* target, UpdateData& data, std::set<Unit*>& visibleNow);
  9401. template void Player::UpdateVisibilityOf(GameObject* target, UpdateData& data, std::set<Unit*>& visibleNow);
  9402. template void Player::UpdateVisibilityOf(DynamicObject* target, UpdateData& data, std::set<Unit*>& visibleNow);
  9403.  
  9404. void Player::UpdateObjectVisibility(bool forced)
  9405. {
  9406. if (!forced)
  9407. AddToNotify(NOTIFY_VISIBILITY_CHANGED);
  9408. else
  9409. {
  9410. Unit::UpdateObjectVisibility(true);
  9411. UpdateVisibilityForPlayer();
  9412. }
  9413. }
  9414.  
  9415. void Player::UpdateVisibilityForPlayer()
  9416. {
  9417. // updates visibility of all objects around point of view for current player
  9418. Trinity::VisibleNotifier notifier(*this);
  9419. m_seer->VisitNearbyObject(GetSightRange(), notifier);
  9420. notifier.SendToSelf(); // send gathered data
  9421. }
  9422.  
  9423. void Player::InitPrimaryProfessions()
  9424. {
  9425. SetFreePrimaryProfessions(sWorld->getIntConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL));
  9426. }
  9427.  
  9428. void Player::ModifyMoney(int32 d)
  9429. {
  9430. sScriptMgr->OnPlayerMoneyChanged(this, d);
  9431.  
  9432. if (d < 0)
  9433. SetMoney (GetMoney() > uint32(-d) ? GetMoney() + d : 0);
  9434. else
  9435. {
  9436. uint32 newAmount = 0;
  9437. if (GetMoney() < uint32(MAX_MONEY_AMOUNT - d))
  9438. newAmount = GetMoney() + d;
  9439. else
  9440. {
  9441. // "At Gold Limit"
  9442. newAmount = MAX_MONEY_AMOUNT;
  9443. if (d)
  9444. SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL);
  9445. }
  9446. SetMoney (newAmount);
  9447. }
  9448. }
  9449.  
  9450. Unit* Player::GetSelectedUnit() const
  9451. {
  9452. if (m_curSelection)
  9453. return ObjectAccessor::GetUnit(*this, m_curSelection);
  9454. return NULL;
  9455. }
  9456.  
  9457. Player* Player::GetSelectedPlayer() const
  9458. {
  9459. if (m_curSelection)
  9460. return ObjectAccessor::GetPlayer(*this, m_curSelection);
  9461. return NULL;
  9462. }
  9463.  
  9464. void Player::SendComboPoints()
  9465. {
  9466. Unit* combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget);
  9467. if (combotarget)
  9468. {
  9469. WorldPacket data;
  9470. if (m_mover != this)
  9471. {
  9472. data.Initialize(SMSG_PET_UPDATE_COMBO_POINTS, m_mover->GetPackGUID().size()+combotarget->GetPackGUID().size()+1);
  9473. data.append(m_mover->GetPackGUID());
  9474. }
  9475. else
  9476. data.Initialize(SMSG_UPDATE_COMBO_POINTS, combotarget->GetPackGUID().size()+1);
  9477. data.append(combotarget->GetPackGUID());
  9478. data << uint8(m_comboPoints);
  9479. GetSession()->SendPacket(&data);
  9480. }
  9481. }
  9482.  
  9483. void Player::AddComboPoints(Unit* target, int8 count, Spell* spell)
  9484. {
  9485. if (!count)
  9486. return;
  9487.  
  9488. int8 * comboPoints = spell ? &spell->m_comboPointGain : &m_comboPoints;
  9489.  
  9490. // without combo points lost (duration checked in aura)
  9491. RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS);
  9492.  
  9493. if (target->GetGUID() == m_comboTarget)
  9494. *comboPoints += count;
  9495. else
  9496. {
  9497. if (m_comboTarget)
  9498. if (Unit* target2 = ObjectAccessor::GetUnit(*this, m_comboTarget))
  9499. target2->RemoveComboPointHolder(GetGUIDLow());
  9500.  
  9501. // Spells will always add value to m_comboPoints eventualy, so it must be cleared first
  9502. if (spell)
  9503. m_comboPoints = 0;
  9504.  
  9505. m_comboTarget = target->GetGUID();
  9506. *comboPoints = count;
  9507.  
  9508. target->AddComboPointHolder(GetGUIDLow());
  9509. }
  9510.  
  9511. if (*comboPoints > 5)
  9512. *comboPoints = 5;
  9513. else if (*comboPoints < 0)
  9514. *comboPoints = 0;
  9515.  
  9516. if (!spell)
  9517. SendComboPoints();
  9518. }
  9519.  
  9520. void Player::GainSpellComboPoints(int8 count)
  9521. {
  9522. if (!count)
  9523. return;
  9524.  
  9525. m_comboPoints += count;
  9526. if (m_comboPoints > 5) m_comboPoints = 5;
  9527. else if (m_comboPoints < 0) m_comboPoints = 0;
  9528.  
  9529. SendComboPoints();
  9530. }
  9531.  
  9532. void Player::ClearComboPoints()
  9533. {
  9534. if (!m_comboTarget)
  9535. return;
  9536.  
  9537. // without combopoints lost (duration checked in aura)
  9538. RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS);
  9539.  
  9540. m_comboPoints = 0;
  9541.  
  9542. SendComboPoints();
  9543.  
  9544. if (Unit* target = ObjectAccessor::GetUnit(*this, m_comboTarget))
  9545. target->RemoveComboPointHolder(GetGUIDLow());
  9546.  
  9547. m_comboTarget = 0;
  9548. }
  9549.  
  9550. void Player::SetGroup(Group* group, int8 subgroup)
  9551. {
  9552. if (group == NULL)
  9553. m_group.unlink();
  9554. else
  9555. {
  9556. // never use SetGroup without a subgroup unless you specify NULL for group
  9557. ASSERT(subgroup >= 0);
  9558. m_group.link(group, this);
  9559. m_group.setSubGroup((uint8)subgroup);
  9560. }
  9561.  
  9562. UpdateObjectVisibility(false);
  9563. }
  9564.  
  9565. void Player::SendInitialPacketsBeforeAddToMap()
  9566. {
  9567. /// Pass 'this' as argument because we're not stored in ObjectAccessor yet
  9568. GetSocial()->SendSocialList(this);
  9569.  
  9570. // guild bank list wtf?
  9571.  
  9572. // Homebind
  9573. WorldPacket data(SMSG_BINDPOINTUPDATE, 5*4);
  9574. data << m_homebindX << m_homebindY << m_homebindZ;
  9575. data << (uint32) m_homebindMapId;
  9576. data << (uint32) m_homebindAreaId;
  9577. GetSession()->SendPacket(&data);
  9578.  
  9579. // SMSG_SET_PROFICIENCY
  9580. // SMSG_SET_PCT_SPELL_MODIFIER
  9581. // SMSG_SET_FLAT_SPELL_MODIFIER
  9582. // SMSG_UPDATE_AURA_DURATION
  9583.  
  9584. SendTalentsInfoData(false);
  9585.  
  9586. // SMSG_INSTANCE_DIFFICULTY
  9587. data.Initialize(SMSG_INSTANCE_DIFFICULTY, 4+4);
  9588. data << uint32(GetMap()->GetDifficulty());
  9589. data << uint32(0);
  9590. GetSession()->SendPacket(&data);
  9591.  
  9592. SendInitialSpells();
  9593.  
  9594. data.Initialize(SMSG_SEND_UNLEARN_SPELLS, 4);
  9595. data << uint32(0); // count, for (count) uint32;
  9596. GetSession()->SendPacket(&data);
  9597.  
  9598. SendInitialActionButtons();
  9599. m_reputationMgr.SendInitialReputations();
  9600. m_achievementMgr.SendAllAchievementData();
  9601.  
  9602. SendEquipmentSetList();
  9603.  
  9604. data.Initialize(SMSG_LOGIN_SETTIMESPEED, 4 + 4 + 4);
  9605. data << uint32(secsToTimeBitFields(sWorld->GetGameTime()));
  9606. data << float(0.01666667f); // game speed
  9607. data << uint32(0); // added in 3.1.2
  9608. GetSession()->SendPacket(&data);
  9609.  
  9610. GetReputationMgr().SendForceReactions(); // SMSG_SET_FORCED_REACTIONS
  9611.  
  9612. // SMSG_TALENTS_INFO x 2 for pet (unspent points and talents in separate packets...)
  9613. // SMSG_PET_GUIDS
  9614. // SMSG_UPDATE_WORLD_STATE
  9615. // SMSG_POWER_UPDATE
  9616.  
  9617. SetMover(this);
  9618. }
  9619.  
  9620. void Player::SendInitialPacketsAfterAddToMap()
  9621. {
  9622. UpdateVisibilityForPlayer();
  9623.  
  9624. // update zone
  9625. uint32 newzone, newarea;
  9626. GetZoneAndAreaId(newzone, newarea);
  9627. UpdateZone(newzone, newarea); // also call SendInitWorldStates();
  9628.  
  9629. ResetTimeSync();
  9630. SendTimeSync();
  9631.  
  9632. CastSpell(this, 836, true); // LOGINEFFECT
  9633.  
  9634. // set some aura effects that send packet to player client after add player to map
  9635. // SendMessageToSet not send it to player not it map, only for aura that not changed anything at re-apply
  9636. // same auras state lost at far teleport, send it one more time in this case also
  9637. static const AuraType auratypes[] =
  9638. {
  9639. SPELL_AURA_MOD_FEAR, SPELL_AURA_TRANSFORM, SPELL_AURA_WATER_WALK,
  9640. SPELL_AURA_FEATHER_FALL, SPELL_AURA_HOVER, SPELL_AURA_SAFE_FALL,
  9641. SPELL_AURA_FLY, SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED, SPELL_AURA_NONE
  9642. };
  9643. for (AuraType const* itr = &auratypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr)
  9644. {
  9645. Unit::AuraEffectList const& auraList = GetAuraEffectsByType(*itr);
  9646. if (!auraList.empty())
  9647. auraList.front()->HandleEffect(this, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true);
  9648. }
  9649.  
  9650. if (HasAuraType(SPELL_AURA_MOD_STUN))
  9651. SetMovement(MOVE_ROOT);
  9652.  
  9653. // manual send package (have code in HandleEffect(this, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true); that must not be re-applied.
  9654. if (HasAuraType(SPELL_AURA_MOD_ROOT))
  9655. {
  9656. WorldPacket data2(SMSG_FORCE_MOVE_ROOT, 10);
  9657. data2.append(GetPackGUID());
  9658. data2 << (uint32)2;
  9659. SendMessageToSet(&data2, true);
  9660. }
  9661.  
  9662. SendAurasForTarget(this);
  9663. SendEnchantmentDurations(); // must be after add to map
  9664. SendItemDurations(); // must be after add to map
  9665.  
  9666. // raid downscaling - send difficulty to player
  9667. if (GetMap()->IsRaid())
  9668. {
  9669. if (GetMap()->GetDifficulty() != GetRaidDifficulty())
  9670. {
  9671. StoreRaidMapDifficulty();
  9672. SendRaidDifficulty(GetGroup() != NULL, GetStoredRaidDifficulty());
  9673. }
  9674. }
  9675. else if (GetRaidDifficulty() != GetStoredRaidDifficulty())
  9676. SendRaidDifficulty(GetGroup() != NULL);
  9677. }
  9678.  
  9679. void Player::SendUpdateToOutOfRangeGroupMembers()
  9680. {
  9681. if (m_groupUpdateMask == GROUP_UPDATE_FLAG_NONE)
  9682. return;
  9683. if (Group* group = GetGroup())
  9684. group->UpdatePlayerOutOfRange(this);
  9685.  
  9686. m_groupUpdateMask = GROUP_UPDATE_FLAG_NONE;
  9687. m_auraRaidUpdateMask = 0;
  9688. if (Pet* pet = GetPet())
  9689. pet->ResetAuraUpdateMaskForRaid();
  9690. }
  9691.  
  9692. void Player::SendTransferAborted(uint32 mapid, TransferAbortReason reason, uint8 arg)
  9693. {
  9694. WorldPacket data(SMSG_TRANSFER_ABORTED, 4+2);
  9695. data << uint32(mapid);
  9696. data << uint8(reason); // transfer abort reason
  9697. switch (reason)
  9698. {
  9699. case TRANSFER_ABORT_INSUF_EXPAN_LVL:
  9700. case TRANSFER_ABORT_DIFFICULTY:
  9701. case TRANSFER_ABORT_UNIQUE_MESSAGE:
  9702. // these are the ONLY cases that have an extra argument in the packet!!!
  9703. data << uint8(arg);
  9704. break;
  9705. default:
  9706. break;
  9707. }
  9708. GetSession()->SendPacket(&data);
  9709. }
  9710.  
  9711. void Player::SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time)
  9712. {
  9713. // type of warning, based on the time remaining until reset
  9714. uint32 type;
  9715. if (time > 3600)
  9716. type = RAID_INSTANCE_WELCOME;
  9717. else if (time > 900 && time <= 3600)
  9718. type = RAID_INSTANCE_WARNING_HOURS;
  9719. else if (time > 300 && time <= 900)
  9720. type = RAID_INSTANCE_WARNING_MIN;
  9721. else
  9722. type = RAID_INSTANCE_WARNING_MIN_SOON;
  9723.  
  9724. WorldPacket data(SMSG_RAID_INSTANCE_MESSAGE, 4+4+4+4);
  9725. data << uint32(type);
  9726. data << uint32(mapid);
  9727. data << uint32(difficulty); // difficulty
  9728. data << uint32(time);
  9729. if (type == RAID_INSTANCE_WELCOME)
  9730. {
  9731. data << uint8(0); // is locked
  9732. data << uint8(0); // is extended, ignored if prev field is 0
  9733. }
  9734. GetSession()->SendPacket(&data);
  9735. }
  9736.  
  9737. void Player::ApplyEquipCooldown(Item* pItem)
  9738. {
  9739. if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_PROTO_FLAG_NO_EQUIP_COOLDOWN))
  9740. return;
  9741.  
  9742. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  9743. {
  9744. _Spell const& spellData = pItem->GetTemplate()->Spells[i];
  9745.  
  9746. // no spell
  9747. if (!spellData.SpellId)
  9748. continue;
  9749.  
  9750. // wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown)
  9751. if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
  9752. continue;
  9753.  
  9754. AddSpellCooldown(spellData.SpellId, pItem->GetEntry(), time(NULL) + 30);
  9755.  
  9756. WorldPacket data(SMSG_ITEM_COOLDOWN, 12);
  9757. data << pItem->GetGUID();
  9758. data << uint32(spellData.SpellId);
  9759. GetSession()->SendPacket(&data);
  9760. }
  9761. }
  9762.  
  9763. void Player::resetSpells(bool myClassOnly)
  9764. {
  9765. // not need after this call
  9766. if (HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
  9767. RemoveAtLoginFlag(AT_LOGIN_RESET_SPELLS, true);
  9768.  
  9769. // make full copy of map (spells removed and marked as deleted at another spell remove
  9770. // and we can't use original map for safe iterative with visit each spell at loop end
  9771. PlayerSpellMap smap = GetSpellMap();
  9772.  
  9773. uint32 family;
  9774.  
  9775. if (myClassOnly)
  9776. {
  9777. ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(getClass());
  9778. if (!clsEntry)
  9779. return;
  9780. family = clsEntry->spellfamily;
  9781.  
  9782. for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter)
  9783. {
  9784. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(iter->first);
  9785. if (!spellInfo)
  9786. continue;
  9787.  
  9788. // skip server-side/triggered spells
  9789. if (spellInfo->SpellLevel == 0)
  9790. continue;
  9791.  
  9792. // skip wrong class/race skills
  9793. if (!IsSpellFitByClassAndRace(spellInfo->Id))
  9794. continue;
  9795.  
  9796. // skip other spell families
  9797. if (spellInfo->SpellFamilyName != family)
  9798. continue;
  9799.  
  9800. // skip spells with first rank learned as talent (and all talents then also)
  9801. uint32 first_rank = sSpellMgr->GetFirstSpellInChain(spellInfo->Id);
  9802. if (GetTalentSpellCost(first_rank) > 0)
  9803. continue;
  9804.  
  9805. // skip broken spells
  9806. if (!SpellMgr::IsSpellValid(spellInfo, this, false))
  9807. continue;
  9808. }
  9809. }
  9810. else
  9811. for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter)
  9812. removeSpell(iter->first, false, false); // only iter->first can be accessed, object by iter->second can be deleted already
  9813.  
  9814. learnDefaultSpells();
  9815. learnQuestRewardedSpells();
  9816. }
  9817.  
  9818. void Player::learnDefaultSpells()
  9819. {
  9820. // learn default race/class spells
  9821. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  9822. for (PlayerCreateInfoSpells::const_iterator itr = info->spell.begin(); itr != info->spell.end(); ++itr)
  9823. {
  9824. uint32 tspell = *itr;
  9825. sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell);
  9826. if (!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add
  9827. addSpell(tspell, true, true, true, false);
  9828. else // but send in normal spell in game learn case
  9829. learnSpell(tspell, true);
  9830. }
  9831. }
  9832.  
  9833. void Player::learnQuestRewardedSpells(Quest const* quest)
  9834. {
  9835. int32 spell_id = quest->GetRewSpellCast();
  9836. uint32 src_spell_id = quest->GetSrcSpell();
  9837.  
  9838. // skip quests without rewarded spell
  9839. if (!spell_id)
  9840. return;
  9841.  
  9842. // if RewSpellCast = -1 we remove aura do to SrcSpell from player.
  9843. if (spell_id == -1 && src_spell_id)
  9844. {
  9845. RemoveAurasDueToSpell(src_spell_id);
  9846. return;
  9847. }
  9848.  
  9849. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
  9850. if (!spellInfo)
  9851. return;
  9852.  
  9853. // check learned spells state
  9854. bool found = false;
  9855. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  9856. {
  9857. if (spellInfo->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL && !HasSpell(spellInfo->Effects[i].TriggerSpell))
  9858. {
  9859. found = true;
  9860. break;
  9861. }
  9862. }
  9863.  
  9864. // skip quests with not teaching spell or already known spell
  9865. if (!found)
  9866. return;
  9867.  
  9868. // prevent learn non first rank unknown profession and second specialization for same profession)
  9869. uint32 learned_0 = spellInfo->Effects[0].TriggerSpell;
  9870. if (sSpellMgr->GetSpellRank(learned_0) > 1 && !HasSpell(learned_0))
  9871. {
  9872. // not have first rank learned (unlearned prof?)
  9873. uint32 first_spell = sSpellMgr->GetFirstSpellInChain(learned_0);
  9874. if (!HasSpell(first_spell))
  9875. return;
  9876.  
  9877. SpellInfo const* learnedInfo = sSpellMgr->GetSpellInfo(learned_0);
  9878. if (!learnedInfo)
  9879. return;
  9880.  
  9881. SpellsRequiringSpellMapBounds spellsRequired = sSpellMgr->GetSpellsRequiredForSpellBounds(learned_0);
  9882. for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequired.first; itr2 != spellsRequired.second; ++itr2)
  9883. {
  9884. uint32 profSpell = itr2->second;
  9885.  
  9886. // specialization
  9887. if (learnedInfo->Effects[0].Effect == SPELL_EFFECT_TRADE_SKILL && learnedInfo->Effects[1].Effect == 0 && profSpell)
  9888. {
  9889. // search other specialization for same prof
  9890. for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
  9891. {
  9892. if (itr->second->state == PLAYERSPELL_REMOVED || itr->first == learned_0)
  9893. continue;
  9894.  
  9895. SpellInfo const* itrInfo = sSpellMgr->GetSpellInfo(itr->first);
  9896. if (!itrInfo)
  9897. return;
  9898.  
  9899. // compare only specializations
  9900. if (itrInfo->Effects[0].Effect != SPELL_EFFECT_TRADE_SKILL || itrInfo->Effects[1].Effect != 0)
  9901. continue;
  9902.  
  9903. // compare same chain spells
  9904. if (sSpellMgr->IsSpellRequiringSpell(itr->first, profSpell))
  9905. return;
  9906. }
  9907. }
  9908. }
  9909. }
  9910.  
  9911. CastSpell(this, spell_id, true);
  9912. }
  9913.  
  9914. void Player::learnQuestRewardedSpells()
  9915. {
  9916. // learn spells received from quest completing
  9917. for (RewardedQuestSet::const_iterator itr = m_RewardedQuests.begin(); itr != m_RewardedQuests.end(); ++itr)
  9918. {
  9919. Quest const* quest = sObjectMgr->GetQuestTemplate(*itr);
  9920. if (!quest)
  9921. continue;
  9922.  
  9923. learnQuestRewardedSpells(quest);
  9924. }
  9925. }
  9926.  
  9927. void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value)
  9928. {
  9929. uint32 raceMask = getRaceMask();
  9930. uint32 classMask = getClassMask();
  9931. for (uint32 j=0; j<sSkillLineAbilityStore.GetNumRows(); ++j)
  9932. {
  9933. SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j);
  9934. if (!pAbility || pAbility->skillId != skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
  9935. continue;
  9936. // Check race if set
  9937. if (pAbility->racemask && !(pAbility->racemask & raceMask))
  9938. continue;
  9939. // Check class if set
  9940. if (pAbility->classmask && !(pAbility->classmask & classMask))
  9941. continue;
  9942.  
  9943. if (sSpellMgr->GetSpellInfo(pAbility->spellId))
  9944. {
  9945. // need unlearn spell
  9946. if (skill_value < pAbility->req_skill_value)
  9947. removeSpell(pAbility->spellId);
  9948. // need learn
  9949. else if (!IsInWorld())
  9950. addSpell(pAbility->spellId, true, true, true, false);
  9951. else
  9952. learnSpell(pAbility->spellId, true);
  9953. }
  9954. }
  9955. }
  9956.  
  9957. void Player::SendAurasForTarget(Unit* target)
  9958. {
  9959. if (!target || target->GetVisibleAuras()->empty()) // speedup things
  9960. return;
  9961.  
  9962. /*! Blizz sends certain movement packets sometimes even before CreateObject
  9963. These movement packets are usually found in SMSG_COMPRESSED_MOVES
  9964. */
  9965. if (target->HasAuraType(SPELL_AURA_FEATHER_FALL))
  9966. target->SendMovementFeatherFall();
  9967.  
  9968. if (target->HasAuraType(SPELL_AURA_WATER_WALK))
  9969. target->SendMovementWaterWalking();
  9970.  
  9971. if (target->HasAuraType(SPELL_AURA_HOVER))
  9972. target->SendMovementHover();
  9973.  
  9974. WorldPacket data(SMSG_AURA_UPDATE_ALL);
  9975. data.append(target->GetPackGUID());
  9976.  
  9977. Unit::VisibleAuraMap const* visibleAuras = target->GetVisibleAuras();
  9978. for (Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr)
  9979. {
  9980. AuraApplication * auraApp = itr->second;
  9981. auraApp->BuildUpdatePacket(data, false);
  9982. }
  9983.  
  9984. GetSession()->SendPacket(&data);
  9985. }
  9986.  
  9987. void Player::SetDailyQuestStatus(uint32 quest_id)
  9988. {
  9989. if (Quest const* qQuest = sObjectMgr->GetQuestTemplate(quest_id))
  9990. {
  9991. if (!qQuest->IsDFQuest())
  9992. {
  9993. for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
  9994. {
  9995. if (!GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
  9996. {
  9997. SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx, quest_id);
  9998. m_lastDailyQuestTime = time(NULL); // last daily quest time
  9999. m_DailyQuestChanged = true;
  10000. break;
  10001. }
  10002. }
  10003. } else
  10004. {
  10005. m_DFQuests.insert(quest_id);
  10006. m_lastDailyQuestTime = time(NULL);
  10007. m_DailyQuestChanged = true;
  10008. }
  10009. }
  10010. }
  10011.  
  10012. void Player::SetWeeklyQuestStatus(uint32 quest_id)
  10013. {
  10014. m_weeklyquests.insert(quest_id);
  10015. m_WeeklyQuestChanged = true;
  10016. }
  10017.  
  10018. void Player::SetSeasonalQuestStatus(uint32 quest_id)
  10019. {
  10020. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  10021. if (!quest)
  10022. return;
  10023.  
  10024. m_seasonalquests[sGameEventMgr->GetEventIdForQuest(quest)].insert(quest_id);
  10025. m_SeasonalQuestChanged = true;
  10026. }
  10027.  
  10028. void Player::ResetDailyQuestStatus()
  10029. {
  10030. for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
  10031. SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx, 0);
  10032.  
  10033. m_DFQuests.clear(); // Dungeon Finder Quests.
  10034.  
  10035. // DB data deleted in caller
  10036. m_DailyQuestChanged = false;
  10037. m_lastDailyQuestTime = 0;
  10038. }
  10039.  
  10040. void Player::ResetWeeklyQuestStatus()
  10041. {
  10042. if (m_weeklyquests.empty())
  10043. return;
  10044.  
  10045. m_weeklyquests.clear();
  10046. // DB data deleted in caller
  10047. m_WeeklyQuestChanged = false;
  10048. }
  10049.  
  10050. void Player::ResetSeasonalQuestStatus(uint16 event_id)
  10051. {
  10052. if (m_seasonalquests.empty() || m_seasonalquests[event_id].empty())
  10053. return;
  10054.  
  10055. m_seasonalquests.erase(event_id);
  10056. // DB data deleted in caller
  10057. m_SeasonalQuestChanged = false;
  10058. }
  10059.  
  10060. Battleground* Player::GetBattleground() const
  10061. {
  10062. if (GetBattlegroundId() == 0)
  10063. return NULL;
  10064.  
  10065. return sBattlegroundMgr->GetBattleground(GetBattlegroundId(), m_bgData.bgTypeID);
  10066. }
  10067.  
  10068. bool Player::InArena() const
  10069. {
  10070. Battleground* bg = GetBattleground();
  10071. if (!bg || !bg->isArena())
  10072. return false;
  10073.  
  10074. return true;
  10075. }
  10076.  
  10077. bool Player::GetBGAccessByLevel(BattlegroundTypeId bgTypeId) const
  10078. {
  10079. // get a template bg instead of running one
  10080. Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
  10081. if (!bg)
  10082. return false;
  10083.  
  10084. // limit check leel to dbc compatible level range
  10085. uint32 level = getLevel();
  10086. if (level > DEFAULT_MAX_LEVEL)
  10087. level = DEFAULT_MAX_LEVEL;
  10088.  
  10089. if (level < bg->GetMinLevel() || level > bg->GetMaxLevel())
  10090. return false;
  10091.  
  10092. return true;
  10093. }
  10094.  
  10095. float Player::GetReputationPriceDiscount(Creature const* creature) const
  10096. {
  10097. FactionTemplateEntry const* vendor_faction = creature->getFactionTemplateEntry();
  10098. if (!vendor_faction || !vendor_faction->faction)
  10099. return 1.0f;
  10100.  
  10101. ReputationRank rank = GetReputationRank(vendor_faction->faction);
  10102. if (rank <= REP_NEUTRAL)
  10103. return 1.0f;
  10104.  
  10105. return 1.0f - 0.05f* (rank - REP_NEUTRAL);
  10106. }
  10107.  
  10108. bool Player::IsSpellFitByClassAndRace(uint32 spell_id) const
  10109. {
  10110. uint32 racemask = getRaceMask();
  10111. uint32 classmask = getClassMask();
  10112.  
  10113. SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spell_id);
  10114. if (bounds.first == bounds.second)
  10115. return true;
  10116.  
  10117. for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
  10118. {
  10119. // skip wrong race skills
  10120. if (_spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0)
  10121. continue;
  10122.  
  10123. // skip wrong class skills
  10124. if (_spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0)
  10125. continue;
  10126.  
  10127. return true;
  10128. }
  10129.  
  10130. return false;
  10131. }
  10132.  
  10133. bool Player::HasQuestForGO(int32 GOId) const
  10134. {
  10135. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  10136. {
  10137. uint32 questid = GetQuestSlotQuestId(i);
  10138. if (questid == 0)
  10139. continue;
  10140.  
  10141. QuestStatusMap::const_iterator qs_itr = m_QuestStatus.find(questid);
  10142. if (qs_itr == m_QuestStatus.end())
  10143. continue;
  10144.  
  10145. QuestStatusData const& qs = qs_itr->second;
  10146.  
  10147. if (qs.Status == QUEST_STATUS_INCOMPLETE)
  10148. {
  10149. Quest const* qinfo = sObjectMgr->GetQuestTemplate(questid);
  10150. if (!qinfo)
  10151. continue;
  10152.  
  10153. if (GetGroup() && GetGroup()->isRaidGroup() && !qinfo->IsAllowedInRaid())
  10154. continue;
  10155.  
  10156. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  10157. {
  10158. if (qinfo->RequiredNpcOrGo[j] >= 0) //skip non GO case
  10159. continue;
  10160.  
  10161. if ((-1)*GOId == qinfo->RequiredNpcOrGo[j] && qs.CreatureOrGOCount[j] < qinfo->RequiredNpcOrGoCount[j])
  10162. return true;
  10163. }
  10164. }
  10165. }
  10166. return false;
  10167. }
  10168.  
  10169. void Player::UpdateForQuestWorldObjects()
  10170. {
  10171. if (m_clientGUIDs.empty())
  10172. return;
  10173.  
  10174. UpdateData udata;
  10175. WorldPacket packet;
  10176. for (ClientGUIDs::iterator itr=m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
  10177. {
  10178. if (IS_GAMEOBJECT_GUID(*itr))
  10179. {
  10180. if (GameObject* obj = HashMapHolder<GameObject>::Find(*itr))
  10181. obj->BuildValuesUpdateBlockForPlayer(&udata, this);
  10182. }
  10183. else if (IS_CRE_OR_VEH_GUID(*itr))
  10184. {
  10185. Creature* obj = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr);
  10186. if (!obj)
  10187. continue;
  10188.  
  10189. // check if this unit requires quest specific flags
  10190. if (!obj->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK))
  10191. continue;
  10192.  
  10193. SpellClickInfoMapBounds clickPair = sObjectMgr->GetSpellClickInfoMapBounds(obj->GetEntry());
  10194. for (SpellClickInfoContainer::const_iterator _itr = clickPair.first; _itr != clickPair.second; ++_itr)
  10195. {
  10196. //! This code doesn't look right, but it was logically converted to condition system to do the exact
  10197. //! same thing it did before. It definitely needs to be overlooked for intended functionality.
  10198. ConditionList conds = sConditionMgr->GetConditionsForSpellClickEvent(obj->GetEntry(), _itr->second.spellId);
  10199. bool buildUpdateBlock = false;
  10200. for (ConditionList::const_iterator jtr = conds.begin(); jtr != conds.end() && !buildUpdateBlock; ++jtr)
  10201. if ((*jtr)->ConditionType == CONDITION_QUESTREWARDED || (*jtr)->ConditionType == CONDITION_QUESTTAKEN)
  10202. buildUpdateBlock = true;
  10203.  
  10204. if (buildUpdateBlock)
  10205. {
  10206. obj->BuildCreateUpdateBlockForPlayer(&udata, this);
  10207. break;
  10208. }
  10209. }
  10210. }
  10211. }
  10212. udata.BuildPacket(&packet);
  10213. GetSession()->SendPacket(&packet);
  10214. }
  10215.  
  10216. void Player::SummonIfPossible(bool agree)
  10217. {
  10218. if (!agree)
  10219. {
  10220. m_summon_expire = 0;
  10221. return;
  10222. }
  10223.  
  10224. // expire and auto declined
  10225. if (m_summon_expire < time(NULL))
  10226. return;
  10227.  
  10228. // stop taxi flight at summon
  10229. if (isInFlight())
  10230. {
  10231. GetMotionMaster()->MovementExpired();
  10232. CleanupAfterTaxiFlight();
  10233. }
  10234.  
  10235. // drop flag at summon
  10236. // this code can be reached only when GM is summoning player who carries flag, because player should be immune to summoning spells when he carries flag
  10237. if (Battleground* bg = GetBattleground())
  10238. bg->EventPlayerDroppedFlag(this);
  10239.  
  10240. m_summon_expire = 0;
  10241.  
  10242. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS, 1);
  10243.  
  10244. TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z, GetOrientation());
  10245. }
  10246.  
  10247. void Player::RemoveItemDurations(Item* item)
  10248. {
  10249. for (ItemDurationList::iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end(); ++itr)
  10250. {
  10251. if (*itr == item)
  10252. {
  10253. m_itemDuration.erase(itr);
  10254. break;
  10255. }
  10256. }
  10257. }
  10258.  
  10259. void Player::AddItemDurations(Item* item)
  10260. {
  10261. if (item->GetUInt32Value(ITEM_FIELD_DURATION))
  10262. {
  10263. m_itemDuration.push_back(item);
  10264. item->SendTimeUpdate(this);
  10265. }
  10266. }
  10267.  
  10268. void Player::AutoUnequipOffhandIfNeed(bool force /*= false*/)
  10269. {
  10270. Item* offItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
  10271. if (!offItem)
  10272. return;
  10273.  
  10274. // unequip offhand weapon if player doesn't have dual wield anymore
  10275. if (!CanDualWield() && (offItem->GetTemplate()->InventoryType == INVTYPE_WEAPONOFFHAND || offItem->GetTemplate()->InventoryType == INVTYPE_WEAPON))
  10276. force = true;
  10277.  
  10278. // need unequip offhand for 2h-weapon without TitanGrip (in any from hands)
  10279. if (!force && (CanTitanGrip() || (offItem->GetTemplate()->InventoryType != INVTYPE_2HWEAPON && !IsTwoHandUsed())))
  10280. return;
  10281.  
  10282. ItemPosCountVec off_dest;
  10283. uint8 off_msg = CanStoreItem(NULL_BAG, NULL_SLOT, off_dest, offItem, false);
  10284. if (off_msg == EQUIP_ERR_OK)
  10285. {
  10286. RemoveItem(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
  10287. StoreItem(off_dest, offItem, true);
  10288. }
  10289. else
  10290. {
  10291. MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
  10292. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  10293. offItem->DeleteFromInventoryDB(trans); // deletes item from character's inventory
  10294. offItem->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
  10295.  
  10296. std::string subject = GetSession()->GetTrinityString(LANG_NOT_EQUIPPED_ITEM);
  10297. MailDraft(subject, "There were problems with equipping one or several items").AddItem(offItem).SendMailTo(trans, this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED);
  10298.  
  10299. CharacterDatabase.CommitTransaction(trans);
  10300. }
  10301. }
  10302.  
  10303. OutdoorPvP* Player::GetOutdoorPvP() const
  10304. {
  10305. return sOutdoorPvPMgr->GetOutdoorPvPToZoneId(GetZoneId());
  10306. }
  10307.  
  10308. bool Player::HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item const* ignoreItem)
  10309. {
  10310. if (spellInfo->EquippedItemClass < 0)
  10311. return true;
  10312.  
  10313. // scan other equipped items for same requirements (mostly 2 daggers/etc)
  10314. // for optimize check 2 used cases only
  10315. switch (spellInfo->EquippedItemClass)
  10316. {
  10317. case ITEM_CLASS_WEAPON:
  10318. {
  10319. for (uint8 i= EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i)
  10320. if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i))
  10321. if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
  10322. return true;
  10323. break;
  10324. }
  10325. case ITEM_CLASS_ARMOR:
  10326. {
  10327. // tabard not have dependent spells
  10328. for (uint8 i= EQUIPMENT_SLOT_START; i< EQUIPMENT_SLOT_MAINHAND; ++i)
  10329. if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i))
  10330. if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
  10331. return true;
  10332.  
  10333. // shields can be equipped to offhand slot
  10334. if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
  10335. if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
  10336. return true;
  10337.  
  10338. // ranged slot can have some armor subclasses
  10339. if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
  10340. if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
  10341. return true;
  10342.  
  10343. break;
  10344. }
  10345. default:
  10346. sLog->outError(LOG_FILTER_PLAYER, "HasItemFitToSpellRequirements: Not handled spell requirement for item class %u", spellInfo->EquippedItemClass);
  10347. break;
  10348. }
  10349.  
  10350. return false;
  10351. }
  10352.  
  10353. bool Player::CanNoReagentCast(SpellInfo const* spellInfo) const
  10354. {
  10355. // don't take reagents for spells with SPELL_ATTR5_NO_REAGENT_WHILE_PREP
  10356. if (spellInfo->AttributesEx5 & SPELL_ATTR5_NO_REAGENT_WHILE_PREP &&
  10357. HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))
  10358. return true;
  10359.  
  10360. // Check no reagent use mask
  10361. flag96 noReagentMask;
  10362. noReagentMask[0] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1);
  10363. noReagentMask[1] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1+1);
  10364. noReagentMask[2] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1+2);
  10365. if (spellInfo->SpellFamilyFlags & noReagentMask)
  10366. return true;
  10367.  
  10368. return false;
  10369. }
  10370.  
  10371. void Player::RemoveItemDependentAurasAndCasts(Item* pItem)
  10372. {
  10373. for (AuraMap::iterator itr = m_ownedAuras.begin(); itr != m_ownedAuras.end();)
  10374. {
  10375. Aura* aura = itr->second;
  10376.  
  10377. // skip passive (passive item dependent spells work in another way) and not self applied auras
  10378. SpellInfo const* spellInfo = aura->GetSpellInfo();
  10379. if (aura->IsPassive() || aura->GetCasterGUID() != GetGUID())
  10380. {
  10381. ++itr;
  10382. continue;
  10383. }
  10384.  
  10385. // skip if not item dependent or have alternative item
  10386. if (HasItemFitToSpellRequirements(spellInfo, pItem))
  10387. {
  10388. ++itr;
  10389. continue;
  10390. }
  10391.  
  10392. // no alt item, remove aura, restart check
  10393. RemoveOwnedAura(itr);
  10394. }
  10395.  
  10396. // currently casted spells can be dependent from item
  10397. for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i)
  10398. if (Spell* spell = GetCurrentSpell(CurrentSpellTypes(i)))
  10399. if (spell->getState() != SPELL_STATE_DELAYED && !HasItemFitToSpellRequirements(spell->m_spellInfo, pItem))
  10400. InterruptSpell(CurrentSpellTypes(i));
  10401. }
  10402.  
  10403. uint32 Player::GetResurrectionSpellId()
  10404. {
  10405. // search priceless resurrection possibilities
  10406. uint32 prio = 0;
  10407. uint32 spell_id = 0;
  10408. AuraEffectList const& dummyAuras = GetAuraEffectsByType(SPELL_AURA_DUMMY);
  10409. for (AuraEffectList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
  10410. {
  10411. // Soulstone Resurrection // prio: 3 (max, non death persistent)
  10412. if (prio < 2 && (*itr)->GetSpellInfo()->SpellVisual[0] == 99 && (*itr)->GetSpellInfo()->SpellIconID == 92)
  10413. {
  10414. switch ((*itr)->GetId())
  10415. {
  10416. case 20707: spell_id = 3026; break; // rank 1
  10417. case 20762: spell_id = 20758; break; // rank 2
  10418. case 20763: spell_id = 20759; break; // rank 3
  10419. case 20764: spell_id = 20760; break; // rank 4
  10420. case 20765: spell_id = 20761; break; // rank 5
  10421. case 27239: spell_id = 27240; break; // rank 6
  10422. case 47883: spell_id = 47882; break; // rank 7
  10423. default:
  10424. sLog->outError(LOG_FILTER_PLAYER, "Unhandled spell %u: S.Resurrection", (*itr)->GetId());
  10425. continue;
  10426. }
  10427.  
  10428. prio = 3;
  10429. }
  10430. // Twisting Nether // prio: 2 (max)
  10431. else if ((*itr)->GetId() == 23701 && roll_chance_i(10))
  10432. {
  10433. prio = 2;
  10434. spell_id = 23700;
  10435. }
  10436. }
  10437.  
  10438. // Reincarnation (passive spell) // prio: 1 // Glyph of Renewed Life
  10439. if (prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169) && (HasAura(58059) || HasItemCount(17030, 1)))
  10440. spell_id = 21169;
  10441.  
  10442. return spell_id;
  10443. }
  10444.  
  10445. // Used in triggers for check "Only to targets that grant experience or honor" req
  10446. bool Player::isHonorOrXPTarget(Unit* victim)
  10447. {
  10448. uint8 v_level = victim->getLevel();
  10449. uint8 k_grey = Trinity::XP::GetGrayLevel(getLevel());
  10450.  
  10451. // Victim level less gray level
  10452. if (v_level <= k_grey)
  10453. return false;
  10454.  
  10455. if (victim->GetTypeId() == TYPEID_UNIT)
  10456. {
  10457. if (victim->ToCreature()->isTotem() ||
  10458. victim->ToCreature()->isPet() ||
  10459. victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL)
  10460. return false;
  10461. }
  10462. return true;
  10463. }
  10464.  
  10465. bool Player::GetsRecruitAFriendBonus(bool forXP)
  10466. {
  10467. bool recruitAFriend = false;
  10468. if (getLevel() <= sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL) || !forXP)
  10469. {
  10470. if (Group* group = this->GetGroup())
  10471. {
  10472. for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  10473. {
  10474. Player* player = itr->getSource();
  10475. if (!player)
  10476. continue;
  10477.  
  10478. if (!player->IsAtRecruitAFriendDistance(this))
  10479. continue; // member (alive or dead) or his corpse at req. distance
  10480.  
  10481. if (forXP)
  10482. {
  10483. // level must be allowed to get RaF bonus
  10484. if (player->getLevel() > sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL))
  10485. continue;
  10486.  
  10487. // level difference must be small enough to get RaF bonus, UNLESS we are lower level
  10488. if (player->getLevel() < getLevel())
  10489. if (uint8(getLevel() - player->getLevel()) > sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL_DIFFERENCE))
  10490. continue;
  10491. }
  10492.  
  10493. bool ARecruitedB = (player->GetSession()->GetRecruiterId() == GetSession()->GetAccountId());
  10494. bool BRecruitedA = (GetSession()->GetRecruiterId() == player->GetSession()->GetAccountId());
  10495. if (ARecruitedB || BRecruitedA)
  10496. {
  10497. recruitAFriend = true;
  10498. break;
  10499. }
  10500. }
  10501. }
  10502. }
  10503. return recruitAFriend;
  10504. }
  10505.  
  10506. void Player::RewardPlayerAndGroupAtKill(Unit* victim, bool isBattleGround)
  10507. {
  10508. KillRewarder(this, victim, isBattleGround).Reward();
  10509. }
  10510.  
  10511. void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewardSource)
  10512. {
  10513. if (!pRewardSource)
  10514. return;
  10515. uint64 creature_guid = (pRewardSource->GetTypeId() == TYPEID_UNIT) ? pRewardSource->GetGUID() : uint64(0);
  10516.  
  10517. // prepare data for near group iteration
  10518. if (Group* group = GetGroup())
  10519. {
  10520. for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  10521. {
  10522. Player* player = itr->getSource();
  10523. if (!player)
  10524. continue;
  10525.  
  10526. if (!player->IsAtGroupRewardDistance(pRewardSource))
  10527. continue; // member (alive or dead) or his corpse at req. distance
  10528.  
  10529. // quest objectives updated only for alive group member or dead but with not released body
  10530. if (player->isAlive()|| !player->GetCorpse())
  10531. player->KilledMonsterCredit(creature_id, creature_guid);
  10532. }
  10533. }
  10534. else // if (!group)
  10535. KilledMonsterCredit(creature_id, creature_guid);
  10536. }
  10537.  
  10538. bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const
  10539. {
  10540. if (!pRewardSource)
  10541. return false;
  10542. const WorldObject* player = GetCorpse();
  10543. if (!player || isAlive())
  10544. player = this;
  10545.  
  10546. if (player->GetMapId() != pRewardSource->GetMapId() || player->GetInstanceId() != pRewardSource->GetInstanceId())
  10547. return false;
  10548.  
  10549. return pRewardSource->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE);
  10550. }
  10551.  
  10552. bool Player::IsAtRecruitAFriendDistance(WorldObject const* pOther) const
  10553. {
  10554. if (!pOther)
  10555. return false;
  10556. const WorldObject* player = GetCorpse();
  10557. if (!player || isAlive())
  10558. player = this;
  10559.  
  10560. if (player->GetMapId() != pOther->GetMapId() || player->GetInstanceId() != pOther->GetInstanceId())
  10561. return false;
  10562.  
  10563. return pOther->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE);
  10564. }
  10565.  
  10566. uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
  10567. {
  10568. Item* item = GetWeaponForAttack(attType, true);
  10569.  
  10570. // unarmed only with base attack
  10571. if (attType != BASE_ATTACK && !item)
  10572. return 0;
  10573.  
  10574. // weapon skill or (unarmed for base attack and for fist weapons)
  10575. uint32 skill = (item && item->GetSkill() != SKILL_FIST_WEAPONS) ? item->GetSkill() : uint32(SKILL_UNARMED);
  10576. return GetBaseSkillValue(skill);
  10577. }
  10578.  
  10579. void Player::ResurectUsingRequestData()
  10580. {
  10581. /// Teleport before resurrecting by player, otherwise the player might get attacked from creatures near his corpse
  10582. TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation());
  10583.  
  10584. if (IsBeingTeleported())
  10585. {
  10586. ScheduleDelayedOperation(DELAYED_RESURRECT_PLAYER);
  10587. return;
  10588. }
  10589.  
  10590. ResurrectPlayer(0.0f, false);
  10591.  
  10592. if (GetMaxHealth() > m_resurrectHealth)
  10593. SetHealth(m_resurrectHealth);
  10594. else
  10595. SetFullHealth();
  10596.  
  10597. if (GetMaxPower(POWER_MANA) > m_resurrectMana)
  10598. SetPower(POWER_MANA, m_resurrectMana);
  10599. else
  10600. SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
  10601.  
  10602. SetPower(POWER_RAGE, 0);
  10603.  
  10604. SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
  10605.  
  10606. SpawnCorpseBones();
  10607. }
  10608.  
  10609. void Player::SetClientControl(Unit* target, uint8 allowMove)
  10610. {
  10611. WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size()+1);
  10612. data.append(target->GetPackGUID());
  10613. data << uint8(allowMove);
  10614. GetSession()->SendPacket(&data);
  10615. if (target == this)
  10616. SetMover(this);
  10617. }
  10618.  
  10619. void Player::UpdateZoneDependentAuras(uint32 newZone)
  10620. {
  10621. // Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update
  10622. SpellAreaForAreaMapBounds saBounds = sSpellMgr->GetSpellAreaForAreaMapBounds(newZone);
  10623. for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
  10624. if (itr->second->autocast && itr->second->IsFitToRequirements(this, newZone, 0))
  10625. if (!HasAura(itr->second->spellId))
  10626. CastSpell(this, itr->second->spellId, true);
  10627. }
  10628.  
  10629. void Player::UpdateAreaDependentAuras(uint32 newArea)
  10630. {
  10631. // remove auras from spells with area limitations
  10632. for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
  10633. {
  10634. // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date
  10635. if (iter->second->GetSpellInfo()->CheckLocation(GetMapId(), m_zoneUpdateId, newArea, this) != SPELL_CAST_OK)
  10636. RemoveOwnedAura(iter);
  10637. else
  10638. ++iter;
  10639. }
  10640.  
  10641. // some auras applied at subzone enter
  10642. SpellAreaForAreaMapBounds saBounds = sSpellMgr->GetSpellAreaForAreaMapBounds(newArea);
  10643. for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
  10644. if (itr->second->autocast && itr->second->IsFitToRequirements(this, m_zoneUpdateId, newArea))
  10645. if (!HasAura(itr->second->spellId))
  10646. CastSpell(this, itr->second->spellId, true);
  10647.  
  10648. if (newArea == 4273 && GetVehicle() && GetPositionX() > 400) // Ulduar
  10649. {
  10650. switch (GetVehicleBase()->GetEntry())
  10651. {
  10652. case 33062:
  10653. case 33109:
  10654. case 33060:
  10655. GetVehicle()->Dismiss();
  10656. break;
  10657. }
  10658. }
  10659. }
  10660.  
  10661. uint32 Player::GetCorpseReclaimDelay(bool pvp) const
  10662. {
  10663. if (pvp)
  10664. {
  10665. if (!sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP))
  10666. return copseReclaimDelay[0];
  10667. }
  10668. else if (!sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE))
  10669. return 0;
  10670.  
  10671. time_t now = time(NULL);
  10672. // 0..2 full period
  10673. // should be ceil(x)-1 but not floor(x)
  10674. uint64 count = (now < m_deathExpireTime - 1) ? (m_deathExpireTime - 1 - now)/DEATH_EXPIRE_STEP : 0;
  10675. return copseReclaimDelay[count];
  10676. }
  10677.  
  10678. void Player::UpdateCorpseReclaimDelay()
  10679. {
  10680. bool pvp = m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH;
  10681.  
  10682. if ((pvp && !sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP)) ||
  10683. (!pvp && !sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE)))
  10684. return;
  10685.  
  10686. time_t now = time(NULL);
  10687. if (now < m_deathExpireTime)
  10688. {
  10689. // full and partly periods 1..3
  10690. uint64 count = (m_deathExpireTime - now)/DEATH_EXPIRE_STEP +1;
  10691. if (count < MAX_DEATH_COUNT)
  10692. m_deathExpireTime = now+(count+1)*DEATH_EXPIRE_STEP;
  10693. else
  10694. m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP;
  10695. }
  10696. else
  10697. m_deathExpireTime = now+DEATH_EXPIRE_STEP;
  10698. }
  10699.  
  10700. void Player::SendCorpseReclaimDelay(bool load)
  10701. {
  10702. Corpse* corpse = GetCorpse();
  10703. if (load && !corpse)
  10704. return;
  10705.  
  10706. bool pvp;
  10707. if (corpse)
  10708. pvp = (corpse->GetType() == CORPSE_RESURRECTABLE_PVP);
  10709. else
  10710. pvp = (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH);
  10711.  
  10712. time_t delay;
  10713. if (load)
  10714. {
  10715. if (corpse->GetGhostTime() > m_deathExpireTime)
  10716. return;
  10717.  
  10718. uint64 count;
  10719. if ((pvp && sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP)) ||
  10720. (!pvp && sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE)))
  10721. {
  10722. count = (m_deathExpireTime-corpse->GetGhostTime())/DEATH_EXPIRE_STEP;
  10723. if (count >= MAX_DEATH_COUNT)
  10724. count = MAX_DEATH_COUNT-1;
  10725. }
  10726. else
  10727. count=0;
  10728.  
  10729. time_t expected_time = corpse->GetGhostTime()+copseReclaimDelay[count];
  10730.  
  10731. time_t now = time(NULL);
  10732. if (now >= expected_time)
  10733. return;
  10734.  
  10735. delay = expected_time-now;
  10736. }
  10737. else
  10738. delay = GetCorpseReclaimDelay(pvp);
  10739.  
  10740. if (!delay)
  10741. return;
  10742.  
  10743. //! corpse reclaim delay 30 * 1000ms or longer at often deaths
  10744. WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4);
  10745. data << uint32(delay*IN_MILLISECONDS);
  10746. GetSession()->SendPacket(&data);
  10747. }
  10748.  
  10749. Player* Player::GetNextRandomRaidMember(float radius)
  10750. {
  10751. Group* group = GetGroup();
  10752. if (!group)
  10753. return NULL;
  10754.  
  10755. std::vector<Player*> nearMembers;
  10756. nearMembers.reserve(group->GetMembersCount());
  10757.  
  10758. for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  10759. {
  10760. Player* Target = itr->getSource();
  10761.  
  10762. // IsHostileTo check duel and controlled by enemy
  10763. if (Target && Target != this && IsWithinDistInMap(Target, radius) &&
  10764. !Target->HasInvisibilityAura() && !IsHostileTo(Target))
  10765. nearMembers.push_back(Target);
  10766. }
  10767.  
  10768. if (nearMembers.empty())
  10769. return NULL;
  10770.  
  10771. uint32 randTarget = urand(0, nearMembers.size()-1);
  10772. return nearMembers[randTarget];
  10773. }
  10774.  
  10775. PartyResult Player::CanUninviteFromGroup() const
  10776. {
  10777. Group const* grp = GetGroup();
  10778. if (!grp)
  10779. return ERR_NOT_IN_GROUP;
  10780.  
  10781. if (grp->isLFGGroup())
  10782. {
  10783. uint64 gguid = grp->GetGUID();
  10784. if (!sLFGMgr->GetKicksLeft(gguid))
  10785. return ERR_PARTY_LFG_BOOT_LIMIT;
  10786.  
  10787. LfgState state = sLFGMgr->GetState(gguid);
  10788. if (state == LFG_STATE_BOOT)
  10789. return ERR_PARTY_LFG_BOOT_IN_PROGRESS;
  10790.  
  10791. if (grp->GetMembersCount() <= sLFGMgr->GetVotesNeeded(gguid))
  10792. return ERR_PARTY_LFG_BOOT_TOO_FEW_PLAYERS;
  10793.  
  10794. if (state == LFG_STATE_FINISHED_DUNGEON)
  10795. return ERR_PARTY_LFG_BOOT_DUNGEON_COMPLETE;
  10796.  
  10797. if (grp->isRollLootActive())
  10798. return ERR_PARTY_LFG_BOOT_LOOT_ROLLS;
  10799.  
  10800. // TODO: Should also be sent when anyone has recently left combat, with an aprox ~5 seconds timer.
  10801. for (GroupReference const* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
  10802. if (itr->getSource() && itr->getSource()->isInCombat())
  10803. return ERR_PARTY_LFG_BOOT_IN_COMBAT;
  10804.  
  10805. /* Missing support for these types
  10806. return ERR_PARTY_LFG_BOOT_COOLDOWN_S;
  10807. return ERR_PARTY_LFG_BOOT_NOT_ELIGIBLE_S;
  10808. */
  10809. }
  10810. else
  10811. {
  10812. if (!grp->IsLeader(GetGUID()) && !grp->IsAssistant(GetGUID()))
  10813. return ERR_NOT_LEADER;
  10814.  
  10815. if (InBattleground())
  10816. return ERR_INVITE_RESTRICTED;
  10817. }
  10818.  
  10819. return ERR_PARTY_RESULT_OK;
  10820. }
  10821.  
  10822. bool Player::isUsingLfg()
  10823. {
  10824. uint64 guid = GetGUID();
  10825. return sLFGMgr->GetState(guid) != LFG_STATE_NONE;
  10826. }
  10827.  
  10828. void Player::SetBattlegroundRaid(Group* group, int8 subgroup)
  10829. {
  10830. //we must move references from m_group to m_originalGroup
  10831. SetOriginalGroup(GetGroup(), GetSubGroup());
  10832.  
  10833. m_group.unlink();
  10834. m_group.link(group, this);
  10835. m_group.setSubGroup((uint8)subgroup);
  10836. }
  10837.  
  10838. void Player::RemoveFromBattlegroundRaid()
  10839. {
  10840. //remove existing reference
  10841. m_group.unlink();
  10842. if (Group* group = GetOriginalGroup())
  10843. {
  10844. m_group.link(group, this);
  10845. m_group.setSubGroup(GetOriginalSubGroup());
  10846. }
  10847. SetOriginalGroup(NULL);
  10848. }
  10849.  
  10850. void Player::SetOriginalGroup(Group* group, int8 subgroup)
  10851. {
  10852. if (group == NULL)
  10853. m_originalGroup.unlink();
  10854. else
  10855. {
  10856. // never use SetOriginalGroup without a subgroup unless you specify NULL for group
  10857. ASSERT(subgroup >= 0);
  10858. m_originalGroup.link(group, this);
  10859. m_originalGroup.setSubGroup((uint8)subgroup);
  10860. }
  10861. }
  10862.  
  10863. void Player::UpdateUnderwaterState(Map* m, float x, float y, float z)
  10864. {
  10865. LiquidData liquid_status;
  10866. ZLiquidStatus res = m->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status);
  10867. if (!res)
  10868. {
  10869. m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWARER_INDARKWATER);
  10870. if (_lastLiquid && _lastLiquid->SpellId)
  10871. RemoveAurasDueToSpell(_lastLiquid->SpellId);
  10872.  
  10873. _lastLiquid = NULL;
  10874. return;
  10875. }
  10876.  
  10877. if (uint32 liqEntry = liquid_status.entry)
  10878. {
  10879. LiquidTypeEntry const* liquid = sLiquidTypeStore.LookupEntry(liqEntry);
  10880. if (_lastLiquid && _lastLiquid->SpellId && _lastLiquid->Id != liqEntry)
  10881. RemoveAurasDueToSpell(_lastLiquid->SpellId);
  10882.  
  10883. if (liquid && liquid->SpellId)
  10884. {
  10885. if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER))
  10886. CastSpell(this, liquid->SpellId, true);
  10887. else
  10888. RemoveAurasDueToSpell(liquid->SpellId);
  10889. }
  10890.  
  10891. _lastLiquid = liquid;
  10892. }
  10893. else if (_lastLiquid && _lastLiquid->SpellId)
  10894. {
  10895. RemoveAurasDueToSpell(_lastLiquid->SpellId);
  10896. _lastLiquid = NULL;
  10897. }
  10898.  
  10899.  
  10900. // All liquids type - check under water position
  10901. if (liquid_status.type_flags & (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME))
  10902. {
  10903. if (res & LIQUID_MAP_UNDER_WATER)
  10904. m_MirrorTimerFlags |= UNDERWATER_INWATER;
  10905. else
  10906. m_MirrorTimerFlags &= ~UNDERWATER_INWATER;
  10907. }
  10908.  
  10909. // Allow travel in dark water on taxi or transport
  10910. if ((liquid_status.type_flags & MAP_LIQUID_TYPE_DARK_WATER) && !isInFlight() && !GetTransport())
  10911. m_MirrorTimerFlags |= UNDERWARER_INDARKWATER;
  10912. else
  10913. m_MirrorTimerFlags &= ~UNDERWARER_INDARKWATER;
  10914.  
  10915. // in lava check, anywhere in lava level
  10916. if (liquid_status.type_flags & MAP_LIQUID_TYPE_MAGMA)
  10917. {
  10918. if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER | LIQUID_MAP_WATER_WALK))
  10919. m_MirrorTimerFlags |= UNDERWATER_INLAVA;
  10920. else
  10921. m_MirrorTimerFlags &= ~UNDERWATER_INLAVA;
  10922. }
  10923. // in slime check, anywhere in slime level
  10924. if (liquid_status.type_flags & MAP_LIQUID_TYPE_SLIME)
  10925. {
  10926. if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER | LIQUID_MAP_WATER_WALK))
  10927. m_MirrorTimerFlags |= UNDERWATER_INSLIME;
  10928. else
  10929. m_MirrorTimerFlags &= ~UNDERWATER_INSLIME;
  10930. }
  10931. }
  10932.  
  10933. void Player::SetCanParry(bool value)
  10934. {
  10935. if (m_canParry == value)
  10936. return;
  10937.  
  10938. m_canParry = value;
  10939. UpdateParryPercentage();
  10940. }
  10941.  
  10942. void Player::SetCanBlock(bool value)
  10943. {
  10944. if (m_canBlock == value)
  10945. return;
  10946.  
  10947. m_canBlock = value;
  10948. UpdateBlockPercentage();
  10949. }
  10950.  
  10951. bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const
  10952. {
  10953. for (ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end(); ++itr)
  10954. if (itr->pos == pos)
  10955. return true;
  10956. return false;
  10957. }
  10958.  
  10959. void Player::StopCastingBindSight()
  10960. {
  10961. if (WorldObject* target = GetViewpoint())
  10962. {
  10963. if (target->isType(TYPEMASK_UNIT))
  10964. {
  10965. ((Unit*)target)->RemoveAurasByType(SPELL_AURA_BIND_SIGHT, GetGUID());
  10966. ((Unit*)target)->RemoveAurasByType(SPELL_AURA_MOD_POSSESS, GetGUID());
  10967. ((Unit*)target)->RemoveAurasByType(SPELL_AURA_MOD_POSSESS_PET, GetGUID());
  10968. }
  10969. }
  10970. }
  10971.  
  10972. void Player::SetViewpoint(WorldObject* target, bool apply)
  10973. {
  10974. if (apply)
  10975. {
  10976. sLog->outDebug(LOG_FILTER_MAPS, "Player::CreateViewpoint: Player %s create seer %u (TypeId: %u).", GetName(), target->GetEntry(), target->GetTypeId());
  10977.  
  10978. if (!AddUInt64Value(PLAYER_FARSIGHT, target->GetGUID()))
  10979. {
  10980. sLog->outFatal(LOG_FILTER_PLAYER, "Player::CreateViewpoint: Player %s cannot add new viewpoint!", GetName());
  10981. return;
  10982. }
  10983.  
  10984. // farsight dynobj or puppet may be very far away
  10985. UpdateVisibilityOf(target);
  10986.  
  10987. if (target->isType(TYPEMASK_UNIT) && !GetVehicle())
  10988. ((Unit*)target)->AddPlayerToVision(this);
  10989. }
  10990. else
  10991. {
  10992. sLog->outDebug(LOG_FILTER_MAPS, "Player::CreateViewpoint: Player %s remove seer", GetName());
  10993.  
  10994. if (!RemoveUInt64Value(PLAYER_FARSIGHT, target->GetGUID()))
  10995. {
  10996. sLog->outFatal(LOG_FILTER_PLAYER, "Player::CreateViewpoint: Player %s cannot remove current viewpoint!", GetName());
  10997. return;
  10998. }
  10999.  
  11000. if (target->isType(TYPEMASK_UNIT) && !GetVehicle())
  11001. ((Unit*)target)->RemovePlayerFromVision(this);
  11002.  
  11003. //must immediately set seer back otherwise may crash
  11004. m_seer = this;
  11005.  
  11006. //WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0);
  11007. //GetSession()->SendPacket(&data);
  11008. }
  11009. }
  11010.  
  11011. WorldObject* Player::GetViewpoint() const
  11012. {
  11013. if (uint64 guid = GetUInt64Value(PLAYER_FARSIGHT))
  11014. return (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*this, guid, TYPEMASK_SEER);
  11015. return NULL;
  11016. }
  11017.  
  11018. bool Player::CanUseBattlegroundObject()
  11019. {
  11020. // TODO : some spells gives player ForceReaction to one faction (ReputationMgr::ApplyForceReaction)
  11021. // maybe gameobject code should handle that ForceReaction usage
  11022. // BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet
  11023. return (//InBattleground() && // in battleground - not need, check in other cases
  11024. //!IsMounted() && - not correct, player is dismounted when he clicks on flag
  11025. //player cannot use object when he is invulnerable (immune)
  11026. !isTotalImmune() && // not totally immune
  11027. //i'm not sure if these two are correct, because invisible players should get visible when they click on flag
  11028. !HasStealthAura() && // not stealthed
  11029. !HasInvisibilityAura() && // not invisible
  11030. !HasAura(SPELL_RECENTLY_DROPPED_FLAG) && // can't pickup
  11031. isAlive() // live player
  11032. );
  11033. }
  11034.  
  11035. bool Player::CanCaptureTowerPoint()
  11036. {
  11037. return (!HasStealthAura() && // not stealthed
  11038. !HasInvisibilityAura() && // not invisible
  11039. isAlive() // live player
  11040. );
  11041. }
  11042.  
  11043. uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair, BarberShopStyleEntry const* newSkin)
  11044. {
  11045. uint8 level = getLevel();
  11046.  
  11047. if (level > GT_MAX_LEVEL)
  11048. level = GT_MAX_LEVEL; // max level in this dbc
  11049.  
  11050. uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2);
  11051. uint8 haircolor = GetByteValue(PLAYER_BYTES, 3);
  11052. uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0);
  11053. uint8 skincolor = GetByteValue(PLAYER_BYTES, 0);
  11054.  
  11055. if ((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair) && (!newSkin || (newSkin->hair_id == skincolor)))
  11056. return 0;
  11057.  
  11058. GtBarberShopCostBaseEntry const* bsc = sGtBarberShopCostBaseStore.LookupEntry(level - 1);
  11059.  
  11060. if (!bsc) // shouldn't happen
  11061. return 0xFFFFFFFF;
  11062.  
  11063. float cost = 0;
  11064.  
  11065. if (hairstyle != newhairstyle)
  11066. cost += bsc->cost; // full price
  11067.  
  11068. if ((haircolor != newhaircolor) && (hairstyle == newhairstyle))
  11069. cost += bsc->cost * 0.5f; // +1/2 of price
  11070.  
  11071. if (facialhair != newfacialhair)
  11072. cost += bsc->cost * 0.75f; // +3/4 of price
  11073.  
  11074. if (newSkin && skincolor != newSkin->hair_id)
  11075. cost += bsc->cost * 0.75f; // +5/6 of price
  11076.  
  11077. return uint32(cost);
  11078. }
  11079.  
  11080. void Player::InitGlyphsForLevel()
  11081. {
  11082. for (uint32 i = 0; i < sGlyphSlotStore.GetNumRows(); ++i)
  11083. if (GlyphSlotEntry const* gs = sGlyphSlotStore.LookupEntry(i))
  11084. if (gs->Order)
  11085. SetGlyphSlot(gs->Order - 1, gs->Id);
  11086.  
  11087. uint8 level = getLevel();
  11088. uint32 value = 0;
  11089.  
  11090. // 0x3F = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 for 80 level
  11091. if (level >= 15)
  11092. value |= (0x01 | 0x02);
  11093. if (level >= 30)
  11094. value |= 0x08;
  11095. if (level >= 50)
  11096. value |= 0x04;
  11097. if (level >= 70)
  11098. value |= 0x10;
  11099. if (level >= 80)
  11100. value |= 0x20;
  11101.  
  11102. SetUInt32Value(PLAYER_GLYPHS_ENABLED, value);
  11103. }
  11104.  
  11105. bool Player::isTotalImmune()
  11106. {
  11107. AuraEffectList const& immune = GetAuraEffectsByType(SPELL_AURA_SCHOOL_IMMUNITY);
  11108.  
  11109. uint32 immuneMask = 0;
  11110. for (AuraEffectList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr)
  11111. {
  11112. immuneMask |= (*itr)->GetMiscValue();
  11113. if (immuneMask & SPELL_SCHOOL_MASK_ALL) // total immunity
  11114. return true;
  11115. }
  11116. return false;
  11117. }
  11118.  
  11119. bool Player::HasTitle(uint32 bitIndex)
  11120. {
  11121. if (bitIndex > MAX_TITLE_INDEX)
  11122. return false;
  11123.  
  11124. uint32 fieldIndexOffset = bitIndex / 32;
  11125. uint32 flag = 1 << (bitIndex % 32);
  11126. return HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
  11127. }
  11128.  
  11129. void Player::SetTitle(CharTitlesEntry const* title, bool lost)
  11130. {
  11131. uint32 fieldIndexOffset = title->bit_index / 32;
  11132. uint32 flag = 1 << (title->bit_index % 32);
  11133.  
  11134. if (lost)
  11135. {
  11136. if (!HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag))
  11137. return;
  11138.  
  11139. RemoveFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
  11140. }
  11141. else
  11142. {
  11143. if (HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag))
  11144. return;
  11145.  
  11146. SetFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
  11147. }
  11148.  
  11149. WorldPacket data(SMSG_TITLE_EARNED, 4 + 4);
  11150. data << uint32(title->bit_index);
  11151. data << uint32(lost ? 0 : 1); // 1 - earned, 0 - lost
  11152. GetSession()->SendPacket(&data);
  11153. }
  11154.  
  11155. bool Player::isTotalImmunity()
  11156. {
  11157. AuraEffectList const& immune = GetAuraEffectsByType(SPELL_AURA_SCHOOL_IMMUNITY);
  11158.  
  11159. for (AuraEffectList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr)
  11160. {
  11161. if (((*itr)->GetMiscValue() & SPELL_SCHOOL_MASK_ALL) !=0) // total immunity
  11162. {
  11163. return true;
  11164. }
  11165. if (((*itr)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) !=0) // physical damage immunity
  11166. {
  11167. for (AuraEffectList::const_iterator i = immune.begin(); i != immune.end(); ++i)
  11168. {
  11169. if (((*i)->GetMiscValue() & SPELL_SCHOOL_MASK_MAGIC) !=0) // magic immunity
  11170. {
  11171. return true;
  11172. }
  11173. }
  11174. }
  11175. }
  11176. return false;
  11177. }
  11178.  
  11179. void Player::UpdateCharmedAI()
  11180. {
  11181. //This should only called in Player::Update
  11182. Creature* charmer = GetCharmer()->ToCreature();
  11183.  
  11184. //kill self if charm aura has infinite duration
  11185. if (charmer->IsInEvadeMode())
  11186. {
  11187. AuraEffectList const& auras = GetAuraEffectsByType(SPELL_AURA_MOD_CHARM);
  11188. for (AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
  11189. if ((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetBase()->IsPermanent())
  11190. {
  11191. charmer->DealDamage(this, GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
  11192. return;
  11193. }
  11194. }
  11195.  
  11196. if (!charmer->isInCombat())
  11197. GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
  11198.  
  11199. Unit* target = getVictim();
  11200. if (!target || !charmer->IsValidAttackTarget(target))
  11201. {
  11202. target = charmer->SelectNearestTarget();
  11203. if (!target)
  11204. return;
  11205.  
  11206. GetMotionMaster()->MoveChase(target);
  11207. Attack(target, true);
  11208. }
  11209. }
  11210.  
  11211. uint32 Player::GetRuneBaseCooldown(uint8 index)
  11212. {
  11213. uint8 rune = GetBaseRune(index);
  11214. uint32 cooldown = RUNE_BASE_COOLDOWN;
  11215.  
  11216. AuraEffectList const& regenAura = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
  11217. for (AuraEffectList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i)
  11218. {
  11219. if ((*i)->GetMiscValue() == POWER_RUNE && (*i)->GetMiscValueB() == rune)
  11220. cooldown = cooldown*(100-(*i)->GetAmount())/100;
  11221. }
  11222.  
  11223. return cooldown;
  11224. }
  11225.  
  11226. void Player::RemoveRunesByAuraEffect(AuraEffect const* aura)
  11227. {
  11228. for (uint8 i = 0; i < MAX_RUNES; ++i)
  11229. {
  11230. if (m_runes->runes[i].ConvertAura == aura)
  11231. {
  11232. ConvertRune(i, GetBaseRune(i));
  11233. SetRuneConvertAura(i, NULL);
  11234. }
  11235. }
  11236. }
  11237.  
  11238. void Player::RestoreBaseRune(uint8 index)
  11239. {
  11240. AuraEffect const* aura = m_runes->runes[index].ConvertAura;
  11241. // If rune was converted by a non-pasive aura that still active we should keep it converted
  11242. if (aura && !(aura->GetSpellInfo()->Attributes & SPELL_ATTR0_PASSIVE))
  11243. return;
  11244. ConvertRune(index, GetBaseRune(index));
  11245. SetRuneConvertAura(index, NULL);
  11246. // Don't drop passive talents providing rune convertion
  11247. if (!aura || aura->GetAuraType() != SPELL_AURA_CONVERT_RUNE)
  11248. return;
  11249. for (uint8 i = 0; i < MAX_RUNES; ++i)
  11250. {
  11251. if (aura == m_runes->runes[i].ConvertAura)
  11252. return;
  11253. }
  11254. aura->GetBase()->Remove();
  11255. }
  11256.  
  11257. void Player::ConvertRune(uint8 index, RuneType newType)
  11258. {
  11259. SetCurrentRune(index, newType);
  11260.  
  11261. WorldPacket data(SMSG_CONVERT_RUNE, 2);
  11262. data << uint8(index);
  11263. data << uint8(newType);
  11264. GetSession()->SendPacket(&data);
  11265. }
  11266.  
  11267. void Player::ResyncRunes(uint8 count)
  11268. {
  11269. WorldPacket data(SMSG_RESYNC_RUNES, 4 + count * 2);
  11270. data << uint32(count);
  11271. for (uint32 i = 0; i < count; ++i)
  11272. {
  11273. data << uint8(GetCurrentRune(i)); // rune type
  11274. data << uint8(255 - (GetRuneCooldown(i) * 51)); // passed cooldown time (0-255)
  11275. }
  11276. GetSession()->SendPacket(&data);
  11277. }
  11278.  
  11279. void Player::AddRunePower(uint8 index)
  11280. {
  11281. WorldPacket data(SMSG_ADD_RUNE_POWER, 4);
  11282. data << uint32(1 << index); // mask (0x00-0x3F probably)
  11283. GetSession()->SendPacket(&data);
  11284. }
  11285.  
  11286. static RuneType runeSlotTypes[MAX_RUNES] =
  11287. {
  11288. /*0*/ RUNE_BLOOD,
  11289. /*1*/ RUNE_BLOOD,
  11290. /*2*/ RUNE_UNHOLY,
  11291. /*3*/ RUNE_UNHOLY,
  11292. /*4*/ RUNE_FROST,
  11293. /*5*/ RUNE_FROST
  11294. };
  11295.  
  11296. void Player::InitRunes()
  11297. {
  11298. if (getClass() != CLASS_DEATH_KNIGHT)
  11299. return;
  11300.  
  11301. m_runes = new Runes;
  11302.  
  11303. m_runes->runeState = 0;
  11304. m_runes->lastUsedRune = RUNE_BLOOD;
  11305.  
  11306. for (uint8 i = 0; i < MAX_RUNES; ++i)
  11307. {
  11308. SetBaseRune(i, runeSlotTypes[i]); // init base types
  11309. SetCurrentRune(i, runeSlotTypes[i]); // init current types
  11310. SetRuneCooldown(i, 0); // reset cooldowns
  11311. SetRuneConvertAura(i, NULL);
  11312. m_runes->SetRuneState(i);
  11313. }
  11314.  
  11315. for (uint8 i = 0; i < NUM_RUNE_TYPES; ++i)
  11316. SetFloatValue(PLAYER_RUNE_REGEN_1 + i, 0.1f);
  11317. }
  11318.  
  11319. bool Player::IsBaseRuneSlotsOnCooldown(RuneType runeType) const
  11320. {
  11321. for (uint8 i = 0; i < MAX_RUNES; ++i)
  11322. if (GetBaseRune(i) == runeType && GetRuneCooldown(i) == 0)
  11323. return false;
  11324.  
  11325. return true;
  11326. }
  11327.  
  11328. void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast)
  11329. {
  11330. Loot loot;
  11331. loot.FillLoot (loot_id, store, this, true);
  11332.  
  11333. uint32 max_slot = loot.GetMaxSlotInLootFor(this);
  11334. for (uint32 i = 0; i < max_slot; ++i)
  11335. {
  11336. LootItem* lootItem = loot.LootItemInSlot(i, this);
  11337.  
  11338. ItemPosCountVec dest;
  11339. InventoryResult msg = CanStoreNewItem(bag, slot, dest, lootItem->itemid, lootItem->count);
  11340. if (msg != EQUIP_ERR_OK && slot != NULL_SLOT)
  11341. msg = CanStoreNewItem(bag, NULL_SLOT, dest, lootItem->itemid, lootItem->count);
  11342. if (msg != EQUIP_ERR_OK && bag != NULL_BAG)
  11343. msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, lootItem->itemid, lootItem->count);
  11344. if (msg != EQUIP_ERR_OK)
  11345. {
  11346. SendEquipError(msg, NULL, NULL, lootItem->itemid);
  11347. continue;
  11348. }
  11349.  
  11350. Item* pItem = StoreNewItem(dest, lootItem->itemid, true, lootItem->randomPropertyId);
  11351. SendNewItem(pItem, lootItem->count, false, false, broadcast);
  11352. }
  11353. }
  11354.  
  11355. void Player::StoreLootItem(uint8 lootSlot, Loot* loot)
  11356. {
  11357. QuestItem* qitem = NULL;
  11358. QuestItem* ffaitem = NULL;
  11359. QuestItem* conditem = NULL;
  11360.  
  11361. LootItem* item = loot->LootItemInSlot(lootSlot, this, &qitem, &ffaitem, &conditem);
  11362.  
  11363. if (!item)
  11364. {
  11365. SendEquipError(EQUIP_ERR_ALREADY_LOOTED, NULL, NULL);
  11366. return;
  11367. }
  11368.  
  11369. // questitems use the blocked field for other purposes
  11370. if (!qitem && item->is_blocked)
  11371. {
  11372. SendLootRelease(GetLootGUID());
  11373. return;
  11374. }
  11375.  
  11376. ItemPosCountVec dest;
  11377. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item->itemid, item->count);
  11378. if (msg == EQUIP_ERR_OK)
  11379. {
  11380. AllowedLooterSet looters = item->GetAllowedLooters();
  11381. Item* newitem = StoreNewItem(dest, item->itemid, true, item->randomPropertyId, looters);
  11382.  
  11383. if (qitem)
  11384. {
  11385. qitem->is_looted = true;
  11386. //freeforall is 1 if everyone's supposed to get the quest item.
  11387. if (item->freeforall || loot->GetPlayerQuestItems().size() == 1)
  11388. SendNotifyLootItemRemoved(lootSlot);
  11389. else
  11390. loot->NotifyQuestItemRemoved(qitem->index);
  11391. }
  11392. else
  11393. {
  11394. if (ffaitem)
  11395. {
  11396. //freeforall case, notify only one player of the removal
  11397. ffaitem->is_looted = true;
  11398. SendNotifyLootItemRemoved(lootSlot);
  11399. }
  11400. else
  11401. {
  11402. //not freeforall, notify everyone
  11403. if (conditem)
  11404. conditem->is_looted = true;
  11405. loot->NotifyItemRemoved(lootSlot);
  11406. }
  11407. }
  11408.  
  11409. //if only one person is supposed to loot the item, then set it to looted
  11410. if (!item->freeforall)
  11411. item->is_looted = true;
  11412.  
  11413. --loot->unlootedCount;
  11414.  
  11415. SendNewItem(newitem, uint32(item->count), false, false, true);
  11416. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count);
  11417. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item->count);
  11418. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count);
  11419. }
  11420. else
  11421. SendEquipError(msg, NULL, NULL, item->itemid);
  11422. }
  11423.  
  11424. uint32 Player::CalculateTalentsPoints() const
  11425. {
  11426. uint32 base_talent = getLevel() < 10 ? 0 : getLevel()-9;
  11427.  
  11428. if (getClass() != CLASS_DEATH_KNIGHT || GetMapId() != 609)
  11429. return uint32(base_talent * sWorld->getRate(RATE_TALENT));
  11430.  
  11431. uint32 talentPointsForLevel = getLevel() < 56 ? 0 : getLevel() - 55;
  11432. talentPointsForLevel += m_questRewardTalentCount;
  11433.  
  11434. if (talentPointsForLevel > base_talent)
  11435. talentPointsForLevel = base_talent;
  11436.  
  11437. return uint32(talentPointsForLevel * sWorld->getRate(RATE_TALENT));
  11438. }
  11439.  
  11440. bool Player::IsKnowHowFlyIn(uint32 mapid, uint32 zone) const
  11441. {
  11442. // continent checked in SpellInfo::CheckLocation at cast and area update
  11443. uint32 v_map = GetVirtualMapForMapAndZone(mapid, zone);
  11444. return v_map != 571 || HasSpell(54197); // Cold Weather Flying
  11445. }
  11446.  
  11447. void Player::learnSpellHighRank(uint32 spellid)
  11448. {
  11449. learnSpell(spellid, false);
  11450.  
  11451. if (uint32 next = sSpellMgr->GetNextSpellInChain(spellid))
  11452. learnSpellHighRank(next);
  11453. }
  11454.  
  11455. void Player::_LoadSkills(PreparedQueryResult result)
  11456. {
  11457. // 0 1 2
  11458. // SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid));
  11459.  
  11460. uint32 count = 0;
  11461. if (result)
  11462. {
  11463. do
  11464. {
  11465. Field* fields = result->Fetch();
  11466. uint16 skill = fields[0].GetUInt16();
  11467. uint16 value = fields[1].GetUInt16();
  11468. uint16 max = fields[2].GetUInt16();
  11469.  
  11470. SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
  11471. if (!pSkill)
  11472. {
  11473. sLog->outError(LOG_FILTER_PLAYER, "Character %u has skill %u that does not exist.", GetGUIDLow(), skill);
  11474. continue;
  11475. }
  11476.  
  11477. // set fixed skill ranges
  11478. switch (GetSkillRangeType(pSkill, false))
  11479. {
  11480. case SKILL_RANGE_LANGUAGE: // 300..300
  11481. value = max = 300;
  11482. break;
  11483. case SKILL_RANGE_MONO: // 1..1, grey monolite bar
  11484. value = max = 1;
  11485. break;
  11486. default:
  11487. break;
  11488. }
  11489. if (value == 0)
  11490. {
  11491. sLog->outError(LOG_FILTER_PLAYER, "Character %u has skill %u with value 0. Will be deleted.", GetGUIDLow(), skill);
  11492.  
  11493. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SKILL);
  11494.  
  11495. stmt->setUInt32(0, GetGUIDLow());
  11496. stmt->setUInt16(1, skill);
  11497.  
  11498. CharacterDatabase.Execute(stmt);
  11499.  
  11500. continue;
  11501. }
  11502.  
  11503. // enable unlearn button for primary professions only
  11504. if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
  11505. SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 1));
  11506. else
  11507. SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 0));
  11508.  
  11509. SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), MAKE_SKILL_VALUE(value, max));
  11510. SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0);
  11511.  
  11512. mSkillStatus.insert(SkillStatusMap::value_type(skill, SkillStatusData(count, SKILL_UNCHANGED)));
  11513.  
  11514. learnSkillRewardedSpells(skill, value);
  11515.  
  11516. ++count;
  11517.  
  11518. if (count >= PLAYER_MAX_SKILLS) // client limit
  11519. {
  11520. sLog->outError(LOG_FILTER_PLAYER, "Character %u has more than %u skills.", GetGUIDLow(), PLAYER_MAX_SKILLS);
  11521. break;
  11522. }
  11523. }
  11524. while (result->NextRow());
  11525. }
  11526.  
  11527. for (; count < PLAYER_MAX_SKILLS; ++count)
  11528. {
  11529. SetUInt32Value(PLAYER_SKILL_INDEX(count), 0);
  11530. SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), 0);
  11531. SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0);
  11532. }
  11533.  
  11534. // special settings
  11535. if (getClass() == CLASS_DEATH_KNIGHT)
  11536. {
  11537. uint8 base_level = std::min(getLevel(), uint8(sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)));
  11538. if (base_level < 1)
  11539. base_level = 1;
  11540. uint16 base_skill = (base_level-1)*5; // 270 at starting level 55
  11541. if (base_skill < 1)
  11542. base_skill = 1; // skill mast be known and then > 0 in any case
  11543.  
  11544. if (GetPureSkillValue(SKILL_FIRST_AID) < base_skill)
  11545. SetSkill(SKILL_FIRST_AID, 0, base_skill, base_skill);
  11546. if (GetPureSkillValue(SKILL_AXES) < base_skill)
  11547. SetSkill(SKILL_AXES, 0, base_skill, base_skill);
  11548. if (GetPureSkillValue(SKILL_DEFENSE) < base_skill)
  11549. SetSkill(SKILL_DEFENSE, 0, base_skill, base_skill);
  11550. if (GetPureSkillValue(SKILL_POLEARMS) < base_skill)
  11551. SetSkill(SKILL_POLEARMS, 0, base_skill, base_skill);
  11552. if (GetPureSkillValue(SKILL_SWORDS) < base_skill)
  11553. SetSkill(SKILL_SWORDS, 0, base_skill, base_skill);
  11554. if (GetPureSkillValue(SKILL_2H_AXES) < base_skill)
  11555. SetSkill(SKILL_2H_AXES, 0, base_skill, base_skill);
  11556. if (GetPureSkillValue(SKILL_2H_SWORDS) < base_skill)
  11557. SetSkill(SKILL_2H_SWORDS, 0, base_skill, base_skill);
  11558. if (GetPureSkillValue(SKILL_UNARMED) < base_skill)
  11559. SetSkill(SKILL_UNARMED, 0, base_skill, base_skill);
  11560. }
  11561. }
  11562.  
  11563. uint32 Player::GetPhaseMaskForSpawn() const
  11564. {
  11565. uint32 phase = PHASEMASK_NORMAL;
  11566. if (!isGameMaster())
  11567. phase = GetPhaseMask();
  11568. else
  11569. {
  11570. AuraEffectList const& phases = GetAuraEffectsByType(SPELL_AURA_PHASE);
  11571. if (!phases.empty())
  11572. phase = phases.front()->GetMiscValue();
  11573. }
  11574.  
  11575. // some aura phases include 1 normal map in addition to phase itself
  11576. if (uint32 n_phase = phase & ~PHASEMASK_NORMAL)
  11577. return n_phase;
  11578.  
  11579. return PHASEMASK_NORMAL;
  11580. }
  11581.  
  11582. InventoryResult Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) const
  11583. {
  11584. ItemTemplate const* pProto = pItem->GetTemplate();
  11585.  
  11586. // proto based limitations
  11587. if (InventoryResult res = CanEquipUniqueItem(pProto, eslot, limit_count))
  11588. return res;
  11589.  
  11590. // check unique-equipped on gems
  11591. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
  11592. {
  11593. uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
  11594. if (!enchant_id)
  11595. continue;
  11596. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  11597. if (!enchantEntry)
  11598. continue;
  11599.  
  11600. ItemTemplate const* pGem = sObjectMgr->GetItemTemplate(enchantEntry->GemID);
  11601. if (!pGem)
  11602. continue;
  11603.  
  11604. // include for check equip another gems with same limit category for not equipped item (and then not counted)
  11605. uint32 gem_limit_count = !pItem->IsEquipped() && pGem->ItemLimitCategory
  11606. ? pItem->GetGemCountWithLimitCategory(pGem->ItemLimitCategory) : 1;
  11607.  
  11608. if (InventoryResult res = CanEquipUniqueItem(pGem, eslot, gem_limit_count))
  11609. return res;
  11610. }
  11611.  
  11612. return EQUIP_ERR_OK;
  11613. }
  11614.  
  11615. InventoryResult Player::CanEquipUniqueItem(ItemTemplate const* itemProto, uint8 except_slot, uint32 limit_count) const
  11616. {
  11617. // check unique-equipped on item
  11618. if (itemProto->Flags & ITEM_PROTO_FLAG_UNIQUE_EQUIPPED)
  11619. {
  11620. // there is an equip limit on this item
  11621. if (HasItemOrGemWithIdEquipped(itemProto->ItemId, 1, except_slot))
  11622. return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
  11623. }
  11624.  
  11625. // check unique-equipped limit
  11626. if (itemProto->ItemLimitCategory)
  11627. {
  11628. ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(itemProto->ItemLimitCategory);
  11629. if (!limitEntry)
  11630. return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
  11631.  
  11632. // NOTE: limitEntry->mode not checked because if item have have-limit then it applied and to equip case
  11633.  
  11634. if (limit_count > limitEntry->maxCount)
  11635. return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED;
  11636.  
  11637. // there is an equip limit on this item
  11638. if (HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory, limitEntry->maxCount - limit_count + 1, except_slot))
  11639. return EQUIP_ERR_ITEM_MAX_COUNT_EQUIPPED_SOCKETED;
  11640. }
  11641.  
  11642. return EQUIP_ERR_OK;
  11643. }
  11644.  
  11645. void Player::HandleFall(MovementInfo const& movementInfo)
  11646. {
  11647. // calculate total z distance of the fall
  11648. float z_diff = m_lastFallZ - movementInfo.pos.GetPositionZ();
  11649. //sLog->outDebug("zDiff = %f", z_diff);
  11650.  
  11651. //Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored
  11652. // 14.57 can be calculated by resolving damageperc formula below to 0
  11653. if (z_diff >= 14.57f && !isDead() && !isGameMaster() &&
  11654. !HasAuraType(SPELL_AURA_HOVER) && !HasAuraType(SPELL_AURA_FEATHER_FALL) &&
  11655. !HasAuraType(SPELL_AURA_FLY) && !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL))
  11656. {
  11657. //Safe fall, fall height reduction
  11658. int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL);
  11659.  
  11660. float damageperc = 0.018f*(z_diff-safe_fall)-0.2426f;
  11661.  
  11662. if (damageperc > 0)
  11663. {
  11664. uint32 damage = (uint32)(damageperc * GetMaxHealth()*sWorld->getRate(RATE_DAMAGE_FALL));
  11665.  
  11666. float height = movementInfo.pos.m_positionZ;
  11667. UpdateGroundPositionZ(movementInfo.pos.m_positionX, movementInfo.pos.m_positionY, height);
  11668.  
  11669. if (damage > 0)
  11670. {
  11671. //Prevent fall damage from being more than the player maximum health
  11672. if (damage > GetMaxHealth())
  11673. damage = GetMaxHealth();
  11674.  
  11675. // Gust of Wind
  11676. if (HasAura(43621))
  11677. damage = GetMaxHealth()/2;
  11678.  
  11679. uint32 original_health = GetHealth();
  11680. uint32 final_damage = EnvironmentalDamage(DAMAGE_FALL, damage);
  11681.  
  11682. // recheck alive, might have died of EnvironmentalDamage, avoid cases when player die in fact like Spirit of Redemption case
  11683. if (isAlive() && final_damage < original_health)
  11684. GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING, uint32(z_diff*100));
  11685. }
  11686.  
  11687. //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction
  11688. sLog->outDebug(LOG_FILTER_PLAYER, "FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d", movementInfo.pos.GetPositionZ(), height, GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall);
  11689. }
  11690. }
  11691. RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LANDING); // No fly zone - Parachute
  11692. }
  11693.  
  11694. void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 /*= 0*/, uint32 miscValue2 /*= 0*/, Unit* unit /*= NULL*/)
  11695. {
  11696. GetAchievementMgr().UpdateAchievementCriteria(type, miscValue1, miscValue2, unit);
  11697. }
  11698.  
  11699. void Player::CompletedAchievement(AchievementEntry const* entry)
  11700. {
  11701. GetAchievementMgr().CompletedAchievement(entry);
  11702. }
  11703.  
  11704. void Player::LearnTalent(uint32 talentId, uint32 talentRank)
  11705. {
  11706. uint32 CurTalentPoints = GetFreeTalentPoints();
  11707.  
  11708. if (CurTalentPoints == 0)
  11709. return;
  11710.  
  11711. if (talentRank >= MAX_TALENT_RANK)
  11712. return;
  11713.  
  11714. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  11715.  
  11716. if (!talentInfo)
  11717. return;
  11718.  
  11719. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
  11720.  
  11721. if (!talentTabInfo)
  11722. return;
  11723.  
  11724. // prevent learn talent for different class (cheating)
  11725. if ((getClassMask() & talentTabInfo->ClassMask) == 0)
  11726. return;
  11727.  
  11728. // find current max talent rank (0~5)
  11729. uint8 curtalent_maxrank = 0; // 0 = not learned any rank
  11730. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  11731. {
  11732. if (talentInfo->RankID[rank] && HasSpell(talentInfo->RankID[rank]))
  11733. {
  11734. curtalent_maxrank = (rank + 1);
  11735. break;
  11736. }
  11737. }
  11738.  
  11739. // we already have same or higher talent rank learned
  11740. if (curtalent_maxrank >= (talentRank + 1))
  11741. return;
  11742.  
  11743. // check if we have enough talent points
  11744. if (CurTalentPoints < (talentRank - curtalent_maxrank + 1))
  11745. return;
  11746.  
  11747. // Check if it requires another talent
  11748. if (talentInfo->DependsOn > 0)
  11749. {
  11750. if (TalentEntry const* depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
  11751. {
  11752. bool hasEnoughRank = false;
  11753. for (uint8 rank = talentInfo->DependsOnRank; rank < MAX_TALENT_RANK; rank++)
  11754. {
  11755. if (depTalentInfo->RankID[rank] != 0)
  11756. if (HasSpell(depTalentInfo->RankID[rank]))
  11757. hasEnoughRank = true;
  11758. }
  11759. if (!hasEnoughRank)
  11760. return;
  11761. }
  11762. }
  11763.  
  11764. // Find out how many points we have in this field
  11765. uint32 spentPoints = 0;
  11766.  
  11767. uint32 tTab = talentInfo->TalentTab;
  11768. if (talentInfo->Row > 0)
  11769. {
  11770. uint32 numRows = sTalentStore.GetNumRows();
  11771. for (uint32 i = 0; i < numRows; i++) // Loop through all talents.
  11772. {
  11773. // Someday, someone needs to revamp
  11774. const TalentEntry* tmpTalent = sTalentStore.LookupEntry(i);
  11775. if (tmpTalent) // the way talents are tracked
  11776. {
  11777. if (tmpTalent->TalentTab == tTab)
  11778. {
  11779. for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++)
  11780. {
  11781. if (tmpTalent->RankID[rank] != 0)
  11782. {
  11783. if (HasSpell(tmpTalent->RankID[rank]))
  11784. {
  11785. spentPoints += (rank + 1);
  11786. }
  11787. }
  11788. }
  11789. }
  11790. }
  11791. }
  11792. }
  11793.  
  11794. // not have required min points spent in talent tree
  11795. if (spentPoints < (talentInfo->Row * MAX_TALENT_RANK))
  11796. return;
  11797.  
  11798. // spell not set in talent.dbc
  11799. uint32 spellid = talentInfo->RankID[talentRank];
  11800. if (spellid == 0)
  11801. {
  11802. sLog->outError(LOG_FILTER_PLAYER, "Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
  11803. return;
  11804. }
  11805.  
  11806. // already known
  11807. if (HasSpell(spellid))
  11808. return;
  11809.  
  11810. // learn! (other talent ranks will unlearned at learning)
  11811. learnSpell(spellid, false);
  11812. AddTalent(spellid, m_activeSpec, true);
  11813.  
  11814. sLog->outInfo(LOG_FILTER_PLAYER, "TalentID: %u Rank: %u Spell: %u Spec: %u\n", talentId, talentRank, spellid, m_activeSpec);
  11815.  
  11816. // update free talent points
  11817. SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
  11818. }
  11819.  
  11820. void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
  11821. {
  11822. Pet* pet = GetPet();
  11823.  
  11824. if (!pet)
  11825. return;
  11826.  
  11827. if (petGuid != pet->GetGUID())
  11828. return;
  11829.  
  11830. uint32 CurTalentPoints = pet->GetFreeTalentPoints();
  11831.  
  11832. if (CurTalentPoints == 0)
  11833. return;
  11834.  
  11835. if (talentRank >= MAX_PET_TALENT_RANK)
  11836. return;
  11837.  
  11838. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  11839.  
  11840. if (!talentInfo)
  11841. return;
  11842.  
  11843. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
  11844.  
  11845. if (!talentTabInfo)
  11846. return;
  11847.  
  11848. CreatureTemplate const* ci = pet->GetCreatureTemplate();
  11849.  
  11850. if (!ci)
  11851. return;
  11852.  
  11853. CreatureFamilyEntry const* pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
  11854.  
  11855. if (!pet_family)
  11856. return;
  11857.  
  11858. if (pet_family->petTalentType < 0) // not hunter pet
  11859. return;
  11860.  
  11861. // prevent learn talent for different family (cheating)
  11862. if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
  11863. return;
  11864.  
  11865. // find current max talent rank (0~5)
  11866. uint8 curtalent_maxrank = 0; // 0 = not learned any rank
  11867. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  11868. {
  11869. if (talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank]))
  11870. {
  11871. curtalent_maxrank = (rank + 1);
  11872. break;
  11873. }
  11874. }
  11875.  
  11876. // we already have same or higher talent rank learned
  11877. if (curtalent_maxrank >= (talentRank + 1))
  11878. return;
  11879.  
  11880. // check if we have enough talent points
  11881. if (CurTalentPoints < (talentRank - curtalent_maxrank + 1))
  11882. return;
  11883.  
  11884. // Check if it requires another talent
  11885. if (talentInfo->DependsOn > 0)
  11886. {
  11887. if (TalentEntry const* depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
  11888. {
  11889. bool hasEnoughRank = false;
  11890. for (uint8 rank = talentInfo->DependsOnRank; rank < MAX_TALENT_RANK; rank++)
  11891. {
  11892. if (depTalentInfo->RankID[rank] != 0)
  11893. if (pet->HasSpell(depTalentInfo->RankID[rank]))
  11894. hasEnoughRank = true;
  11895. }
  11896. if (!hasEnoughRank)
  11897. return;
  11898. }
  11899. }
  11900.  
  11901. // Find out how many points we have in this field
  11902. uint32 spentPoints = 0;
  11903.  
  11904. uint32 tTab = talentInfo->TalentTab;
  11905. if (talentInfo->Row > 0)
  11906. {
  11907. uint32 numRows = sTalentStore.GetNumRows();
  11908. for (uint32 i = 0; i < numRows; ++i) // Loop through all talents.
  11909. {
  11910. // Someday, someone needs to revamp
  11911. const TalentEntry* tmpTalent = sTalentStore.LookupEntry(i);
  11912. if (tmpTalent) // the way talents are tracked
  11913. {
  11914. if (tmpTalent->TalentTab == tTab)
  11915. {
  11916. for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++)
  11917. {
  11918. if (tmpTalent->RankID[rank] != 0)
  11919. {
  11920. if (pet->HasSpell(tmpTalent->RankID[rank]))
  11921. {
  11922. spentPoints += (rank + 1);
  11923. }
  11924. }
  11925. }
  11926. }
  11927. }
  11928. }
  11929. }
  11930.  
  11931. // not have required min points spent in talent tree
  11932. if (spentPoints < (talentInfo->Row * MAX_PET_TALENT_RANK))
  11933. return;
  11934.  
  11935. // spell not set in talent.dbc
  11936. uint32 spellid = talentInfo->RankID[talentRank];
  11937. if (spellid == 0)
  11938. {
  11939. sLog->outError(LOG_FILTER_PLAYER, "Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
  11940. return;
  11941. }
  11942.  
  11943. // already known
  11944. if (pet->HasSpell(spellid))
  11945. return;
  11946.  
  11947. // learn! (other talent ranks will unlearned at learning)
  11948. pet->learnSpell(spellid);
  11949. sLog->outInfo(LOG_FILTER_PLAYER, "PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
  11950.  
  11951. // update free talent points
  11952. pet->SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
  11953. }
  11954.  
  11955. void Player::AddKnownCurrency(uint32 itemId)
  11956. {
  11957. if (CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId))
  11958. SetFlag64(PLAYER_FIELD_KNOWN_CURRENCIES, (1LL << (ctEntry->BitIndex-1)));
  11959. }
  11960.  
  11961. void Player::UpdateFallInformationIfNeed(MovementInfo const& minfo, uint16 opcode)
  11962. {
  11963. if (m_lastFallTime >= minfo.fallTime || m_lastFallZ <= minfo.pos.GetPositionZ() || opcode == MSG_MOVE_FALL_LAND)
  11964. SetFallInformation(minfo.fallTime, minfo.pos.GetPositionZ());
  11965. }
  11966.  
  11967. void Player::UnsummonPetTemporaryIfAny()
  11968. {
  11969. Pet* pet = GetPet();
  11970. if (!pet)
  11971. return;
  11972.  
  11973. if (!m_temporaryUnsummonedPetNumber && pet->isControlled() && !pet->isTemporarySummoned())
  11974. {
  11975. m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
  11976. m_oldpetspell = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL);
  11977. }
  11978.  
  11979. RemovePet(pet, PET_SAVE_AS_CURRENT);
  11980. }
  11981.  
  11982. void Player::ResummonPetTemporaryUnSummonedIfAny()
  11983. {
  11984. if (!m_temporaryUnsummonedPetNumber)
  11985. return;
  11986.  
  11987. // not resummon in not appropriate state
  11988. if (IsPetNeedBeTemporaryUnsummoned())
  11989. return;
  11990.  
  11991. if (GetPetGUID())
  11992. return;
  11993.  
  11994. Pet* NewPet = new Pet(this);
  11995. if (!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
  11996. delete NewPet;
  11997.  
  11998. m_temporaryUnsummonedPetNumber = 0;
  11999. }
  12000.  
  12001. bool Player::canSeeSpellClickOn(Creature const* c) const
  12002. {
  12003. if (!c->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK))
  12004. return false;
  12005.  
  12006. SpellClickInfoMapBounds clickPair = sObjectMgr->GetSpellClickInfoMapBounds(c->GetEntry());
  12007. if (clickPair.first == clickPair.second)
  12008. return true;
  12009.  
  12010. for (SpellClickInfoContainer::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr)
  12011. {
  12012. if (!itr->second.IsFitToRequirements(this, c))
  12013. return false;
  12014.  
  12015. ConditionList conds = sConditionMgr->GetConditionsForSpellClickEvent(c->GetEntry(), itr->second.spellId);
  12016. ConditionSourceInfo info = ConditionSourceInfo(const_cast<Player*>(this), const_cast<Creature*>(c));
  12017. if (!sConditionMgr->IsObjectMeetToConditions(info, conds))
  12018. return false;
  12019. }
  12020.  
  12021. return true;
  12022. }
  12023.  
  12024. void Player::BuildPlayerTalentsInfoData(WorldPacket* data)
  12025. {
  12026. *data << uint32(GetFreeTalentPoints()); // unspentTalentPoints
  12027. *data << uint8(m_specsCount); // talent group count (0, 1 or 2)
  12028. *data << uint8(m_activeSpec); // talent group index (0 or 1)
  12029.  
  12030. if (m_specsCount)
  12031. {
  12032. if (m_specsCount > MAX_TALENT_SPECS)
  12033. m_specsCount = MAX_TALENT_SPECS;
  12034.  
  12035. // loop through all specs (only 1 for now)
  12036. for (uint32 specIdx = 0; specIdx < m_specsCount; ++specIdx)
  12037. {
  12038. uint8 talentIdCount = 0;
  12039. size_t pos = data->wpos();
  12040. *data << uint8(talentIdCount); // [PH], talentIdCount
  12041.  
  12042. // find class talent tabs (all players have 3 talent tabs)
  12043. uint32 const* talentTabIds = GetTalentTabPages(getClass());
  12044.  
  12045. for (uint8 i = 0; i < MAX_TALENT_TABS; ++i)
  12046. {
  12047. uint32 talentTabId = talentTabIds[i];
  12048.  
  12049. for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
  12050. {
  12051. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  12052. if (!talentInfo)
  12053. continue;
  12054.  
  12055. // skip another tab talents
  12056. if (talentInfo->TalentTab != talentTabId)
  12057. continue;
  12058.  
  12059. // find max talent rank (0~4)
  12060. int8 curtalent_maxrank = -1;
  12061. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  12062. {
  12063. if (talentInfo->RankID[rank] && HasTalent(talentInfo->RankID[rank], specIdx))
  12064. {
  12065. curtalent_maxrank = rank;
  12066. break;
  12067. }
  12068. }
  12069.  
  12070. // not learned talent
  12071. if (curtalent_maxrank < 0)
  12072. continue;
  12073.  
  12074. *data << uint32(talentInfo->TalentID); // Talent.dbc
  12075. *data << uint8(curtalent_maxrank); // talentMaxRank (0-4)
  12076.  
  12077. ++talentIdCount;
  12078. }
  12079. }
  12080.  
  12081. data->put<uint8>(pos, talentIdCount); // put real count
  12082.  
  12083. *data << uint8(MAX_GLYPH_SLOT_INDEX); // glyphs count
  12084.  
  12085. for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
  12086. *data << uint16(m_Glyphs[specIdx][i]); // GlyphProperties.dbc
  12087. }
  12088. }
  12089. }
  12090.  
  12091. void Player::BuildPetTalentsInfoData(WorldPacket* data)
  12092. {
  12093. uint32 unspentTalentPoints = 0;
  12094. size_t pointsPos = data->wpos();
  12095. *data << uint32(unspentTalentPoints); // [PH], unspentTalentPoints
  12096.  
  12097. uint8 talentIdCount = 0;
  12098. size_t countPos = data->wpos();
  12099. *data << uint8(talentIdCount); // [PH], talentIdCount
  12100.  
  12101. Pet* pet = GetPet();
  12102. if (!pet)
  12103. return;
  12104.  
  12105. unspentTalentPoints = pet->GetFreeTalentPoints();
  12106.  
  12107. data->put<uint32>(pointsPos, unspentTalentPoints); // put real points
  12108.  
  12109. CreatureTemplate const* ci = pet->GetCreatureTemplate();
  12110. if (!ci)
  12111. return;
  12112.  
  12113. CreatureFamilyEntry const* pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
  12114. if (!pet_family || pet_family->petTalentType < 0)
  12115. return;
  12116.  
  12117. for (uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId)
  12118. {
  12119. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentTabId);
  12120. if (!talentTabInfo)
  12121. continue;
  12122.  
  12123. if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
  12124. continue;
  12125.  
  12126. for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
  12127. {
  12128. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  12129. if (!talentInfo)
  12130. continue;
  12131.  
  12132. // skip another tab talents
  12133. if (talentInfo->TalentTab != talentTabId)
  12134. continue;
  12135.  
  12136. // find max talent rank (0~4)
  12137. int8 curtalent_maxrank = -1;
  12138. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  12139. {
  12140. if (talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank]))
  12141. {
  12142. curtalent_maxrank = rank;
  12143. break;
  12144. }
  12145. }
  12146.  
  12147. // not learned talent
  12148. if (curtalent_maxrank < 0)
  12149. continue;
  12150.  
  12151. *data << uint32(talentInfo->TalentID); // Talent.dbc
  12152. *data << uint8(curtalent_maxrank); // talentMaxRank (0-4)
  12153.  
  12154. ++talentIdCount;
  12155. }
  12156.  
  12157. data->put<uint8>(countPos, talentIdCount); // put real count
  12158.  
  12159. break;
  12160. }
  12161. }
  12162.  
  12163. void Player::SendTalentsInfoData(bool pet)
  12164. {
  12165. WorldPacket data(SMSG_TALENTS_INFO, 50);
  12166. data << uint8(pet ? 1 : 0);
  12167. if (pet)
  12168. BuildPetTalentsInfoData(&data);
  12169. else
  12170. BuildPlayerTalentsInfoData(&data);
  12171. GetSession()->SendPacket(&data);
  12172. }
  12173.  
  12174. void Player::BuildEnchantmentsInfoData(WorldPacket* data)
  12175. {
  12176. uint32 slotUsedMask = 0;
  12177. size_t slotUsedMaskPos = data->wpos();
  12178. *data << uint32(slotUsedMask); // slotUsedMask < 0x80000
  12179.  
  12180. for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
  12181. {
  12182. Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
  12183.  
  12184. if (!item)
  12185. continue;
  12186.  
  12187. slotUsedMask |= (1 << i);
  12188.  
  12189. *data << uint32(item->GetEntry()); // item entry
  12190.  
  12191. uint16 enchantmentMask = 0;
  12192. size_t enchantmentMaskPos = data->wpos();
  12193. *data << uint16(enchantmentMask); // enchantmentMask < 0x1000
  12194.  
  12195. for (uint32 j = 0; j < MAX_ENCHANTMENT_SLOT; ++j)
  12196. {
  12197. uint32 enchId = item->GetEnchantmentId(EnchantmentSlot(j));
  12198.  
  12199. if (!enchId)
  12200. continue;
  12201.  
  12202. enchantmentMask |= (1 << j);
  12203.  
  12204. *data << uint16(enchId); // enchantmentId?
  12205. }
  12206.  
  12207. data->put<uint16>(enchantmentMaskPos, enchantmentMask);
  12208.  
  12209. *data << uint16(0); // unknown
  12210. data->appendPackGUID(item->GetUInt64Value(ITEM_FIELD_CREATOR)); // item creator
  12211. *data << uint32(0); // seed?
  12212. }
  12213.  
  12214. data->put<uint32>(slotUsedMaskPos, slotUsedMask);
  12215. }
  12216.  
  12217. void Player::SendEquipmentSetList()
  12218. {
  12219. uint32 count = 0;
  12220. WorldPacket data(SMSG_EQUIPMENT_SET_LIST, 4);
  12221. size_t count_pos = data.wpos();
  12222. data << uint32(count); // count placeholder
  12223. for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
  12224. {
  12225. if (itr->second.state == EQUIPMENT_SET_DELETED)
  12226. continue;
  12227. data.appendPackGUID(itr->second.Guid);
  12228. data << uint32(itr->first);
  12229. data << itr->second.Name;
  12230. data << itr->second.IconName;
  12231. for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
  12232. {
  12233. // ignored slots stored in IgnoreMask, client wants "1" as raw GUID, so no HIGHGUID_ITEM
  12234. if (itr->second.IgnoreMask & (1 << i))
  12235. data.appendPackGUID(uint64(1));
  12236. else
  12237. data.appendPackGUID(MAKE_NEW_GUID(itr->second.Items[i], 0, HIGHGUID_ITEM));
  12238. }
  12239.  
  12240. ++count; // client have limit but it checked at loading and set
  12241. }
  12242. data.put<uint32>(count_pos, count);
  12243. GetSession()->SendPacket(&data);
  12244. }
  12245.  
  12246. void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset)
  12247. {
  12248. if (eqset.Guid != 0)
  12249. {
  12250. bool found = false;
  12251.  
  12252. for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
  12253. {
  12254. if ((itr->second.Guid == eqset.Guid) && (itr->first == index))
  12255. {
  12256. found = true;
  12257. break;
  12258. }
  12259. }
  12260.  
  12261. if (!found) // something wrong...
  12262. {
  12263. sLog->outError(LOG_FILTER_PLAYER, "Player %s tried to save equipment set "UI64FMTD" (index %u), but that equipment set not found!", GetName(), eqset.Guid, index);
  12264. return;
  12265. }
  12266. }
  12267.  
  12268. EquipmentSet& eqslot = m_EquipmentSets[index];
  12269.  
  12270. EquipmentSetUpdateState old_state = eqslot.state;
  12271.  
  12272. eqslot = eqset;
  12273.  
  12274. if (eqset.Guid == 0)
  12275. {
  12276. eqslot.Guid = sObjectMgr->GenerateEquipmentSetGuid();
  12277.  
  12278. WorldPacket data(SMSG_EQUIPMENT_SET_SAVED, 4 + 1);
  12279. data << uint32(index);
  12280. data.appendPackGUID(eqslot.Guid);
  12281. GetSession()->SendPacket(&data);
  12282. }
  12283.  
  12284. eqslot.state = old_state == EQUIPMENT_SET_NEW ? EQUIPMENT_SET_NEW : EQUIPMENT_SET_CHANGED;
  12285. }
  12286.  
  12287. void Player::_SaveEquipmentSets(SQLTransaction& trans)
  12288. {
  12289. for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end();)
  12290. {
  12291. uint32 index = itr->first;
  12292. EquipmentSet& eqset = itr->second;
  12293. PreparedStatement* stmt = NULL;
  12294. uint8 j = 0;
  12295. switch (eqset.state)
  12296. {
  12297. case EQUIPMENT_SET_UNCHANGED:
  12298. ++itr;
  12299. break; // nothing do
  12300. case EQUIPMENT_SET_CHANGED:
  12301. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_EQUIP_SET);
  12302. stmt->setString(j++, eqset.Name.c_str());
  12303. stmt->setString(j++, eqset.IconName.c_str());
  12304. stmt->setUInt32(j++, eqset.IgnoreMask);
  12305. for (uint8 i=0; i<EQUIPMENT_SLOT_END; ++i)
  12306. stmt->setUInt32(j++, eqset.Items[i]);
  12307. stmt->setUInt32(j++, GetGUIDLow());
  12308. stmt->setUInt64(j++, eqset.Guid);
  12309. stmt->setUInt32(j, index);
  12310. trans->Append(stmt);
  12311. eqset.state = EQUIPMENT_SET_UNCHANGED;
  12312. ++itr;
  12313. break;
  12314. case EQUIPMENT_SET_NEW:
  12315. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_EQUIP_SET);
  12316. stmt->setUInt32(j++, GetGUIDLow());
  12317. stmt->setUInt64(j++, eqset.Guid);
  12318. stmt->setUInt32(j++, index);
  12319. stmt->setString(j++, eqset.Name.c_str());
  12320. stmt->setString(j++, eqset.IconName.c_str());
  12321. stmt->setUInt32(j++, eqset.IgnoreMask);
  12322. for (uint8 i=0; i<EQUIPMENT_SLOT_END; ++i)
  12323. stmt->setUInt32(j++, eqset.Items[i]);
  12324. trans->Append(stmt);
  12325. eqset.state = EQUIPMENT_SET_UNCHANGED;
  12326. ++itr;
  12327. break;
  12328. case EQUIPMENT_SET_DELETED:
  12329. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EQUIP_SET);
  12330. stmt->setUInt64(0, eqset.Guid);
  12331. trans->Append(stmt);
  12332. m_EquipmentSets.erase(itr++);
  12333. break;
  12334. }
  12335. }
  12336. }
  12337.  
  12338. void Player::_SaveBGData(SQLTransaction& trans)
  12339. {
  12340. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_BGDATA);
  12341. stmt->setUInt32(0, GetGUIDLow());
  12342. trans->Append(stmt);
  12343. /* guid, bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */
  12344. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_BGDATA);
  12345. stmt->setUInt32(0, GetGUIDLow());
  12346. stmt->setUInt32(1, m_bgData.bgInstanceID);
  12347. stmt->setUInt16(2, m_bgData.bgTeam);
  12348. stmt->setFloat (3, m_bgData.joinPos.GetPositionX());
  12349. stmt->setFloat (4, m_bgData.joinPos.GetPositionY());
  12350. stmt->setFloat (5, m_bgData.joinPos.GetPositionZ());
  12351. stmt->setFloat (6, m_bgData.joinPos.GetOrientation());
  12352. stmt->setUInt16(7, m_bgData.joinPos.GetMapId());
  12353. stmt->setUInt16(8, m_bgData.taxiPath[0]);
  12354. stmt->setUInt16(9, m_bgData.taxiPath[1]);
  12355. stmt->setUInt16(10, m_bgData.mountSpell);
  12356. trans->Append(stmt);
  12357. }
  12358.  
  12359. void Player::DeleteEquipmentSet(uint64 setGuid)
  12360. {
  12361. for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
  12362. {
  12363. if (itr->second.Guid == setGuid)
  12364. {
  12365. if (itr->second.state == EQUIPMENT_SET_NEW)
  12366. m_EquipmentSets.erase(itr);
  12367. else
  12368. itr->second.state = EQUIPMENT_SET_DELETED;
  12369. break;
  12370. }
  12371. }
  12372. }
  12373.  
  12374. void Player::RemoveAtLoginFlag(AtLoginFlags flags, bool persist /*= false*/)
  12375. {
  12376. m_atLoginFlags &= ~flags;
  12377.  
  12378. if (persist)
  12379. {
  12380. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_REM_AT_LOGIN_FLAG);
  12381.  
  12382. stmt->setUInt16(0, uint16(flags));
  12383. stmt->setUInt32(1, GetGUIDLow());
  12384.  
  12385. CharacterDatabase.Execute(stmt);
  12386. }
  12387. }
  12388.  
  12389. void Player::SendClearCooldown(uint32 spell_id, Unit* target)
  12390. {
  12391. WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8);
  12392. data << uint32(spell_id);
  12393. data << uint64(target->GetGUID());
  12394. SendDirectMessage(&data);
  12395. }
  12396.  
  12397. void Player::ResetMap()
  12398. {
  12399. // this may be called during Map::Update
  12400. // after decrement+unlink, ++m_mapRefIter will continue correctly
  12401. // when the first element of the list is being removed
  12402. // nocheck_prev will return the padding element of the RefManager
  12403. // instead of NULL in the case of prev
  12404. GetMap()->UpdateIteratorBack(this);
  12405. Unit::ResetMap();
  12406. GetMapRef().unlink();
  12407. }
  12408.  
  12409. void Player::SetMap(Map* map)
  12410. {
  12411. Unit::SetMap(map);
  12412. m_mapRef.link(map, this);
  12413. }
  12414.  
  12415. void Player::_LoadGlyphs(PreparedQueryResult result)
  12416. {
  12417. // SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 from character_glyphs WHERE guid = '%u'
  12418. if (!result)
  12419. return;
  12420.  
  12421. do
  12422. {
  12423. Field* fields = result->Fetch();
  12424.  
  12425. uint8 spec = fields[0].GetUInt8();
  12426. if (spec >= m_specsCount)
  12427. continue;
  12428.  
  12429. m_Glyphs[spec][0] = fields[1].GetUInt16();
  12430. m_Glyphs[spec][1] = fields[2].GetUInt16();
  12431. m_Glyphs[spec][2] = fields[3].GetUInt16();
  12432. m_Glyphs[spec][3] = fields[4].GetUInt16();
  12433. m_Glyphs[spec][4] = fields[5].GetUInt16();
  12434. m_Glyphs[spec][5] = fields[6].GetUInt16();
  12435. }
  12436. while (result->NextRow());
  12437. }
  12438.  
  12439. void Player::_SaveGlyphs(SQLTransaction& trans)
  12440. {
  12441. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GLYPHS);
  12442. stmt->setUInt32(0, GetGUIDLow());
  12443. trans->Append(stmt);
  12444.  
  12445.  
  12446. for (uint8 spec = 0; spec < m_specsCount; ++spec)
  12447. {
  12448. uint8 index = 0;
  12449.  
  12450. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_GLYPHS);
  12451. stmt->setUInt32(index++, GetGUIDLow());
  12452.  
  12453. stmt->setUInt8(index++, spec);
  12454.  
  12455. for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
  12456. stmt->setUInt16(index++, uint16(m_Glyphs[spec][i]));
  12457.  
  12458. trans->Append(stmt);
  12459. }
  12460. }
  12461.  
  12462. void Player::_LoadTalents(PreparedQueryResult result)
  12463. {
  12464. // SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid = '%u'", GUID_LOPART(m_guid));
  12465. if (result)
  12466. {
  12467. do
  12468. AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt8(), false);
  12469. while (result->NextRow());
  12470. }
  12471. }
  12472.  
  12473. void Player::_SaveTalents(SQLTransaction& trans)
  12474. {
  12475. PreparedStatement* stmt = NULL;
  12476.  
  12477. for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
  12478. {
  12479. for (PlayerTalentMap::iterator itr = m_talents[i]->begin(); itr != m_talents[i]->end();)
  12480. {
  12481. if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
  12482. {
  12483. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC);
  12484. stmt->setUInt32(0, GetGUIDLow());
  12485. stmt->setUInt32(1, itr->first);
  12486. stmt->setUInt8(2, itr->second->spec);
  12487. trans->Append(stmt);
  12488. }
  12489.  
  12490. if (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED)
  12491. {
  12492. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_TALENT);
  12493. stmt->setUInt32(0, GetGUIDLow());
  12494. stmt->setUInt32(1, itr->first);
  12495. stmt->setUInt8(2, itr->second->spec);
  12496. trans->Append(stmt);
  12497. }
  12498.  
  12499. if (itr->second->state == PLAYERSPELL_REMOVED)
  12500. {
  12501. delete itr->second;
  12502. m_talents[i]->erase(itr++);
  12503. }
  12504. else
  12505. {
  12506. itr->second->state = PLAYERSPELL_UNCHANGED;
  12507. ++itr;
  12508. }
  12509. }
  12510. }
  12511. }
  12512.  
  12513. void Player::UpdateSpecCount(uint8 count)
  12514. {
  12515. uint32 curCount = GetSpecsCount();
  12516. if (curCount == count)
  12517. return;
  12518.  
  12519. if (m_activeSpec >= count)
  12520. ActivateSpec(0);
  12521.  
  12522. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  12523. PreparedStatement* stmt = NULL;
  12524.  
  12525. // Copy spec data
  12526. if (count > curCount)
  12527. {
  12528. _SaveActions(trans); // make sure the button list is cleaned up
  12529. for (ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end(); ++itr)
  12530. {
  12531. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACTION);
  12532. stmt->setUInt32(0, GetGUIDLow());
  12533. stmt->setUInt8(1, 1);
  12534. stmt->setUInt8(2, itr->first);
  12535. stmt->setUInt32(3, itr->second.GetAction());
  12536. stmt->setUInt8(4, uint8(itr->second.GetType()));
  12537. trans->Append(stmt);
  12538. }
  12539. }
  12540. // Delete spec data for removed spec.
  12541. else if (count < curCount)
  12542. {
  12543. _SaveActions(trans);
  12544.  
  12545. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC);
  12546. stmt->setUInt8(0, m_activeSpec);
  12547. stmt->setUInt32(1, GetGUIDLow());
  12548. trans->Append(stmt);
  12549.  
  12550. m_activeSpec = 0;
  12551. }
  12552.  
  12553. CharacterDatabase.CommitTransaction(trans);
  12554.  
  12555. SetSpecsCount(count);
  12556.  
  12557. SendTalentsInfoData(false);
  12558. }
  12559.  
  12560. void Player::ActivateSpec(uint8 spec)
  12561. {
  12562. if (GetActiveSpec() == spec)
  12563. return;
  12564.  
  12565. if (spec > GetSpecsCount())
  12566. return;
  12567.  
  12568. if (IsNonMeleeSpellCasted(false))
  12569. InterruptNonMeleeSpells(false);
  12570.  
  12571. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  12572. _SaveActions(trans);
  12573. CharacterDatabase.CommitTransaction(trans);
  12574.  
  12575. // TO-DO: We need more research to know what happens with warlock's reagent
  12576. if (Pet* pet = GetPet())
  12577. RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
  12578.  
  12579. ClearComboPointHolders();
  12580. ClearAllReactives();
  12581. UnsummonAllTotems();
  12582. RemoveAllControlled();
  12583. /*RemoveAllAurasOnDeath();
  12584. if (GetPet())
  12585. GetPet()->RemoveAllAurasOnDeath();*/
  12586.  
  12587. //RemoveAllAuras(GetGUID(), NULL, false, true); // removes too many auras
  12588. //ExitVehicle(); // should be impossible to switch specs from inside a vehicle..
  12589.  
  12590. // Let client clear his current Actions
  12591. SendActionButtons(2);
  12592. // m_actionButtons.clear() is called in the next _LoadActionButtons
  12593. for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
  12594. {
  12595. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  12596.  
  12597. if (!talentInfo)
  12598. continue;
  12599.  
  12600. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
  12601.  
  12602. if (!talentTabInfo)
  12603. continue;
  12604.  
  12605. // unlearn only talents for character class
  12606. // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
  12607. // to prevent unexpected lost normal learned spell skip another class talents
  12608. if ((getClassMask() & talentTabInfo->ClassMask) == 0)
  12609. continue;
  12610.  
  12611. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  12612. {
  12613. // skip non-existant talent ranks
  12614. if (talentInfo->RankID[rank] == 0)
  12615. continue;
  12616. removeSpell(talentInfo->RankID[rank], true); // removes the talent, and all dependant, learned, and chained spells..
  12617. if (const SpellInfo* _spellEntry = sSpellMgr->GetSpellInfo(talentInfo->RankID[rank]))
  12618. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) // search through the SpellInfo for valid trigger spells
  12619. if (_spellEntry->Effects[i].TriggerSpell > 0 && _spellEntry->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL)
  12620. removeSpell(_spellEntry->Effects[i].TriggerSpell, true); // and remove any spells that the talent teaches
  12621. // if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
  12622. //PlayerTalentMap::iterator plrTalent = m_talents[m_activeSpec]->find(talentInfo->RankID[rank]);
  12623. //if (plrTalent != m_talents[m_activeSpec]->end())
  12624. // plrTalent->second->state = PLAYERSPELL_REMOVED;
  12625. }
  12626. }
  12627.  
  12628. // set glyphs
  12629. for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
  12630. // remove secondary glyph
  12631. if (uint32 oldglyph = m_Glyphs[m_activeSpec][slot])
  12632. if (GlyphPropertiesEntry const* old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph))
  12633. RemoveAurasDueToSpell(old_gp->SpellId);
  12634.  
  12635. SetActiveSpec(spec);
  12636. uint32 spentTalents = 0;
  12637.  
  12638. for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
  12639. {
  12640. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  12641.  
  12642. if (!talentInfo)
  12643. continue;
  12644.  
  12645. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
  12646.  
  12647. if (!talentTabInfo)
  12648. continue;
  12649.  
  12650. // learn only talents for character class
  12651. if ((getClassMask() & talentTabInfo->ClassMask) == 0)
  12652. continue;
  12653.  
  12654. // learn highest talent rank that exists in newly activated spec
  12655. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  12656. {
  12657. // skip non-existant talent ranks
  12658. if (talentInfo->RankID[rank] == 0)
  12659. continue;
  12660. // if the talent can be found in the newly activated PlayerTalentMap
  12661. if (HasTalent(talentInfo->RankID[rank], m_activeSpec))
  12662. {
  12663. learnSpell(talentInfo->RankID[rank], false); // add the talent to the PlayerSpellMap
  12664. spentTalents += (rank + 1); // increment the spentTalents count
  12665. }
  12666. }
  12667. }
  12668.  
  12669. // set glyphs
  12670. for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
  12671. {
  12672. uint32 glyph = m_Glyphs[m_activeSpec][slot];
  12673.  
  12674. // apply primary glyph
  12675. if (glyph)
  12676. if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph))
  12677. CastSpell(this, gp->SpellId, true);
  12678.  
  12679. SetGlyph(slot, glyph);
  12680. }
  12681.  
  12682. m_usedTalentCount = spentTalents;
  12683. InitTalentForLevel();
  12684.  
  12685. {
  12686. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC);
  12687. stmt->setUInt32(0, GetGUIDLow());
  12688. stmt->setUInt8(1, m_activeSpec);
  12689. if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
  12690. _LoadActions(result);
  12691. }
  12692.  
  12693. SendActionButtons(1);
  12694.  
  12695. Powers pw = getPowerType();
  12696. if (pw != POWER_MANA)
  12697. SetPower(POWER_MANA, 0); // Mana must be 0 even if it isn't the active power type.
  12698.  
  12699. SetPower(pw, 0);
  12700. }
  12701.  
  12702. void Player::ResetTimeSync()
  12703. {
  12704. m_timeSyncCounter = 0;
  12705. m_timeSyncTimer = 0;
  12706. m_timeSyncClient = 0;
  12707. m_timeSyncServer = getMSTime();
  12708. }
  12709.  
  12710. void Player::SendTimeSync()
  12711. {
  12712. WorldPacket data(SMSG_TIME_SYNC_REQ, 4);
  12713. data << uint32(m_timeSyncCounter++);
  12714. GetSession()->SendPacket(&data);
  12715.  
  12716. // Schedule next sync in 10 sec
  12717. m_timeSyncTimer = 10000;
  12718. m_timeSyncServer = getMSTime();
  12719. }
  12720.  
  12721. void Player::SetReputation(uint32 factionentry, uint32 value)
  12722. {
  12723. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(factionentry), value);
  12724. }
  12725. uint32 Player::GetReputation(uint32 factionentry)
  12726. {
  12727. return GetReputationMgr().GetReputation(sFactionStore.LookupEntry(factionentry));
  12728. }
  12729.  
  12730. std::string Player::GetGuildName()
  12731. {
  12732. return sGuildMgr->GetGuildById(GetGuildId())->GetName();
  12733. }
  12734.  
  12735. void Player::SendDuelCountdown(uint32 counter)
  12736. {
  12737. WorldPacket data(SMSG_DUEL_COUNTDOWN, 4);
  12738. data << uint32(counter); // seconds
  12739. GetSession()->SendPacket(&data);
  12740. }
  12741.  
  12742. void Player::AddRefundReference(uint32 it)
  12743. {
  12744. m_refundableItems.insert(it);
  12745. }
  12746.  
  12747. void Player::DeleteRefundReference(uint32 it)
  12748. {
  12749. std::set<uint32>::iterator itr = m_refundableItems.find(it);
  12750. if (itr != m_refundableItems.end())
  12751. {
  12752. m_refundableItems.erase(itr);
  12753. }
  12754. }
  12755.  
  12756. void Player::SendRefundInfo(Item* item)
  12757. {
  12758. // This function call unsets ITEM_FLAGS_REFUNDABLE if played time is over 2 hours.
  12759. item->UpdatePlayedTime(this);
  12760.  
  12761. if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE))
  12762. {
  12763. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "Item refund: item not refundable!");
  12764. return;
  12765. }
  12766.  
  12767. if (GetGUIDLow() != item->GetRefundRecipient()) // Formerly refundable item got traded
  12768. {
  12769. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "Item refund: item was traded!");
  12770. item->SetNotRefundable(this);
  12771. return;
  12772. }
  12773.  
  12774. ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(item->GetPaidExtendedCost());
  12775. if (!iece)
  12776. {
  12777. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "Item refund: cannot find extendedcost data.");
  12778. return;
  12779. }
  12780.  
  12781. WorldPacket data(SMSG_ITEM_REFUND_INFO_RESPONSE, 8+4+4+4+4*4+4*4+4+4);
  12782. data << uint64(item->GetGUID()); // item guid
  12783. data << uint32(item->GetPaidMoney()); // money cost
  12784. data << uint32(iece->reqhonorpoints); // honor point cost
  12785. data << uint32(iece->reqarenapoints); // arena point cost
  12786. for (uint8 i = 0; i < MAX_ITEM_EXTENDED_COST_REQUIREMENTS; ++i) // item cost data
  12787. {
  12788. data << uint32(iece->reqitem[i]);
  12789. data << uint32(iece->reqitemcount[i]);
  12790. }
  12791. data << uint32(0);
  12792. data << uint32(GetTotalPlayedTime() - item->GetPlayedTime());
  12793. GetSession()->SendPacket(&data);
  12794. }
  12795.  
  12796. bool Player::AddItem(uint32 itemId, uint32 count)
  12797. {
  12798. uint32 noSpaceForCount = 0;
  12799. ItemPosCountVec dest;
  12800. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount);
  12801. if (msg != EQUIP_ERR_OK)
  12802. count = noSpaceForCount;
  12803.  
  12804. if (count == 0 || dest.empty())
  12805. {
  12806. // -- TODO: Send to mailbox if no space
  12807. ChatHandler(this).PSendSysMessage("You don't have any space in your bags.");
  12808. return false;
  12809. }
  12810.  
  12811. Item* item = StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
  12812. if (item)
  12813. SendNewItem(item, count, true, false);
  12814. else
  12815. return false;
  12816. return true;
  12817. }
  12818.  
  12819. void Player::RefundItem(Item* item)
  12820. {
  12821. if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE))
  12822. {
  12823. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "Item refund: item not refundable!");
  12824. return;
  12825. }
  12826.  
  12827. if (item->IsRefundExpired()) // item refund has expired
  12828. {
  12829. item->SetNotRefundable(this);
  12830. WorldPacket data(SMSG_ITEM_REFUND_RESULT, 8+4);
  12831. data << uint64(item->GetGUID()); // Guid
  12832. data << uint32(10); // Error!
  12833. GetSession()->SendPacket(&data);
  12834. return;
  12835. }
  12836.  
  12837. if (GetGUIDLow() != item->GetRefundRecipient()) // Formerly refundable item got traded
  12838. {
  12839. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "Item refund: item was traded!");
  12840. item->SetNotRefundable(this);
  12841. return;
  12842. }
  12843.  
  12844. ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(item->GetPaidExtendedCost());
  12845. if (!iece)
  12846. {
  12847. sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "Item refund: cannot find extendedcost data.");
  12848. return;
  12849. }
  12850.  
  12851. bool store_error = false;
  12852. for (uint8 i = 0; i < MAX_ITEM_EXTENDED_COST_REQUIREMENTS; ++i)
  12853. {
  12854. uint32 count = iece->reqitemcount[i];
  12855. uint32 itemid = iece->reqitem[i];
  12856.  
  12857. if (count && itemid)
  12858. {
  12859. ItemPosCountVec dest;
  12860. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemid, count);
  12861. if (msg != EQUIP_ERR_OK)
  12862. {
  12863. store_error = true;
  12864. break;
  12865. }
  12866. }
  12867. }
  12868.  
  12869. if (store_error)
  12870. {
  12871. WorldPacket data(SMSG_ITEM_REFUND_RESULT, 8+4);
  12872. data << uint64(item->GetGUID()); // Guid
  12873. data << uint32(10); // Error!
  12874. GetSession()->SendPacket(&data);
  12875. return;
  12876. }
  12877.  
  12878. WorldPacket data(SMSG_ITEM_REFUND_RESULT, 8+4+4+4+4+4*4+4*4);
  12879. data << uint64(item->GetGUID()); // item guid
  12880. data << uint32(0); // 0, or error code
  12881. data << uint32(item->GetPaidMoney()); // money cost
  12882. data << uint32(iece->reqhonorpoints); // honor point cost
  12883. data << uint32(iece->reqarenapoints); // arena point cost
  12884. for (uint8 i = 0; i < MAX_ITEM_EXTENDED_COST_REQUIREMENTS; ++i) // item cost data
  12885. {
  12886. data << uint32(iece->reqitem[i]);
  12887. data << uint32(iece->reqitemcount[i]);
  12888. }
  12889. GetSession()->SendPacket(&data);
  12890.  
  12891. uint32 moneyRefund = item->GetPaidMoney(); // item-> will be invalidated in DestroyItem
  12892.  
  12893. // Save all relevant data to DB to prevent desynchronisation exploits
  12894. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  12895.  
  12896. // Delete any references to the refund data
  12897. item->SetNotRefundable(this, true, &trans);
  12898.  
  12899. // Destroy item
  12900. DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
  12901.  
  12902. // Grant back extendedcost items
  12903. for (uint8 i = 0; i < MAX_ITEM_EXTENDED_COST_REQUIREMENTS; ++i)
  12904. {
  12905. uint32 count = iece->reqitemcount[i];
  12906. uint32 itemid = iece->reqitem[i];
  12907. if (count && itemid)
  12908. {
  12909. ItemPosCountVec dest;
  12910. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemid, count);
  12911. ASSERT(msg == EQUIP_ERR_OK) /// Already checked before
  12912. Item* it = StoreNewItem(dest, itemid, true);
  12913. SendNewItem(it, count, true, false, true);
  12914. }
  12915. }
  12916.  
  12917. // Grant back money
  12918. if (moneyRefund)
  12919. ModifyMoney(moneyRefund); // Saved in SaveInventoryAndGoldToDB
  12920.  
  12921. // Grant back Honor points
  12922. if (uint32 honorRefund = iece->reqhonorpoints)
  12923. ModifyHonorPoints(honorRefund, &trans);
  12924.  
  12925. // Grant back Arena points
  12926. if (uint32 arenaRefund = iece->reqarenapoints)
  12927. ModifyArenaPoints(arenaRefund, &trans);
  12928.  
  12929. SaveInventoryAndGoldToDB(trans);
  12930.  
  12931. CharacterDatabase.CommitTransaction(trans);
  12932. }
  12933.  
  12934. void Player::SetRandomWinner(bool isWinner)
  12935. {
  12936. m_IsBGRandomWinner = isWinner;
  12937. if (m_IsBGRandomWinner)
  12938. {
  12939. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BATTLEGROUND_RANDOM);
  12940.  
  12941. stmt->setUInt32(0, GetGUIDLow());
  12942.  
  12943. CharacterDatabase.Execute(stmt);
  12944. }
  12945. }
  12946.  
  12947. void Player::_LoadRandomBGStatus(PreparedQueryResult result)
  12948. {
  12949. //QueryResult result = CharacterDatabase.PQuery("SELECT guid FROM character_battleground_random WHERE guid = '%u'", GetGUIDLow());
  12950.  
  12951. if (result)
  12952. m_IsBGRandomWinner = true;
  12953. }
  12954.  
  12955. float Player::GetAverageItemLevel()
  12956. {
  12957. float sum = 0;
  12958. uint32 count = 0;
  12959.  
  12960. for (int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
  12961. {
  12962. // don't check tabard, ranged, offhand or shirt
  12963. if (i == EQUIPMENT_SLOT_TABARD || i == EQUIPMENT_SLOT_RANGED || i == EQUIPMENT_SLOT_OFFHAND || i == EQUIPMENT_SLOT_BODY)
  12964. continue;
  12965.  
  12966. if (m_items[i] && m_items[i]->GetTemplate())
  12967. sum += m_items[i]->GetTemplate()->GetItemLevelIncludingQuality();
  12968.  
  12969. ++count;
  12970. }
  12971.  
  12972. return ((float)sum) / count;
  12973. }
  12974.  
  12975. void Player::_LoadInstanceTimeRestrictions(PreparedQueryResult result)
  12976. {
  12977. if (!result)
  12978. return;
  12979.  
  12980. do
  12981. {
  12982. Field* fields = result->Fetch();
  12983. _instanceResetTimes.insert(InstanceTimeMap::value_type(fields[0].GetUInt32(), fields[1].GetUInt64()));
  12984. } while (result->NextRow());
  12985. }
  12986.  
  12987. void Player::_SaveInstanceTimeRestrictions(SQLTransaction& trans)
  12988. {
  12989. if (_instanceResetTimes.empty())
  12990. return;
  12991.  
  12992. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES);
  12993. stmt->setUInt32(0, GetSession()->GetAccountId());
  12994. trans->Append(stmt);
  12995.  
  12996. for (InstanceTimeMap::const_iterator itr = _instanceResetTimes.begin(); itr != _instanceResetTimes.end(); ++itr)
  12997. {
  12998. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES);
  12999. stmt->setUInt32(0, GetSession()->GetAccountId());
  13000. stmt->setUInt32(1, itr->first);
  13001. stmt->setUInt64(2, itr->second);
  13002. trans->Append(stmt);
  13003. }
  13004. }
  13005.  
  13006. /** World of Warcraft Armory **/
  13007. void Player::InitWowarmoryFeeds() {
  13008. // Clear feeds
  13009. m_wowarmory_feeds.clear();
  13010. }
  13011.  
  13012. void Player::CreateWowarmoryFeed(uint32 type, uint32 data, uint32 item_guid, uint32 item_quality) {
  13013. /*
  13014. 1 - TYPE_ACHIEVEMENT_FEED
  13015. 2 - TYPE_ITEM_FEED
  13016. 3 - TYPE_BOSS_FEED
  13017. */
  13018. if (GetGUIDLow() == 0)
  13019. {
  13020. sLog->outError(LOG_FILTER_UNITS,"[Wowarmory]: player is not initialized, unable to create log entry!");
  13021. return;
  13022. }
  13023. if (type <= 0 || type > 3)
  13024. {
  13025. sLog->outError(LOG_FILTER_UNITS,"[Wowarmory]: unknown feed type: %d, ignore.", type);
  13026. return;
  13027. }
  13028. if (data == 0)
  13029. {
  13030. sLog->outError(LOG_FILTER_UNITS,"[Wowarmory]: empty data (GUID: %u), ignore.", GetGUIDLow());
  13031. return;
  13032. }
  13033. WowarmoryFeedEntry feed;
  13034. feed.guid = GetGUIDLow();
  13035. feed.type = type;
  13036. feed.data = data;
  13037. feed.difficulty = type == 3 ? GetMap()->GetDifficulty() : 0;
  13038. feed.item_guid = item_guid;
  13039. feed.item_quality = item_quality;
  13040. feed.counter = 0;
  13041. feed.date = time(NULL);
  13042. sLog->outDebug(LOG_FILTER_UNITS, "[Wowarmory]: create wowarmory feed (GUID: %u, type: %d, data: %u).", feed.guid, feed.type, feed.data);
  13043. m_wowarmory_feeds.push_back(feed);
  13044. }
  13045. /** World of Warcraft Armory **/
  13046.  
  13047. bool Player::IsInWhisperWhiteList(uint64 guid)
  13048. {
  13049. for (WhisperListContainer::const_iterator itr = WhisperList.begin(); itr != WhisperList.end(); ++itr)
  13050. {
  13051. if (*itr == guid)
  13052. return true;
  13053. }
  13054. return false;
  13055. }
  13056.  
  13057. bool Player::SetHover(bool enable)
  13058. {
  13059. if (!Unit::SetHover(enable))
  13060. return false;
  13061.  
  13062. return true;
  13063. }
  13064.  
  13065. void Player::SendMovementSetCanFly(bool apply)
  13066. {
  13067. WorldPacket data(apply ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, 12);
  13068. data.append(GetPackGUID());
  13069. data << uint32(0); //! movement counter
  13070. SendDirectMessage(&data);
  13071. }
  13072.  
  13073. void Player::SendMovementSetCanTransitionBetweenSwimAndFly(bool apply)
  13074. {
  13075. WorldPacket data(apply ?
  13076. SMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY :
  13077. SMSG_MOVE_UNSET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY, 12);
  13078. data.append(GetPackGUID());
  13079. data << uint32(0); //! movement counter
  13080. SendDirectMessage(&data);
  13081. }
  13082.  
  13083. void Player::SendMovementSetHover(bool apply)
  13084. {
  13085. WorldPacket data(apply ? SMSG_MOVE_SET_HOVER : SMSG_MOVE_UNSET_HOVER, 12);
  13086. data.append(GetPackGUID());
  13087. data << uint32(0); //! movement counter
  13088. SendDirectMessage(&data);
  13089. }
  13090.  
  13091. void Player::SendMovementSetWaterWalking(bool apply)
  13092. {
  13093. WorldPacket data(apply ? SMSG_MOVE_WATER_WALK : SMSG_MOVE_LAND_WALK, 12);
  13094. data.append(GetPackGUID());
  13095. data << uint32(0); //! movement counter
  13096. SendDirectMessage(&data);
  13097. }
  13098.  
  13099. void Player::SendMovementSetFeatherFall(bool apply)
  13100. {
  13101. WorldPacket data(apply ? SMSG_MOVE_FEATHER_FALL : SMSG_MOVE_NORMAL_FALL, 12);
  13102. data.append(GetPackGUID());
  13103. data << uint32(0); //! movement counter
  13104. SendDirectMessage(&data);
  13105. }
Add Comment
Please, Sign In to add comment