PageRenderTime 67ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/server/game/Entities/Item/Item.cpp

https://gitlab.com/IlluminatiCore/IlluminatiCore
C++ | 1705 lines | 1316 code | 279 blank | 110 comment | 383 complexity | 1f09f33919c85af56c7d75b47f8fd49d MD5 | raw file
Possible License(s): GPL-2.0, BSD-2-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
  3. * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the
  7. * Free Software Foundation; either version 2 of the License, or (at your
  8. * option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but WITHOUT
  11. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13. * more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "Common.h"
  19. #include "Item.h"
  20. #include "ObjectMgr.h"
  21. #include "WorldPacket.h"
  22. #include "DatabaseEnv.h"
  23. #include "ItemEnchantmentMgr.h"
  24. #include "SpellMgr.h"
  25. #include "SpellInfo.h"
  26. #include "ScriptMgr.h"
  27. #include "ConditionMgr.h"
  28. #include "Player.h"
  29. #include "Opcodes.h"
  30. #include "WorldSession.h"
  31. void AddItemsSetItem(Player* player, Item* item)
  32. {
  33. ItemTemplate const* proto = item->GetTemplate();
  34. uint32 setid = proto->ItemSet;
  35. ItemSetEntry const* set = sItemSetStore.LookupEntry(setid);
  36. if (!set)
  37. {
  38. TC_LOG_ERROR("sql.sql", "Item set %u for item (id %u) not found, mods not applied.", setid, proto->ItemId);
  39. return;
  40. }
  41. if (set->required_skill_id && player->GetSkillValue(set->required_skill_id) < set->required_skill_value)
  42. return;
  43. ItemSetEffect* eff = NULL;
  44. for (size_t x = 0; x < player->ItemSetEff.size(); ++x)
  45. {
  46. if (player->ItemSetEff[x] && player->ItemSetEff[x]->setid == setid)
  47. {
  48. eff = player->ItemSetEff[x];
  49. break;
  50. }
  51. }
  52. if (!eff)
  53. {
  54. eff = new ItemSetEffect();
  55. eff->setid = setid;
  56. size_t x = 0;
  57. for (; x < player->ItemSetEff.size(); ++x)
  58. if (!player->ItemSetEff[x])
  59. break;
  60. if (x < player->ItemSetEff.size())
  61. player->ItemSetEff[x]=eff;
  62. else
  63. player->ItemSetEff.push_back(eff);
  64. }
  65. ++eff->item_count;
  66. for (uint32 x = 0; x < MAX_ITEM_SET_SPELLS; ++x)
  67. {
  68. if (!set->spells [x])
  69. continue;
  70. //not enough for spell
  71. if (set->items_to_triggerspell[x] > eff->item_count)
  72. continue;
  73. uint32 z = 0;
  74. for (; z < MAX_ITEM_SET_SPELLS; ++z)
  75. if (eff->spells[z] && eff->spells[z]->Id == set->spells[x])
  76. break;
  77. if (z < MAX_ITEM_SET_SPELLS)
  78. continue;
  79. //new spell
  80. for (uint32 y = 0; y < MAX_ITEM_SET_SPELLS; ++y)
  81. {
  82. if (!eff->spells[y]) // free slot
  83. {
  84. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(set->spells[x]);
  85. if (!spellInfo)
  86. {
  87. TC_LOG_ERROR("entities.player.items", "WORLD: unknown spell id %u in items set %u effects", set->spells[x], setid);
  88. break;
  89. }
  90. // spell cast only if fit form requirement, in other case will cast at form change
  91. player->ApplyEquipSpell(spellInfo, NULL, true);
  92. eff->spells[y] = spellInfo;
  93. break;
  94. }
  95. }
  96. }
  97. }
  98. void RemoveItemsSetItem(Player*player, ItemTemplate const* proto)
  99. {
  100. uint32 setid = proto->ItemSet;
  101. ItemSetEntry const* set = sItemSetStore.LookupEntry(setid);
  102. if (!set)
  103. {
  104. TC_LOG_ERROR("sql.sql", "Item set #%u for item #%u not found, mods not removed.", setid, proto->ItemId);
  105. return;
  106. }
  107. ItemSetEffect* eff = NULL;
  108. size_t setindex = 0;
  109. for (; setindex < player->ItemSetEff.size(); setindex++)
  110. {
  111. if (player->ItemSetEff[setindex] && player->ItemSetEff[setindex]->setid == setid)
  112. {
  113. eff = player->ItemSetEff[setindex];
  114. break;
  115. }
  116. }
  117. // can be in case now enough skill requirement for set appling but set has been appliend when skill requirement not enough
  118. if (!eff)
  119. return;
  120. --eff->item_count;
  121. for (uint32 x = 0; x < MAX_ITEM_SET_SPELLS; x++)
  122. {
  123. if (!set->spells[x])
  124. continue;
  125. // enough for spell
  126. if (set->items_to_triggerspell[x] <= eff->item_count)
  127. continue;
  128. for (uint32 z = 0; z < MAX_ITEM_SET_SPELLS; z++)
  129. {
  130. if (eff->spells[z] && eff->spells[z]->Id == set->spells[x])
  131. {
  132. // spell can be not active if not fit form requirement
  133. player->ApplyEquipSpell(eff->spells[z], NULL, false);
  134. eff->spells[z]=NULL;
  135. break;
  136. }
  137. }
  138. }
  139. if (!eff->item_count) //all items of a set were removed
  140. {
  141. ASSERT(eff == player->ItemSetEff[setindex]);
  142. delete eff;
  143. player->ItemSetEff[setindex] = NULL;
  144. }
  145. }
  146. bool ItemCanGoIntoBag(ItemTemplate const* pProto, ItemTemplate const* pBagProto)
  147. {
  148. if (!pProto || !pBagProto)
  149. return false;
  150. switch (pBagProto->Class)
  151. {
  152. case ITEM_CLASS_CONTAINER:
  153. switch (pBagProto->SubClass)
  154. {
  155. case ITEM_SUBCLASS_CONTAINER:
  156. return true;
  157. case ITEM_SUBCLASS_SOUL_CONTAINER:
  158. if (!(pProto->BagFamily & BAG_FAMILY_MASK_SOUL_SHARDS))
  159. return false;
  160. return true;
  161. case ITEM_SUBCLASS_HERB_CONTAINER:
  162. if (!(pProto->BagFamily & BAG_FAMILY_MASK_HERBS))
  163. return false;
  164. return true;
  165. case ITEM_SUBCLASS_ENCHANTING_CONTAINER:
  166. if (!(pProto->BagFamily & BAG_FAMILY_MASK_ENCHANTING_SUPP))
  167. return false;
  168. return true;
  169. case ITEM_SUBCLASS_MINING_CONTAINER:
  170. if (!(pProto->BagFamily & BAG_FAMILY_MASK_MINING_SUPP))
  171. return false;
  172. return true;
  173. case ITEM_SUBCLASS_ENGINEERING_CONTAINER:
  174. if (!(pProto->BagFamily & BAG_FAMILY_MASK_ENGINEERING_SUPP))
  175. return false;
  176. return true;
  177. case ITEM_SUBCLASS_GEM_CONTAINER:
  178. if (!(pProto->BagFamily & BAG_FAMILY_MASK_GEMS))
  179. return false;
  180. return true;
  181. case ITEM_SUBCLASS_LEATHERWORKING_CONTAINER:
  182. if (!(pProto->BagFamily & BAG_FAMILY_MASK_LEATHERWORKING_SUPP))
  183. return false;
  184. return true;
  185. case ITEM_SUBCLASS_INSCRIPTION_CONTAINER:
  186. if (!(pProto->BagFamily & BAG_FAMILY_MASK_INSCRIPTION_SUPP))
  187. return false;
  188. return true;
  189. case ITEM_SUBCLASS_TACKLE_CONTAINER:
  190. if (!(pProto->BagFamily & BAG_FAMILY_MASK_FISHING_SUPP))
  191. return false;
  192. return true;
  193. default:
  194. return false;
  195. }
  196. case ITEM_CLASS_QUIVER:
  197. switch (pBagProto->SubClass)
  198. {
  199. case ITEM_SUBCLASS_QUIVER:
  200. if (!(pProto->BagFamily & BAG_FAMILY_MASK_ARROWS))
  201. return false;
  202. return true;
  203. case ITEM_SUBCLASS_AMMO_POUCH:
  204. if (!(pProto->BagFamily & BAG_FAMILY_MASK_BULLETS))
  205. return false;
  206. return true;
  207. default:
  208. return false;
  209. }
  210. }
  211. return false;
  212. }
  213. Item::Item()
  214. {
  215. m_objectType |= TYPEMASK_ITEM;
  216. m_objectTypeId = TYPEID_ITEM;
  217. m_updateFlag = 0;
  218. m_valuesCount = ITEM_END;
  219. m_slot = 0;
  220. uState = ITEM_NEW;
  221. uQueuePos = -1;
  222. m_container = NULL;
  223. m_lootGenerated = false;
  224. mb_in_trade = false;
  225. m_lastPlayedTimeUpdate = time(NULL);
  226. m_refundRecipient = 0;
  227. m_paidMoney = 0;
  228. m_paidExtendedCost = 0;
  229. }
  230. bool Item::Create(uint32 guidlow, uint32 itemid, Player const* owner)
  231. {
  232. Object::_Create(guidlow, 0, HIGHGUID_ITEM);
  233. SetEntry(itemid);
  234. SetObjectScale(1.0f);
  235. if (owner)
  236. {
  237. SetGuidValue(ITEM_FIELD_OWNER, owner->GetGUID());
  238. SetGuidValue(ITEM_FIELD_CONTAINED, owner->GetGUID());
  239. }
  240. ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(itemid);
  241. if (!itemProto)
  242. return false;
  243. SetUInt32Value(ITEM_FIELD_STACK_COUNT, 1);
  244. SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability);
  245. SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability);
  246. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  247. SetSpellCharges(i, itemProto->Spells[i].SpellCharges);
  248. SetUInt32Value(ITEM_FIELD_DURATION, itemProto->Duration);
  249. SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, 0);
  250. return true;
  251. }
  252. // Returns true if Item is a bag AND it is not empty.
  253. // Returns false if Item is not a bag OR it is an empty bag.
  254. bool Item::IsNotEmptyBag() const
  255. {
  256. if (Bag const* bag = ToBag())
  257. return !bag->IsEmpty();
  258. return false;
  259. }
  260. void Item::UpdateDuration(Player* owner, uint32 diff)
  261. {
  262. if (!GetUInt32Value(ITEM_FIELD_DURATION))
  263. return;
  264. TC_LOG_DEBUG("entities.player.items", "Item::UpdateDuration Item (Entry: %u Duration %u Diff %u)", GetEntry(), GetUInt32Value(ITEM_FIELD_DURATION), diff);
  265. if (GetUInt32Value(ITEM_FIELD_DURATION) <= diff)
  266. {
  267. sScriptMgr->OnItemExpire(owner, GetTemplate());
  268. owner->DestroyItem(GetBagSlot(), GetSlot(), true);
  269. return;
  270. }
  271. SetUInt32Value(ITEM_FIELD_DURATION, GetUInt32Value(ITEM_FIELD_DURATION) - diff);
  272. SetState(ITEM_CHANGED, owner); // save new time in database
  273. }
  274. void Item::SaveToDB(SQLTransaction& trans)
  275. {
  276. bool isInTransaction = bool(trans);
  277. if (!isInTransaction)
  278. trans = CharacterDatabase.BeginTransaction();
  279. uint32 guid = GetGUIDLow();
  280. switch (uState)
  281. {
  282. case ITEM_NEW:
  283. case ITEM_CHANGED:
  284. {
  285. uint8 index = 0;
  286. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(uState == ITEM_NEW ? CHAR_REP_ITEM_INSTANCE : CHAR_UPD_ITEM_INSTANCE);
  287. stmt->setUInt32( index, GetEntry());
  288. stmt->setUInt32(++index, GetOwnerGUID().GetCounter());
  289. stmt->setUInt32(++index, GetGuidValue(ITEM_FIELD_CREATOR).GetCounter());
  290. stmt->setUInt32(++index, GetGuidValue(ITEM_FIELD_GIFTCREATOR).GetCounter());
  291. stmt->setUInt32(++index, GetCount());
  292. stmt->setUInt32(++index, GetUInt32Value(ITEM_FIELD_DURATION));
  293. std::ostringstream ssSpells;
  294. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  295. ssSpells << GetSpellCharges(i) << ' ';
  296. stmt->setString(++index, ssSpells.str());
  297. stmt->setUInt32(++index, GetUInt32Value(ITEM_FIELD_FLAGS));
  298. std::ostringstream ssEnchants;
  299. for (uint8 i = 0; i < MAX_ENCHANTMENT_SLOT; ++i)
  300. {
  301. ssEnchants << GetEnchantmentId(EnchantmentSlot(i)) << ' ';
  302. ssEnchants << GetEnchantmentDuration(EnchantmentSlot(i)) << ' ';
  303. ssEnchants << GetEnchantmentCharges(EnchantmentSlot(i)) << ' ';
  304. }
  305. stmt->setString(++index, ssEnchants.str());
  306. stmt->setInt16 (++index, GetItemRandomPropertyId());
  307. stmt->setUInt16(++index, GetUInt32Value(ITEM_FIELD_DURABILITY));
  308. stmt->setUInt32(++index, GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME));
  309. stmt->setString(++index, m_text);
  310. stmt->setUInt32(++index, guid);
  311. trans->Append(stmt);
  312. if ((uState == ITEM_CHANGED) && HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
  313. {
  314. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GIFT_OWNER);
  315. stmt->setUInt32(0, GetOwnerGUID().GetCounter());
  316. stmt->setUInt32(1, guid);
  317. trans->Append(stmt);
  318. }
  319. break;
  320. }
  321. case ITEM_REMOVED:
  322. {
  323. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  324. stmt->setUInt32(0, guid);
  325. trans->Append(stmt);
  326. if (HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
  327. {
  328. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
  329. stmt->setUInt32(0, guid);
  330. trans->Append(stmt);
  331. }
  332. if (!isInTransaction)
  333. CharacterDatabase.CommitTransaction(trans);
  334. // Delete the items if this is a container
  335. if (!loot.isLooted())
  336. ItemContainerDeleteLootMoneyAndLootItemsFromDB();
  337. delete this;
  338. return;
  339. }
  340. case ITEM_UNCHANGED:
  341. break;
  342. }
  343. SetState(ITEM_UNCHANGED);
  344. if (!isInTransaction)
  345. CharacterDatabase.CommitTransaction(trans);
  346. }
  347. bool Item::LoadFromDB(uint32 guid, ObjectGuid owner_guid, Field* fields, uint32 entry)
  348. {
  349. // 0 1 2 3 4 5 6 7 8 9 10
  350. //result = CharacterDatabase.PQuery("SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text FROM item_instance WHERE guid = '%u'", guid);
  351. // create item before any checks for store correct guid
  352. // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB
  353. Object::_Create(guid, 0, HIGHGUID_ITEM);
  354. // Set entry, MUST be before proto check
  355. SetEntry(entry);
  356. SetObjectScale(1.0f);
  357. ItemTemplate const* proto = GetTemplate();
  358. if (!proto)
  359. return false;
  360. // set owner (not if item is only loaded for gbank/auction/mail
  361. if (owner_guid)
  362. SetOwnerGUID(owner_guid);
  363. bool need_save = false; // need explicit save data at load fixes
  364. SetGuidValue(ITEM_FIELD_CREATOR, ObjectGuid(HIGHGUID_PLAYER, fields[0].GetUInt32()));
  365. SetGuidValue(ITEM_FIELD_GIFTCREATOR, ObjectGuid(HIGHGUID_PLAYER, fields[1].GetUInt32()));
  366. SetCount(fields[2].GetUInt32());
  367. uint32 duration = fields[3].GetUInt32();
  368. SetUInt32Value(ITEM_FIELD_DURATION, duration);
  369. // update duration if need, and remove if not need
  370. if ((proto->Duration == 0) != (duration == 0))
  371. {
  372. SetUInt32Value(ITEM_FIELD_DURATION, proto->Duration);
  373. need_save = true;
  374. }
  375. Tokenizer tokens(fields[4].GetString(), ' ', MAX_ITEM_PROTO_SPELLS);
  376. if (tokens.size() == MAX_ITEM_PROTO_SPELLS)
  377. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  378. SetSpellCharges(i, atoi(tokens[i]));
  379. SetUInt32Value(ITEM_FIELD_FLAGS, fields[5].GetUInt32());
  380. // Remove bind flag for items vs NO_BIND set
  381. if (IsSoulBound() && proto->Bonding == NO_BIND)
  382. {
  383. ApplyModFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_SOULBOUND, false);
  384. need_save = true;
  385. }
  386. std::string enchants = fields[6].GetString();
  387. _LoadIntoDataField(enchants.c_str(), ITEM_FIELD_ENCHANTMENT_1_1, MAX_ENCHANTMENT_SLOT * MAX_ENCHANTMENT_OFFSET);
  388. SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID, fields[7].GetInt16());
  389. // recalculate suffix factor
  390. if (GetItemRandomPropertyId() < 0)
  391. UpdateItemSuffixFactor();
  392. uint32 durability = fields[8].GetUInt16();
  393. SetUInt32Value(ITEM_FIELD_DURABILITY, durability);
  394. // update max durability (and durability) if need
  395. SetUInt32Value(ITEM_FIELD_MAXDURABILITY, proto->MaxDurability);
  396. if (durability > proto->MaxDurability)
  397. {
  398. SetUInt32Value(ITEM_FIELD_DURABILITY, proto->MaxDurability);
  399. need_save = true;
  400. }
  401. SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, fields[9].GetUInt32());
  402. SetText(fields[10].GetString());
  403. if (need_save) // normal item changed state set not work at loading
  404. {
  405. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD);
  406. stmt->setUInt32(0, GetUInt32Value(ITEM_FIELD_DURATION));
  407. stmt->setUInt32(1, GetUInt32Value(ITEM_FIELD_FLAGS));
  408. stmt->setUInt32(2, GetUInt32Value(ITEM_FIELD_DURABILITY));
  409. stmt->setUInt32(3, guid);
  410. CharacterDatabase.Execute(stmt);
  411. }
  412. return true;
  413. }
  414. /*static*/
  415. void Item::DeleteFromDB(SQLTransaction& trans, uint32 itemGuid)
  416. {
  417. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  418. stmt->setUInt32(0, itemGuid);
  419. trans->Append(stmt);
  420. }
  421. void Item::DeleteFromDB(SQLTransaction& trans)
  422. {
  423. DeleteFromDB(trans, GetGUIDLow());
  424. // Delete the items if this is a container
  425. if (!loot.isLooted())
  426. ItemContainerDeleteLootMoneyAndLootItemsFromDB();
  427. }
  428. /*static*/
  429. void Item::DeleteFromInventoryDB(SQLTransaction& trans, uint32 itemGuid)
  430. {
  431. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
  432. stmt->setUInt32(0, itemGuid);
  433. trans->Append(stmt);
  434. }
  435. void Item::DeleteFromInventoryDB(SQLTransaction& trans)
  436. {
  437. DeleteFromInventoryDB(trans, GetGUIDLow());
  438. }
  439. ItemTemplate const* Item::GetTemplate() const
  440. {
  441. return sObjectMgr->GetItemTemplate(GetEntry());
  442. }
  443. Player* Item::GetOwner()const
  444. {
  445. return ObjectAccessor::FindPlayer(GetOwnerGUID());
  446. }
  447. uint32 Item::GetSkill()
  448. {
  449. const static uint32 item_weapon_skills[MAX_ITEM_SUBCLASS_WEAPON] =
  450. {
  451. SKILL_AXES, SKILL_2H_AXES, SKILL_BOWS, SKILL_GUNS, SKILL_MACES,
  452. SKILL_2H_MACES, SKILL_POLEARMS, SKILL_SWORDS, SKILL_2H_SWORDS, 0,
  453. SKILL_STAVES, 0, 0, SKILL_FIST_WEAPONS, 0,
  454. SKILL_DAGGERS, SKILL_THROWN, SKILL_ASSASSINATION, SKILL_CROSSBOWS, SKILL_WANDS,
  455. SKILL_FISHING
  456. };
  457. const static uint32 item_armor_skills[MAX_ITEM_SUBCLASS_ARMOR] =
  458. {
  459. 0, SKILL_CLOTH, SKILL_LEATHER, SKILL_MAIL, SKILL_PLATE_MAIL, 0, SKILL_SHIELD, 0, 0, 0, 0
  460. };
  461. ItemTemplate const* proto = GetTemplate();
  462. switch (proto->Class)
  463. {
  464. case ITEM_CLASS_WEAPON:
  465. if (proto->SubClass >= MAX_ITEM_SUBCLASS_WEAPON)
  466. return 0;
  467. else
  468. return item_weapon_skills[proto->SubClass];
  469. case ITEM_CLASS_ARMOR:
  470. if (proto->SubClass >= MAX_ITEM_SUBCLASS_ARMOR)
  471. return 0;
  472. else
  473. return item_armor_skills[proto->SubClass];
  474. default:
  475. return 0;
  476. }
  477. }
  478. int32 Item::GenerateItemRandomPropertyId(uint32 item_id)
  479. {
  480. ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(item_id);
  481. if (!itemProto)
  482. return 0;
  483. // item must have one from this field values not null if it can have random enchantments
  484. if ((!itemProto->RandomProperty) && (!itemProto->RandomSuffix))
  485. return 0;
  486. // item can have not null only one from field values
  487. if ((itemProto->RandomProperty) && (itemProto->RandomSuffix))
  488. {
  489. TC_LOG_ERROR("sql.sql", "Item template %u have RandomProperty == %u and RandomSuffix == %u, but must have one from field =0", itemProto->ItemId, itemProto->RandomProperty, itemProto->RandomSuffix);
  490. return 0;
  491. }
  492. // RandomProperty case
  493. if (itemProto->RandomProperty)
  494. {
  495. uint32 randomPropId = GetItemEnchantMod(itemProto->RandomProperty);
  496. ItemRandomPropertiesEntry const* random_id = sItemRandomPropertiesStore.LookupEntry(randomPropId);
  497. if (!random_id)
  498. {
  499. TC_LOG_ERROR("sql.sql", "Enchantment id #%u used but it doesn't have records in 'ItemRandomProperties.dbc'", randomPropId);
  500. return 0;
  501. }
  502. return random_id->ID;
  503. }
  504. // RandomSuffix case
  505. else
  506. {
  507. uint32 randomPropId = GetItemEnchantMod(itemProto->RandomSuffix);
  508. ItemRandomSuffixEntry const* random_id = sItemRandomSuffixStore.LookupEntry(randomPropId);
  509. if (!random_id)
  510. {
  511. TC_LOG_ERROR("sql.sql", "Enchantment id #%u used but it doesn't have records in sItemRandomSuffixStore.", randomPropId);
  512. return 0;
  513. }
  514. return -int32(random_id->ID);
  515. }
  516. }
  517. void Item::SetItemRandomProperties(int32 randomPropId)
  518. {
  519. if (!randomPropId)
  520. return;
  521. if (randomPropId > 0)
  522. {
  523. ItemRandomPropertiesEntry const* item_rand = sItemRandomPropertiesStore.LookupEntry(randomPropId);
  524. if (item_rand)
  525. {
  526. if (GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID) != int32(item_rand->ID))
  527. {
  528. SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID, item_rand->ID);
  529. SetState(ITEM_CHANGED, GetOwner());
  530. }
  531. for (uint32 i = PROP_ENCHANTMENT_SLOT_1; i < PROP_ENCHANTMENT_SLOT_1 + 3; ++i)
  532. SetEnchantment(EnchantmentSlot(i), item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_1], 0, 0);
  533. }
  534. }
  535. else
  536. {
  537. ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(-randomPropId);
  538. if (item_rand)
  539. {
  540. if (GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID) != -int32(item_rand->ID) ||
  541. !GetItemSuffixFactor())
  542. {
  543. SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID, -int32(item_rand->ID));
  544. UpdateItemSuffixFactor();
  545. SetState(ITEM_CHANGED, GetOwner());
  546. }
  547. for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i <= PROP_ENCHANTMENT_SLOT_4; ++i)
  548. SetEnchantment(EnchantmentSlot(i), item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_0], 0, 0);
  549. }
  550. }
  551. }
  552. void Item::UpdateItemSuffixFactor()
  553. {
  554. uint32 suffixFactor = GenerateEnchSuffixFactor(GetEntry());
  555. if (GetItemSuffixFactor() == suffixFactor)
  556. return;
  557. SetUInt32Value(ITEM_FIELD_PROPERTY_SEED, suffixFactor);
  558. }
  559. void Item::SetState(ItemUpdateState state, Player* forplayer)
  560. {
  561. if (uState == ITEM_NEW && state == ITEM_REMOVED)
  562. {
  563. // pretend the item never existed
  564. if (forplayer)
  565. {
  566. RemoveFromUpdateQueueOf(forplayer);
  567. forplayer->DeleteRefundReference(GetGUID());
  568. }
  569. delete this;
  570. return;
  571. }
  572. if (state != ITEM_UNCHANGED)
  573. {
  574. // new items must stay in new state until saved
  575. if (uState != ITEM_NEW)
  576. uState = state;
  577. if (forplayer)
  578. AddToUpdateQueueOf(forplayer);
  579. }
  580. else
  581. {
  582. // unset in queue
  583. // the item must be removed from the queue manually
  584. uQueuePos = -1;
  585. uState = ITEM_UNCHANGED;
  586. }
  587. }
  588. void Item::AddToUpdateQueueOf(Player* player)
  589. {
  590. if (IsInUpdateQueue())
  591. return;
  592. ASSERT(player != NULL);
  593. if (player->GetGUID() != GetOwnerGUID())
  594. {
  595. TC_LOG_DEBUG("entities.player.items", "Item::AddToUpdateQueueOf - Owner's guid (%s) and player's guid (%s) don't match!",
  596. GetOwnerGUID().ToString().c_str(), player->GetGUID().ToString().c_str());
  597. return;
  598. }
  599. if (player->m_itemUpdateQueueBlocked)
  600. return;
  601. player->m_itemUpdateQueue.push_back(this);
  602. uQueuePos = player->m_itemUpdateQueue.size()-1;
  603. }
  604. void Item::RemoveFromUpdateQueueOf(Player* player)
  605. {
  606. if (!IsInUpdateQueue())
  607. return;
  608. ASSERT(player != NULL);
  609. if (player->GetGUID() != GetOwnerGUID())
  610. {
  611. TC_LOG_DEBUG("entities.player.items", "Item::RemoveFromUpdateQueueOf - Owner's guid (%s) and player's guid (%s) don't match!",
  612. GetOwnerGUID().ToString().c_str(), player->GetGUID().ToString().c_str());
  613. return;
  614. }
  615. if (player->m_itemUpdateQueueBlocked)
  616. return;
  617. player->m_itemUpdateQueue[uQueuePos] = NULL;
  618. uQueuePos = -1;
  619. }
  620. uint8 Item::GetBagSlot() const
  621. {
  622. return m_container ? m_container->GetSlot() : uint8(INVENTORY_SLOT_BAG_0);
  623. }
  624. bool Item::IsEquipped() const
  625. {
  626. return !IsInBag() && m_slot < EQUIPMENT_SLOT_END;
  627. }
  628. bool Item::CanBeTraded(bool mail, bool trade) const
  629. {
  630. if (m_lootGenerated)
  631. return false;
  632. if ((!mail || !IsBoundAccountWide()) && (IsSoulBound() && (!HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE) || !trade)))
  633. return false;
  634. if (IsBag() && (Player::IsBagPos(GetPos()) || !((Bag const*)this)->IsEmpty()))
  635. return false;
  636. if (Player* owner = GetOwner())
  637. {
  638. if (owner->CanUnequipItem(GetPos(), false) != EQUIP_ERR_OK)
  639. return false;
  640. if (owner->GetLootGUID() == GetGUID())
  641. return false;
  642. }
  643. if (IsBoundByEnchant())
  644. return false;
  645. return true;
  646. }
  647. bool Item::HasEnchantRequiredSkill(const Player* player) const
  648. {
  649. // Check all enchants for required skill
  650. for (uint32 enchant_slot = PERM_ENCHANTMENT_SLOT; enchant_slot < MAX_ENCHANTMENT_SLOT; ++enchant_slot)
  651. {
  652. if (enchant_slot > PRISMATIC_ENCHANTMENT_SLOT && enchant_slot < PROP_ENCHANTMENT_SLOT_0) // not holding enchantment id
  653. continue;
  654. if (uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot)))
  655. if (SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id))
  656. if (enchantEntry->requiredSkill && player->GetSkillValue(enchantEntry->requiredSkill) < enchantEntry->requiredSkillValue)
  657. return false;
  658. }
  659. return true;
  660. }
  661. uint32 Item::GetEnchantRequiredLevel() const
  662. {
  663. uint32 level = 0;
  664. // Check all enchants for required level
  665. for (uint32 enchant_slot = PERM_ENCHANTMENT_SLOT; enchant_slot < MAX_ENCHANTMENT_SLOT; ++enchant_slot)
  666. {
  667. if (enchant_slot > PRISMATIC_ENCHANTMENT_SLOT && enchant_slot < PROP_ENCHANTMENT_SLOT_0) // not holding enchantment id
  668. continue;
  669. if (uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot)))
  670. if (SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id))
  671. if (enchantEntry->requiredLevel > level)
  672. level = enchantEntry->requiredLevel;
  673. }
  674. return level;
  675. }
  676. bool Item::IsBoundByEnchant() const
  677. {
  678. // Check all enchants for soulbound
  679. for (uint32 enchant_slot = PERM_ENCHANTMENT_SLOT; enchant_slot < MAX_ENCHANTMENT_SLOT; ++enchant_slot)
  680. {
  681. if (enchant_slot > PRISMATIC_ENCHANTMENT_SLOT && enchant_slot < PROP_ENCHANTMENT_SLOT_0) // not holding enchantment id
  682. continue;
  683. if (uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot)))
  684. if (SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id))
  685. if (enchantEntry->slot & ENCHANTMENT_CAN_SOULBOUND)
  686. return true;
  687. }
  688. return false;
  689. }
  690. InventoryResult Item::CanBeMergedPartlyWith(ItemTemplate const* proto) const
  691. {
  692. // not allow merge looting currently items
  693. if (m_lootGenerated)
  694. return EQUIP_ERR_LOOT_GONE;
  695. // check item type
  696. if (GetEntry() != proto->ItemId)
  697. return EQUIP_ERR_CANT_STACK;
  698. // check free space (full stacks can't be target of merge
  699. if (GetCount() >= proto->GetMaxStackSize())
  700. return EQUIP_ERR_CANT_STACK;
  701. return EQUIP_ERR_OK;
  702. }
  703. bool Item::IsFitToSpellRequirements(SpellInfo const* spellInfo) const
  704. {
  705. ItemTemplate const* proto = GetTemplate();
  706. if (spellInfo->EquippedItemClass != -1) // -1 == any item class
  707. {
  708. // Special case - accept vellum for armor/weapon requirements
  709. if ((spellInfo->EquippedItemClass == ITEM_CLASS_ARMOR ||
  710. spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON) && proto->IsVellum())
  711. if (spellInfo->IsAbilityOfSkillType(SKILL_ENCHANTING)) // only for enchanting spells
  712. return true;
  713. if (spellInfo->EquippedItemClass != int32(proto->Class))
  714. return false; // wrong item class
  715. if (spellInfo->EquippedItemSubClassMask != 0) // 0 == any subclass
  716. {
  717. if ((spellInfo->EquippedItemSubClassMask & (1 << proto->SubClass)) == 0)
  718. return false; // subclass not present in mask
  719. }
  720. }
  721. if (spellInfo->EquippedItemInventoryTypeMask != 0) // 0 == any inventory type
  722. {
  723. // Special case - accept weapon type for main and offhand requirements
  724. if (proto->InventoryType == INVTYPE_WEAPON &&
  725. (spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPONMAINHAND) ||
  726. spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPONOFFHAND)))
  727. return true;
  728. else if ((spellInfo->EquippedItemInventoryTypeMask & (1 << proto->InventoryType)) == 0)
  729. return false; // inventory type not present in mask
  730. }
  731. return true;
  732. }
  733. void Item::SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint32 charges, ObjectGuid caster /*= ObjectGuid::Empty*/)
  734. {
  735. // Better lost small time at check in comparison lost time at item save to DB.
  736. if ((GetEnchantmentId(slot) == id) && (GetEnchantmentDuration(slot) == duration) && (GetEnchantmentCharges(slot) == charges))
  737. return;
  738. Player* owner = GetOwner();
  739. if (slot < MAX_INSPECTED_ENCHANTMENT_SLOT)
  740. {
  741. if (uint32 oldEnchant = GetEnchantmentId(slot))
  742. owner->GetSession()->SendEnchantmentLog(GetOwnerGUID(), ObjectGuid::Empty, GetEntry(), oldEnchant);
  743. if (id)
  744. owner->GetSession()->SendEnchantmentLog(GetOwnerGUID(), caster, GetEntry(), id);
  745. }
  746. SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET, id);
  747. SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET, duration);
  748. SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET, charges);
  749. SetState(ITEM_CHANGED, owner);
  750. }
  751. void Item::SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration, Player* owner)
  752. {
  753. if (GetEnchantmentDuration(slot) == duration)
  754. return;
  755. SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET, duration);
  756. SetState(ITEM_CHANGED, owner);
  757. // Cannot use GetOwner() here, has to be passed as an argument to avoid freeze due to hashtable locking
  758. }
  759. void Item::SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges)
  760. {
  761. if (GetEnchantmentCharges(slot) == charges)
  762. return;
  763. SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET, charges);
  764. SetState(ITEM_CHANGED, GetOwner());
  765. }
  766. void Item::ClearEnchantment(EnchantmentSlot slot)
  767. {
  768. if (!GetEnchantmentId(slot))
  769. return;
  770. for (uint8 x = 0; x < MAX_ITEM_ENCHANTMENT_EFFECTS; ++x)
  771. SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + x, 0);
  772. SetState(ITEM_CHANGED, GetOwner());
  773. }
  774. bool Item::GemsFitSockets() const
  775. {
  776. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot)
  777. {
  778. uint8 SocketColor = GetTemplate()->Socket[enchant_slot-SOCK_ENCHANTMENT_SLOT].Color;
  779. if (!SocketColor) // no socket slot
  780. continue;
  781. uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot));
  782. if (!enchant_id) // no gems on this socket
  783. return false;
  784. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  785. if (!enchantEntry) // invalid gem id on this socket
  786. return false;
  787. uint8 GemColor = 0;
  788. uint32 gemid = enchantEntry->GemID;
  789. if (gemid)
  790. {
  791. ItemTemplate const* gemProto = sObjectMgr->GetItemTemplate(gemid);
  792. if (gemProto)
  793. {
  794. GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties);
  795. if (gemProperty)
  796. GemColor = gemProperty->color;
  797. }
  798. }
  799. if (!(GemColor & SocketColor)) // bad gem color on this socket
  800. return false;
  801. }
  802. return true;
  803. }
  804. uint8 Item::GetGemCountWithID(uint32 GemID) const
  805. {
  806. uint8 count = 0;
  807. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot)
  808. {
  809. uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot));
  810. if (!enchant_id)
  811. continue;
  812. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  813. if (!enchantEntry)
  814. continue;
  815. if (GemID == enchantEntry->GemID)
  816. ++count;
  817. }
  818. return count;
  819. }
  820. uint8 Item::GetGemCountWithLimitCategory(uint32 limitCategory) const
  821. {
  822. uint8 count = 0;
  823. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot)
  824. {
  825. uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot));
  826. if (!enchant_id)
  827. continue;
  828. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  829. if (!enchantEntry)
  830. continue;
  831. ItemTemplate const* gemProto = sObjectMgr->GetItemTemplate(enchantEntry->GemID);
  832. if (!gemProto)
  833. continue;
  834. if (gemProto->ItemLimitCategory == limitCategory)
  835. ++count;
  836. }
  837. return count;
  838. }
  839. bool Item::IsLimitedToAnotherMapOrZone(uint32 cur_mapId, uint32 cur_zoneId) const
  840. {
  841. ItemTemplate const* proto = GetTemplate();
  842. return proto && ((proto->Map && proto->Map != cur_mapId) || (proto->Area && proto->Area != cur_zoneId));
  843. }
  844. void Item::SendUpdateSockets()
  845. {
  846. WorldPacket data(SMSG_SOCKET_GEMS_RESULT, 8+4+4+4+4);
  847. data << uint64(GetGUID());
  848. for (uint32 i = SOCK_ENCHANTMENT_SLOT; i <= BONUS_ENCHANTMENT_SLOT; ++i)
  849. data << uint32(GetEnchantmentId(EnchantmentSlot(i)));
  850. GetOwner()->GetSession()->SendPacket(&data);
  851. }
  852. // Though the client has the information in the item's data field,
  853. // we have to send SMSG_ITEM_TIME_UPDATE to display the remaining
  854. // time.
  855. void Item::SendTimeUpdate(Player* owner)
  856. {
  857. uint32 duration = GetUInt32Value(ITEM_FIELD_DURATION);
  858. if (!duration)
  859. return;
  860. WorldPacket data(SMSG_ITEM_TIME_UPDATE, (8+4));
  861. data << uint64(GetGUID());
  862. data << uint32(duration);
  863. owner->GetSession()->SendPacket(&data);
  864. }
  865. Item* Item::CreateItem(uint32 itemEntry, uint32 count, Player const* player)
  866. {
  867. if (count < 1)
  868. return NULL; //don't create item at zero count
  869. ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
  870. if (proto)
  871. {
  872. if (count > proto->GetMaxStackSize())
  873. count = proto->GetMaxStackSize();
  874. ASSERT(count != 0 && "pProto->Stackable == 0 but checked at loading already");
  875. Item* item = NewItemOrBag(proto);
  876. if (item->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_ITEM), itemEntry, player))
  877. {
  878. item->SetCount(count);
  879. return item;
  880. }
  881. else
  882. delete item;
  883. }
  884. else
  885. ASSERT(false);
  886. return NULL;
  887. }
  888. Item* Item::CloneItem(uint32 count, Player const* player) const
  889. {
  890. Item* newItem = CreateItem(GetEntry(), count, player);
  891. if (!newItem)
  892. return NULL;
  893. newItem->SetGuidValue(ITEM_FIELD_CREATOR, GetGuidValue(ITEM_FIELD_CREATOR));
  894. newItem->SetGuidValue(ITEM_FIELD_GIFTCREATOR, GetGuidValue(ITEM_FIELD_GIFTCREATOR));
  895. newItem->SetUInt32Value(ITEM_FIELD_FLAGS, GetUInt32Value(ITEM_FIELD_FLAGS) & ~(ITEM_FLAG_REFUNDABLE | ITEM_FLAG_BOP_TRADEABLE));
  896. newItem->SetUInt32Value(ITEM_FIELD_DURATION, GetUInt32Value(ITEM_FIELD_DURATION));
  897. // player CAN be NULL in which case we must not update random properties because that accesses player's item update queue
  898. if (player)
  899. newItem->SetItemRandomProperties(GetItemRandomPropertyId());
  900. return newItem;
  901. }
  902. bool Item::IsBindedNotWith(Player const* player) const
  903. {
  904. // not binded item
  905. if (!IsSoulBound())
  906. return false;
  907. // own item
  908. if (GetOwnerGUID() == player->GetGUID())
  909. return false;
  910. if (HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
  911. if (allowedGUIDs.find(player->GetGUIDLow()) != allowedGUIDs.end())
  912. return false;
  913. // BOA item case
  914. if (IsBoundAccountWide())
  915. return false;
  916. return true;
  917. }
  918. void Item::BuildUpdate(UpdateDataMapType& data_map)
  919. {
  920. if (Player* owner = GetOwner())
  921. BuildFieldsUpdate(owner, data_map);
  922. ClearUpdateMask(false);
  923. }
  924. void Item::SaveRefundDataToDB()
  925. {
  926. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  927. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
  928. stmt->setUInt32(0, GetGUIDLow());
  929. trans->Append(stmt);
  930. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_REFUND_INSTANCE);
  931. stmt->setUInt32(0, GetGUIDLow());
  932. stmt->setUInt32(1, GetRefundRecipient());
  933. stmt->setUInt32(2, GetPaidMoney());
  934. stmt->setUInt16(3, uint16(GetPaidExtendedCost()));
  935. trans->Append(stmt);
  936. CharacterDatabase.CommitTransaction(trans);
  937. }
  938. void Item::DeleteRefundDataFromDB(SQLTransaction* trans)
  939. {
  940. if (trans)
  941. {
  942. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
  943. stmt->setUInt32(0, GetGUIDLow());
  944. (*trans)->Append(stmt);
  945. }
  946. }
  947. void Item::SetNotRefundable(Player* owner, bool changestate /*=true*/, SQLTransaction* trans /*=NULL*/)
  948. {
  949. if (!HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE))
  950. return;
  951. RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE);
  952. // Following is not applicable in the trading procedure
  953. if (changestate)
  954. SetState(ITEM_CHANGED, owner);
  955. SetRefundRecipient(0);
  956. SetPaidMoney(0);
  957. SetPaidExtendedCost(0);
  958. DeleteRefundDataFromDB(trans);
  959. owner->DeleteRefundReference(GetGUID());
  960. }
  961. void Item::UpdatePlayedTime(Player* owner)
  962. {
  963. /* Here we update our played time
  964. We simply add a number to the current played time,
  965. based on the time elapsed since the last update hereof.
  966. */
  967. // Get current played time
  968. uint32 current_playtime = GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME);
  969. // Calculate time elapsed since last played time update
  970. time_t curtime = time(NULL);
  971. uint32 elapsed = uint32(curtime - m_lastPlayedTimeUpdate);
  972. uint32 new_playtime = current_playtime + elapsed;
  973. // Check if the refund timer has expired yet
  974. if (new_playtime <= 2*HOUR)
  975. {
  976. // No? Proceed.
  977. // Update the data field
  978. SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, new_playtime);
  979. // Flag as changed to get saved to DB
  980. SetState(ITEM_CHANGED, owner);
  981. // Speaks for itself
  982. m_lastPlayedTimeUpdate = curtime;
  983. return;
  984. }
  985. // Yes
  986. SetNotRefundable(owner);
  987. }
  988. uint32 Item::GetPlayedTime()
  989. {
  990. time_t curtime = time(NULL);
  991. uint32 elapsed = uint32(curtime - m_lastPlayedTimeUpdate);
  992. return GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME) + elapsed;
  993. }
  994. bool Item::IsRefundExpired()
  995. {
  996. return (GetPlayedTime() > 2*HOUR);
  997. }
  998. void Item::SetSoulboundTradeable(AllowedLooterSet const& allowedLooters)
  999. {
  1000. SetFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE);
  1001. allowedGUIDs = allowedLooters;
  1002. }
  1003. void Item::ClearSoulboundTradeable(Player* currentOwner)
  1004. {
  1005. RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE);
  1006. if (allowedGUIDs.empty())
  1007. return;
  1008. allowedGUIDs.clear();
  1009. SetState(ITEM_CHANGED, currentOwner);
  1010. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_BOP_TRADE);
  1011. stmt->setUInt32(0, GetGUIDLow());
  1012. CharacterDatabase.Execute(stmt);
  1013. }
  1014. bool Item::CheckSoulboundTradeExpire()
  1015. {
  1016. // called from owner's update - GetOwner() MUST be valid
  1017. if (GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME) + 2*HOUR < GetOwner()->GetTotalPlayedTime())
  1018. {
  1019. ClearSoulboundTradeable(GetOwner());
  1020. return true; // remove from tradeable list
  1021. }
  1022. return false;
  1023. }
  1024. bool Item::CanBeTransmogrified() const
  1025. {
  1026. ItemTemplate const* proto = GetTemplate();
  1027. if (!proto)
  1028. return false;
  1029. if (proto->Quality == ITEM_QUALITY_LEGENDARY)
  1030. return false;
  1031. if (proto->Class != ITEM_CLASS_ARMOR &&
  1032. proto->Class != ITEM_CLASS_WEAPON)
  1033. return false;
  1034. if (proto->Class == ITEM_CLASS_WEAPON && proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE)
  1035. return false;
  1036. if (proto->Flags2 & ITEM_FLAGS_EXTRA_CANNOT_BE_TRANSMOG)
  1037. return false;
  1038. if (!HasStats())
  1039. return false;
  1040. return true;
  1041. }
  1042. bool Item::CanTransmogrify() const
  1043. {
  1044. ItemTemplate const* proto = GetTemplate();
  1045. if (!proto)
  1046. return false;
  1047. if (proto->Flags2 & ITEM_FLAGS_EXTRA_CANNOT_TRANSMOG)
  1048. return false;
  1049. if (proto->Quality == ITEM_QUALITY_LEGENDARY)
  1050. return false;
  1051. if (proto->Class != ITEM_CLASS_ARMOR &&
  1052. proto->Class != ITEM_CLASS_WEAPON)
  1053. return false;
  1054. if (proto->Class == ITEM_CLASS_WEAPON && proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE)
  1055. return false;
  1056. if (proto->Flags2 & ITEM_FLAGS_EXTRA_CAN_TRANSMOG)
  1057. return true;
  1058. if (!HasStats())
  1059. return false;
  1060. return true;
  1061. }
  1062. bool Item::CanTransmogrifyItemWithItem(Item const* transmogrified, Item const* transmogrifier)
  1063. {
  1064. if (!transmogrifier || !transmogrified)
  1065. return false;
  1066. ItemTemplate const* proto1 = transmogrifier->GetTemplate(); // source
  1067. ItemTemplate const* proto2 = transmogrified->GetTemplate(); // dest
  1068. if (proto1->ItemId == proto2->ItemId)
  1069. return false;
  1070. if (!transmogrified->CanTransmogrify() || !transmogrifier->CanBeTransmogrified())
  1071. return false;
  1072. if (proto1->InventoryType == INVTYPE_BAG ||
  1073. proto1->InventoryType == INVTYPE_RELIC ||
  1074. proto1->InventoryType == INVTYPE_BODY ||
  1075. proto1->InventoryType == INVTYPE_FINGER ||
  1076. proto1->InventoryType == INVTYPE_TRINKET ||
  1077. proto1->InventoryType == INVTYPE_AMMO ||
  1078. proto1->InventoryType == INVTYPE_QUIVER)
  1079. return false;
  1080. if (proto1->SubClass != proto2->SubClass && (proto1->Class != ITEM_CLASS_WEAPON || !proto2->IsRangedWeapon() || !proto1->IsRangedWeapon()))
  1081. return false;
  1082. if (proto1->InventoryType != proto2->InventoryType &&
  1083. (proto1->Class != ITEM_CLASS_WEAPON || (proto2->InventoryType != INVTYPE_WEAPONMAINHAND && proto2->InventoryType != INVTYPE_WEAPONOFFHAND)) &&
  1084. (proto1->Class != ITEM_CLASS_ARMOR || (proto1->InventoryType != INVTYPE_CHEST && proto2->InventoryType != INVTYPE_ROBE && proto1->InventoryType != INVTYPE_ROBE && proto2->InventoryType != INVTYPE_CHEST)))
  1085. return false;
  1086. return true;
  1087. }
  1088. bool Item::HasStats() const
  1089. {
  1090. if (GetItemRandomPropertyId() != 0)
  1091. return true;
  1092. ItemTemplate const* proto = GetTemplate();
  1093. for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
  1094. if (proto->ItemStat[i].ItemStatValue != 0)
  1095. return true;
  1096. return false;
  1097. }
  1098. // used by mail items, transmog cost, stationeryinfo and others
  1099. uint32 Item::GetSellPrice(ItemTemplate const* proto, bool& normalSellPrice)
  1100. {
  1101. normalSellPrice = true;
  1102. if (proto->Flags2 & ITEM_FLAGS_EXTRA_HAS_NORMAL_PRICE)
  1103. {
  1104. return proto->BuyPrice;
  1105. }
  1106. else
  1107. {
  1108. ImportPriceQualityEntry const* qualityPrice = sImportPriceQualityStore.LookupEntry(proto->Quality + 1);
  1109. ItemPriceBaseEntry const* basePrice = sItemPriceBaseStore.LookupEntry(proto->ItemLevel);
  1110. if (!qualityPrice || !basePrice)
  1111. return 0;
  1112. float qualityFactor = qualityPrice->Factor;
  1113. float baseFactor = 0.0f;
  1114. uint32 inventoryType = proto->InventoryType;
  1115. if (inventoryType == INVTYPE_WEAPON ||
  1116. inventoryType == INVTYPE_2HWEAPON ||
  1117. inventoryType == INVTYPE_WEAPONMAINHAND ||
  1118. inventoryType == INVTYPE_WEAPONOFFHAND ||
  1119. inventoryType == INVTYPE_RANGED ||
  1120. inventoryType == INVTYPE_THROWN ||
  1121. inventoryType == INVTYPE_RANGEDRIGHT)
  1122. baseFactor = basePrice->WeaponFactor;
  1123. else
  1124. baseFactor = basePrice->ArmorFactor;
  1125. if (inventoryType == INVTYPE_ROBE)
  1126. inventoryType = INVTYPE_CHEST;
  1127. float typeFactor = 0.0f;
  1128. int8 weapType = -1;
  1129. switch (inventoryType)
  1130. {
  1131. case INVTYPE_HEAD:
  1132. case INVTYPE_SHOULDERS:
  1133. case INVTYPE_CHEST:
  1134. case INVTYPE_WAIST:
  1135. case INVTYPE_LEGS:
  1136. case INVTYPE_FEET:
  1137. case INVTYPE_WRISTS:
  1138. case INVTYPE_HANDS:
  1139. case INVTYPE_CLOAK:
  1140. {
  1141. ImportPriceArmorEntry const* armorPrice = sImportPriceArmorStore.LookupEntry(inventoryType);
  1142. if (!armorPrice)
  1143. return 0;
  1144. switch (proto->SubClass)
  1145. {
  1146. case ITEM_SUBCLASS_ARMOR_MISCELLANEOUS:
  1147. case ITEM_SUBCLASS_ARMOR_CLOTH:
  1148. typeFactor = armorPrice->ClothFactor;
  1149. break;
  1150. case ITEM_SUBCLASS_ARMOR_LEATHER:
  1151. typeFactor = armorPrice->LeatherFactor;
  1152. break;
  1153. case ITEM_SUBCLASS_ARMOR_MAIL:
  1154. typeFactor = armorPrice->MailFactor;
  1155. break;
  1156. case ITEM_SUBCLASS_ARMOR_PLATE:
  1157. typeFactor = armorPrice->PlateFactor;
  1158. break;
  1159. default:
  1160. return 0;
  1161. }
  1162. break;
  1163. }
  1164. case INVTYPE_SHIELD:
  1165. {
  1166. ImportPriceShieldEntry const* shieldPrice = sImportPriceShieldStore.LookupEntry(1); // it only has two rows, it's unclear which is the one used
  1167. if (!shieldPrice)
  1168. return 0;
  1169. typeFactor = shieldPrice->Factor;
  1170. break;
  1171. }
  1172. case INVTYPE_WEAPONMAINHAND:
  1173. weapType = 0;
  1174. break;
  1175. case INVTYPE_WEAPONOFFHAND:
  1176. weapType = 1;
  1177. break;
  1178. case INVTYPE_WEAPON:
  1179. weapType = 2;
  1180. break;
  1181. case INVTYPE_2HWEAPON:
  1182. weapType = 3;
  1183. break;
  1184. case INVTYPE_RANGED:
  1185. case INVTYPE_RANGEDRIGHT:
  1186. case INVTYPE_RELIC:
  1187. weapType = 4;
  1188. break;
  1189. default:
  1190. return proto->BuyPrice;
  1191. }
  1192. if (weapType != -1)
  1193. {
  1194. ImportPriceWeaponEntry const* weaponPrice = sImportPriceWeaponStore.LookupEntry(weapType + 1);
  1195. if (!weaponPrice)
  1196. return 0;
  1197. typeFactor = weaponPrice->Factor;
  1198. }
  1199. normalSellPrice = false;
  1200. return uint32(qualityFactor * proto->Unk430_2 * proto->Unk430_1 * typeFactor * baseFactor);
  1201. }
  1202. }
  1203. uint32 Item::GetSpecialPrice(ItemTemplate const* proto, uint32 minimumPrice /*= 10000*/)
  1204. {
  1205. uint32 cost = 0;
  1206. if (proto->Flags2 & ITEM_FLAGS_EXTRA_HAS_NORMAL_PRICE)
  1207. cost = proto->SellPrice;
  1208. else
  1209. {
  1210. bool normalPrice;
  1211. cost = Item::GetSellPrice(proto, normalPrice);
  1212. if (!normalPrice)
  1213. {
  1214. if (proto->BuyCount <= 1)
  1215. {
  1216. ItemClassEntry const* classEntry = sItemClassStore.LookupEntry(proto->Class);
  1217. if (classEntry)
  1218. cost *= classEntry->PriceFactor;
  1219. else
  1220. cost = 0;
  1221. }
  1222. else
  1223. cost /= 4 * proto->BuyCount;
  1224. }
  1225. else
  1226. cost = proto->SellPrice;
  1227. }
  1228. if (cost < minimumPrice)
  1229. cost = minimumPrice;
  1230. return cost;
  1231. }
  1232. int32 Item::GetReforgableStat(ItemModType statType) const
  1233. {
  1234. ItemTemplate const* proto = GetTemplate();
  1235. for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
  1236. if (ItemModType(proto->ItemStat[i].ItemStatType) == statType)
  1237. return proto->ItemStat[i].ItemStatValue;
  1238. int32 randomPropId = GetItemRandomPropertyId();
  1239. if (!randomPropId)
  1240. return 0;
  1241. if (randomPropId < 0)
  1242. {
  1243. ItemRandomSuffixEntry const* randomSuffix = sItemRandomSuffixStore.LookupEntry(-randomPropId);
  1244. if (!randomSuffix)
  1245. return 0;
  1246. for (uint32 e = PROP_ENCHANTMENT_SLOT_0; e <= PROP_ENCHANTMENT_SLOT_4; ++e)
  1247. if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(GetEnchantmentId(EnchantmentSlot(e))))
  1248. for (uint32 f = 0; f < MAX_ITEM_ENCHANTMENT_EFFECTS; ++f)
  1249. if (enchant->type[f] == ITEM_ENCHANTMENT_TYPE_STAT && ItemModType(enchant->spellid[f]) == statType)
  1250. for (int k = 0; k < 5; ++k)
  1251. if (randomSuffix->enchant_id[k] == enchant->ID)
  1252. return int32((randomSuffix->prefix[k] * GetItemSuffixFactor()) / 10000);
  1253. }
  1254. else
  1255. {
  1256. ItemRandomPropertiesEntry const* randomProp = sItemRandomPropertiesStore.LookupEntry(randomPropId);
  1257. if (!randomProp)
  1258. return 0;
  1259. for (uint32 e = PROP_ENCHANTMENT_SLOT_0; e <= PROP_ENCHAN

Large files files are truncated, but you can click here to view the full file