/src/server/game/Entities/Player/Player.cpp

https://gitlab.com/IlluminatiCore/IlluminatiCore · C++ · 28013 lines · 21766 code · 4305 blank · 1942 comment · 6571 complexity · 52a59747d8ad70a97d9fdd1dc2962043 MD5 · raw 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 "Player.h"
  19. #include "AccountMgr.h"
  20. #include "AchievementMgr.h"
  21. #include "ArenaTeam.h"
  22. #include "ArenaTeamMgr.h"
  23. #include "Battlefield.h"
  24. #include "BattlefieldMgr.h"
  25. #include "BattlefieldWG.h"
  26. #include "Battleground.h"
  27. #include "BattlegroundMgr.h"
  28. #include "BattlegroundScore.h"
  29. #include "CellImpl.h"
  30. #include "Channel.h"
  31. #include "ChannelMgr.h"
  32. #include "CharacterDatabaseCleaner.h"
  33. #include "Chat.h"
  34. #include "Common.h"
  35. #include "ConditionMgr.h"
  36. #include "CreatureAI.h"
  37. #include "DatabaseEnv.h"
  38. #include "DB2Stores.h"
  39. #include "DisableMgr.h"
  40. #include "Formulas.h"
  41. #include "GameEventMgr.h"
  42. #include "GossipDef.h"
  43. #include "GridNotifiers.h"
  44. #include "GridNotifiersImpl.h"
  45. #include "Group.h"
  46. #include "GroupMgr.h"
  47. #include "Guild.h"
  48. #include "GuildMgr.h"
  49. #include "InstanceSaveMgr.h"
  50. #include "InstanceScript.h"
  51. #include "LFGMgr.h"
  52. #include "Language.h"
  53. #include "Log.h"
  54. #include "MapInstanced.h"
  55. #include "MapManager.h"
  56. #include "ObjectAccessor.h"
  57. #include "ObjectMgr.h"
  58. #include "Opcodes.h"
  59. #include "OutdoorPvP.h"
  60. #include "OutdoorPvPMgr.h"
  61. #include "Pet.h"
  62. #include "QuestDef.h"
  63. #include "ReputationMgr.h"
  64. #include "revision.h"
  65. #include "SkillDiscovery.h"
  66. #include "SocialMgr.h"
  67. #include "Spell.h"
  68. #include "SpellAuraEffects.h"
  69. #include "SpellAuras.h"
  70. #include "SpellMgr.h"
  71. #include "Transport.h"
  72. #include "UpdateData.h"
  73. #include "UpdateFieldFlags.h"
  74. #include "UpdateMask.h"
  75. #include "Util.h"
  76. #include "Vehicle.h"
  77. #include "Weather.h"
  78. #include "WeatherMgr.h"
  79. #include "World.h"
  80. #include "WorldPacket.h"
  81. #include "WorldSession.h"
  82. #include "MovementStructures.h"
  83. #include "GameObjectAI.h"
  84. #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)
  85. enum CharacterFlags
  86. {
  87. CHARACTER_FLAG_NONE = 0x00000000,
  88. CHARACTER_FLAG_UNK1 = 0x00000001,
  89. CHARACTER_FLAG_UNK2 = 0x00000002,
  90. CHARACTER_LOCKED_FOR_TRANSFER = 0x00000004,
  91. CHARACTER_FLAG_UNK4 = 0x00000008,
  92. CHARACTER_FLAG_UNK5 = 0x00000010,
  93. CHARACTER_FLAG_UNK6 = 0x00000020,
  94. CHARACTER_FLAG_UNK7 = 0x00000040,
  95. CHARACTER_FLAG_UNK8 = 0x00000080,
  96. CHARACTER_FLAG_UNK9 = 0x00000100,
  97. CHARACTER_FLAG_UNK10 = 0x00000200,
  98. CHARACTER_FLAG_HIDE_HELM = 0x00000400,
  99. CHARACTER_FLAG_HIDE_CLOAK = 0x00000800,
  100. CHARACTER_FLAG_UNK13 = 0x00001000,
  101. CHARACTER_FLAG_GHOST = 0x00002000,
  102. CHARACTER_FLAG_RENAME = 0x00004000,
  103. CHARACTER_FLAG_UNK16 = 0x00008000,
  104. CHARACTER_FLAG_UNK17 = 0x00010000,
  105. CHARACTER_FLAG_UNK18 = 0x00020000,
  106. CHARACTER_FLAG_UNK19 = 0x00040000,
  107. CHARACTER_FLAG_UNK20 = 0x00080000,
  108. CHARACTER_FLAG_UNK21 = 0x00100000,
  109. CHARACTER_FLAG_UNK22 = 0x00200000,
  110. CHARACTER_FLAG_UNK23 = 0x00400000,
  111. CHARACTER_FLAG_UNK24 = 0x00800000,
  112. CHARACTER_FLAG_LOCKED_BY_BILLING = 0x01000000,
  113. CHARACTER_FLAG_DECLINED = 0x02000000,
  114. CHARACTER_FLAG_UNK27 = 0x04000000,
  115. CHARACTER_FLAG_UNK28 = 0x08000000,
  116. CHARACTER_FLAG_UNK29 = 0x10000000,
  117. CHARACTER_FLAG_UNK30 = 0x20000000,
  118. CHARACTER_FLAG_UNK31 = 0x40000000,
  119. CHARACTER_FLAG_UNK32 = 0x80000000
  120. };
  121. enum CharacterCustomizeFlags
  122. {
  123. CHAR_CUSTOMIZE_FLAG_NONE = 0x00000000,
  124. CHAR_CUSTOMIZE_FLAG_CUSTOMIZE = 0x00000001, // name, gender, etc...
  125. CHAR_CUSTOMIZE_FLAG_FACTION = 0x00010000, // name, gender, faction, etc...
  126. CHAR_CUSTOMIZE_FLAG_RACE = 0x00100000 // name, gender, race, etc...
  127. };
  128. // corpse reclaim times
  129. #define DEATH_EXPIRE_STEP (5*MINUTE)
  130. #define MAX_DEATH_COUNT 3
  131. static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
  132. uint32 const MasterySpells[MAX_CLASSES] =
  133. {
  134. 0,
  135. 87500, // Warrior
  136. 87494, // Paladin
  137. 87493, // Hunter
  138. 87496, // Rogue
  139. 87495, // Priest
  140. 87492, // Death Knight
  141. 87497, // Shaman
  142. 86467, // Mage
  143. 87498, // Warlock
  144. 0,
  145. 87491, // Druid
  146. };
  147. uint64 const MAX_MONEY_AMOUNT = 9999999999ULL;
  148. // == PlayerTaxi ================================================
  149. PlayerTaxi::PlayerTaxi()
  150. {
  151. memset(m_taximask, 0, sizeof(m_taximask));
  152. }
  153. void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level)
  154. {
  155. // class specific initial known nodes
  156. switch (chrClass)
  157. {
  158. case CLASS_DEATH_KNIGHT:
  159. {
  160. for (uint8 i = 0; i < TaxiMaskSize; ++i)
  161. m_taximask[i] |= sOldContinentsNodesMask[i];
  162. break;
  163. }
  164. }
  165. // race specific initial known nodes: capital and taxi hub masks
  166. switch (race)
  167. {
  168. case RACE_HUMAN: SetTaximaskNode(2); break; // Human
  169. case RACE_ORC: SetTaximaskNode(23); break; // Orc
  170. case RACE_DWARF: SetTaximaskNode(6); break; // Dwarf
  171. case RACE_NIGHTELF: SetTaximaskNode(26);
  172. SetTaximaskNode(27); break; // Night Elf
  173. case RACE_UNDEAD_PLAYER: SetTaximaskNode(11); break;// Undead
  174. case RACE_TAUREN: SetTaximaskNode(22); break; // Tauren
  175. case RACE_GNOME: SetTaximaskNode(6); break; // Gnome
  176. case RACE_TROLL: SetTaximaskNode(23); break; // Troll
  177. case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf
  178. case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei
  179. }
  180. // new continent starting masks (It will be accessible only at new map)
  181. switch (Player::TeamForRace(race))
  182. {
  183. case ALLIANCE: SetTaximaskNode(100); break;
  184. case HORDE: SetTaximaskNode(99); break;
  185. }
  186. // level dependent taxi hubs
  187. if (level >= 68)
  188. SetTaximaskNode(213); //Shattered Sun Staging Area
  189. }
  190. void PlayerTaxi::LoadTaxiMask(std::string const &data)
  191. {
  192. Tokenizer tokens(data, ' ');
  193. uint8 index = 0;
  194. for (Tokenizer::const_iterator iter = tokens.begin(); index < TaxiMaskSize && iter != tokens.end(); ++iter, ++index)
  195. {
  196. // load and set bits only for existing taxi nodes
  197. m_taximask[index] = sTaxiNodesMask[index] & uint32(atol(*iter));
  198. }
  199. }
  200. void PlayerTaxi::AppendTaximaskTo(ByteBuffer& data, bool all)
  201. {
  202. data << uint32(TaxiMaskSize);
  203. if (all)
  204. {
  205. for (uint8 i = 0; i < TaxiMaskSize; ++i)
  206. data << uint8(sTaxiNodesMask[i]); // all existed nodes
  207. }
  208. else
  209. {
  210. for (uint8 i = 0; i < TaxiMaskSize; ++i)
  211. data << uint8(m_taximask[i]); // known nodes
  212. }
  213. }
  214. bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, uint32 team)
  215. {
  216. ClearTaxiDestinations();
  217. Tokenizer Tokenizer(values, ' ');
  218. for (Tokenizer::const_iterator iter = Tokenizer.begin(); iter != Tokenizer.end(); ++iter)
  219. {
  220. uint32 node = uint32(atol(*iter));
  221. AddTaxiDestination(node);
  222. }
  223. if (m_TaxiDestinations.empty())
  224. return true;
  225. // Check integrity
  226. if (m_TaxiDestinations.size() < 2)
  227. return false;
  228. for (size_t i = 1; i < m_TaxiDestinations.size(); ++i)
  229. {
  230. uint32 cost;
  231. uint32 path;
  232. sObjectMgr->GetTaxiPath(m_TaxiDestinations[i-1], m_TaxiDestinations[i], path, cost);
  233. if (!path)
  234. return false;
  235. }
  236. // can't load taxi path without mount set (quest taxi path?)
  237. if (!sObjectMgr->GetTaxiMountDisplayId(GetTaxiSource(), team, true))
  238. return false;
  239. return true;
  240. }
  241. std::string PlayerTaxi::SaveTaxiDestinationsToString()
  242. {
  243. if (m_TaxiDestinations.empty())
  244. return "";
  245. std::ostringstream ss;
  246. for (size_t i=0; i < m_TaxiDestinations.size(); ++i)
  247. ss << m_TaxiDestinations[i] << ' ';
  248. return ss.str();
  249. }
  250. uint32 PlayerTaxi::GetCurrentTaxiPath() const
  251. {
  252. if (m_TaxiDestinations.size() < 2)
  253. return 0;
  254. uint32 path;
  255. uint32 cost;
  256. sObjectMgr->GetTaxiPath(m_TaxiDestinations[0], m_TaxiDestinations[1], path, cost);
  257. return path;
  258. }
  259. std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
  260. {
  261. for (uint8 i = 0; i < TaxiMaskSize; ++i)
  262. ss << uint32(taxi.m_taximask[i]) << ' ';
  263. return ss;
  264. }
  265. //== TradeData =================================================
  266. TradeData* TradeData::GetTraderData() const
  267. {
  268. return m_trader->GetTradeData();
  269. }
  270. Item* TradeData::GetItem(TradeSlots slot) const
  271. {
  272. return m_items[slot] ? m_player->GetItemByGuid(m_items[slot]) : NULL;
  273. }
  274. bool TradeData::HasItem(ObjectGuid itemGuid) const
  275. {
  276. for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
  277. if (m_items[i] == itemGuid)
  278. return true;
  279. return false;
  280. }
  281. TradeSlots TradeData::GetTradeSlotForItem(ObjectGuid itemGuid) const
  282. {
  283. for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
  284. if (m_items[i] == itemGuid)
  285. return TradeSlots(i);
  286. return TRADE_SLOT_INVALID;
  287. }
  288. Item* TradeData::GetSpellCastItem() const
  289. {
  290. return m_spellCastItem ? m_player->GetItemByGuid(m_spellCastItem) : NULL;
  291. }
  292. void TradeData::SetItem(TradeSlots slot, Item* item)
  293. {
  294. ObjectGuid itemGuid;
  295. if (item)
  296. itemGuid = item->GetGUID();
  297. if (m_items[slot] == itemGuid)
  298. return;
  299. m_items[slot] = itemGuid;
  300. SetAccepted(false);
  301. GetTraderData()->SetAccepted(false);
  302. Update();
  303. // need remove possible trader spell applied to changed item
  304. if (slot == TRADE_SLOT_NONTRADED)
  305. GetTraderData()->SetSpell(0);
  306. // need remove possible player spell applied (possible move reagent)
  307. SetSpell(0);
  308. }
  309. void TradeData::SetSpell(uint32 spell_id, Item* castItem /*= NULL*/)
  310. {
  311. ObjectGuid itemGuid = castItem ? castItem->GetGUID() : ObjectGuid::Empty;
  312. if (m_spell == spell_id && m_spellCastItem == itemGuid)
  313. return;
  314. m_spell = spell_id;
  315. m_spellCastItem = itemGuid;
  316. SetAccepted(false);
  317. GetTraderData()->SetAccepted(false);
  318. Update(true); // send spell info to item owner
  319. Update(false); // send spell info to caster self
  320. }
  321. void TradeData::SetMoney(uint64 money)
  322. {
  323. if (m_money == money)
  324. return;
  325. if (!m_player->HasEnoughMoney(money))
  326. {
  327. TradeStatusInfo info;
  328. info.Status = TRADE_STATUS_CLOSE_WINDOW;
  329. info.Result = EQUIP_ERR_NOT_ENOUGH_MONEY;
  330. m_player->GetSession()->SendTradeStatus(info);
  331. return;
  332. }
  333. m_money = money;
  334. SetAccepted(false);
  335. GetTraderData()->SetAccepted(false);
  336. Update(true);
  337. }
  338. void TradeData::Update(bool forTarget /*= true*/)
  339. {
  340. if (forTarget)
  341. m_trader->GetSession()->SendUpdateTrade(true); // player state for trader
  342. else
  343. m_player->GetSession()->SendUpdateTrade(false); // player state for player
  344. }
  345. void TradeData::SetAccepted(bool state, bool crosssend /*= false*/)
  346. {
  347. m_accepted = state;
  348. if (!state)
  349. {
  350. TradeStatusInfo info;
  351. info.Status = TRADE_STATUS_BACK_TO_TRADE;
  352. if (crosssend)
  353. m_trader->GetSession()->SendTradeStatus(info);
  354. else
  355. m_player->GetSession()->SendTradeStatus(info);
  356. }
  357. }
  358. // == KillRewarder ====================================================
  359. // KillRewarder incapsulates logic of rewarding player upon kill with:
  360. // * XP;
  361. // * honor;
  362. // * reputation;
  363. // * kill credit (for quest objectives).
  364. // Rewarding is initiated in two cases: when player kills unit in Unit::Kill()
  365. // and on battlegrounds in Battleground::RewardXPAtKill().
  366. //
  367. // Rewarding algorithm is:
  368. // 1. Initialize internal variables to default values.
  369. // 2. In case when player is in group, initialize variables necessary for group calculations:
  370. // 2.1. _count - number of alive group members within reward distance;
  371. // 2.2. _sumLevel - sum of levels of alive group members within reward distance;
  372. // 2.3. _maxLevel - maximum level of alive group member within reward distance;
  373. // 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
  374. // for whom victim is not gray;
  375. // 2.5. _isFullXP - flag identifying that for all group members victim is not gray,
  376. // so 100% XP will be rewarded (50% otherwise).
  377. // 3. Reward killer (and group, if necessary).
  378. // 3.1. If killer is in group, reward group.
  379. // 3.1.1. Initialize initial XP amount based on maximum level of group member,
  380. // for whom victim is not gray.
  381. // 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
  382. // 3.1.3. Reward each group member (even dead) within reward distance (see 4. for more details).
  383. // 3.2. Reward single killer (not group case).
  384. // 3.2.1. Initialize initial XP amount based on killer's level.
  385. // 3.2.2. Reward killer (see 4. for more details).
  386. // 4. Reward player.
  387. // 4.1. Give honor (player must be alive and not on BG).
  388. // 4.2. Give XP.
  389. // 4.2.1. If player is in group, adjust XP:
  390. // * set to 0 if player's level is more than maximum level of not gray member;
  391. // * cut XP in half if _isFullXP is false.
  392. // 4.2.2. Apply auras modifying rewarded XP.
  393. // 4.2.3. Give XP to player.
  394. // 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
  395. // 4.3. Give reputation (player must not be on BG).
  396. // 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
  397. // 5. Credit instance encounter.
  398. // 6. Update guild achievements.
  399. KillRewarder::KillRewarder(Player* killer, Unit* victim, bool isBattleGround) :
  400. // 1. Initialize internal variables to default values.
  401. _killer(killer), _victim(victim), _group(killer->GetGroup()),
  402. _groupRate(1.0f), _maxNotGrayMember(NULL), _count(0), _sumLevel(0), _xp(0),
  403. _isFullXP(false), _maxLevel(0), _isBattleGround(isBattleGround), _isPvP(false)
  404. {
  405. // mark the credit as pvp if victim is player
  406. if (victim->GetTypeId() == TYPEID_PLAYER)
  407. _isPvP = true;
  408. // or if its owned by player and its not a vehicle
  409. else if (victim->GetCharmerOrOwnerGUID().IsPlayer())
  410. _isPvP = !victim->IsVehicle();
  411. _InitGroupData();
  412. }
  413. inline void KillRewarder::_InitGroupData()
  414. {
  415. if (_group)
  416. {
  417. // 2. In case when player is in group, initialize variables necessary for group calculations:
  418. for (GroupReference* itr = _group->GetFirstMember(); itr != NULL; itr = itr->next())
  419. if (Player* member = itr->GetSource())
  420. if (member->IsAlive() && member->IsAtGroupRewardDistance(_victim))
  421. {
  422. const uint8 lvl = member->getLevel();
  423. // 2.1. _count - number of alive group members within reward distance;
  424. ++_count;
  425. // 2.2. _sumLevel - sum of levels of alive group members within reward distance;
  426. _sumLevel += lvl;
  427. // 2.3. _maxLevel - maximum level of alive group member within reward distance;
  428. if (_maxLevel < lvl)
  429. _maxLevel = lvl;
  430. // 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
  431. // for whom victim is not gray;
  432. uint32 grayLevel = Trinity::XP::GetGrayLevel(lvl);
  433. if (_victim->getLevel() > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->getLevel() < lvl))
  434. _maxNotGrayMember = member;
  435. }
  436. // 2.5. _isFullXP - flag identifying that for all group members victim is not gray,
  437. // so 100% XP will be rewarded (50% otherwise).
  438. _isFullXP = _maxNotGrayMember && (_maxLevel == _maxNotGrayMember->getLevel());
  439. }
  440. else
  441. _count = 1;
  442. }
  443. inline void KillRewarder::_InitXP(Player* player)
  444. {
  445. // Get initial value of XP for kill.
  446. // XP is given:
  447. // * on battlegrounds;
  448. // * otherwise, not in PvP;
  449. // * not if killer is on vehicle.
  450. if (_isBattleGround || (!_isPvP && !_killer->GetVehicle()))
  451. _xp = Trinity::XP::Gain(player, _victim);
  452. }
  453. inline void KillRewarder::_RewardHonor(Player* player)
  454. {
  455. // Rewarded player must be alive.
  456. if (player->IsAlive())
  457. player->RewardHonor(_victim, _count, -1, true);
  458. }
  459. inline void KillRewarder::_RewardXP(Player* player, float rate)
  460. {
  461. uint32 xp(_xp);
  462. if (_group)
  463. {
  464. // 4.2.1. If player is in group, adjust XP:
  465. // * set to 0 if player's level is more than maximum level of not gray member;
  466. // * cut XP in half if _isFullXP is false.
  467. if (_maxNotGrayMember && player->IsAlive() &&
  468. _maxNotGrayMember->getLevel() >= player->getLevel())
  469. xp = _isFullXP ?
  470. uint32(xp * rate) : // Reward FULL XP if all group members are not gray.
  471. uint32(xp * rate / 2) + 1; // Reward only HALF of XP if some of group members are gray.
  472. else
  473. xp = 0;
  474. }
  475. if (xp)
  476. {
  477. // 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT).
  478. Unit::AuraEffectList const& auras = player->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT);
  479. for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i)
  480. AddPct(xp, (*i)->GetAmount());
  481. // 4.2.3. Calculate expansion penalty
  482. if (_victim->GetTypeId() == TYPEID_UNIT && player->getLevel() >= GetMaxLevelForExpansion(_victim->ToCreature()->GetCreatureTemplate()->expansion))
  483. xp = CalculatePct(xp, 10); // Players get only 10% xp for killing creatures of lower expansion levels than himself
  484. // 4.2.4. Give XP to player.
  485. player->GiveXP(xp, _victim, _groupRate);
  486. if (Pet* pet = player->GetPet())
  487. // 4.2.5. If player has pet, reward pet with XP (100% for single player, 50% for group case).
  488. pet->GivePetXP(_group ? xp / 2 : xp);
  489. }
  490. }
  491. inline void KillRewarder::_RewardReputation(Player* player, float rate)
  492. {
  493. // 4.3. Give reputation (player must not be on BG).
  494. // Even dead players and corpses are rewarded.
  495. player->RewardReputation(_victim, rate);
  496. }
  497. inline void KillRewarder::_RewardKillCredit(Player* player)
  498. {
  499. // 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
  500. if (!_group || player->IsAlive() || !player->GetCorpse())
  501. if (Creature* target = _victim->ToCreature())
  502. {
  503. player->KilledMonster(target->GetCreatureTemplate(), target->GetGUID());
  504. player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE, target->GetCreatureType(), 1, 0, target);
  505. }
  506. }
  507. void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
  508. {
  509. // 4. Reward player.
  510. if (!_isBattleGround)
  511. {
  512. // 4.1. Give honor (player must be alive and not on BG).
  513. _RewardHonor(player);
  514. // 4.1.1 Send player killcredit for quests with PlayerSlain
  515. if (_victim->GetTypeId() == TYPEID_PLAYER)
  516. player->KilledPlayerCredit();
  517. }
  518. // Give XP only in PvE or in battlegrounds.
  519. // Give reputation and kill credit only in PvE.
  520. if (!_isPvP || _isBattleGround)
  521. {
  522. const float rate = _group ?
  523. _groupRate * float(player->getLevel()) / _sumLevel : // Group rate depends on summary level.
  524. 1.0f; // Personal rate is 100%.
  525. if (_xp)
  526. // 4.2. Give XP.
  527. _RewardXP(player, rate);
  528. if (!_isBattleGround)
  529. {
  530. // If killer is in dungeon then all members receive full reputation at kill.
  531. _RewardReputation(player, isDungeon ? 1.0f : rate);
  532. _RewardKillCredit(player);
  533. }
  534. }
  535. }
  536. void KillRewarder::_RewardGroup()
  537. {
  538. if (_maxLevel)
  539. {
  540. if (_maxNotGrayMember)
  541. // 3.1.1. Initialize initial XP amount based on maximum level of group member,
  542. // for whom victim is not gray.
  543. _InitXP(_maxNotGrayMember);
  544. // To avoid unnecessary calculations and calls,
  545. // proceed only if XP is not ZERO or player is not on battleground
  546. // (battleground rewards only XP, that's why).
  547. if (!_isBattleGround || _xp)
  548. {
  549. const bool isDungeon = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsDungeon();
  550. if (!_isBattleGround)
  551. {
  552. // 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
  553. const bool isRaid = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsRaid() && _group->isRaidGroup();
  554. _groupRate = Trinity::XP::xp_in_group_rate(_count, isRaid);
  555. }
  556. // 3.1.3. Reward each group member (even dead or corpse) within reward distance.
  557. for (GroupReference* itr = _group->GetFirstMember(); itr != NULL; itr = itr->next())
  558. {
  559. if (Player* member = itr->GetSource())
  560. {
  561. if (member->IsAtGroupRewardDistance(_victim))
  562. {
  563. _RewardPlayer(member, isDungeon);
  564. member->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL, 1, 0, 0, _victim);
  565. }
  566. }
  567. }
  568. }
  569. }
  570. }
  571. void KillRewarder::Reward()
  572. {
  573. // 3. Reward killer (and group, if necessary).
  574. if (_group)
  575. // 3.1. If killer is in group, reward group.
  576. _RewardGroup();
  577. else
  578. {
  579. // 3.2. Reward single killer (not group case).
  580. // 3.2.1. Initialize initial XP amount based on killer's level.
  581. _InitXP(_killer);
  582. // To avoid unnecessary calculations and calls,
  583. // proceed only if XP is not ZERO or player is not on battleground
  584. // (battleground rewards only XP, that's why).
  585. if (!_isBattleGround || _xp)
  586. // 3.2.2. Reward killer.
  587. _RewardPlayer(_killer, false);
  588. }
  589. // 5. Credit instance encounter.
  590. // 6. Update guild achievements.
  591. if (Creature* victim = _victim->ToCreature())
  592. {
  593. if (victim->IsDungeonBoss())
  594. if (InstanceScript* instance = _victim->GetInstanceScript())
  595. instance->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, _victim->GetEntry(), _victim);
  596. if (uint32 guildId = victim->GetMap()->GetOwnerGuildId())
  597. if (Guild* guild = sGuildMgr->GetGuildById(guildId))
  598. guild->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, victim->GetEntry(), 1, 0, victim, _killer);
  599. }
  600. }
  601. Player::Player(WorldSession* session): Unit(true)
  602. {
  603. m_speakTime = 0;
  604. m_speakCount = 0;
  605. m_objectType |= TYPEMASK_PLAYER;
  606. m_objectTypeId = TYPEID_PLAYER;
  607. m_valuesCount = PLAYER_END;
  608. m_session = session;
  609. m_ingametime = 0;
  610. m_ExtraFlags = 0;
  611. m_spellModTakingSpell = NULL;
  612. //m_pad = 0;
  613. // players always accept
  614. if (!GetSession()->HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS))
  615. SetAcceptWhispers(true);
  616. m_comboPoints = 0;
  617. m_regenTimer = 0;
  618. m_regenTimerCount = 0;
  619. m_holyPowerRegenTimerCount = 0;
  620. m_focusRegenTimerCount = 0;
  621. m_weaponChangeTimer = 0;
  622. m_zoneUpdateId = uint32(-1);
  623. m_zoneUpdateTimer = 0;
  624. m_areaUpdateId = 0;
  625. m_team = 0;
  626. m_needsZoneUpdate = false;
  627. m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE);
  628. _resurrectionData = NULL;
  629. memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT);
  630. m_social = NULL;
  631. // group is initialized in the reference constructor
  632. SetGroupInvite(NULL);
  633. m_groupUpdateMask = 0;
  634. m_auraRaidUpdateMask = 0;
  635. m_bPassOnGroupLoot = false;
  636. duel = NULL;
  637. m_GuildIdInvited = 0;
  638. m_ArenaTeamIdInvited = 0;
  639. m_atLoginFlags = AT_LOGIN_NONE;
  640. mSemaphoreTeleport_Near = false;
  641. mSemaphoreTeleport_Far = false;
  642. m_DelayedOperations = 0;
  643. m_bCanDelayTeleport = false;
  644. m_bHasDelayedTeleport = false;
  645. m_teleport_options = 0;
  646. m_trade = NULL;
  647. m_cinematic = 0;
  648. PlayerTalkClass = new PlayerMenu(GetSession());
  649. m_currentBuybackSlot = BUYBACK_SLOT_START;
  650. m_DailyQuestChanged = false;
  651. m_lastDailyQuestTime = 0;
  652. // Init rune flags
  653. for (uint8 i = 0; i < MAX_RUNES; ++i)
  654. {
  655. SetRuneTimer(i, 0xFFFFFFFF);
  656. SetLastRuneGraceTimer(i, 0);
  657. }
  658. for (uint8 i=0; i < MAX_TIMERS; i++)
  659. m_MirrorTimer[i] = DISABLED_MIRROR_TIMER;
  660. m_MirrorTimerFlags = UNDERWATER_NONE;
  661. m_MirrorTimerFlagsLast = UNDERWATER_NONE;
  662. m_isInWater = false;
  663. m_drunkTimer = 0;
  664. m_restTime = 0;
  665. m_deathTimer = 0;
  666. m_deathExpireTime = 0;
  667. m_swingErrorMsg = 0;
  668. for (uint8 j = 0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j)
  669. {
  670. m_bgBattlegroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE;
  671. m_bgBattlegroundQueueID[j].invitedToInstance = 0;
  672. }
  673. m_logintime = time(NULL);
  674. m_Last_tick = m_logintime;
  675. m_Played_time[PLAYED_TIME_TOTAL] = 0;
  676. m_Played_time[PLAYED_TIME_LEVEL] = 0;
  677. m_WeaponProficiency = 0;
  678. m_ArmorProficiency = 0;
  679. m_canParry = false;
  680. m_canBlock = false;
  681. m_canTitanGrip = false;
  682. m_temporaryUnsummonedPetNumber = 0;
  683. //cache for UNIT_CREATED_BY_SPELL to allow
  684. //returning reagents for temporarily removed pets
  685. //when dying/logging out
  686. m_oldpetspell = 0;
  687. m_lastpetnumber = 0;
  688. ////////////////////Rest System/////////////////////
  689. time_inn_enter = 0;
  690. inn_pos_mapid = 0;
  691. inn_pos_x = 0.0f;
  692. inn_pos_y = 0.0f;
  693. inn_pos_z = 0.0f;
  694. m_rest_bonus = 0;
  695. rest_type = REST_TYPE_NO;
  696. ////////////////////Rest System/////////////////////
  697. m_mailsLoaded = false;
  698. m_mailsUpdated = false;
  699. unReadMails = 0;
  700. m_nextMailDelivereTime = 0;
  701. m_itemUpdateQueueBlocked = false;
  702. for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
  703. m_forced_speed_changes[i] = 0;
  704. m_stableSlots = 0;
  705. /////////////////// Instance System /////////////////////
  706. m_HomebindTimer = 0;
  707. m_InstanceValid = true;
  708. m_dungeonDifficulty = DUNGEON_DIFFICULTY_NORMAL;
  709. m_raidDifficulty = RAID_DIFFICULTY_10MAN_NORMAL;
  710. m_raidMapDifficulty = RAID_DIFFICULTY_10MAN_NORMAL;
  711. m_lastPotionId = 0;
  712. _talentMgr = new PlayerTalentInfo();
  713. for (uint8 i = 0; i < BASEMOD_END; ++i)
  714. {
  715. m_auraBaseMod[i][FLAT_MOD] = 0.0f;
  716. m_auraBaseMod[i][PCT_MOD] = 1.0f;
  717. }
  718. for (uint8 i = 0; i < MAX_COMBAT_RATING; i++)
  719. m_baseRatingValue[i] = 0;
  720. m_baseSpellPower = 0;
  721. m_baseManaRegen = 0;
  722. m_baseHealthRegen = 0;
  723. m_spellPenetrationItemMod = 0;
  724. // Honor System
  725. m_lastHonorUpdateTime = time(NULL);
  726. m_IsBGRandomWinner = false;
  727. // Player summoning
  728. m_summon_expire = 0;
  729. m_summon_mapid = 0;
  730. m_summon_x = 0.0f;
  731. m_summon_y = 0.0f;
  732. m_summon_z = 0.0f;
  733. m_mover = this;
  734. m_movedPlayer = this;
  735. m_seer = this;
  736. m_recallMap = 0;
  737. m_recallX = 0;
  738. m_recallY = 0;
  739. m_recallZ = 0;
  740. m_recallO = 0;
  741. m_homebindMapId = 0;
  742. m_homebindAreaId = 0;
  743. m_homebindX = 0;
  744. m_homebindY = 0;
  745. m_homebindZ = 0;
  746. m_contestedPvPTimer = 0;
  747. m_declinedname = NULL;
  748. m_isActive = true;
  749. m_runes = NULL;
  750. m_lastFallTime = 0;
  751. m_lastFallZ = 0;
  752. m_grantableLevels = 0;
  753. m_ControlledByPlayer = true;
  754. sWorld->IncreasePlayerCount();
  755. m_ChampioningFaction = 0;
  756. m_timeSyncTimer = 0;
  757. m_timeSyncClient = 0;
  758. m_timeSyncServer = 0;
  759. for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i)
  760. m_powerFraction[i] = 0;
  761. isDebugAreaTriggers = false;
  762. m_WeeklyQuestChanged = false;
  763. m_MonthlyQuestChanged = false;
  764. m_SeasonalQuestChanged = false;
  765. SetPendingBind(0, 0);
  766. _activeCheats = CHEAT_NONE;
  767. _maxPersonalArenaRate = 0;
  768. memset(_voidStorageItems, 0, VOID_STORAGE_MAX_SLOT * sizeof(VoidStorageItem*));
  769. memset(_CUFProfiles, 0, MAX_CUF_PROFILES * sizeof(CUFProfile*));
  770. m_achievementMgr = new AchievementMgr<Player>(this);
  771. m_reputationMgr = new ReputationMgr(this);
  772. }
  773. Player::~Player()
  774. {
  775. // it must be unloaded already in PlayerLogout and accessed only for loggined player
  776. //m_social = NULL;
  777. // Note: buy back item already deleted from DB when player was saved
  778. for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; ++i)
  779. delete m_items[i];
  780. for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
  781. delete itr->second;
  782. delete _talentMgr;
  783. //all mailed items should be deleted, also all mail should be deallocated
  784. for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
  785. delete *itr;
  786. for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
  787. delete iter->second; //if item is duplicated... then server may crash ... but that item should be deallocated
  788. delete PlayerTalkClass;
  789. for (size_t x = 0; x < ItemSetEff.size(); x++)
  790. delete ItemSetEff[x];
  791. delete m_declinedname;
  792. delete m_runes;
  793. delete m_achievementMgr;
  794. delete m_reputationMgr;
  795. for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
  796. delete _voidStorageItems[i];
  797. for (uint8 i = 0; i < MAX_CUF_PROFILES; ++i)
  798. delete _CUFProfiles[i];
  799. ClearResurrectRequestData();
  800. sWorld->DecreasePlayerCount();
  801. }
  802. void Player::CleanupsBeforeDelete(bool finalCleanup)
  803. {
  804. TradeCancel(false);
  805. DuelComplete(DUEL_INTERRUPTED);
  806. Unit::CleanupsBeforeDelete(finalCleanup);
  807. // clean up player-instance binds, may unload some instance saves
  808. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  809. for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
  810. itr->second.save->RemovePlayer(this);
  811. }
  812. bool Player::Create(uint32 guidlow, CharacterCreateInfo* createInfo)
  813. {
  814. //FIXME: outfitId not used in player creating
  815. /// @todo need more checks against packet modifications
  816. // should check that skin, face, hair* are valid via DBC per race/class
  817. // also do it in Player::BuildEnumData, Player::LoadFromDB
  818. Object::_Create(guidlow, 0, HIGHGUID_PLAYER);
  819. m_name = createInfo->Name;
  820. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(createInfo->Race, createInfo->Class);
  821. if (!info)
  822. {
  823. TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid race/class pair (%u/%u) - refusing to do so.",
  824. GetSession()->GetAccountId(), m_name.c_str(), createInfo->Race, createInfo->Class);
  825. return false;
  826. }
  827. for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; i++)
  828. m_items[i] = NULL;
  829. Relocate(info->positionX, info->positionY, info->positionZ, info->orientation);
  830. ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(createInfo->Class);
  831. if (!cEntry)
  832. {
  833. TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid character class (%u) - refusing to do so (wrong DBC-files?)",
  834. GetSession()->GetAccountId(), m_name.c_str(), createInfo->Class);
  835. return false;
  836. }
  837. SetMap(sMapMgr->CreateMap(info->mapId, this));
  838. uint8 powertype = cEntry->powerType;
  839. SetObjectScale(1.0f);
  840. setFactionForRace(createInfo->Race);
  841. if (!IsValidGender(createInfo->Gender))
  842. {
  843. TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid gender (%u) - refusing to do so",
  844. GetSession()->GetAccountId(), m_name.c_str(), createInfo->Gender);
  845. return false;
  846. }
  847. uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Gender << 16);
  848. SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24)));
  849. InitDisplayIds();
  850. if (sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP)
  851. {
  852. SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP);
  853. SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
  854. }
  855. SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER);
  856. SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // default for players in 3.0.3
  857. SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); // -1 is default value
  858. SetUInt32Value(PLAYER_BYTES, (createInfo->Skin | (createInfo->Face << 8) | (createInfo->HairStyle << 16) | (createInfo->HairColor << 24)));
  859. SetUInt32Value(PLAYER_BYTES_2, (createInfo->FacialHair |
  860. (0x00 << 8) |
  861. (0x00 << 16) |
  862. (((GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0) ? REST_STATE_RAF_LINKED : REST_STATE_NOT_RAF_LINKED) << 24)));
  863. SetByteValue(PLAYER_BYTES_3, 0, createInfo->Gender);
  864. SetByteValue(PLAYER_BYTES_3, 3, 0); // BattlefieldArenaFaction (0 or 1)
  865. SetUInt64Value(OBJECT_FIELD_DATA, 0);
  866. SetUInt32Value(PLAYER_GUILDRANK, 0);
  867. SetGuildLevel(0);
  868. SetUInt32Value(PLAYER_GUILD_TIMESTAMP, 0);
  869. for (int i = 0; i < KNOWN_TITLES_SIZE; ++i)
  870. SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES + i, 0); // 0=disabled
  871. SetUInt32Value(PLAYER_CHOSEN_TITLE, 0);
  872. SetUInt32Value(PLAYER_FIELD_KILLS, 0);
  873. SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, 0);
  874. // set starting level
  875. uint32 start_level = getClass() != CLASS_DEATH_KNIGHT
  876. ? sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL)
  877. : sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL);
  878. if (m_session->HasPermission(rbac::RBAC_PERM_USE_START_GM_LEVEL))
  879. {
  880. uint32 gm_level = sWorld->getIntConfig(CONFIG_START_GM_LEVEL);
  881. if (gm_level > start_level)
  882. start_level = gm_level;
  883. }
  884. SetUInt32Value(UNIT_FIELD_LEVEL, start_level);
  885. InitRunes();
  886. SetUInt32Value(PLAYER_FIELD_COINAGE, sWorld->getIntConfig(CONFIG_START_PLAYER_MONEY));
  887. SetCurrency(CURRENCY_TYPE_HONOR_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_HONOR_POINTS));
  888. SetCurrency(CURRENCY_TYPE_JUSTICE_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_JUSTICE_POINTS));
  889. SetCurrency(CURRENCY_TYPE_CONQUEST_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_CONQUEST_POINTS));
  890. // start with every map explored
  891. if (sWorld->getBoolConfig(CONFIG_START_ALL_EXPLORED))
  892. {
  893. for (uint8 i=0; i<PLAYER_EXPLORED_ZONES_SIZE; i++)
  894. SetFlag(PLAYER_EXPLORED_ZONES_1+i, 0xFFFFFFFF);
  895. }
  896. //Reputations if "StartAllReputation" is enabled, -- @todo Fix this in a better way
  897. if (sWorld->getBoolConfig(CONFIG_START_ALL_REP))
  898. {
  899. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(942), 42999);
  900. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(935), 42999);
  901. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(936), 42999);
  902. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1011), 42999);
  903. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(970), 42999);
  904. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(967), 42999);
  905. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(989), 42999);
  906. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(932), 42999);
  907. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(934), 42999);
  908. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1038), 42999);
  909. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1077), 42999);
  910. // Factions depending on team, like cities and some more stuff
  911. switch (GetTeam())
  912. {
  913. case ALLIANCE:
  914. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(72), 42999);
  915. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(47), 42999);
  916. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(69), 42999);
  917. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(930), 42999);
  918. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(730), 42999);
  919. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(978), 42999);
  920. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(54), 42999);
  921. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(946), 42999);
  922. break;
  923. case HORDE:
  924. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(76), 42999);
  925. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(68), 42999);
  926. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(81), 42999);
  927. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(911), 42999);
  928. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(729), 42999);
  929. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(941), 42999);
  930. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(530), 42999);
  931. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(947), 42999);
  932. break;
  933. default:
  934. break;
  935. }
  936. }
  937. // Played time
  938. m_Last_tick = time(NULL);
  939. m_Played_time[PLAYED_TIME_TOTAL] = 0;
  940. m_Played_time[PLAYED_TIME_LEVEL] = 0;
  941. // base stats and related field values
  942. InitStatsForLevel();
  943. InitTaxiNodesForLevel();
  944. InitGlyphsForLevel();
  945. InitTalentForLevel();
  946. InitPrimaryProfessions(); // to max set before any spell added
  947. // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
  948. UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
  949. SetFullHealth();
  950. if (getPowerType() == POWER_MANA)
  951. {
  952. UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intellect)
  953. SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
  954. }
  955. if (getPowerType() == POWER_RUNIC_POWER)
  956. {
  957. SetPower(POWER_RUNES, 8);
  958. SetMaxPower(POWER_RUNES, 8);
  959. SetPower(POWER_RUNIC_POWER, 0);
  960. SetMaxPower(POWER_RUNIC_POWER, 1000);
  961. }
  962. // original spells
  963. LearnDefaultSkills();
  964. LearnCustomSpells();
  965. // original action bar
  966. for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr)
  967. addActionButton(action_itr->button, action_itr->action, action_itr->type);
  968. // original items
  969. if (CharStartOutfitEntry const* oEntry = GetCharStartOutfitEntry(createInfo->Race, createInfo->Class, createInfo->Gender))
  970. {
  971. for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
  972. {
  973. if (oEntry->ItemId[j] <= 0)
  974. continue;
  975. uint32 itemId = oEntry->ItemId[j];
  976. // just skip, reported in ObjectMgr::LoadItemTemplates
  977. ItemTemplate const* iProto = sObjectMgr->GetItemTemplate(itemId);
  978. if (!iProto)
  979. continue;
  980. // BuyCount by default
  981. uint32 count = iProto->BuyCount;
  982. // special amount for food/drink
  983. if (iProto->Class == ITEM_CLASS_CONSUMABLE && iProto->SubClass == ITEM_SUBCLASS_FOOD_DRINK)
  984. {
  985. switch (iProto->Spells[0].SpellCategory)
  986. {
  987. case SPELL_CATEGORY_FOOD: // food
  988. count = getClass() == CLASS_DEATH_KNIGHT ? 10 : 4;
  989. break;
  990. case SPELL_CATEGORY_DRINK: // drink
  991. count = 2;
  992. break;
  993. }
  994. if (iProto->GetMaxStackSize() < count)
  995. count = iProto->GetMaxStackSize();
  996. }
  997. StoreNewItemInBestSlots(itemId, count);
  998. }
  999. }
  1000. for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr != info->item.end(); ++item_id_itr)
  1001. StoreNewItemInBestSlots(item_id_itr->item_id, item_id_itr->item_amount);
  1002. // bags and main-hand weapon must equipped at this moment
  1003. // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon)
  1004. // or ammo not equipped in special bag
  1005. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
  1006. {
  1007. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  1008. {
  1009. uint16 eDest;
  1010. // equip offhand weapon/shield if it attempt equipped before main-hand weapon
  1011. InventoryResult msg = CanEquipItem(NULL_SLOT, eDest, pItem, false);
  1012. if (msg == EQUIP_ERR_OK)
  1013. {
  1014. RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
  1015. EquipItem(eDest, pItem, true);
  1016. }
  1017. // move other items to more appropriate slots
  1018. else
  1019. {
  1020. ItemPosCountVec sDest;
  1021. msg = CanStoreItem(NULL_BAG, NULL_SLOT, sDest, pItem, false);
  1022. if (msg == EQUIP_ERR_OK)
  1023. {
  1024. RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
  1025. pItem = StoreItem(sDest, pItem, true);
  1026. }
  1027. }
  1028. }
  1029. }
  1030. // all item positions resolved
  1031. return true;
  1032. }
  1033. bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount)
  1034. {
  1035. TC_LOG_DEBUG("entities.player.items", "STORAGE: Creating initial item, itemId = %u, count = %u", titem_id, titem_amount);
  1036. // attempt equip by one
  1037. while (titem_amount > 0)
  1038. {
  1039. uint16 eDest;
  1040. InventoryResult msg = CanEquipNewItem(NULL_SLOT, eDest, titem_id, false);
  1041. if (msg != EQUIP_ERR_OK)
  1042. break;
  1043. EquipNewItem(eDest, titem_id, true);
  1044. AutoUnequipOffhandIfNeed();
  1045. --titem_amount;
  1046. }
  1047. if (titem_amount == 0)
  1048. return true; // equipped
  1049. // attempt store
  1050. ItemPosCountVec sDest;
  1051. // store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
  1052. InventoryResult msg = CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount);
  1053. if (msg == EQUIP_ERR_OK)
  1054. {
  1055. StoreNewItem(sDest, titem_id, true, Item::GenerateItemRandomPropertyId(titem_id));
  1056. return true; // stored
  1057. }
  1058. // item can't be added
  1059. TC_LOG_ERROR("entities.player.items", "STORAGE: Can't equip or store initial item %u for race %u class %u, error msg = %u", titem_id, getRace(), getClass(), msg);
  1060. return false;
  1061. }
  1062. void Player::SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, int32 Regen)
  1063. {
  1064. if (int(MaxValue) == DISABLED_MIRROR_TIMER)
  1065. {
  1066. if (int(CurrentValue) != DISABLED_MIRROR_TIMER)
  1067. StopMirrorTimer(Type);
  1068. return;
  1069. }
  1070. WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
  1071. data << (uint32)Type;
  1072. data << CurrentValue;
  1073. data << MaxValue;
  1074. data << Regen;
  1075. data << (uint8)0;
  1076. data << (uint32)0; // spell id
  1077. GetSession()->SendPacket(&data);
  1078. }
  1079. void Player::StopMirrorTimer(MirrorTimerType Type)
  1080. {
  1081. m_MirrorTimer[Type] = DISABLED_MIRROR_TIMER;
  1082. WorldPacket data(SMSG_STOP_MIRROR_TIMER, 4);
  1083. data << (uint32)Type;
  1084. GetSession()->SendPacket(&data);
  1085. }
  1086. bool Player::IsImmuneToEnvironmentalDamage()
  1087. {
  1088. // check for GM and death state included in isAttackableByAOE
  1089. return (!isTargetableForAttack(false));
  1090. }
  1091. uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage)
  1092. {
  1093. if (IsImmuneToEnvironmentalDamage())
  1094. return 0;
  1095. // Absorb, resist some environmental damage type
  1096. uint32 absorb = 0;
  1097. uint32 resist = 0;
  1098. if (type == DAMAGE_LAVA)
  1099. CalcAbsorbResist(this, SPELL_SCHOOL_MASK_FIRE, DIRECT_DAMAGE, damage, &absorb, &resist);
  1100. else if (type == DAMAGE_SLIME)
  1101. CalcAbsorbResist(this, SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, damage, &absorb, &resist);
  1102. damage -= absorb + resist;
  1103. DealDamageMods(this, damage, &absorb);
  1104. WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21));
  1105. data << uint64(GetGUID());
  1106. data << uint8(type != DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL);
  1107. data << uint32(damage);
  1108. data << uint32(absorb);
  1109. data << uint32(resist);
  1110. SendMessageToSet(&data, true);
  1111. uint32 final_damage = DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
  1112. if (!IsAlive())
  1113. {
  1114. if (type == DAMAGE_FALL) // DealDamage not apply item durability loss at self damage
  1115. {
  1116. TC_LOG_DEBUG("entities.player", "We are fall to death, loosing 10 percents durability");
  1117. DurabilityLossAll(0.10f, false);
  1118. // durability lost message
  1119. SendDurabilityLoss(this, 10);
  1120. }
  1121. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM, 1, type);
  1122. }
  1123. return final_damage;
  1124. }
  1125. int32 Player::getMaxTimer(MirrorTimerType timer)
  1126. {
  1127. switch (timer)
  1128. {
  1129. case FATIGUE_TIMER:
  1130. return MINUTE * IN_MILLISECONDS;
  1131. case BREATH_TIMER:
  1132. {
  1133. if (!IsAlive() || HasAuraType(SPELL_AURA_WATER_BREATHING) || GetSession()->GetSecurity() >= AccountTypes(sWorld->getIntConfig(CONFIG_DISABLE_BREATHING)))
  1134. return DISABLED_MIRROR_TIMER;
  1135. int32 UnderWaterTime = 3 * MINUTE * IN_MILLISECONDS;
  1136. AuraEffectList const& mModWaterBreathing = GetAuraEffectsByType(SPELL_AURA_MOD_WATER_BREATHING);
  1137. for (AuraEffectList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i)
  1138. AddPct(UnderWaterTime, (*i)->GetAmount());
  1139. return UnderWaterTime;
  1140. }
  1141. case FIRE_TIMER:
  1142. {
  1143. if (!IsAlive())
  1144. return DISABLED_MIRROR_TIMER;
  1145. return 1 * IN_MILLISECONDS;
  1146. }
  1147. default:
  1148. return 0;
  1149. }
  1150. }
  1151. void Player::UpdateMirrorTimers()
  1152. {
  1153. // Desync flags for update on next HandleDrowning
  1154. if (m_MirrorTimerFlags)
  1155. m_MirrorTimerFlagsLast = ~m_MirrorTimerFlags;
  1156. }
  1157. void Player::StopMirrorTimers()
  1158. {
  1159. StopMirrorTimer(FATIGUE_TIMER);
  1160. StopMirrorTimer(BREATH_TIMER);
  1161. StopMirrorTimer(FIRE_TIMER);
  1162. }
  1163. bool Player::IsMirrorTimerActive(MirrorTimerType type)
  1164. {
  1165. return m_MirrorTimer[type] == getMaxTimer(type);
  1166. }
  1167. void Player::HandleDrowning(uint32 time_diff)
  1168. {
  1169. if (!m_MirrorTimerFlags)
  1170. return;
  1171. // In water
  1172. if (m_MirrorTimerFlags & UNDERWATER_INWATER)
  1173. {
  1174. // Breath timer not activated - activate it
  1175. if (m_MirrorTimer[BREATH_TIMER] == DISABLED_MIRROR_TIMER)
  1176. {
  1177. m_MirrorTimer[BREATH_TIMER] = getMaxTimer(BREATH_TIMER);
  1178. SendMirrorTimer(BREATH_TIMER, m_MirrorTimer[BREATH_TIMER], m_MirrorTimer[BREATH_TIMER], -1);
  1179. }
  1180. else // If activated - do tick
  1181. {
  1182. m_MirrorTimer[BREATH_TIMER]-=time_diff;
  1183. // Timer limit - need deal damage
  1184. if (m_MirrorTimer[BREATH_TIMER] < 0)
  1185. {
  1186. m_MirrorTimer[BREATH_TIMER]+= 1*IN_MILLISECONDS;
  1187. // Calculate and deal damage
  1188. /// @todo Check this formula
  1189. uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1);
  1190. EnvironmentalDamage(DAMAGE_DROWNING, damage);
  1191. }
  1192. else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INWATER)) // Update time in client if need
  1193. SendMirrorTimer(BREATH_TIMER, getMaxTimer(BREATH_TIMER), m_MirrorTimer[BREATH_TIMER], -1);
  1194. }
  1195. }
  1196. else if (m_MirrorTimer[BREATH_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
  1197. {
  1198. int32 UnderWaterTime = getMaxTimer(BREATH_TIMER);
  1199. // Need breath regen
  1200. m_MirrorTimer[BREATH_TIMER]+=10*time_diff;
  1201. if (m_MirrorTimer[BREATH_TIMER] >= UnderWaterTime || !IsAlive())
  1202. StopMirrorTimer(BREATH_TIMER);
  1203. else if (m_MirrorTimerFlagsLast & UNDERWATER_INWATER)
  1204. SendMirrorTimer(BREATH_TIMER, UnderWaterTime, m_MirrorTimer[BREATH_TIMER], 10);
  1205. }
  1206. // In dark water
  1207. if (m_MirrorTimerFlags & UNDERWARER_INDARKWATER)
  1208. {
  1209. // Fatigue timer not activated - activate it
  1210. if (m_MirrorTimer[FATIGUE_TIMER] == DISABLED_MIRROR_TIMER)
  1211. {
  1212. m_MirrorTimer[FATIGUE_TIMER] = getMaxTimer(FATIGUE_TIMER);
  1213. SendMirrorTimer(FATIGUE_TIMER, m_MirrorTimer[FATIGUE_TIMER], m_MirrorTimer[FATIGUE_TIMER], -1);
  1214. }
  1215. else
  1216. {
  1217. m_MirrorTimer[FATIGUE_TIMER]-=time_diff;
  1218. // Timer limit - need deal damage or teleport ghost to graveyard
  1219. if (m_MirrorTimer[FATIGUE_TIMER] < 0)
  1220. {
  1221. m_MirrorTimer[FATIGUE_TIMER]+= 1*IN_MILLISECONDS;
  1222. if (IsAlive()) // Calculate and deal damage
  1223. {
  1224. uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1);
  1225. EnvironmentalDamage(DAMAGE_EXHAUSTED, damage);
  1226. }
  1227. else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) // Teleport ghost to graveyard
  1228. RepopAtGraveyard();
  1229. }
  1230. else if (!(m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER))
  1231. SendMirrorTimer(FATIGUE_TIMER, getMaxTimer(FATIGUE_TIMER), m_MirrorTimer[FATIGUE_TIMER], -1);
  1232. }
  1233. }
  1234. else if (m_MirrorTimer[FATIGUE_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
  1235. {
  1236. int32 DarkWaterTime = getMaxTimer(FATIGUE_TIMER);
  1237. m_MirrorTimer[FATIGUE_TIMER]+=10*time_diff;
  1238. if (m_MirrorTimer[FATIGUE_TIMER] >= DarkWaterTime || !IsAlive())
  1239. StopMirrorTimer(FATIGUE_TIMER);
  1240. else if (m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER)
  1241. SendMirrorTimer(FATIGUE_TIMER, DarkWaterTime, m_MirrorTimer[FATIGUE_TIMER], 10);
  1242. }
  1243. if (m_MirrorTimerFlags & (UNDERWATER_INLAVA /*| UNDERWATER_INSLIME*/) && !(_lastLiquid && _lastLiquid->SpellId))
  1244. {
  1245. // Breath timer not activated - activate it
  1246. if (m_MirrorTimer[FIRE_TIMER] == DISABLED_MIRROR_TIMER)
  1247. m_MirrorTimer[FIRE_TIMER] = getMaxTimer(FIRE_TIMER);
  1248. else
  1249. {
  1250. m_MirrorTimer[FIRE_TIMER] -= time_diff;
  1251. if (m_MirrorTimer[FIRE_TIMER] < 0)
  1252. {
  1253. m_MirrorTimer[FIRE_TIMER]+= 1*IN_MILLISECONDS;
  1254. // Calculate and deal damage
  1255. /// @todo Check this formula
  1256. uint32 damage = urand(600, 700);
  1257. if (m_MirrorTimerFlags & UNDERWATER_INLAVA)
  1258. EnvironmentalDamage(DAMAGE_LAVA, damage);
  1259. // need to skip Slime damage in Undercity,
  1260. // maybe someone can find better way to handle environmental damage
  1261. //else if (m_zoneUpdateId != 1497)
  1262. // EnvironmentalDamage(DAMAGE_SLIME, damage);
  1263. }
  1264. }
  1265. }
  1266. else
  1267. m_MirrorTimer[FIRE_TIMER] = DISABLED_MIRROR_TIMER;
  1268. // Recheck timers flag
  1269. m_MirrorTimerFlags&=~UNDERWATER_EXIST_TIMERS;
  1270. for (uint8 i = 0; i< MAX_TIMERS; ++i)
  1271. if (m_MirrorTimer[i] != DISABLED_MIRROR_TIMER)
  1272. {
  1273. m_MirrorTimerFlags|=UNDERWATER_EXIST_TIMERS;
  1274. break;
  1275. }
  1276. m_MirrorTimerFlagsLast = m_MirrorTimerFlags;
  1277. }
  1278. ///The player sobers by 1% every 9 seconds
  1279. void Player::HandleSobering()
  1280. {
  1281. m_drunkTimer = 0;
  1282. uint8 currentDrunkValue = GetDrunkValue();
  1283. uint8 drunk = currentDrunkValue ? --currentDrunkValue : 0;
  1284. SetDrunkValue(drunk);
  1285. }
  1286. DrunkenState Player::GetDrunkenstateByValue(uint8 value)
  1287. {
  1288. if (value >= 90)
  1289. return DRUNKEN_SMASHED;
  1290. if (value >= 50)
  1291. return DRUNKEN_DRUNK;
  1292. if (value)
  1293. return DRUNKEN_TIPSY;
  1294. return DRUNKEN_SOBER;
  1295. }
  1296. void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/)
  1297. {
  1298. bool isSobering = newDrunkValue < GetDrunkValue();
  1299. uint32 oldDrunkenState = Player::GetDrunkenstateByValue(GetDrunkValue());
  1300. if (newDrunkValue > 100)
  1301. newDrunkValue = 100;
  1302. // select drunk percent or total SPELL_AURA_MOD_FAKE_INEBRIATE amount, whichever is higher for visibility updates
  1303. int32 drunkPercent = std::max<int32>(newDrunkValue, GetTotalAuraModifier(SPELL_AURA_MOD_FAKE_INEBRIATE));
  1304. if (drunkPercent)
  1305. {
  1306. m_invisibilityDetect.AddFlag(INVISIBILITY_DRUNK);
  1307. m_invisibilityDetect.SetValue(INVISIBILITY_DRUNK, drunkPercent);
  1308. }
  1309. else if (!HasAuraType(SPELL_AURA_MOD_FAKE_INEBRIATE) && !newDrunkValue)
  1310. m_invisibilityDetect.DelFlag(INVISIBILITY_DRUNK);
  1311. uint32 newDrunkenState = Player::GetDrunkenstateByValue(newDrunkValue);
  1312. SetByteValue(PLAYER_BYTES_3, 1, newDrunkValue);
  1313. UpdateObjectVisibility();
  1314. if (!isSobering)
  1315. m_drunkTimer = 0; // reset sobering timer
  1316. if (newDrunkenState == oldDrunkenState)
  1317. return;
  1318. WorldPacket data(SMSG_CROSSED_INEBRIATION_THRESHOLD, (8+4+4));
  1319. data << uint64(GetGUID());
  1320. data << uint32(newDrunkenState);
  1321. data << uint32(itemId);
  1322. SendMessageToSet(&data, true);
  1323. }
  1324. void Player::Update(uint32 p_time)
  1325. {
  1326. if (!IsInWorld())
  1327. return;
  1328. // undelivered mail
  1329. if (m_nextMailDelivereTime && m_nextMailDelivereTime <= time(NULL))
  1330. {
  1331. SendNewMail();
  1332. ++unReadMails;
  1333. // It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated)
  1334. m_nextMailDelivereTime = 0;
  1335. }
  1336. // If this is set during update SetSpellModTakingSpell call is missing somewhere in the code
  1337. // Having this would prevent more aura charges to be dropped, so let's crash
  1338. //ASSERT (!m_spellModTakingSpell);
  1339. if (m_spellModTakingSpell)
  1340. {
  1341. //TC_LOG_FATAL("entities.player", "Player has m_pad %u during update!", m_pad);
  1342. //if (m_spellModTakingSpell)
  1343. TC_LOG_FATAL("spells", "Player has m_spellModTakingSpell %u during update!", m_spellModTakingSpell->m_spellInfo->Id);
  1344. m_spellModTakingSpell = NULL;
  1345. }
  1346. //used to implement delayed far teleports
  1347. SetCanDelayTeleport(true);
  1348. Unit::Update(p_time);
  1349. SetCanDelayTeleport(false);
  1350. time_t now = time(NULL);
  1351. UpdatePvPFlag(now);
  1352. UpdateContestedPvP(p_time);
  1353. UpdateDuelFlag(now);
  1354. CheckDuelDistance(now);
  1355. UpdateAfkReport(now);
  1356. if (IsCharmed())
  1357. if (Unit* charmer = GetCharmer())
  1358. if (charmer->GetTypeId() == TYPEID_UNIT && charmer->IsAlive())
  1359. UpdateCharmedAI();
  1360. // Update items that have just a limited lifetime
  1361. if (now > m_Last_tick)
  1362. UpdateItemDuration(uint32(now - m_Last_tick));
  1363. // check every second
  1364. if (now > m_Last_tick + 1)
  1365. UpdateSoulboundTradeItems();
  1366. // If mute expired, remove it from the DB
  1367. if (GetSession()->m_muteTime && GetSession()->m_muteTime < now)
  1368. {
  1369. GetSession()->m_muteTime = 0;
  1370. PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME);
  1371. stmt->setInt64(0, 0); // Set the mute time to 0
  1372. stmt->setString(1, "");
  1373. stmt->setString(2, "");
  1374. stmt->setUInt32(3, GetSession()->GetAccountId());
  1375. LoginDatabase.Execute(stmt);
  1376. }
  1377. if (!m_timedquests.empty())
  1378. {
  1379. QuestSet::iterator iter = m_timedquests.begin();
  1380. while (iter != m_timedquests.end())
  1381. {
  1382. QuestStatusData& q_status = m_QuestStatus[*iter];
  1383. if (q_status.Timer <= p_time)
  1384. {
  1385. uint32 quest_id = *iter;
  1386. ++iter; // current iter will be removed in FailQuest
  1387. FailQuest(quest_id);
  1388. }
  1389. else
  1390. {
  1391. q_status.Timer -= p_time;
  1392. m_QuestStatusSave[*iter] = QUEST_DEFAULT_SAVE_TYPE;
  1393. ++iter;
  1394. }
  1395. }
  1396. }
  1397. m_achievementMgr->UpdateTimedAchievements(p_time);
  1398. if (HasUnitState(UNIT_STATE_MELEE_ATTACKING) && !HasUnitState(UNIT_STATE_CASTING))
  1399. {
  1400. if (Unit* victim = GetVictim())
  1401. {
  1402. // default combat reach 10
  1403. /// @todo add weapon, skill check
  1404. if (isAttackReady(BASE_ATTACK))
  1405. {
  1406. if (!IsWithinMeleeRange(victim))
  1407. {
  1408. setAttackTimer(BASE_ATTACK, 100);
  1409. if (m_swingErrorMsg != 1) // send single time (client auto repeat)
  1410. {
  1411. SendAttackSwingNotInRange();
  1412. m_swingErrorMsg = 1;
  1413. }
  1414. }
  1415. //120 degrees of radiant range
  1416. else if (!HasInArc(2 * float(M_PI) / 3, victim))
  1417. {
  1418. setAttackTimer(BASE_ATTACK, 100);
  1419. if (m_swingErrorMsg != 2) // send single time (client auto repeat)
  1420. {
  1421. SendAttackSwingBadFacingAttack();
  1422. m_swingErrorMsg = 2;
  1423. }
  1424. }
  1425. else
  1426. {
  1427. m_swingErrorMsg = 0; // reset swing error state
  1428. // prevent base and off attack in same time, delay attack at 0.2 sec
  1429. if (haveOffhandWeapon())
  1430. if (getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
  1431. setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
  1432. // do attack
  1433. AttackerStateUpdate(victim, BASE_ATTACK);
  1434. resetAttackTimer(BASE_ATTACK);
  1435. }
  1436. }
  1437. if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
  1438. {
  1439. if (!IsWithinMeleeRange(victim))
  1440. setAttackTimer(OFF_ATTACK, 100);
  1441. else if (!HasInArc(2 * float(M_PI) / 3, victim))
  1442. setAttackTimer(OFF_ATTACK, 100);
  1443. else
  1444. {
  1445. // prevent base and off attack in same time, delay attack at 0.2 sec
  1446. if (getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY)
  1447. setAttackTimer(BASE_ATTACK, ATTACK_DISPLAY_DELAY);
  1448. // do attack
  1449. AttackerStateUpdate(victim, OFF_ATTACK);
  1450. resetAttackTimer(OFF_ATTACK);
  1451. }
  1452. }
  1453. /*Unit* owner = victim->GetOwner();
  1454. Unit* u = owner ? owner : victim;
  1455. if (u->IsPvP() && (!duel || duel->opponent != u))
  1456. {
  1457. UpdatePvP(true);
  1458. RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
  1459. }*/
  1460. }
  1461. }
  1462. if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
  1463. {
  1464. if (roll_chance_i(3) && GetTimeInnEnter() > 0) // freeze update
  1465. {
  1466. time_t time_inn = time(NULL)-GetTimeInnEnter();
  1467. if (time_inn >= 10) // freeze update
  1468. {
  1469. float bubble = 0.125f*sWorld->getRate(RATE_REST_INGAME);
  1470. // speed collect rest bonus (section/in hour)
  1471. SetRestBonus(GetRestBonus()+ time_inn*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble);
  1472. UpdateInnerTime(time(NULL));
  1473. }
  1474. }
  1475. }
  1476. if (m_weaponChangeTimer > 0)
  1477. {
  1478. if (p_time >= m_weaponChangeTimer)
  1479. m_weaponChangeTimer = 0;
  1480. else
  1481. m_weaponChangeTimer -= p_time;
  1482. }
  1483. if (m_zoneUpdateTimer > 0)
  1484. {
  1485. if (p_time >= m_zoneUpdateTimer)
  1486. {
  1487. uint32 newzone, newarea;
  1488. GetZoneAndAreaId(newzone, newarea);
  1489. if (m_zoneUpdateId != newzone)
  1490. UpdateZone(newzone, newarea); // also update area
  1491. else
  1492. {
  1493. // use area updates as well
  1494. // needed for free far all arenas for example
  1495. if (m_areaUpdateId != newarea)
  1496. UpdateArea(newarea);
  1497. m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
  1498. }
  1499. }
  1500. else
  1501. m_zoneUpdateTimer -= p_time;
  1502. }
  1503. if (m_timeSyncTimer > 0)
  1504. {
  1505. if (p_time >= m_timeSyncTimer)
  1506. SendTimeSync();
  1507. else
  1508. m_timeSyncTimer -= p_time;
  1509. }
  1510. if (IsAlive())
  1511. {
  1512. m_regenTimer += p_time;
  1513. RegenerateAll();
  1514. }
  1515. if (m_deathState == JUST_DIED)
  1516. KillPlayer();
  1517. if (m_nextSave > 0)
  1518. {
  1519. if (p_time >= m_nextSave)
  1520. {
  1521. // m_nextSave reset in SaveToDB call
  1522. SaveToDB();
  1523. TC_LOG_DEBUG("entities.player", "Player '%s' (GUID: %u) saved", GetName().c_str(), GetGUIDLow());
  1524. }
  1525. else
  1526. m_nextSave -= p_time;
  1527. }
  1528. //Handle Water/drowning
  1529. HandleDrowning(p_time);
  1530. // Played time
  1531. if (now > m_Last_tick)
  1532. {
  1533. uint32 elapsed = uint32(now - m_Last_tick);
  1534. m_Played_time[PLAYED_TIME_TOTAL] += elapsed; // Total played time
  1535. m_Played_time[PLAYED_TIME_LEVEL] += elapsed; // Level played time
  1536. m_Last_tick = now;
  1537. }
  1538. if (GetDrunkValue())
  1539. {
  1540. m_drunkTimer += p_time;
  1541. if (m_drunkTimer > 9 * IN_MILLISECONDS)
  1542. HandleSobering();
  1543. }
  1544. if (HasPendingBind())
  1545. {
  1546. if (_pendingBindTimer <= p_time)
  1547. {
  1548. // Player left the instance
  1549. if (_pendingBindId == GetInstanceId())
  1550. BindToInstance();
  1551. SetPendingBind(0, 0);
  1552. }
  1553. else
  1554. _pendingBindTimer -= p_time;
  1555. }
  1556. // not auto-free ghost from body in instances
  1557. if (m_deathTimer > 0 && !GetBaseMap()->Instanceable() && !HasAuraType(SPELL_AURA_PREVENT_RESURRECTION))
  1558. {
  1559. if (p_time >= m_deathTimer)
  1560. {
  1561. m_deathTimer = 0;
  1562. BuildPlayerRepop();
  1563. RepopAtGraveyard();
  1564. }
  1565. else
  1566. m_deathTimer -= p_time;
  1567. }
  1568. UpdateEnchantTime(p_time);
  1569. UpdateHomebindTime(p_time);
  1570. if (!_instanceResetTimes.empty())
  1571. {
  1572. for (InstanceTimeMap::iterator itr = _instanceResetTimes.begin(); itr != _instanceResetTimes.end();)
  1573. {
  1574. if (itr->second < now)
  1575. _instanceResetTimes.erase(itr++);
  1576. else
  1577. ++itr;
  1578. }
  1579. }
  1580. if (getClass() == CLASS_DEATH_KNIGHT)
  1581. {
  1582. // Update rune timers
  1583. for (uint8 i = 0; i < MAX_RUNES; ++i)
  1584. {
  1585. uint32 timer = GetRuneTimer(i);
  1586. // Don't update timer if rune is disabled
  1587. if (GetRuneCooldown(i))
  1588. continue;
  1589. // Timer has began
  1590. if (timer < 0xFFFFFFFF)
  1591. {
  1592. timer += p_time;
  1593. SetRuneTimer(i, std::min(uint32(2500), timer));
  1594. }
  1595. }
  1596. }
  1597. // group update
  1598. SendUpdateToOutOfRangeGroupMembers();
  1599. Pet* pet = GetPet();
  1600. if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityRange()) && !pet->isPossessed())
  1601. //if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID())))
  1602. RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
  1603. //we should execute delayed teleports only for alive(!) players
  1604. //because we don't want player's ghost teleported from graveyard
  1605. if (IsHasDelayedTeleport() && IsAlive())
  1606. TeleportTo(m_teleport_dest, m_teleport_options);
  1607. }
  1608. void Player::setDeathState(DeathState s)
  1609. {
  1610. uint32 ressSpellId = 0;
  1611. bool cur = IsAlive();
  1612. if (s == JUST_DIED)
  1613. {
  1614. if (!cur)
  1615. {
  1616. TC_LOG_ERROR("entities.player", "setDeathState: attempt to kill a dead player %s(%d)", GetName().c_str(), GetGUIDLow());
  1617. return;
  1618. }
  1619. // drunken state is cleared on death
  1620. SetDrunkValue(0);
  1621. // lost combo points at any target (targeted combo points clear in Unit::setDeathState)
  1622. ClearComboPoints();
  1623. ClearResurrectRequestData();
  1624. //FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
  1625. RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
  1626. // save value before aura remove in Unit::setDeathState
  1627. ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL);
  1628. // passive spell
  1629. if (!ressSpellId)
  1630. ressSpellId = GetResurrectionSpellId();
  1631. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP, 1);
  1632. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH, 1);
  1633. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON, 1);
  1634. ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH);
  1635. ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL, ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH);
  1636. ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS, ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH);
  1637. }
  1638. Unit::setDeathState(s);
  1639. // restore resurrection spell id for player after aura remove
  1640. if (s == JUST_DIED && cur && ressSpellId)
  1641. SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId);
  1642. if (IsAlive() && !cur)
  1643. //clear aura case after resurrection by another way (spells will be applied before next death)
  1644. SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
  1645. }
  1646. void Player::InnEnter(time_t time, uint32 mapid, float x, float y, float z)
  1647. {
  1648. inn_pos_mapid = mapid;
  1649. inn_pos_x = x;
  1650. inn_pos_y = y;
  1651. inn_pos_z = z;
  1652. time_inn_enter = time;
  1653. }
  1654. bool Player::BuildEnumData(PreparedQueryResult result, ByteBuffer* dataBuffer, ByteBuffer* bitBuffer)
  1655. {
  1656. // 0 1 2 3 4 5 6 7
  1657. // "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
  1658. // 8 9 10 11 12 13 14
  1659. // "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
  1660. // 15 16 17 18 19 20 21 22
  1661. // "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, character_banned.guid, characters.slot, character_declinedname.genitive"
  1662. Field* fields = result->Fetch();
  1663. ObjectGuid guid(HIGHGUID_PLAYER, fields[0].GetUInt32());
  1664. std::string name = fields[1].GetString();
  1665. uint8 plrRace = fields[2].GetUInt8();
  1666. uint8 plrClass = fields[3].GetUInt8();
  1667. uint8 gender = fields[4].GetUInt8();
  1668. uint8 skin = uint8(fields[5].GetUInt32() & 0xFF);
  1669. uint8 face = uint8((fields[5].GetUInt32() >> 8) & 0xFF);
  1670. uint8 hairStyle = uint8((fields[5].GetUInt32() >> 16) & 0xFF);
  1671. uint8 hairColor = uint8((fields[5].GetUInt32() >> 24) & 0xFF);
  1672. uint8 facialHair = uint8(fields[6].GetUInt32() & 0xFF);
  1673. uint8 level = fields[7].GetUInt8();
  1674. uint32 zone = fields[8].GetUInt16();
  1675. uint32 mapId = uint32(fields[9].GetUInt16());
  1676. float x = fields[10].GetFloat();
  1677. float y = fields[11].GetFloat();
  1678. float z = fields[12].GetFloat();
  1679. uint32 guildId = fields[13].GetUInt32();
  1680. ObjectGuid guildGuid;
  1681. if (guildId)
  1682. guildGuid = ObjectGuid(HIGHGUID_GUILD, guildId);
  1683. uint32 playerFlags = fields[14].GetUInt32();
  1684. uint32 atLoginFlags = fields[15].GetUInt16();
  1685. Tokenizer equipment(fields[19].GetString(), ' ');
  1686. uint8 slot = fields[21].GetUInt8();
  1687. uint32 charFlags = 0;
  1688. if (playerFlags & PLAYER_FLAGS_HIDE_HELM)
  1689. charFlags |= CHARACTER_FLAG_HIDE_HELM;
  1690. if (playerFlags & PLAYER_FLAGS_HIDE_CLOAK)
  1691. charFlags |= CHARACTER_FLAG_HIDE_CLOAK;
  1692. if (playerFlags & PLAYER_FLAGS_GHOST)
  1693. charFlags |= CHARACTER_FLAG_GHOST;
  1694. if (atLoginFlags & AT_LOGIN_RENAME)
  1695. charFlags |= CHARACTER_FLAG_RENAME;
  1696. if (fields[20].GetUInt32())
  1697. charFlags |= CHARACTER_FLAG_LOCKED_BY_BILLING;
  1698. if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED) && !fields[22].GetString().empty())
  1699. charFlags |= CHARACTER_FLAG_DECLINED;
  1700. uint32 customizationFlag = 0;
  1701. if (atLoginFlags & AT_LOGIN_CUSTOMIZE)
  1702. customizationFlag = CHAR_CUSTOMIZE_FLAG_CUSTOMIZE;
  1703. else if (atLoginFlags & AT_LOGIN_CHANGE_FACTION)
  1704. customizationFlag = CHAR_CUSTOMIZE_FLAG_FACTION;
  1705. else if (atLoginFlags & AT_LOGIN_CHANGE_RACE)
  1706. customizationFlag = CHAR_CUSTOMIZE_FLAG_RACE;
  1707. uint32 petDisplayId = 0;
  1708. uint32 petLevel = 0;
  1709. uint32 petFamily = 0;
  1710. // show pet at selection character in character list only for non-ghost character
  1711. if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (plrClass == CLASS_WARLOCK || plrClass == CLASS_HUNTER || plrClass == CLASS_DEATH_KNIGHT))
  1712. {
  1713. uint32 entry = fields[16].GetUInt32();
  1714. CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(entry);
  1715. if (creatureInfo)
  1716. {
  1717. petDisplayId = fields[17].GetUInt32();
  1718. petLevel = fields[18].GetUInt16();
  1719. petFamily = creatureInfo->family;
  1720. }
  1721. }
  1722. // Packet content flags
  1723. bitBuffer->WriteBit(guid[3]);
  1724. bitBuffer->WriteBit(guildGuid[1]);
  1725. bitBuffer->WriteBit(guildGuid[7]);
  1726. bitBuffer->WriteBit(guildGuid[2]);
  1727. bitBuffer->WriteBits(uint32(name.length()), 7);
  1728. bitBuffer->WriteBit(guid[4]);
  1729. bitBuffer->WriteBit(guid[7]);
  1730. bitBuffer->WriteBit(guildGuid[3]);
  1731. bitBuffer->WriteBit(guid[5]);
  1732. bitBuffer->WriteBit(guildGuid[6]);
  1733. bitBuffer->WriteBit(guid[1]);
  1734. bitBuffer->WriteBit(guildGuid[5]);
  1735. bitBuffer->WriteBit(guildGuid[4]);
  1736. bitBuffer->WriteBit(atLoginFlags & AT_LOGIN_FIRST);
  1737. bitBuffer->WriteBit(guid[0]);
  1738. bitBuffer->WriteBit(guid[2]);
  1739. bitBuffer->WriteBit(guid[6]);
  1740. bitBuffer->WriteBit(guildGuid[0]);
  1741. // Character data
  1742. *dataBuffer << uint8(plrClass); // Class
  1743. for (uint8 slot = 0; slot < INVENTORY_SLOT_BAG_END; ++slot)
  1744. {
  1745. uint32 visualbase = slot * 2;
  1746. uint32 itemId = GetUInt32ValueFromArray(equipment, visualbase);
  1747. ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
  1748. if (!proto)
  1749. {
  1750. *dataBuffer << uint8(0);
  1751. *dataBuffer << uint32(0);
  1752. *dataBuffer << uint32(0);
  1753. continue;
  1754. }
  1755. SpellItemEnchantmentEntry const* enchant = NULL;
  1756. uint32 enchants = GetUInt32ValueFromArray(equipment, visualbase + 1);
  1757. for (uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot)
  1758. {
  1759. // values stored in 2 uint16
  1760. uint32 enchantId = 0x0000FFFF & (enchants >> enchantSlot*16);
  1761. if (!enchantId)
  1762. continue;
  1763. enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
  1764. if (enchant)
  1765. break;
  1766. }
  1767. *dataBuffer << uint8(proto->InventoryType);
  1768. *dataBuffer << uint32(proto->DisplayInfoID);
  1769. *dataBuffer << uint32(enchant ? enchant->aura_id : 0);
  1770. }
  1771. *dataBuffer << uint32(petFamily); // Pet family
  1772. dataBuffer->WriteByteSeq(guildGuid[2]);
  1773. *dataBuffer << uint8(slot); // List order
  1774. *dataBuffer << uint8(hairStyle); // Hair style
  1775. dataBuffer->WriteByteSeq(guildGuid[3]);
  1776. *dataBuffer << uint32(petDisplayId); // Pet DisplayID
  1777. *dataBuffer << uint32(charFlags); // Character flags
  1778. *dataBuffer << uint8(hairColor); // Hair color
  1779. dataBuffer->WriteByteSeq(guid[4]);
  1780. *dataBuffer << uint32(mapId); // Map Id
  1781. dataBuffer->WriteByteSeq(guildGuid[5]);
  1782. *dataBuffer << float(z); // Z
  1783. dataBuffer->WriteByteSeq(guildGuid[6]);
  1784. *dataBuffer << uint32(petLevel); // Pet level
  1785. dataBuffer->WriteByteSeq(guid[3]);
  1786. *dataBuffer << float(y); // Y
  1787. *dataBuffer << uint32(customizationFlag); // Character customization flags
  1788. *dataBuffer << uint8(facialHair); // Facial hair
  1789. dataBuffer->WriteByteSeq(guid[7]);
  1790. *dataBuffer << uint8(gender); // Gender
  1791. dataBuffer->append(name.c_str(), name.length()); // Name
  1792. *dataBuffer << uint8(face); // Face
  1793. dataBuffer->WriteByteSeq(guid[0]);
  1794. dataBuffer->WriteByteSeq(guid[2]);
  1795. dataBuffer->WriteByteSeq(guildGuid[1]);
  1796. dataBuffer->WriteByteSeq(guildGuid[7]);
  1797. *dataBuffer << float(x); // X
  1798. *dataBuffer << uint8(skin); // Skin
  1799. *dataBuffer << uint8(plrRace); // Race
  1800. *dataBuffer << uint8(level); // Level
  1801. dataBuffer->WriteByteSeq(guid[6]);
  1802. dataBuffer->WriteByteSeq(guildGuid[4]);
  1803. dataBuffer->WriteByteSeq(guildGuid[0]);
  1804. dataBuffer->WriteByteSeq(guid[5]);
  1805. dataBuffer->WriteByteSeq(guid[1]);
  1806. *dataBuffer << uint32(zone); // Zone id
  1807. return true;
  1808. }
  1809. void Player::ToggleAFK()
  1810. {
  1811. ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);
  1812. // afk player not allowed in battleground
  1813. if (isAFK() && InBattleground() && !InArena())
  1814. LeaveBattleground();
  1815. }
  1816. void Player::ToggleDND()
  1817. {
  1818. ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);
  1819. }
  1820. uint8 Player::GetChatTag() const
  1821. {
  1822. uint8 tag = CHAT_TAG_NONE;
  1823. if (isGMChat())
  1824. tag |= CHAT_TAG_GM;
  1825. if (isDND())
  1826. tag |= CHAT_TAG_DND;
  1827. if (isAFK())
  1828. tag |= CHAT_TAG_AFK;
  1829. if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_DEVELOPER))
  1830. tag |= CHAT_TAG_DEV;
  1831. return tag;
  1832. }
  1833. bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options)
  1834. {
  1835. if (!MapManager::IsValidMapCoord(mapid, x, y, z, orientation))
  1836. {
  1837. TC_LOG_ERROR("maps", "TeleportTo: invalid map (%d) or invalid coordinates (X: %f, Y: %f, Z: %f, O: %f) given when teleporting player (GUID: %u, name: %s, map: %d, X: %f, Y: %f, Z: %f, O: %f).",
  1838. mapid, x, y, z, orientation, GetGUIDLow(), GetName().c_str(), GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  1839. return false;
  1840. }
  1841. if (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_DISABLE_MAP) && DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, mapid, this))
  1842. {
  1843. TC_LOG_ERROR("maps", "Player (GUID: %u, name: %s) tried to enter a forbidden map %u", GetGUIDLow(), GetName().c_str(), mapid);
  1844. SendTransferAborted(mapid, TRANSFER_ABORT_MAP_NOT_ALLOWED);
  1845. return false;
  1846. }
  1847. // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
  1848. Pet* pet = GetPet();
  1849. MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
  1850. // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)...
  1851. // don't let gm level > 1 either
  1852. if (!InBattleground() && mEntry->IsBattlegroundOrArena())
  1853. return false;
  1854. // client without expansion support
  1855. if (GetSession()->Expansion() < mEntry->Expansion())
  1856. {
  1857. TC_LOG_DEBUG("maps", "Player %s using client without required expansion tried teleport to non accessible map %u", GetName().c_str(), mapid);
  1858. if (Transport* transport = GetTransport())
  1859. {
  1860. transport->RemovePassenger(this);
  1861. RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :)
  1862. }
  1863. SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL, mEntry->Expansion());
  1864. return false; // normal client can't teleport to this map...
  1865. }
  1866. else
  1867. TC_LOG_DEBUG("maps", "Player %s is being teleported to map %u", GetName().c_str(), mapid);
  1868. if (m_vehicle)
  1869. ExitVehicle();
  1870. // reset movement flags at teleport, because player will continue move with these flags after teleport
  1871. SetUnitMovementFlags(GetUnitMovementFlags() & MOVEMENTFLAG_MASK_HAS_PLAYER_STATUS_OPCODE);
  1872. m_movementInfo.ResetJump();
  1873. DisableSpline();
  1874. if (Transport* transport = GetTransport())
  1875. {
  1876. if (!(options & TELE_TO_NOT_LEAVE_TRANSPORT))
  1877. transport->RemovePassenger(this);
  1878. }
  1879. // The player was ported to another map and loses the duel immediately.
  1880. // We have to perform this check before the teleport, otherwise the
  1881. // ObjectAccessor won't find the flag.
  1882. if (duel && GetMapId() != mapid && GetMap()->GetGameObject(GetGuidValue(PLAYER_DUEL_ARBITER)))
  1883. DuelComplete(DUEL_FLED);
  1884. if (GetMapId() == mapid)
  1885. {
  1886. //lets reset far teleport flag if it wasn't reset during chained teleports
  1887. SetSemaphoreTeleportFar(false);
  1888. //setup delayed teleport flag
  1889. SetDelayedTeleportFlag(IsCanDelayTeleport());
  1890. //if teleport spell is cast in Unit::Update() func
  1891. //then we need to delay it until update process will be finished
  1892. if (IsHasDelayedTeleport())
  1893. {
  1894. SetSemaphoreTeleportNear(true);
  1895. //lets save teleport destination for player
  1896. m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
  1897. m_teleport_options = options;
  1898. return true;
  1899. }
  1900. if (!(options & TELE_TO_NOT_UNSUMMON_PET))
  1901. {
  1902. //same map, only remove pet if out of range for new position
  1903. if (pet && !pet->IsWithinDist3d(x, y, z, GetMap()->GetVisibilityRange()))
  1904. UnsummonPetTemporaryIfAny();
  1905. }
  1906. if (!(options & TELE_TO_NOT_LEAVE_COMBAT))
  1907. CombatStop();
  1908. // this will be used instead of the current location in SaveToDB
  1909. m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
  1910. SetFallInformation(0, z);
  1911. // code for finish transfer called in WorldSession::HandleMovementOpcodes()
  1912. // at client packet CMSG_MOVE_TELEPORT_ACK
  1913. SetSemaphoreTeleportNear(true);
  1914. // near teleport, triggering send CMSG_MOVE_TELEPORT_ACK from client at landing
  1915. if (!GetSession()->PlayerLogout())
  1916. {
  1917. Position oldPos = GetPosition();
  1918. if (HasUnitMovementFlag(MOVEMENTFLAG_HOVER))
  1919. z += GetFloatValue(UNIT_FIELD_HOVERHEIGHT);
  1920. Relocate(x, y, z, orientation);
  1921. SendTeleportPacket(oldPos); // this automatically relocates to oldPos in order to broadcast the packet in the right place
  1922. }
  1923. }
  1924. else
  1925. {
  1926. if (getClass() == CLASS_DEATH_KNIGHT && GetMapId() == 609 && !IsGameMaster() && !HasSpell(50977))
  1927. return false;
  1928. // far teleport to another map
  1929. Map* oldmap = IsInWorld() ? GetMap() : NULL;
  1930. // check if we can enter before stopping combat / removing pet / totems / interrupting spells
  1931. // Check enter rights before map getting to avoid creating instance copy for player
  1932. // this check not dependent from map instance copy and same for all instance copies of selected map
  1933. if (!sMapMgr->CanPlayerEnter(mapid, this, false))
  1934. return false;
  1935. //I think this always returns true. Correct me if I am wrong.
  1936. // If the map is not created, assume it is possible to enter it.
  1937. // It will be created in the WorldPortAck.
  1938. //Map* map = sMapMgr->FindBaseNonInstanceMap(mapid);
  1939. //if (!map || map->CanEnter(this))
  1940. {
  1941. //lets reset near teleport flag if it wasn't reset during chained teleports
  1942. SetSemaphoreTeleportNear(false);
  1943. //setup delayed teleport flag
  1944. SetDelayedTeleportFlag(IsCanDelayTeleport());
  1945. //if teleport spell is cast in Unit::Update() func
  1946. //then we need to delay it until update process will be finished
  1947. if (IsHasDelayedTeleport())
  1948. {
  1949. SetSemaphoreTeleportFar(true);
  1950. //lets save teleport destination for player
  1951. m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
  1952. m_teleport_options = options;
  1953. return true;
  1954. }
  1955. SetSelection(ObjectGuid::Empty);
  1956. CombatStop();
  1957. ResetContestedPvP();
  1958. // remove player from battleground on far teleport (when changing maps)
  1959. if (Battleground const* bg = GetBattleground())
  1960. {
  1961. // Note: at battleground join battleground id set before teleport
  1962. // and we already will found "current" battleground
  1963. // just need check that this is targeted map or leave
  1964. if (bg->GetMapId() != mapid)
  1965. LeaveBattleground(false); // don't teleport to entry point
  1966. }
  1967. // remove arena spell coldowns/buffs now to also remove pet's cooldowns before it's temporarily unsummoned
  1968. if (mEntry->IsBattleArena())
  1969. {
  1970. RemoveArenaSpellCooldowns(true);
  1971. RemoveArenaAuras();
  1972. if (pet)
  1973. pet->RemoveArenaAuras();
  1974. }
  1975. // remove pet on map change
  1976. if (pet)
  1977. UnsummonPetTemporaryIfAny();
  1978. // remove all dyn objects
  1979. RemoveAllDynObjects();
  1980. // stop spellcasting
  1981. // not attempt interrupt teleportation spell at caster teleport
  1982. if (!(options & TELE_TO_SPELL))
  1983. if (IsNonMeleeSpellCast(true))
  1984. InterruptNonMeleeSpells(true);
  1985. //remove auras before removing from map...
  1986. RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING);
  1987. if (!GetSession()->PlayerLogout())
  1988. {
  1989. // send transfer packets
  1990. WorldPacket data(SMSG_TRANSFER_PENDING, 4 + 4 + 4);
  1991. data.WriteBit(0); // unknown
  1992. if (Transport* transport = GetTransport())
  1993. {
  1994. data.WriteBit(1); // has transport
  1995. data << GetMapId() << transport->GetEntry();
  1996. }
  1997. else
  1998. data.WriteBit(0); // has transport
  1999. data << uint32(mapid);
  2000. GetSession()->SendPacket(&data);
  2001. }
  2002. // remove from old map now
  2003. if (oldmap)
  2004. oldmap->RemovePlayerFromMap(this, false);
  2005. m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
  2006. SetFallInformation(0, z);
  2007. // if the player is saved before worldportack (at logout for example)
  2008. // this will be used instead of the current location in SaveToDB
  2009. if (!GetSession()->PlayerLogout())
  2010. {
  2011. WorldPacket data(SMSG_NEW_WORLD, 4 + 4 + 4 + 4 + 4);
  2012. data << float(m_teleport_dest.GetPositionX());
  2013. data << float(m_teleport_dest.GetOrientation());
  2014. data << float(m_teleport_dest.GetPositionZ());
  2015. data << uint32(mapid);
  2016. data << float(m_teleport_dest.GetPositionY());
  2017. GetSession()->SendPacket(&data);
  2018. SendSavedInstances();
  2019. }
  2020. // move packet sent by client always after far teleport
  2021. // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
  2022. SetSemaphoreTeleportFar(true);
  2023. }
  2024. //else
  2025. // return false;
  2026. }
  2027. return true;
  2028. }
  2029. bool Player::TeleportTo(WorldLocation const &loc, uint32 options /*= 0*/)
  2030. {
  2031. return TeleportTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation(), options);
  2032. }
  2033. bool Player::TeleportToBGEntryPoint()
  2034. {
  2035. if (m_bgData.joinPos.m_mapId == MAPID_INVALID)
  2036. return false;
  2037. ScheduleDelayedOperation(DELAYED_BG_MOUNT_RESTORE);
  2038. ScheduleDelayedOperation(DELAYED_BG_TAXI_RESTORE);
  2039. ScheduleDelayedOperation(DELAYED_BG_GROUP_RESTORE);
  2040. return TeleportTo(m_bgData.joinPos);
  2041. }
  2042. void Player::ProcessDelayedOperations()
  2043. {
  2044. if (m_DelayedOperations == 0)
  2045. return;
  2046. if (m_DelayedOperations & DELAYED_RESURRECT_PLAYER)
  2047. {
  2048. ResurrectPlayer(0.0f, false);
  2049. if (GetMaxHealth() > _resurrectionData->Health)
  2050. SetHealth(_resurrectionData->Health);
  2051. else
  2052. SetFullHealth();
  2053. if (uint32(GetMaxPower(POWER_MANA)) > _resurrectionData->Mana)
  2054. SetPower(POWER_MANA, _resurrectionData->Mana);
  2055. else
  2056. SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
  2057. SetPower(POWER_RAGE, 0);
  2058. SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
  2059. SetPower(POWER_ECLIPSE, 0);
  2060. if (uint32 aura = _resurrectionData->Aura)
  2061. CastSpell(this, aura, true, NULL, NULL, _resurrectionData->GUID);
  2062. SpawnCorpseBones();
  2063. }
  2064. if (m_DelayedOperations & DELAYED_SAVE_PLAYER)
  2065. SaveToDB();
  2066. if (m_DelayedOperations & DELAYED_SPELL_CAST_DESERTER)
  2067. CastSpell(this, 26013, true); // Deserter
  2068. if (m_DelayedOperations & DELAYED_BG_MOUNT_RESTORE)
  2069. {
  2070. if (m_bgData.mountSpell)
  2071. {
  2072. CastSpell(this, m_bgData.mountSpell, true);
  2073. m_bgData.mountSpell = 0;
  2074. }
  2075. }
  2076. if (m_DelayedOperations & DELAYED_BG_TAXI_RESTORE)
  2077. {
  2078. if (m_bgData.HasTaxiPath())
  2079. {
  2080. m_taxi.AddTaxiDestination(m_bgData.taxiPath[0]);
  2081. m_taxi.AddTaxiDestination(m_bgData.taxiPath[1]);
  2082. m_bgData.ClearTaxiPath();
  2083. ContinueTaxiFlight();
  2084. }
  2085. }
  2086. if (m_DelayedOperations & DELAYED_BG_GROUP_RESTORE)
  2087. {
  2088. if (Group* g = GetGroup())
  2089. g->SendUpdateToPlayer(GetGUID());
  2090. }
  2091. //we have executed ALL delayed ops, so clear the flag
  2092. m_DelayedOperations = 0;
  2093. }
  2094. void Player::AddToWorld()
  2095. {
  2096. ///- Do not add/remove the player from the object storage
  2097. ///- It will crash when updating the ObjectAccessor
  2098. ///- The player should only be added when logging in
  2099. Unit::AddToWorld();
  2100. for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
  2101. if (m_items[i])
  2102. m_items[i]->AddToWorld();
  2103. }
  2104. void Player::RemoveFromWorld()
  2105. {
  2106. // cleanup
  2107. if (IsInWorld())
  2108. {
  2109. ///- Release charmed creatures, unsummon totems and remove pets/guardians
  2110. StopCastingCharm();
  2111. StopCastingBindSight();
  2112. UnsummonPetTemporaryIfAny();
  2113. sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
  2114. sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
  2115. }
  2116. ///- Do not add/remove the player from the object storage
  2117. ///- It will crash when updating the ObjectAccessor
  2118. ///- The player should only be removed when logging out
  2119. Unit::RemoveFromWorld();
  2120. for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
  2121. {
  2122. if (m_items[i])
  2123. m_items[i]->RemoveFromWorld();
  2124. }
  2125. for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
  2126. iter->second->RemoveFromWorld();
  2127. if (m_uint32Values)
  2128. {
  2129. if (WorldObject* viewpoint = GetViewpoint())
  2130. {
  2131. TC_LOG_ERROR("entities.player", "Player %s has viewpoint %u %u when removed from world",
  2132. GetName().c_str(), viewpoint->GetEntry(), viewpoint->GetTypeId());
  2133. SetViewpoint(viewpoint, false);
  2134. }
  2135. }
  2136. }
  2137. void Player::RegenerateAll()
  2138. {
  2139. //if (m_regenTimer <= 500)
  2140. // return;
  2141. m_regenTimerCount += m_regenTimer;
  2142. if (getClass() == CLASS_PALADIN)
  2143. m_holyPowerRegenTimerCount += m_regenTimer;
  2144. if (getClass() == CLASS_HUNTER)
  2145. m_focusRegenTimerCount += m_regenTimer;
  2146. Regenerate(POWER_ENERGY);
  2147. Regenerate(POWER_MANA);
  2148. // Runes act as cooldowns, and they don't need to send any data
  2149. if (getClass() == CLASS_DEATH_KNIGHT)
  2150. {
  2151. for (uint8 i = 0; i < MAX_RUNES; i += 2)
  2152. {
  2153. uint8 runeToRegen = i;
  2154. uint32 cd = GetRuneCooldown(i);
  2155. uint32 secondRuneCd = GetRuneCooldown(i + 1);
  2156. // Regenerate second rune of the same type only after first rune is off the cooldown
  2157. if (secondRuneCd && (cd > secondRuneCd || !cd))
  2158. {
  2159. runeToRegen = i + 1;
  2160. cd = secondRuneCd;
  2161. }
  2162. if (cd)
  2163. SetRuneCooldown(runeToRegen, (cd > m_regenTimer) ? cd - m_regenTimer : 0);
  2164. }
  2165. }
  2166. if (m_focusRegenTimerCount >= 1000 && getClass() == CLASS_HUNTER)
  2167. {
  2168. Regenerate(POWER_FOCUS);
  2169. m_focusRegenTimerCount -= 1000;
  2170. }
  2171. if (m_regenTimerCount >= 2000)
  2172. {
  2173. // Not in combat or they have regeneration
  2174. if (!IsInCombat() || IsPolymorphed() || m_baseHealthRegen ||
  2175. HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) ||
  2176. HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT))
  2177. {
  2178. RegenerateHealth();
  2179. }
  2180. Regenerate(POWER_RAGE);
  2181. if (getClass() == CLASS_DEATH_KNIGHT)
  2182. Regenerate(POWER_RUNIC_POWER);
  2183. m_regenTimerCount -= 2000;
  2184. }
  2185. if (m_holyPowerRegenTimerCount >= 10000 && getClass() == CLASS_PALADIN)
  2186. {
  2187. Regenerate(POWER_HOLY_POWER);
  2188. m_holyPowerRegenTimerCount -= 10000;
  2189. }
  2190. m_regenTimer = 0;
  2191. }
  2192. void Player::Regenerate(Powers power)
  2193. {
  2194. uint32 maxValue = GetMaxPower(power);
  2195. if (!maxValue)
  2196. return;
  2197. uint32 curValue = GetPower(power);
  2198. /// @todo possible use of miscvalueb instead of amount
  2199. if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER, power))
  2200. return;
  2201. // Skip regeneration for power type we cannot have
  2202. uint32 powerIndex = GetPowerIndex(power);
  2203. if (powerIndex == MAX_POWERS)
  2204. return;
  2205. float addvalue = 0.0f;
  2206. // Powers now benefit from haste.
  2207. float rangedHaste = GetFloatValue(PLAYER_FIELD_MOD_RANGED_HASTE);
  2208. float meleeHaste = GetFloatValue(PLAYER_FIELD_MOD_HASTE);
  2209. float spellHaste = GetFloatValue(UNIT_MOD_CAST_SPEED);
  2210. switch (power)
  2211. {
  2212. case POWER_MANA:
  2213. {
  2214. float ManaIncreaseRate = sWorld->getRate(RATE_POWER_MANA);
  2215. if (IsInCombat()) // Trinity Updates Mana in intervals of 2s, which is correct
  2216. addvalue += GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER) * ManaIncreaseRate * ((0.001f * m_regenTimer) + CalculatePct(0.001f, spellHaste));
  2217. else
  2218. addvalue += GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER) * ManaIncreaseRate * ((0.001f * m_regenTimer) + CalculatePct(0.001f, spellHaste));
  2219. }
  2220. break;
  2221. case POWER_RAGE: // Regenerate rage
  2222. {
  2223. if (!IsInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN))
  2224. {
  2225. float RageDecreaseRate = sWorld->getRate(RATE_POWER_RAGE_LOSS);
  2226. addvalue += -25 * RageDecreaseRate / meleeHaste; // 2.5 rage by tick (= 2 seconds => 1.25 rage/sec)
  2227. }
  2228. }
  2229. break;
  2230. case POWER_FOCUS:
  2231. addvalue += (6.0f + CalculatePct(6.0f, rangedHaste)) * sWorld->getRate(RATE_POWER_FOCUS);
  2232. break;
  2233. case POWER_ENERGY: // Regenerate energy (rogue)
  2234. addvalue += ((0.01f * m_regenTimer) + CalculatePct(0.01f, meleeHaste)) * sWorld->getRate(RATE_POWER_ENERGY);
  2235. break;
  2236. case POWER_RUNIC_POWER:
  2237. {
  2238. if (!IsInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN))
  2239. {
  2240. float RunicPowerDecreaseRate = sWorld->getRate(RATE_POWER_RUNICPOWER_LOSS);
  2241. addvalue += -30 * RunicPowerDecreaseRate; // 3 RunicPower by tick
  2242. }
  2243. }
  2244. break;
  2245. case POWER_HOLY_POWER: // Regenerate holy power
  2246. {
  2247. if (!IsInCombat())
  2248. addvalue += -1.0f; // remove 1 each 10 sec
  2249. }
  2250. break;
  2251. case POWER_RUNES:
  2252. break;
  2253. case POWER_HEALTH:
  2254. return;
  2255. default:
  2256. break;
  2257. }
  2258. // Mana regen calculated in Player::UpdateManaRegen()
  2259. if (power != POWER_MANA)
  2260. {
  2261. AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
  2262. for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
  2263. if (Powers((*i)->GetMiscValue()) == power)
  2264. AddPct(addvalue, (*i)->GetAmount());
  2265. // Butchery requires combat for this effect
  2266. if (power != POWER_RUNIC_POWER || IsInCombat())
  2267. addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * ((power != POWER_ENERGY) ? m_regenTimerCount : m_regenTimer) / (5 * IN_MILLISECONDS);
  2268. }
  2269. if (addvalue < 0.0f)
  2270. {
  2271. if (curValue == 0)
  2272. return;
  2273. }
  2274. else if (addvalue > 0.0f)
  2275. {
  2276. if (curValue == maxValue)
  2277. return;
  2278. }
  2279. else
  2280. return;
  2281. addvalue += m_powerFraction[powerIndex];
  2282. uint32 integerValue = uint32(std::fabs(addvalue));
  2283. if (addvalue < 0.0f)
  2284. {
  2285. if (curValue > integerValue)
  2286. {
  2287. curValue -= integerValue;
  2288. m_powerFraction[powerIndex] = addvalue + integerValue;
  2289. }
  2290. else
  2291. {
  2292. curValue = 0;
  2293. m_powerFraction[powerIndex] = 0;
  2294. }
  2295. }
  2296. else
  2297. {
  2298. curValue += integerValue;
  2299. if (curValue > maxValue)
  2300. {
  2301. curValue = maxValue;
  2302. m_powerFraction[powerIndex] = 0;
  2303. }
  2304. else
  2305. m_powerFraction[powerIndex] = addvalue - integerValue;
  2306. }
  2307. if (m_regenTimerCount >= 2000)
  2308. SetPower(power, curValue);
  2309. else
  2310. UpdateUInt32Value(UNIT_FIELD_POWER1 + powerIndex, curValue);
  2311. }
  2312. void Player::RegenerateHealth()
  2313. {
  2314. uint32 curValue = GetHealth();
  2315. uint32 maxValue = GetMaxHealth();
  2316. if (curValue >= maxValue)
  2317. return;
  2318. float HealthIncreaseRate = sWorld->getRate(RATE_HEALTH);
  2319. float addValue = 0.0f;
  2320. // polymorphed case
  2321. if (IsPolymorphed())
  2322. addValue = float(GetMaxHealth()) / 3.0f;
  2323. // normal regen case (maybe partly in combat case)
  2324. else if (!IsInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT))
  2325. {
  2326. addValue = HealthIncreaseRate;
  2327. if (!IsInCombat())
  2328. {
  2329. if (getLevel() < 15)
  2330. addValue = (0.20f * ((float)GetMaxHealth()) / getLevel() * HealthIncreaseRate);
  2331. else
  2332. addValue = 0.015f * ((float)GetMaxHealth()) * HealthIncreaseRate;
  2333. AuraEffectList const& mModHealthRegenPct = GetAuraEffectsByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
  2334. for (AuraEffectList::const_iterator i = mModHealthRegenPct.begin(); i != mModHealthRegenPct.end(); ++i)
  2335. AddPct(addValue, (*i)->GetAmount());
  2336. addValue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * 0.4f;
  2337. }
  2338. else if (HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT))
  2339. ApplyPct(addValue, GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT));
  2340. if (!IsStandState())
  2341. addValue *= 1.5f;
  2342. }
  2343. // always regeneration bonus (including combat)
  2344. addValue += GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT);
  2345. addValue += m_baseHealthRegen / 2.5f;
  2346. if (addValue < 0.0f)
  2347. addValue = 0.0f;
  2348. ModifyHealth(int32(addValue));
  2349. }
  2350. void Player::ResetAllPowers()
  2351. {
  2352. SetHealth(GetMaxHealth());
  2353. switch (getPowerType())
  2354. {
  2355. case POWER_MANA:
  2356. SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
  2357. break;
  2358. case POWER_RAGE:
  2359. SetPower(POWER_RAGE, 0);
  2360. break;
  2361. case POWER_ENERGY:
  2362. SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
  2363. break;
  2364. case POWER_RUNIC_POWER:
  2365. SetPower(POWER_RUNIC_POWER, 0);
  2366. break;
  2367. case POWER_ECLIPSE:
  2368. SetPower(POWER_ECLIPSE, 0);
  2369. break;
  2370. default:
  2371. break;
  2372. }
  2373. }
  2374. bool Player::CanInteractWithQuestGiver(Object* questGiver)
  2375. {
  2376. switch (questGiver->GetTypeId())
  2377. {
  2378. case TYPEID_UNIT:
  2379. return GetNPCIfCanInteractWith(questGiver->GetGUID(), UNIT_NPC_FLAG_QUESTGIVER) != NULL;
  2380. case TYPEID_GAMEOBJECT:
  2381. return GetGameObjectIfCanInteractWith(questGiver->GetGUID(), GAMEOBJECT_TYPE_QUESTGIVER) != NULL;
  2382. case TYPEID_PLAYER:
  2383. return IsAlive() && questGiver->ToPlayer()->IsAlive();
  2384. case TYPEID_ITEM:
  2385. return IsAlive();
  2386. default:
  2387. break;
  2388. }
  2389. return false;
  2390. }
  2391. Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask)
  2392. {
  2393. // unit checks
  2394. if (!guid)
  2395. return NULL;
  2396. if (!IsInWorld())
  2397. return NULL;
  2398. if (IsInFlight())
  2399. return NULL;
  2400. // exist (we need look pets also for some interaction (quest/etc)
  2401. Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid);
  2402. if (!creature)
  2403. return NULL;
  2404. // Deathstate checks
  2405. if (!IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_GHOST))
  2406. return NULL;
  2407. // alive or spirit healer
  2408. if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_DEAD_INTERACT))
  2409. return NULL;
  2410. // appropriate npc type
  2411. if (npcflagmask && !creature->HasFlag(UNIT_NPC_FLAGS, npcflagmask))
  2412. return NULL;
  2413. // not allow interaction under control, but allow with own pets
  2414. if (creature->GetCharmerGUID())
  2415. return NULL;
  2416. // not enemy
  2417. if (creature->IsHostileTo(this))
  2418. return NULL;
  2419. // not unfriendly
  2420. if (FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(creature->getFaction()))
  2421. if (factionTemplate->faction)
  2422. if (FactionEntry const* faction = sFactionStore.LookupEntry(factionTemplate->faction))
  2423. if (faction->reputationListID >= 0 && GetReputationMgr().GetRank(faction) <= REP_UNFRIENDLY)
  2424. return NULL;
  2425. // not too far
  2426. if (!creature->IsWithinDistInMap(this, INTERACTION_DISTANCE))
  2427. return NULL;
  2428. return creature;
  2429. }
  2430. GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTypes type) const
  2431. {
  2432. if (GameObject* go = GetMap()->GetGameObject(guid))
  2433. {
  2434. if (go->GetGoType() == type)
  2435. {
  2436. if (go->IsWithinDistInMap(this, go->GetInteractionDistance()))
  2437. return go;
  2438. TC_LOG_DEBUG("maps", "GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal 10 is allowed)", go->GetGOInfo()->name.c_str(),
  2439. go->GetGUIDLow(), GetName().c_str(), GetGUIDLow(), go->GetDistance(this));
  2440. }
  2441. }
  2442. return NULL;
  2443. }
  2444. bool Player::IsUnderWater() const
  2445. {
  2446. return IsInWater() &&
  2447. GetPositionZ() < (GetBaseMap()->GetWaterLevel(GetPositionX(), GetPositionY())-2);
  2448. }
  2449. void Player::SetInWater(bool apply)
  2450. {
  2451. if (m_isInWater == apply)
  2452. return;
  2453. //define player in water by opcodes
  2454. //move player's guid into HateOfflineList of those mobs
  2455. //which can't swim and move guid back into ThreatList when
  2456. //on surface.
  2457. /// @todo exist also swimming mobs, and function must be symmetric to enter/leave water
  2458. m_isInWater = apply;
  2459. // remove auras that need water/land
  2460. RemoveAurasWithInterruptFlags(apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
  2461. getHostileRefManager().updateThreatTables();
  2462. }
  2463. void Player::SetGameMaster(bool on)
  2464. {
  2465. if (on)
  2466. {
  2467. m_ExtraFlags |= PLAYER_EXTRA_GM_ON;
  2468. setFaction(35);
  2469. SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
  2470. SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS);
  2471. if (Pet* pet = GetPet())
  2472. {
  2473. pet->setFaction(35);
  2474. pet->getHostileRefManager().setOnlineOfflineState(false);
  2475. }
  2476. RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
  2477. ResetContestedPvP();
  2478. getHostileRefManager().setOnlineOfflineState(false);
  2479. CombatStopWithPets();
  2480. m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity());
  2481. }
  2482. else
  2483. {
  2484. m_ExtraFlags &= ~ PLAYER_EXTRA_GM_ON;
  2485. setFactionForRace(getRace());
  2486. RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
  2487. RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS);
  2488. if (Pet* pet = GetPet())
  2489. {
  2490. pet->setFaction(getFaction());
  2491. pet->getHostileRefManager().setOnlineOfflineState(true);
  2492. }
  2493. // restore FFA PvP Server state
  2494. if (sWorld->IsFFAPvPRealm())
  2495. SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
  2496. // restore FFA PvP area state, remove not allowed for GM mounts
  2497. UpdateArea(m_areaUpdateId);
  2498. getHostileRefManager().setOnlineOfflineState(true);
  2499. m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER);
  2500. }
  2501. UpdateObjectVisibility();
  2502. }
  2503. void Player::SetGMVisible(bool on)
  2504. {
  2505. if (on)
  2506. {
  2507. m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; //remove flag
  2508. m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER);
  2509. }
  2510. else
  2511. {
  2512. m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE; //add flag
  2513. SetAcceptWhispers(false);
  2514. SetGameMaster(true);
  2515. m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity());
  2516. }
  2517. }
  2518. bool Player::IsGroupVisibleFor(Player const* p) const
  2519. {
  2520. switch (sWorld->getIntConfig(CONFIG_GROUP_VISIBILITY))
  2521. {
  2522. default: return IsInSameGroupWith(p);
  2523. case 1: return IsInSameRaidWith(p);
  2524. case 2: return GetTeam() == p->GetTeam();
  2525. }
  2526. }
  2527. bool Player::IsInSameGroupWith(Player const* p) const
  2528. {
  2529. return p == this || (GetGroup() != NULL &&
  2530. GetGroup() == p->GetGroup() &&
  2531. GetGroup()->SameSubGroup(this, p));
  2532. }
  2533. bool Player::IsInSameRaidWith(Player const* p) const
  2534. {
  2535. return p == this || (GetGroup() != NULL && GetGroup() == p->GetGroup());
  2536. }
  2537. ///- If the player is invited, remove him. If the group if then only 1 person, disband the group.
  2538. /// @todo Shouldn't we also check if there is no other invitees before disbanding the group?
  2539. void Player::UninviteFromGroup()
  2540. {
  2541. Group* group = GetGroupInvite();
  2542. if (!group)
  2543. return;
  2544. group->RemoveInvite(this);
  2545. if (group->GetMembersCount() <= 1) // group has just 1 member => disband
  2546. {
  2547. if (group->IsCreated())
  2548. {
  2549. group->Disband(true);
  2550. }
  2551. else
  2552. {
  2553. group->RemoveAllInvites();
  2554. delete group;
  2555. }
  2556. }
  2557. }
  2558. void Player::RemoveFromGroup(Group* group, ObjectGuid guid, RemoveMethod method /* = GROUP_REMOVEMETHOD_DEFAULT*/, ObjectGuid kicker /* = ObjectGuid::Empty */, const char* reason /* = NULL */)
  2559. {
  2560. if (!group)
  2561. return;
  2562. group->RemoveMember(guid, method, kicker, reason);
  2563. }
  2564. void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 BonusXP, bool recruitAFriend, float /*group_rate*/)
  2565. {
  2566. WorldPacket data(SMSG_LOG_XPGAIN, 21); // guess size?
  2567. data << uint64(victim ? victim->GetGUID() : ObjectGuid::Empty);
  2568. data << uint32(GivenXP + BonusXP); // given experience
  2569. data << uint8(victim ? 0 : 1); // 00-kill_xp type, 01-non_kill_xp type
  2570. if (victim)
  2571. {
  2572. data << uint32(GivenXP); // experience without bonus
  2573. // should use group_rate here but can't figure out how
  2574. data << float(1); // 1 - none 0 - 100% group bonus output
  2575. }
  2576. data << uint8(recruitAFriend ? 1 : 0); // does the GivenXP include a RaF bonus?
  2577. GetSession()->SendPacket(&data);
  2578. }
  2579. void Player::GiveXP(uint32 xp, Unit* victim, float group_rate)
  2580. {
  2581. if (xp < 1)
  2582. return;
  2583. if (!IsAlive() && !GetBattlegroundId())
  2584. return;
  2585. if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_NO_XP_GAIN))
  2586. return;
  2587. if (victim && victim->GetTypeId() == TYPEID_UNIT && !victim->ToCreature()->hasLootRecipient())
  2588. return;
  2589. uint8 level = getLevel();
  2590. sScriptMgr->OnGivePlayerXP(this, xp, victim);
  2591. // XP to money conversion processed in Player::RewardQuest
  2592. if (level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  2593. return;
  2594. uint32 bonus_xp = 0;
  2595. bool recruitAFriend = GetsRecruitAFriendBonus(true);
  2596. // RaF does NOT stack with rested experience
  2597. if (recruitAFriend)
  2598. bonus_xp = 2 * xp; // xp + bonus_xp must add up to 3 * xp for RaF; calculation for quests done client-side
  2599. else
  2600. bonus_xp = victim ? GetXPRestBonus(xp) : 0; // XP resting bonus
  2601. SendLogXPGain(xp, victim, bonus_xp, recruitAFriend, group_rate);
  2602. uint32 curXP = GetUInt32Value(PLAYER_XP);
  2603. uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
  2604. uint32 newXP = curXP + xp + bonus_xp;
  2605. while (newXP >= nextLvlXP && level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  2606. {
  2607. newXP -= nextLvlXP;
  2608. if (level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  2609. GiveLevel(level + 1);
  2610. level = getLevel();
  2611. nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
  2612. }
  2613. SetUInt32Value(PLAYER_XP, newXP);
  2614. }
  2615. // Update player to next level
  2616. // Current player experience not update (must be update by caller)
  2617. void Player::GiveLevel(uint8 level)
  2618. {
  2619. uint8 oldLevel = getLevel();
  2620. if (level == oldLevel)
  2621. return;
  2622. if (Guild* guild = GetGuild())
  2623. guild->UpdateMemberData(this, GUILD_MEMBER_DATA_LEVEL, level);
  2624. PlayerLevelInfo info;
  2625. sObjectMgr->GetPlayerLevelInfo(getRace(), getClass(), level, &info);
  2626. uint32 basehp = 0, basemana = 0;
  2627. sObjectMgr->GetPlayerClassLevelInfo(getClass(), level, basehp, basemana);
  2628. // send levelup info to client
  2629. WorldPacket data(SMSG_LEVELUP_INFO, (4+4+MAX_POWERS_PER_CLASS*4+MAX_STATS*4));
  2630. data << uint32(level);
  2631. data << uint32(int32(basehp) - int32(GetCreateHealth()));
  2632. // for (int i = 0; i < MAX_STORED_POWERS; ++i) // Powers loop (0-10)
  2633. data << uint32(int32(basemana) - int32(GetCreateMana()));
  2634. data << uint32(0);
  2635. data << uint32(0);
  2636. data << uint32(0);
  2637. data << uint32(0);
  2638. // end for
  2639. for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) // Stats loop (0-4)
  2640. data << uint32(int32(info.stats[i]) - GetCreateStat(Stats(i)));
  2641. GetSession()->SendPacket(&data);
  2642. SetUInt32Value(PLAYER_NEXT_LEVEL_XP, sObjectMgr->GetXPForLevel(level));
  2643. //update level, max level of skills
  2644. m_Played_time[PLAYED_TIME_LEVEL] = 0; // Level Played Time reset
  2645. _ApplyAllLevelScaleItemMods(false);
  2646. SetLevel(level);
  2647. UpdateSkillsForLevel();
  2648. // save base values (bonuses already included in stored stats
  2649. for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
  2650. SetCreateStat(Stats(i), info.stats[i]);
  2651. SetCreateHealth(basehp);
  2652. SetCreateMana(basemana);
  2653. InitTalentForLevel();
  2654. InitTaxiNodesForLevel();
  2655. InitGlyphsForLevel();
  2656. UpdateAllStats();
  2657. if (sWorld->getBoolConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up
  2658. UpdateSkillsToMaxSkillsForLevel();
  2659. _ApplyAllLevelScaleItemMods(true); // Moved to above SetFullHealth so player will have full health from Heirlooms
  2660. // set current level health and mana/energy to maximum after applying all mods.
  2661. SetFullHealth();
  2662. SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
  2663. SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
  2664. if (GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
  2665. SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
  2666. SetPower(POWER_FOCUS, 0);
  2667. // update level to hunter/summon pet
  2668. if (Pet* pet = GetPet())
  2669. pet->SynchronizeLevelWithOwner();
  2670. if (MailLevelReward const* mailReward = sObjectMgr->GetMailLevelReward(level, getRaceMask()))
  2671. {
  2672. /// @todo Poor design of mail system
  2673. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  2674. MailDraft(mailReward->mailTemplateId).SendMailTo(trans, this, MailSender(MAIL_CREATURE, mailReward->senderEntry));
  2675. CharacterDatabase.CommitTransaction(trans);
  2676. }
  2677. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL);
  2678. // Refer-A-Friend
  2679. if (GetSession()->GetRecruiterId())
  2680. if (level < sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL))
  2681. if (level % 2 == 0)
  2682. {
  2683. ++m_grantableLevels;
  2684. if (!HasByteFlag(PLAYER_FIELD_BYTES, 1, 0x01))
  2685. SetByteFlag(PLAYER_FIELD_BYTES, 1, 0x01);
  2686. }
  2687. sScriptMgr->OnPlayerLevelChanged(this, oldLevel);
  2688. }
  2689. void Player::InitTalentForLevel()
  2690. {
  2691. uint8 level = getLevel();
  2692. // talents base at level diff (talents = level - 9 but some can be used already)
  2693. if (level < 10)
  2694. {
  2695. // Remove all talent points
  2696. if (GetUsedTalentCount() > 0) // Free any used talents
  2697. {
  2698. ResetTalents(true);
  2699. SetFreeTalentPoints(0);
  2700. }
  2701. }
  2702. else
  2703. {
  2704. if (level < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL) || GetSpecsCount() == 0)
  2705. {
  2706. SetSpecsCount(1);
  2707. SetActiveSpec(0);
  2708. }
  2709. uint32 talentPointsForLevel = CalculateTalentsPoints();
  2710. // if used more that have then reset
  2711. if (GetUsedTalentCount() > talentPointsForLevel)
  2712. {
  2713. if (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_MORE_TALENTS_THAN_ALLOWED))
  2714. ResetTalents(true);
  2715. else
  2716. SetFreeTalentPoints(0);
  2717. }
  2718. // else update amount of free points
  2719. else
  2720. SetFreeTalentPoints(talentPointsForLevel - GetUsedTalentCount());
  2721. }
  2722. if (!GetSession()->PlayerLoading())
  2723. SendTalentsInfoData(false); // update at client
  2724. }
  2725. void Player::InitStatsForLevel(bool reapplyMods)
  2726. {
  2727. if (reapplyMods) //reapply stats values only on .reset stats (level) command
  2728. _RemoveAllStatBonuses();
  2729. uint32 basehp = 0, basemana = 0;
  2730. sObjectMgr->GetPlayerClassLevelInfo(getClass(), getLevel(), basehp, basemana);
  2731. PlayerLevelInfo info;
  2732. sObjectMgr->GetPlayerLevelInfo(getRace(), getClass(), getLevel(), &info);
  2733. SetUInt32Value(PLAYER_FIELD_MAX_LEVEL, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
  2734. SetUInt32Value(PLAYER_NEXT_LEVEL_XP, sObjectMgr->GetXPForLevel(getLevel()));
  2735. // reset before any aura state sources (health set/aura apply)
  2736. SetUInt32Value(UNIT_FIELD_AURASTATE, 0);
  2737. UpdateSkillsForLevel();
  2738. // set default cast time multiplier
  2739. SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
  2740. SetFloatValue(UNIT_MOD_CAST_HASTE, 1.0f);
  2741. SetFloatValue(PLAYER_FIELD_MOD_HASTE, 1.0f);
  2742. SetFloatValue(PLAYER_FIELD_MOD_RANGED_HASTE, 1.0f);
  2743. // reset size before reapply auras
  2744. SetObjectScale(1.0f);
  2745. // save base values (bonuses already included in stored stats
  2746. for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
  2747. SetCreateStat(Stats(i), info.stats[i]);
  2748. for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
  2749. SetStat(Stats(i), info.stats[i]);
  2750. SetCreateHealth(basehp);
  2751. //set create powers
  2752. SetCreateMana(basemana);
  2753. SetArmor(int32(m_createStats[STAT_AGILITY]*2));
  2754. InitStatBuffMods();
  2755. //reset rating fields values
  2756. for (uint16 index = PLAYER_FIELD_COMBAT_RATING_1; index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index)
  2757. SetUInt32Value(index, 0);
  2758. SetUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, 0);
  2759. SetFloatValue(PLAYER_FIELD_MOD_HEALING_PCT, 1.0f);
  2760. SetFloatValue(PLAYER_FIELD_MOD_HEALING_DONE_PCT, 1.0f);
  2761. for (uint8 i = 0; i < 7; ++i)
  2762. {
  2763. SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i, 0);
  2764. SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, 0);
  2765. SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+i, 1.0f);
  2766. }
  2767. SetFloatValue(PLAYER_FIELD_MOD_SPELL_POWER_PCT, 1.0f);
  2768. //reset attack power, damage and attack speed fields
  2769. SetFloatValue(UNIT_FIELD_BASEATTACKTIME, 2000.0f);
  2770. SetFloatValue(UNIT_FIELD_BASEATTACKTIME + 1, 2000.0f); // offhand attack time
  2771. SetFloatValue(UNIT_FIELD_RANGEDATTACKTIME, 2000.0f);
  2772. SetFloatValue(UNIT_FIELD_MINDAMAGE, 0.0f);
  2773. SetFloatValue(UNIT_FIELD_MAXDAMAGE, 0.0f);
  2774. SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, 0.0f);
  2775. SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, 0.0f);
  2776. SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, 0.0f);
  2777. SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, 0.0f);
  2778. SetFloatValue(PLAYER_FIELD_WEAPON_DMG_MULTIPLIERS, 1.0f);
  2779. SetInt32Value(UNIT_FIELD_ATTACK_POWER, 0);
  2780. SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, 0.0f);
  2781. SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0);
  2782. SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER, 0.0f);
  2783. // Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
  2784. SetFloatValue(PLAYER_CRIT_PERCENTAGE, 0.0f);
  2785. SetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE, 0.0f);
  2786. SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE, 0.0f);
  2787. // Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
  2788. for (uint8 i = 0; i < 7; ++i)
  2789. SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1+i, 0.0f);
  2790. SetFloatValue(PLAYER_PARRY_PERCENTAGE, 0.0f);
  2791. SetFloatValue(PLAYER_BLOCK_PERCENTAGE, 0.0f);
  2792. // Static 30% damage blocked
  2793. SetUInt32Value(PLAYER_SHIELD_BLOCK, 30);
  2794. // Dodge percentage
  2795. SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f);
  2796. // set armor (resistance 0) to original value (create_agility*2)
  2797. SetArmor(int32(m_createStats[STAT_AGILITY]*2));
  2798. SetResistanceBuffMods(SpellSchools(0), true, 0.0f);
  2799. SetResistanceBuffMods(SpellSchools(0), false, 0.0f);
  2800. // set other resistance to original value (0)
  2801. for (uint8 i = 1; i < MAX_SPELL_SCHOOL; ++i)
  2802. {
  2803. SetResistance(SpellSchools(i), 0);
  2804. SetResistanceBuffMods(SpellSchools(i), true, 0.0f);
  2805. SetResistanceBuffMods(SpellSchools(i), false, 0.0f);
  2806. }
  2807. SetUInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, 0);
  2808. SetUInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE, 0);
  2809. for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
  2810. {
  2811. SetUInt32Value(UNIT_FIELD_POWER_COST_MODIFIER+i, 0);
  2812. SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i, 0.0f);
  2813. }
  2814. // Reset no reagent cost field
  2815. for (uint8 i = 0; i < 3; ++i)
  2816. SetUInt32Value(PLAYER_NO_REAGENT_COST_1 + i, 0);
  2817. // Init data for form but skip reapply item mods for form
  2818. InitDataForForm(reapplyMods);
  2819. // save new stats
  2820. for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
  2821. SetMaxPower(Powers(i), GetCreatePowers(Powers(i)));
  2822. SetMaxHealth(basehp); // stamina bonus will applied later
  2823. // cleanup mounted state (it will set correctly at aura loading if player saved at mount.
  2824. SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
  2825. // cleanup unit flags (will be re-applied if need at aura load).
  2826. RemoveFlag(UNIT_FIELD_FLAGS,
  2827. UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 |
  2828. UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_LOOTING |
  2829. UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED |
  2830. UNIT_FLAG_STUNNED | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED |
  2831. UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_NOT_SELECTABLE |
  2832. UNIT_FLAG_SKINNABLE | UNIT_FLAG_MOUNT | UNIT_FLAG_TAXI_FLIGHT );
  2833. SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); // must be set
  2834. SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER);// must be set
  2835. // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example.
  2836. RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST | PLAYER_ALLOW_ONLY_ABILITY);
  2837. RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes
  2838. RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SANCTUARY);
  2839. // restore if need some important flags
  2840. SetUInt32Value(PLAYER_FIELD_BYTES2, 0); // flags empty by default
  2841. if (reapplyMods) // reapply stats values only on .reset stats (level) command
  2842. _ApplyAllStatBonuses();
  2843. // set current level health and mana/energy to maximum after applying all mods.
  2844. SetFullHealth();
  2845. SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
  2846. SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
  2847. if (GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
  2848. SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
  2849. SetPower(POWER_FOCUS, GetMaxPower(POWER_FOCUS));
  2850. SetPower(POWER_RUNIC_POWER, 0);
  2851. // update level to hunter/summon pet
  2852. if (Pet* pet = GetPet())
  2853. pet->SynchronizeLevelWithOwner();
  2854. }
  2855. void Player::SendInitialSpells()
  2856. {
  2857. time_t curTime = time(NULL);
  2858. time_t infTime = curTime + infinityCooldownDelayCheck;
  2859. uint16 spellCount = 0;
  2860. WorldPacket data(SMSG_INITIAL_SPELLS, (1+2+4*m_spells.size()+2+m_spellCooldowns.size()*(2+2+2+4+4)));
  2861. data << uint8(0);
  2862. size_t countPos = data.wpos();
  2863. data << uint16(spellCount); // spell count placeholder
  2864. for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
  2865. {
  2866. if (itr->second->state == PLAYERSPELL_REMOVED)
  2867. continue;
  2868. if (!itr->second->active || itr->second->disabled)
  2869. continue;
  2870. data << uint32(itr->first);
  2871. data << uint16(0); // it's not slot id
  2872. ++spellCount;
  2873. }
  2874. data.put<uint16>(countPos, spellCount); // write real count value
  2875. uint16 spellCooldowns = m_spellCooldowns.size();
  2876. data << uint16(spellCooldowns);
  2877. for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); ++itr)
  2878. {
  2879. SpellInfo const* sEntry = sSpellMgr->GetSpellInfo(itr->first);
  2880. if (!sEntry)
  2881. continue;
  2882. data << uint32(itr->first);
  2883. data << uint32(itr->second.itemid); // cast item id
  2884. data << uint16(sEntry->GetCategory()); // spell category
  2885. // send infinity cooldown in special format
  2886. if (itr->second.end >= infTime)
  2887. {
  2888. data << uint32(1); // cooldown
  2889. data << uint32(0x80000000); // category cooldown
  2890. continue;
  2891. }
  2892. time_t cooldown = itr->second.end > curTime ? (itr->second.end-curTime)*IN_MILLISECONDS : 0;
  2893. if (sEntry->GetCategory()) // may be wrong, but anyway better than nothing...
  2894. {
  2895. data << uint32(0); // cooldown
  2896. data << uint32(cooldown); // category cooldown
  2897. }
  2898. else
  2899. {
  2900. data << uint32(cooldown); // cooldown
  2901. data << uint32(0); // category cooldown
  2902. }
  2903. }
  2904. GetSession()->SendPacket(&data);
  2905. TC_LOG_DEBUG("network", "CHARACTER: Sent Initial Spells");
  2906. }
  2907. void Player::RemoveMail(uint32 id)
  2908. {
  2909. for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
  2910. {
  2911. if ((*itr)->messageID == id)
  2912. {
  2913. //do not delete item, because Player::removeMail() is called when returning mail to sender.
  2914. m_mail.erase(itr);
  2915. return;
  2916. }
  2917. }
  2918. }
  2919. void Player::SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError, uint32 item_guid, uint32 item_count)
  2920. {
  2921. WorldPacket data(SMSG_SEND_MAIL_RESULT, (4+4+4+(mailError == MAIL_ERR_EQUIP_ERROR?4:(mailAction == MAIL_ITEM_TAKEN?4+4:0))));
  2922. data << uint32(mailId);
  2923. data << uint32(mailAction);
  2924. data << uint32(mailError);
  2925. if (mailError == MAIL_ERR_EQUIP_ERROR)
  2926. data << uint32(equipError);
  2927. else if (mailAction == MAIL_ITEM_TAKEN)
  2928. {
  2929. data << uint32(item_guid); // item guid low?
  2930. data << uint32(item_count); // item count?
  2931. }
  2932. GetSession()->SendPacket(&data);
  2933. }
  2934. void Player::SendNewMail()
  2935. {
  2936. // deliver undelivered mail
  2937. WorldPacket data(SMSG_RECEIVED_MAIL, 4);
  2938. data << (uint32) 0;
  2939. GetSession()->SendPacket(&data);
  2940. }
  2941. void Player::UpdateNextMailTimeAndUnreads()
  2942. {
  2943. // calculate next delivery time (min. from non-delivered mails
  2944. // and recalculate unReadMail
  2945. time_t cTime = time(NULL);
  2946. m_nextMailDelivereTime = 0;
  2947. unReadMails = 0;
  2948. for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
  2949. {
  2950. if ((*itr)->deliver_time > cTime)
  2951. {
  2952. if (!m_nextMailDelivereTime || m_nextMailDelivereTime > (*itr)->deliver_time)
  2953. m_nextMailDelivereTime = (*itr)->deliver_time;
  2954. }
  2955. else if (((*itr)->checked & MAIL_CHECK_MASK_READ) == 0)
  2956. ++unReadMails;
  2957. }
  2958. }
  2959. void Player::AddNewMailDeliverTime(time_t deliver_time)
  2960. {
  2961. if (deliver_time <= time(NULL)) // ready now
  2962. {
  2963. ++unReadMails;
  2964. SendNewMail();
  2965. }
  2966. else // not ready and no have ready mails
  2967. {
  2968. if (!m_nextMailDelivereTime || m_nextMailDelivereTime > deliver_time)
  2969. m_nextMailDelivereTime = deliver_time;
  2970. }
  2971. }
  2972. void DeleteSpellFromAllPlayers(uint32 spellId)
  2973. {
  2974. CharacterDatabaseStatements stmts[2] = {CHAR_DEL_INVALID_SPELL_SPELLS, CHAR_DEL_INVALID_SPELL_TALENTS};
  2975. for (uint8 i = 0; i < 2; i++)
  2976. {
  2977. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmts[i]);
  2978. stmt->setUInt32(0, spellId);
  2979. CharacterDatabase.Execute(stmt);
  2980. }
  2981. }
  2982. bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning)
  2983. {
  2984. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  2985. if (!spellInfo)
  2986. {
  2987. // do character spell book cleanup (all characters)
  2988. if (!IsInWorld() && !learning) // spell load case
  2989. {
  2990. TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId);
  2991. DeleteSpellFromAllPlayers(spellId);
  2992. }
  2993. else
  2994. TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId);
  2995. return false;
  2996. }
  2997. if (!SpellMgr::IsSpellValid(spellInfo, this, false))
  2998. {
  2999. // do character spell book cleanup (all characters)
  3000. if (!IsInWorld() && !learning) // spell load case
  3001. {
  3002. TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", spellId);
  3003. DeleteSpellFromAllPlayers(spellId);
  3004. }
  3005. else
  3006. TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed.", spellId);
  3007. return false;
  3008. }
  3009. PlayerTalentMap::iterator itr = GetTalentMap(spec)->find(spellId);
  3010. if (itr != GetTalentMap(spec)->end())
  3011. itr->second->state = PLAYERSPELL_UNCHANGED;
  3012. else if (TalentSpellPos const* talentPos = GetTalentSpellPos(spellId))
  3013. {
  3014. if (TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentPos->talent_id))
  3015. {
  3016. for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
  3017. {
  3018. // skip learning spell and no rank spell case
  3019. uint32 rankSpellId = talentInfo->RankID[rank];
  3020. if (!rankSpellId || rankSpellId == spellId)
  3021. continue;
  3022. itr = GetTalentMap(spec)->find(rankSpellId);
  3023. if (itr != GetTalentMap(spec)->end())
  3024. itr->second->state = PLAYERSPELL_REMOVED;
  3025. }
  3026. }
  3027. PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
  3028. PlayerTalent* newtalent = new PlayerTalent();
  3029. newtalent->state = state;
  3030. newtalent->spec = spec;
  3031. (*GetTalentMap(spec))[spellId] = newtalent;
  3032. return true;
  3033. }
  3034. return false;
  3035. }
  3036. bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading /*= false*/, bool fromSkill /*= false*/)
  3037. {
  3038. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  3039. if (!spellInfo)
  3040. {
  3041. // do character spell book cleanup (all characters)
  3042. if (!IsInWorld() && !learning) // spell load case
  3043. {
  3044. TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId);
  3045. DeleteSpellFromAllPlayers(spellId);
  3046. }
  3047. else
  3048. TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId);
  3049. return false;
  3050. }
  3051. if (!SpellMgr::IsSpellValid(spellInfo, this, false))
  3052. {
  3053. // do character spell book cleanup (all characters)
  3054. if (!IsInWorld() && !learning) // spell load case
  3055. {
  3056. TC_LOG_ERROR("spells", "Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.", spellId);
  3057. DeleteSpellFromAllPlayers(spellId);
  3058. }
  3059. else
  3060. TC_LOG_ERROR("spells", "Player::addSpell: Broken spell #%u learning not allowed.", spellId);
  3061. return false;
  3062. }
  3063. PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
  3064. bool dependent_set = false;
  3065. bool disabled_case = false;
  3066. bool superceded_old = false;
  3067. PlayerSpellMap::iterator itr = m_spells.find(spellId);
  3068. // Remove temporary spell if found to prevent conflicts
  3069. if (itr != m_spells.end() && itr->second->state == PLAYERSPELL_TEMPORARY)
  3070. RemoveTemporarySpell(spellId);
  3071. else if (itr != m_spells.end())
  3072. {
  3073. uint32 next_active_spell_id = 0;
  3074. // fix activate state for non-stackable low rank (and find next spell for !active case)
  3075. if (!spellInfo->IsStackableWithRanks() && spellInfo->IsRanked())
  3076. {
  3077. if (uint32 next = sSpellMgr->GetNextSpellInChain(spellId))
  3078. {
  3079. if (HasSpell(next))
  3080. {
  3081. // high rank already known so this must !active
  3082. active = false;
  3083. next_active_spell_id = next;
  3084. }
  3085. }
  3086. }
  3087. // not do anything if already known in expected state
  3088. if (itr->second->state != PLAYERSPELL_REMOVED && itr->second->active == active &&
  3089. itr->second->dependent == dependent && itr->second->disabled == disabled)
  3090. {
  3091. if (!IsInWorld() && !learning) // explicitly load from DB and then exist in it already and set correctly
  3092. itr->second->state = PLAYERSPELL_UNCHANGED;
  3093. return false;
  3094. }
  3095. // dependent spell known as not dependent, overwrite state
  3096. if (itr->second->state != PLAYERSPELL_REMOVED && !itr->second->dependent && dependent)
  3097. {
  3098. itr->second->dependent = dependent;
  3099. if (itr->second->state != PLAYERSPELL_NEW)
  3100. itr->second->state = PLAYERSPELL_CHANGED;
  3101. dependent_set = true;
  3102. }
  3103. // update active state for known spell
  3104. if (itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled)
  3105. {
  3106. itr->second->active = active;
  3107. if (!IsInWorld() && !learning && !dependent_set) // explicitly load from DB and then exist in it already and set correctly
  3108. itr->second->state = PLAYERSPELL_UNCHANGED;
  3109. else if (itr->second->state != PLAYERSPELL_NEW)
  3110. itr->second->state = PLAYERSPELL_CHANGED;
  3111. if (active)
  3112. {
  3113. if (spellInfo->IsPassive() && IsNeedCastPassiveSpellAtLearn(spellInfo))
  3114. CastSpell (this, spellId, true);
  3115. }
  3116. else if (IsInWorld())
  3117. {
  3118. if (next_active_spell_id)
  3119. SendSupercededSpell(spellId, next_active_spell_id);
  3120. else
  3121. {
  3122. WorldPacket data(SMSG_REMOVED_SPELL, 4);
  3123. data << uint32(spellId);
  3124. GetSession()->SendPacket(&data);
  3125. }
  3126. }
  3127. return active; // learn (show in spell book if active now)
  3128. }
  3129. if (itr->second->disabled != disabled && itr->second->state != PLAYERSPELL_REMOVED)
  3130. {
  3131. if (itr->second->state != PLAYERSPELL_NEW)
  3132. itr->second->state = PLAYERSPELL_CHANGED;
  3133. itr->second->disabled = disabled;
  3134. if (disabled)
  3135. return false;
  3136. disabled_case = true;
  3137. }
  3138. else switch (itr->second->state)
  3139. {
  3140. case PLAYERSPELL_UNCHANGED: // known saved spell
  3141. return false;
  3142. case PLAYERSPELL_REMOVED: // re-learning removed not saved spell
  3143. {
  3144. delete itr->second;
  3145. m_spells.erase(itr);
  3146. state = PLAYERSPELL_CHANGED;
  3147. break; // need re-add
  3148. }
  3149. default: // known not saved yet spell (new or modified)
  3150. {
  3151. // can be in case spell loading but learned at some previous spell loading
  3152. if (!IsInWorld() && !learning && !dependent_set)
  3153. itr->second->state = PLAYERSPELL_UNCHANGED;
  3154. return false;
  3155. }
  3156. }
  3157. }
  3158. if (!disabled_case) // skip new spell adding if spell already known (disabled spells case)
  3159. {
  3160. // talent: unlearn all other talent ranks (high and low)
  3161. if (TalentSpellPos const* talentPos = GetTalentSpellPos(spellId))
  3162. {
  3163. if (TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentPos->talent_id))
  3164. {
  3165. for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
  3166. {
  3167. // skip learning spell and no rank spell case
  3168. uint32 rankSpellId = talentInfo->RankID[rank];
  3169. if (!rankSpellId || rankSpellId == spellId)
  3170. continue;
  3171. RemoveSpell(rankSpellId, false, false);
  3172. }
  3173. }
  3174. }
  3175. // non talent spell: learn low ranks (recursive call)
  3176. else if (uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spellId))
  3177. {
  3178. if (!IsInWorld() || disabled) // at spells loading, no output, but allow save
  3179. AddSpell(prev_spell, active, true, true, disabled, false, fromSkill);
  3180. else // at normal learning
  3181. LearnSpell(prev_spell, true, fromSkill);
  3182. }
  3183. PlayerSpell* newspell = new PlayerSpell;
  3184. newspell->state = state;
  3185. newspell->active = active;
  3186. newspell->dependent = dependent;
  3187. newspell->disabled = disabled;
  3188. // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
  3189. if (newspell->active && !newspell->disabled && !spellInfo->IsStackableWithRanks() && spellInfo->IsRanked() != 0)
  3190. {
  3191. for (PlayerSpellMap::iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2)
  3192. {
  3193. if (itr2->second->state == PLAYERSPELL_REMOVED)
  3194. continue;
  3195. SpellInfo const* i_spellInfo = sSpellMgr->GetSpellInfo(itr2->first);
  3196. if (!i_spellInfo)
  3197. continue;
  3198. if (spellInfo->IsDifferentRankOf(i_spellInfo))
  3199. {
  3200. if (itr2->second->active)
  3201. {
  3202. if (spellInfo->IsHighRankOf(i_spellInfo))
  3203. {
  3204. if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
  3205. SendSupercededSpell(itr2->first, spellId);
  3206. // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
  3207. itr2->second->active = false;
  3208. if (itr2->second->state != PLAYERSPELL_NEW)
  3209. itr2->second->state = PLAYERSPELL_CHANGED;
  3210. superceded_old = true; // new spell replace old in action bars and spell book.
  3211. }
  3212. else
  3213. {
  3214. if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
  3215. SendSupercededSpell(spellId, itr2->first);
  3216. // mark new spell as disable (not learned yet for client and will not learned)
  3217. newspell->active = false;
  3218. if (newspell->state != PLAYERSPELL_NEW)
  3219. newspell->state = PLAYERSPELL_CHANGED;
  3220. }
  3221. }
  3222. }
  3223. }
  3224. }
  3225. m_spells[spellId] = newspell;
  3226. // return false if spell disabled
  3227. if (newspell->disabled)
  3228. return false;
  3229. }
  3230. uint32 talentCost = GetTalentSpellCost(spellId);
  3231. // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
  3232. // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
  3233. if (!loading && talentCost > 0 && spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL))
  3234. {
  3235. // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
  3236. CastSpell(this, spellId, true);
  3237. }
  3238. // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
  3239. else if (spellInfo->IsPassive())
  3240. {
  3241. if (IsNeedCastPassiveSpellAtLearn(spellInfo))
  3242. CastSpell(this, spellId, true);
  3243. }
  3244. else if (spellInfo->HasEffect(SPELL_EFFECT_SKILL_STEP))
  3245. {
  3246. CastSpell(this, spellId, true);
  3247. return false;
  3248. }
  3249. // update used talent points count
  3250. SetUsedTalentCount(GetUsedTalentCount() + talentCost);
  3251. // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
  3252. if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
  3253. {
  3254. if (spellInfo->IsPrimaryProfessionFirstRank())
  3255. SetFreePrimaryProfessions(freeProfs-1);
  3256. }
  3257. SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
  3258. // add dependent skills if this spell is not learned from adding skill already
  3259. if (!fromSkill)
  3260. {
  3261. if (SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId))
  3262. {
  3263. uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
  3264. uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
  3265. if (skill_value < spellLearnSkill->value)
  3266. skill_value = spellLearnSkill->value;
  3267. uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : spellLearnSkill->maxvalue;
  3268. if (skill_max_value < new_skill_max_value)
  3269. skill_max_value = new_skill_max_value;
  3270. SetSkill(spellLearnSkill->skill, spellLearnSkill->step, skill_value, skill_max_value);
  3271. }
  3272. else
  3273. {
  3274. // not ranked skills
  3275. for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
  3276. {
  3277. SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
  3278. if (!pSkill)
  3279. continue;
  3280. ///@todo: confirm if rogues start with lockpicking skill at level 1 but only receive the spell to use it at level 16
  3281. if ((_spell_idx->second->AutolearnType == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(pSkill->id)) || (pSkill->id == SKILL_LOCKPICKING && _spell_idx->second->max_value == 0))
  3282. LearnDefaultSkill(pSkill->id, 0);
  3283. }
  3284. }
  3285. }
  3286. // learn dependent spells
  3287. SpellLearnSpellMapBounds spell_bounds = sSpellMgr->GetSpellLearnSpellMapBounds(spellId);
  3288. for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
  3289. {
  3290. if (!itr2->second.autoLearned)
  3291. {
  3292. if (!IsInWorld() || !itr2->second.active) // at spells loading, no output, but allow save
  3293. AddSpell(itr2->second.spell, itr2->second.active, true, true, false);
  3294. else // at normal learning
  3295. LearnSpell(itr2->second.spell, true);
  3296. }
  3297. }
  3298. if (!GetSession()->PlayerLoading())
  3299. {
  3300. // not ranked skills
  3301. for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
  3302. {
  3303. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE, _spell_idx->second->skillId);
  3304. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS, _spell_idx->second->skillId);
  3305. }
  3306. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL, spellId);
  3307. }
  3308. // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell
  3309. return active && !disabled && !superceded_old;
  3310. }
  3311. void Player::AddTemporarySpell(uint32 spellId)
  3312. {
  3313. PlayerSpellMap::iterator itr = m_spells.find(spellId);
  3314. // spell already added - do not do anything
  3315. if (itr != m_spells.end())
  3316. return;
  3317. PlayerSpell* newspell = new PlayerSpell;
  3318. newspell->state = PLAYERSPELL_TEMPORARY;
  3319. newspell->active = true;
  3320. newspell->dependent = false;
  3321. newspell->disabled = false;
  3322. m_spells[spellId] = newspell;
  3323. }
  3324. void Player::RemoveTemporarySpell(uint32 spellId)
  3325. {
  3326. PlayerSpellMap::iterator itr = m_spells.find(spellId);
  3327. // spell already not in list - do not do anything
  3328. if (itr == m_spells.end())
  3329. return;
  3330. // spell has other state than temporary - do not change it
  3331. if (itr->second->state != PLAYERSPELL_TEMPORARY)
  3332. return;
  3333. delete itr->second;
  3334. m_spells.erase(itr);
  3335. }
  3336. bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const
  3337. {
  3338. // note: form passives activated with shapeshift spells be implemented by HandleShapeshiftBoosts instead of spell_learn_spell
  3339. // talent dependent passives activated at form apply have proper stance data
  3340. ShapeshiftForm form = GetShapeshiftForm();
  3341. bool need_cast = (!spellInfo->Stances || (form && (spellInfo->Stances & (1 << (form - 1)))) ||
  3342. (!form && (spellInfo->AttributesEx2 & SPELL_ATTR2_NOT_NEED_SHAPESHIFT)));
  3343. if (spellInfo->AttributesEx8 & SPELL_ATTR8_MASTERY_SPECIALIZATION)
  3344. need_cast &= IsCurrentSpecMasterySpell(spellInfo);
  3345. //Check CasterAuraStates
  3346. return need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraStateType(spellInfo->CasterAuraState)));
  3347. }
  3348. bool Player::IsCurrentSpecMasterySpell(SpellInfo const* spellInfo) const
  3349. {
  3350. if (TalentTabEntry const* talentTab = sTalentTabStore.LookupEntry(GetPrimaryTalentTree(GetActiveSpec())))
  3351. return spellInfo->Id == talentTab->MasterySpellId[0] || spellInfo->Id == talentTab->MasterySpellId[1];
  3352. return false;
  3353. }
  3354. void Player::LearnSpell(uint32 spell_id, bool dependent, bool fromSkill /*= false*/)
  3355. {
  3356. PlayerSpellMap::iterator itr = m_spells.find(spell_id);
  3357. bool disabled = (itr != m_spells.end()) ? itr->second->disabled : false;
  3358. bool active = disabled ? itr->second->active : true;
  3359. bool learning = AddSpell(spell_id, active, true, dependent, false, false, fromSkill);
  3360. // prevent duplicated entires in spell book, also not send if not in world (loading)
  3361. if (learning && IsInWorld())
  3362. {
  3363. WorldPacket data(SMSG_LEARNED_SPELL, 8);
  3364. data << uint32(spell_id);
  3365. data << uint32(0);
  3366. GetSession()->SendPacket(&data);
  3367. }
  3368. // learn all disabled higher ranks and required spells (recursive)
  3369. if (disabled)
  3370. {
  3371. if (uint32 nextSpell = sSpellMgr->GetNextSpellInChain(spell_id))
  3372. {
  3373. PlayerSpellMap::iterator iter = m_spells.find(nextSpell);
  3374. if (iter != m_spells.end() && iter->second->disabled)
  3375. LearnSpell(nextSpell, false, fromSkill);
  3376. }
  3377. SpellsRequiringSpellMapBounds spellsRequiringSpell = sSpellMgr->GetSpellsRequiringSpellBounds(spell_id);
  3378. for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequiringSpell.first; itr2 != spellsRequiringSpell.second; ++itr2)
  3379. {
  3380. PlayerSpellMap::iterator iter2 = m_spells.find(itr2->second);
  3381. if (iter2 != m_spells.end() && iter2->second->disabled)
  3382. LearnSpell(itr2->second, false, fromSkill);
  3383. }
  3384. }
  3385. }
  3386. void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
  3387. {
  3388. PlayerSpellMap::iterator itr = m_spells.find(spell_id);
  3389. if (itr == m_spells.end())
  3390. return;
  3391. if (itr->second->state == PLAYERSPELL_REMOVED || (disabled && itr->second->disabled) || itr->second->state == PLAYERSPELL_TEMPORARY)
  3392. return;
  3393. // unlearn non talent higher ranks (recursive)
  3394. if (uint32 nextSpell = sSpellMgr->GetNextSpellInChain(spell_id))
  3395. {
  3396. if (HasSpell(nextSpell) && !GetTalentSpellPos(nextSpell))
  3397. RemoveSpell(nextSpell, disabled, false);
  3398. }
  3399. //unlearn spells dependent from recently removed spells
  3400. SpellsRequiringSpellMapBounds spellsRequiringSpell = sSpellMgr->GetSpellsRequiringSpellBounds(spell_id);
  3401. for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequiringSpell.first; itr2 != spellsRequiringSpell.second; ++itr2)
  3402. RemoveSpell(itr2->second, disabled);
  3403. // re-search, it can be corrupted in prev loop
  3404. itr = m_spells.find(spell_id);
  3405. if (itr == m_spells.end())
  3406. return; // already unleared
  3407. bool giveTalentPoints = disabled || !itr->second->disabled;
  3408. bool cur_active = itr->second->active;
  3409. bool cur_dependent = itr->second->dependent;
  3410. if (disabled)
  3411. {
  3412. itr->second->disabled = disabled;
  3413. if (itr->second->state != PLAYERSPELL_NEW)
  3414. itr->second->state = PLAYERSPELL_CHANGED;
  3415. }
  3416. else
  3417. {
  3418. if (itr->second->state == PLAYERSPELL_NEW)
  3419. {
  3420. delete itr->second;
  3421. m_spells.erase(itr);
  3422. }
  3423. else
  3424. itr->second->state = PLAYERSPELL_REMOVED;
  3425. }
  3426. RemoveOwnedAura(spell_id, GetGUID());
  3427. // remove pet auras
  3428. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  3429. if (PetAura const* petSpell = sSpellMgr->GetPetAura(spell_id, i))
  3430. RemovePetAura(petSpell);
  3431. // free talent points
  3432. uint32 talentCosts = GetTalentSpellCost(spell_id);
  3433. if (talentCosts > 0 && giveTalentPoints)
  3434. {
  3435. if (talentCosts < GetUsedTalentCount())
  3436. SetUsedTalentCount(GetUsedTalentCount() - talentCosts);
  3437. else
  3438. SetUsedTalentCount(0);
  3439. }
  3440. // update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
  3441. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
  3442. if (spellInfo && spellInfo->IsPrimaryProfessionFirstRank())
  3443. {
  3444. uint32 freeProfs = GetFreePrimaryProfessionPoints()+1;
  3445. if (freeProfs <= sWorld->getIntConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL))
  3446. SetFreePrimaryProfessions(freeProfs);
  3447. }
  3448. // remove dependent skill
  3449. SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spell_id);
  3450. if (spellLearnSkill)
  3451. {
  3452. uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spell_id);
  3453. if (!prev_spell) // first rank, remove skill
  3454. SetSkill(spellLearnSkill->skill, 0, 0, 0);
  3455. else
  3456. {
  3457. // search prev. skill setting by spell ranks chain
  3458. SpellLearnSkillNode const* prevSkill = sSpellMgr->GetSpellLearnSkill(prev_spell);
  3459. while (!prevSkill && prev_spell)
  3460. {
  3461. prev_spell = sSpellMgr->GetPrevSpellInChain(prev_spell);
  3462. prevSkill = sSpellMgr->GetSpellLearnSkill(sSpellMgr->GetFirstSpellInChain(prev_spell));
  3463. }
  3464. if (!prevSkill) // not found prev skill setting, remove skill
  3465. SetSkill(spellLearnSkill->skill, 0, 0, 0);
  3466. else // set to prev. skill setting values
  3467. {
  3468. uint32 skill_value = GetPureSkillValue(prevSkill->skill);
  3469. uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill);
  3470. if (skill_value > prevSkill->value)
  3471. skill_value = prevSkill->value;
  3472. uint32 new_skill_max_value = prevSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : prevSkill->maxvalue;
  3473. if (skill_max_value > new_skill_max_value)
  3474. skill_max_value = new_skill_max_value;
  3475. SetSkill(prevSkill->skill, prevSkill->step, skill_value, skill_max_value);
  3476. }
  3477. }
  3478. }
  3479. // remove dependent spells
  3480. SpellLearnSpellMapBounds spell_bounds = sSpellMgr->GetSpellLearnSpellMapBounds(spell_id);
  3481. for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
  3482. RemoveSpell(itr2->second.spell, disabled);
  3483. // activate lesser rank in spellbook/action bar, and cast it if need
  3484. bool prev_activate = false;
  3485. if (uint32 prev_id = sSpellMgr->GetPrevSpellInChain(spell_id))
  3486. {
  3487. // if talent then lesser rank also talent and need learn
  3488. if (talentCosts)
  3489. {
  3490. // I cannot see why mangos has these lines.
  3491. //if (learn_low_rank)
  3492. // learnSpell(prev_id, false);
  3493. }
  3494. // if ranked non-stackable spell: need activate lesser rank and update dendence state
  3495. /// No need to check for spellInfo != NULL here because if cur_active is true, then that means that the spell was already in m_spells, and only valid spells can be pushed there.
  3496. else if (cur_active && !spellInfo->IsStackableWithRanks() && spellInfo->IsRanked())
  3497. {
  3498. // need manually update dependence state (learn spell ignore like attempts)
  3499. PlayerSpellMap::iterator prev_itr = m_spells.find(prev_id);
  3500. if (prev_itr != m_spells.end())
  3501. {
  3502. if (prev_itr->second->dependent != cur_dependent)
  3503. {
  3504. prev_itr->second->dependent = cur_dependent;
  3505. if (prev_itr->second->state != PLAYERSPELL_NEW)
  3506. prev_itr->second->state = PLAYERSPELL_CHANGED;
  3507. }
  3508. // now re-learn if need re-activate
  3509. if (cur_active && !prev_itr->second->active && learn_low_rank)
  3510. {
  3511. if (AddSpell(prev_id, true, false, prev_itr->second->dependent, prev_itr->second->disabled))
  3512. {
  3513. // downgrade spell ranks in spellbook and action bar
  3514. SendSupercededSpell(spell_id, prev_id);
  3515. prev_activate = true;
  3516. }
  3517. }
  3518. }
  3519. }
  3520. }
  3521. if (spell_id == 46917 && m_canTitanGrip)
  3522. SetCanTitanGrip(false);
  3523. if (m_canDualWield)
  3524. {
  3525. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
  3526. if (spellInfo->IsPassive())
  3527. {
  3528. for (int i = 0; i < MAX_SPELL_EFFECTS; i++)
  3529. if (spellInfo->Effects[i].Effect == SPELL_EFFECT_DUAL_WIELD)
  3530. {
  3531. SetCanDualWield(false);
  3532. break;
  3533. }
  3534. }
  3535. }
  3536. if (sWorld->getBoolConfig(CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN))
  3537. AutoUnequipOffhandIfNeed();
  3538. // remove from spell book if not replaced by lesser rank
  3539. if (!prev_activate)
  3540. {
  3541. WorldPacket data(SMSG_REMOVED_SPELL, 4);
  3542. data << uint32(spell_id);
  3543. GetSession()->SendPacket(&data);
  3544. }
  3545. }
  3546. void Player::RemoveSpellCooldown(uint32 spell_id, bool update /* = false */)
  3547. {
  3548. m_spellCooldowns.erase(spell_id);
  3549. if (update)
  3550. SendClearCooldown(spell_id, this);
  3551. }
  3552. // I am not sure which one is more efficient
  3553. void Player::RemoveCategoryCooldown(uint32 cat)
  3554. {
  3555. SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(cat);
  3556. if (i_scstore != sSpellsByCategoryStore.end())
  3557. for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
  3558. RemoveSpellCooldown(*i_scset, true);
  3559. }
  3560. void Player::RemoveSpellCategoryCooldown(uint32 cat, bool update /* = false */)
  3561. {
  3562. SpellCategoryStore::const_iterator ct = sSpellsByCategoryStore.find(cat);
  3563. if (ct == sSpellsByCategoryStore.end())
  3564. return;
  3565. const SpellCategorySet& ct_set = ct->second;
  3566. for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end();)
  3567. {
  3568. if (ct_set.find(i->first) != ct_set.end())
  3569. RemoveSpellCooldown((i++)->first, update);
  3570. else
  3571. ++i;
  3572. }
  3573. }
  3574. void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns)
  3575. {
  3576. // remove cooldowns on spells that have <= 10 min CD
  3577. SpellCooldowns::iterator itr, next;
  3578. for (itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); itr = next)
  3579. {
  3580. next = itr;
  3581. ++next;
  3582. SpellInfo const* entry = sSpellMgr->GetSpellInfo(itr->first);
  3583. // check if spellentry is present and if the cooldown is less or equal to 10 min
  3584. if (entry &&
  3585. entry->RecoveryTime <= 10 * MINUTE * IN_MILLISECONDS &&
  3586. entry->CategoryRecoveryTime <= 10 * MINUTE * IN_MILLISECONDS)
  3587. {
  3588. // remove & notify
  3589. RemoveSpellCooldown(itr->first, true);
  3590. }
  3591. }
  3592. // pet cooldowns
  3593. if (removeActivePetCooldowns)
  3594. if (Pet* pet = GetPet())
  3595. {
  3596. // notify player
  3597. for (CreatureSpellCooldowns::const_iterator itr2 = pet->m_CreatureSpellCooldowns.begin(); itr2 != pet->m_CreatureSpellCooldowns.end(); ++itr2)
  3598. SendClearCooldown(itr2->first, pet);
  3599. // actually clear cooldowns
  3600. pet->m_CreatureSpellCooldowns.clear();
  3601. }
  3602. }
  3603. void Player::RemoveAllSpellCooldown()
  3604. {
  3605. if (!m_spellCooldowns.empty())
  3606. {
  3607. SendClearAllCooldowns(this);
  3608. m_spellCooldowns.clear();
  3609. }
  3610. }
  3611. void Player::_LoadSpellCooldowns(PreparedQueryResult result)
  3612. {
  3613. // some cooldowns can be already set at aura loading...
  3614. //QueryResult* result = CharacterDatabase.PQuery("SELECT spell, item, time FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow());
  3615. if (result)
  3616. {
  3617. time_t curTime = time(NULL);
  3618. do
  3619. {
  3620. Field* fields = result->Fetch();
  3621. uint32 spell_id = fields[0].GetUInt32();
  3622. uint32 item_id = fields[1].GetUInt32();
  3623. time_t db_time = time_t(fields[2].GetUInt32());
  3624. if (!sSpellMgr->GetSpellInfo(spell_id))
  3625. {
  3626. TC_LOG_ERROR("entities.player.loading", "Player %u has unknown spell %u in `character_spell_cooldown`, skipping.", GetGUIDLow(), spell_id);
  3627. continue;
  3628. }
  3629. // skip outdated cooldown
  3630. if (db_time <= curTime)
  3631. continue;
  3632. AddSpellCooldown(spell_id, item_id, db_time);
  3633. TC_LOG_DEBUG("entities.player.loading", "Player (GUID: %u) spell %u, item %u cooldown loaded (%u secs).", GetGUIDLow(), spell_id, item_id, uint32(db_time-curTime));
  3634. }
  3635. while (result->NextRow());
  3636. }
  3637. }
  3638. void Player::_SaveSpellCooldowns(SQLTransaction& trans)
  3639. {
  3640. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN);
  3641. stmt->setUInt32(0, GetGUIDLow());
  3642. trans->Append(stmt);
  3643. time_t curTime = time(NULL);
  3644. time_t infTime = curTime + infinityCooldownDelayCheck;
  3645. bool first_round = true;
  3646. std::ostringstream ss;
  3647. // remove outdated and save active
  3648. for (SpellCooldowns::iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end();)
  3649. {
  3650. if (itr->second.end <= curTime)
  3651. m_spellCooldowns.erase(itr++);
  3652. else if (itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload
  3653. {
  3654. if (first_round)
  3655. {
  3656. ss << "INSERT INTO character_spell_cooldown (guid, spell, item, time) VALUES ";
  3657. first_round = false;
  3658. }
  3659. // next new/changed record prefix
  3660. else
  3661. ss << ',';
  3662. ss << '(' << GetGUIDLow() << ',' << itr->first << ',' << itr->second.itemid << ',' << uint64(itr->second.end) << ')';
  3663. ++itr;
  3664. }
  3665. else
  3666. ++itr;
  3667. }
  3668. // if something changed execute
  3669. if (!first_round)
  3670. trans->Append(ss.str().c_str());
  3671. }
  3672. uint32 Player::GetNextResetTalentsCost() const
  3673. {
  3674. // The first time reset costs 1 gold
  3675. if (GetTalentResetCost() < 1*GOLD)
  3676. return 1*GOLD;
  3677. // then 5 gold
  3678. else if (GetTalentResetCost() < 5*GOLD)
  3679. return 5*GOLD;
  3680. // After that it increases in increments of 5 gold
  3681. else if (GetTalentResetCost() < 10*GOLD)
  3682. return 10*GOLD;
  3683. else
  3684. {
  3685. uint64 months = (sWorld->GetGameTime() - GetTalentResetTime())/MONTH;
  3686. if (months > 0)
  3687. {
  3688. // This cost will be reduced by a rate of 5 gold per month
  3689. int32 new_cost = int32(GetTalentResetCost() - 5*GOLD*months);
  3690. // to a minimum of 10 gold.
  3691. return (new_cost < 10*GOLD ? 10*GOLD : new_cost);
  3692. }
  3693. else
  3694. {
  3695. // After that it increases in increments of 5 gold
  3696. int32 new_cost = GetTalentResetCost() + 5*GOLD;
  3697. // until it hits a cap of 50 gold.
  3698. if (new_cost > 50*GOLD)
  3699. new_cost = 50*GOLD;
  3700. return new_cost;
  3701. }
  3702. }
  3703. }
  3704. bool Player::ResetTalents(bool no_cost)
  3705. {
  3706. sScriptMgr->OnPlayerTalentsReset(this, no_cost);
  3707. // not need after this call
  3708. if (HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
  3709. RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS, true);
  3710. uint32 talentPointsForLevel = CalculateTalentsPoints();
  3711. if (!GetUsedTalentCount())
  3712. {
  3713. SetFreeTalentPoints(talentPointsForLevel);
  3714. return false;
  3715. }
  3716. uint32 cost = 0;
  3717. if (!no_cost && !sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST))
  3718. {
  3719. cost = GetNextResetTalentsCost();
  3720. if (!HasEnoughMoney(uint64(cost)))
  3721. {
  3722. SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
  3723. return false;
  3724. }
  3725. }
  3726. RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
  3727. for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
  3728. {
  3729. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  3730. if (!talentInfo)
  3731. continue;
  3732. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
  3733. if (!talentTabInfo)
  3734. continue;
  3735. // unlearn only talents for character class
  3736. // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
  3737. // to prevent unexpected lost normal learned spell skip another class talents
  3738. if ((getClassMask() & talentTabInfo->ClassMask) == 0)
  3739. continue;
  3740. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  3741. {
  3742. // skip non-existant talent ranks
  3743. if (talentInfo->RankID[rank] == 0)
  3744. continue;
  3745. const SpellInfo* _spellEntry = sSpellMgr->GetSpellInfo(talentInfo->RankID[rank]);
  3746. if (!_spellEntry)
  3747. continue;
  3748. RemoveSpell(talentInfo->RankID[rank], true);
  3749. // search for spells that the talent teaches and unlearn them
  3750. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  3751. if (_spellEntry->Effects[i].TriggerSpell > 0 && _spellEntry->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL)
  3752. RemoveSpell(_spellEntry->Effects[i].TriggerSpell, true);
  3753. // if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
  3754. PlayerTalentMap::iterator plrTalent = GetTalentMap(GetActiveSpec())->find(talentInfo->RankID[rank]);
  3755. if (plrTalent != GetTalentMap(GetActiveSpec())->end())
  3756. plrTalent->second->state = PLAYERSPELL_REMOVED;
  3757. }
  3758. }
  3759. // Remove spec specific spells
  3760. uint32 const* talentTabs = GetTalentTabPages(getClass());
  3761. for (uint32 i = 0; i < MAX_TALENT_TABS; ++i)
  3762. {
  3763. if (std::vector<uint32> const* specSpells = GetTalentTreePrimarySpells(talentTabs[i]))
  3764. for (size_t j = 0; j < specSpells->size(); ++j)
  3765. RemoveSpell(specSpells->at(j), true);
  3766. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentTabs[i]);
  3767. for (uint32 j = 0; j < MAX_MASTERY_SPELLS; ++j)
  3768. if (uint32 mastery = talentTabInfo->MasterySpellId[j])
  3769. RemoveAurasDueToSpell(mastery);
  3770. }
  3771. SetPrimaryTalentTree(GetActiveSpec(), 0);
  3772. SetFreeTalentPoints(talentPointsForLevel);
  3773. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  3774. _SaveTalents(trans);
  3775. _SaveSpells(trans);
  3776. CharacterDatabase.CommitTransaction(trans);
  3777. if (!no_cost)
  3778. {
  3779. ModifyMoney(-(int64)cost);
  3780. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost);
  3781. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS, 1);
  3782. SetTalentResetCost(cost);
  3783. SetTalentResetTime(time(NULL));
  3784. }
  3785. /* when prev line will dropped use next line
  3786. if (Pet* pet = GetPet())
  3787. {
  3788. if (pet->getPetType() == HUNTER_PET && !pet->GetCreatureTemplate()->IsTameable(CanTameExoticPets()))
  3789. RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
  3790. }
  3791. */
  3792. return true;
  3793. }
  3794. Mail* Player::GetMail(uint32 id)
  3795. {
  3796. for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
  3797. if ((*itr)->messageID == id)
  3798. return (*itr);
  3799. return NULL;
  3800. }
  3801. void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const
  3802. {
  3803. if (target == this)
  3804. {
  3805. for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i)
  3806. {
  3807. if (m_items[i] == NULL)
  3808. continue;
  3809. m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
  3810. }
  3811. for (uint8 i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
  3812. {
  3813. if (m_items[i] == NULL)
  3814. continue;
  3815. m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
  3816. }
  3817. }
  3818. Unit::BuildCreateUpdateBlockForPlayer(data, target);
  3819. }
  3820. void Player::DestroyForPlayer(Player* target, bool onDeath) const
  3821. {
  3822. Unit::DestroyForPlayer(target, onDeath);
  3823. for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
  3824. {
  3825. if (m_items[i] == NULL)
  3826. continue;
  3827. m_items[i]->DestroyForPlayer(target);
  3828. }
  3829. if (target == this)
  3830. {
  3831. for (uint8 i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
  3832. {
  3833. if (m_items[i] == NULL)
  3834. continue;
  3835. m_items[i]->DestroyForPlayer(target);
  3836. }
  3837. }
  3838. }
  3839. bool Player::HasSpell(uint32 spell) const
  3840. {
  3841. PlayerSpellMap::const_iterator itr = m_spells.find(spell);
  3842. return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED &&
  3843. !itr->second->disabled);
  3844. }
  3845. bool Player::HasTalent(uint32 spell, uint8 spec) const
  3846. {
  3847. PlayerTalentMap::const_iterator itr = GetTalentMap(spec)->find(spell);
  3848. return (itr != GetTalentMap(spec)->end() && itr->second->state != PLAYERSPELL_REMOVED);
  3849. }
  3850. bool Player::HasActiveSpell(uint32 spell) const
  3851. {
  3852. PlayerSpellMap::const_iterator itr = m_spells.find(spell);
  3853. return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED &&
  3854. itr->second->active && !itr->second->disabled);
  3855. }
  3856. TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell) const
  3857. {
  3858. if (!trainer_spell)
  3859. return TRAINER_SPELL_RED;
  3860. bool hasSpell = true;
  3861. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  3862. {
  3863. if (!trainer_spell->learnedSpell[i])
  3864. continue;
  3865. if (!HasSpell(trainer_spell->learnedSpell[i]))
  3866. {
  3867. hasSpell = false;
  3868. break;
  3869. }
  3870. }
  3871. // known spell
  3872. if (hasSpell)
  3873. return TRAINER_SPELL_GRAY;
  3874. // check skill requirement
  3875. if (trainer_spell->reqSkill && GetBaseSkillValue(trainer_spell->reqSkill) < trainer_spell->reqSkillValue)
  3876. return TRAINER_SPELL_RED;
  3877. // check level requirement
  3878. if (getLevel() < trainer_spell->reqLevel)
  3879. return TRAINER_SPELL_RED;
  3880. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  3881. {
  3882. if (!trainer_spell->learnedSpell[i])
  3883. continue;
  3884. // check race/class requirement
  3885. if (!IsSpellFitByClassAndRace(trainer_spell->learnedSpell[i]))
  3886. return TRAINER_SPELL_RED;
  3887. if (uint32 prevSpell = sSpellMgr->GetPrevSpellInChain(trainer_spell->learnedSpell[i]))
  3888. {
  3889. // check prev.rank requirement
  3890. if (prevSpell && !HasSpell(prevSpell))
  3891. return TRAINER_SPELL_RED;
  3892. }
  3893. SpellsRequiringSpellMapBounds spellsRequired = sSpellMgr->GetSpellsRequiredForSpellBounds(trainer_spell->learnedSpell[i]);
  3894. for (SpellsRequiringSpellMap::const_iterator itr = spellsRequired.first; itr != spellsRequired.second; ++itr)
  3895. {
  3896. // check additional spell requirement
  3897. if (!HasSpell(itr->second))
  3898. return TRAINER_SPELL_RED;
  3899. }
  3900. }
  3901. // check primary prof. limit
  3902. // first rank of primary profession spell when there are no proffesions avalible is disabled
  3903. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  3904. {
  3905. if (!trainer_spell->learnedSpell[i])
  3906. continue;
  3907. SpellInfo const* learnedSpellInfo = sSpellMgr->GetSpellInfo(trainer_spell->learnedSpell[i]);
  3908. if (learnedSpellInfo && learnedSpellInfo->IsPrimaryProfessionFirstRank() && (GetFreePrimaryProfessionPoints() == 0))
  3909. return TRAINER_SPELL_GREEN_DISABLED;
  3910. }
  3911. return TRAINER_SPELL_GREEN;
  3912. }
  3913. /**
  3914. * Deletes a character from the database
  3915. *
  3916. * The way characters will be deleted is decided based on the config option.
  3917. *
  3918. * @see Player::DeleteOldCharacters
  3919. *
  3920. * @param playerguid the low-GUID from the player which should be deleted
  3921. * @param accountId the account id from the player
  3922. * @param updateRealmChars when this flag is set, the amount of characters on that realm will be updated in the realmlist
  3923. * @param deleteFinally if this flag is set, the config option will be ignored and the character will be permanently removed from the database
  3924. */
  3925. void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRealmChars, bool deleteFinally)
  3926. {
  3927. // Avoid realm-update for non-existing account
  3928. if (accountId == 0)
  3929. updateRealmChars = false;
  3930. // Convert guid to low GUID for CharacterNameData, but also other methods on success
  3931. uint32 guid = playerguid.GetCounter();
  3932. uint32 charDelete_method = sWorld->getIntConfig(CONFIG_CHARDELETE_METHOD);
  3933. if (deleteFinally)
  3934. charDelete_method = CHAR_DELETE_REMOVE;
  3935. else if (CharacterNameData const* nameData = sWorld->GetCharacterNameData(playerguid)) // To avoid a query, we select loaded data. If it doesn't exist, return.
  3936. {
  3937. // Define the required variables
  3938. uint32 charDelete_minLvl = sWorld->getIntConfig(nameData->m_class != CLASS_DEATH_KNIGHT ? CONFIG_CHARDELETE_MIN_LEVEL : CONFIG_CHARDELETE_HEROIC_MIN_LEVEL);
  3939. // if we want to finalize the character removal or the character does not meet the level requirement of either heroic or non-heroic settings,
  3940. // we set it to mode CHAR_DELETE_REMOVE
  3941. if (nameData->m_level < charDelete_minLvl)
  3942. charDelete_method = CHAR_DELETE_REMOVE;
  3943. }
  3944. // convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry)
  3945. // bones will be deleted by corpse/bones deleting thread shortly
  3946. sObjectAccessor->ConvertCorpseForPlayer(playerguid);
  3947. if (uint32 guildId = GetGuildIdFromDB(playerguid))
  3948. if (Guild* guild = sGuildMgr->GetGuildById(guildId))
  3949. guild->DeleteMember(playerguid, false, false, true);
  3950. // remove from arena teams
  3951. LeaveAllArenaTeams(playerguid);
  3952. // the player was uninvited already on logout so just remove from group
  3953. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GROUP_MEMBER);
  3954. stmt->setUInt32(0, guid);
  3955. PreparedQueryResult resultGroup = CharacterDatabase.Query(stmt);
  3956. if (resultGroup)
  3957. if (Group* group = sGroupMgr->GetGroupByDbStoreId((*resultGroup)[0].GetUInt32()))
  3958. RemoveFromGroup(group, playerguid);
  3959. // Remove signs from petitions (also remove petitions if owner);
  3960. RemovePetitionsAndSigns(playerguid, 10);
  3961. switch (charDelete_method)
  3962. {
  3963. // Completely remove from the database
  3964. case CHAR_DELETE_REMOVE:
  3965. {
  3966. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  3967. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL);
  3968. stmt->setUInt32(0, guid);
  3969. PreparedQueryResult resultMail = CharacterDatabase.Query(stmt);
  3970. if (resultMail)
  3971. {
  3972. do
  3973. {
  3974. Field* mailFields = resultMail->Fetch();
  3975. uint32 mail_id = mailFields[0].GetUInt32();
  3976. uint8 mailType = mailFields[1].GetUInt8();
  3977. uint16 mailTemplateId= mailFields[2].GetUInt16();
  3978. uint32 sender = mailFields[3].GetUInt32();
  3979. std::string subject = mailFields[4].GetString();
  3980. std::string body = mailFields[5].GetString();
  3981. uint64 money = mailFields[6].GetUInt64();
  3982. bool has_items = mailFields[7].GetBool();
  3983. // We can return mail now
  3984. // So firstly delete the old one
  3985. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
  3986. stmt->setUInt32(0, mail_id);
  3987. trans->Append(stmt);
  3988. // Mail is not from player
  3989. if (mailType != MAIL_NORMAL)
  3990. {
  3991. if (has_items)
  3992. {
  3993. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
  3994. stmt->setUInt32(0, mail_id);
  3995. trans->Append(stmt);
  3996. }
  3997. continue;
  3998. }
  3999. MailDraft draft(subject, body);
  4000. if (mailTemplateId)
  4001. draft = MailDraft(mailTemplateId, false); // items are already included
  4002. if (has_items)
  4003. {
  4004. // Data needs to be at first place for Item::LoadFromDB
  4005. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS);
  4006. stmt->setUInt32(0, mail_id);
  4007. PreparedQueryResult resultItems = CharacterDatabase.Query(stmt);
  4008. if (resultItems)
  4009. {
  4010. do
  4011. {
  4012. Field* itemFields = resultItems->Fetch();
  4013. uint32 item_guidlow = itemFields[11].GetUInt32();
  4014. uint32 item_template = itemFields[12].GetUInt32();
  4015. ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(item_template);
  4016. if (!itemProto)
  4017. {
  4018. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  4019. stmt->setUInt32(0, item_guidlow);
  4020. trans->Append(stmt);
  4021. continue;
  4022. }
  4023. Item* pItem = NewItemOrBag(itemProto);
  4024. if (!pItem->LoadFromDB(item_guidlow, playerguid, itemFields, item_template))
  4025. {
  4026. pItem->FSetState(ITEM_REMOVED);
  4027. pItem->SaveToDB(trans); // it also deletes item object!
  4028. continue;
  4029. }
  4030. draft.AddItem(pItem);
  4031. }
  4032. while (resultItems->NextRow());
  4033. }
  4034. }
  4035. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
  4036. stmt->setUInt32(0, mail_id);
  4037. trans->Append(stmt);
  4038. uint32 pl_account = sObjectMgr->GetPlayerAccountIdByGUID(playerguid);
  4039. draft.AddMoney(money).SendReturnToSender(pl_account, guid, sender, trans);
  4040. }
  4041. while (resultMail->NextRow());
  4042. }
  4043. // Unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
  4044. // NOW we can finally clear other DB data related to character
  4045. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PETS);
  4046. stmt->setUInt32(0, guid);
  4047. PreparedQueryResult resultPets = CharacterDatabase.Query(stmt);
  4048. if (resultPets)
  4049. {
  4050. do
  4051. {
  4052. uint32 petguidlow = (*resultPets)[0].GetUInt32();
  4053. Pet::DeleteFromDB(petguidlow);
  4054. } while (resultPets->NextRow());
  4055. }
  4056. // Delete char from social list of online chars
  4057. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_SOCIAL);
  4058. stmt->setUInt32(0, guid);
  4059. PreparedQueryResult resultFriends = CharacterDatabase.Query(stmt);
  4060. if (resultFriends)
  4061. {
  4062. do
  4063. {
  4064. if (Player* pFriend = ObjectAccessor::FindPlayer(ObjectGuid(HIGHGUID_PLAYER, 0, (*resultFriends)[0].GetUInt32())))
  4065. {
  4066. pFriend->GetSocial()->RemoveFromSocialList(guid, false);
  4067. sSocialMgr->SendFriendStatus(pFriend, FRIEND_REMOVED, guid, false);
  4068. }
  4069. } while (resultFriends->NextRow());
  4070. }
  4071. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER);
  4072. stmt->setUInt32(0, guid);
  4073. trans->Append(stmt);
  4074. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_ACCOUNT_DATA);
  4075. stmt->setUInt32(0, guid);
  4076. trans->Append(stmt);
  4077. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_DECLINED_NAME);
  4078. stmt->setUInt32(0, guid);
  4079. trans->Append(stmt);
  4080. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACTION);
  4081. stmt->setUInt32(0, guid);
  4082. trans->Append(stmt);
  4083. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_AURA);
  4084. stmt->setUInt32(0, guid);
  4085. trans->Append(stmt);
  4086. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GIFT);
  4087. stmt->setUInt32(0, guid);
  4088. trans->Append(stmt);
  4089. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_HOMEBIND);
  4090. stmt->setUInt32(0, guid);
  4091. trans->Append(stmt);
  4092. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE);
  4093. stmt->setUInt32(0, guid);
  4094. trans->Append(stmt);
  4095. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY);
  4096. stmt->setUInt32(0, guid);
  4097. trans->Append(stmt);
  4098. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_QUESTSTATUS);
  4099. stmt->setUInt32(0, guid);
  4100. trans->Append(stmt);
  4101. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_QUESTSTATUS_REWARDED);
  4102. stmt->setUInt32(0, guid);
  4103. trans->Append(stmt);
  4104. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_REPUTATION);
  4105. stmt->setUInt32(0, guid);
  4106. trans->Append(stmt);
  4107. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL);
  4108. stmt->setUInt32(0, guid);
  4109. trans->Append(stmt);
  4110. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN);
  4111. stmt->setUInt32(0, guid);
  4112. trans->Append(stmt);
  4113. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_GM_TICKETS);
  4114. stmt->setUInt32(0, guid);
  4115. trans->Append(stmt);
  4116. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER);
  4117. stmt->setUInt32(0, guid);
  4118. trans->Append(stmt);
  4119. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SOCIAL_BY_FRIEND);
  4120. stmt->setUInt32(0, guid);
  4121. trans->Append(stmt);
  4122. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SOCIAL_BY_GUID);
  4123. stmt->setUInt32(0, guid);
  4124. trans->Append(stmt);
  4125. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL);
  4126. stmt->setUInt32(0, guid);
  4127. trans->Append(stmt);
  4128. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEMS);
  4129. stmt->setUInt32(0, guid);
  4130. trans->Append(stmt);
  4131. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_OWNER);
  4132. stmt->setUInt32(0, guid);
  4133. trans->Append(stmt);
  4134. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER);
  4135. stmt->setUInt32(0, guid);
  4136. trans->Append(stmt);
  4137. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENTS);
  4138. stmt->setUInt32(0, guid);
  4139. trans->Append(stmt);
  4140. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS);
  4141. stmt->setUInt32(0, guid);
  4142. trans->Append(stmt);
  4143. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_EQUIPMENTSETS);
  4144. stmt->setUInt32(0, guid);
  4145. trans->Append(stmt);
  4146. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOG_BY_PLAYER);
  4147. stmt->setUInt32(0, guid);
  4148. stmt->setUInt32(1, guid);
  4149. trans->Append(stmt);
  4150. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_EVENTLOG_BY_PLAYER);
  4151. stmt->setUInt32(0, guid);
  4152. trans->Append(stmt);
  4153. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_BGDATA);
  4154. stmt->setUInt32(0, guid);
  4155. trans->Append(stmt);
  4156. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GLYPHS);
  4157. stmt->setUInt32(0, guid);
  4158. trans->Append(stmt);
  4159. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_DAILY);
  4160. stmt->setUInt32(0, guid);
  4161. trans->Append(stmt);
  4162. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_WEEKLY);
  4163. stmt->setUInt32(0, guid);
  4164. trans->Append(stmt);
  4165. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_MONTHLY);
  4166. stmt->setUInt32(0, guid);
  4167. trans->Append(stmt);
  4168. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_SEASONAL);
  4169. stmt->setUInt32(0, guid);
  4170. trans->Append(stmt);
  4171. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_TALENT);
  4172. stmt->setUInt32(0, guid);
  4173. trans->Append(stmt);
  4174. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SKILLS);
  4175. stmt->setUInt32(0, guid);
  4176. trans->Append(stmt);
  4177. CharacterDatabase.CommitTransaction(trans);
  4178. break;
  4179. }
  4180. // The character gets unlinked from the account, the name gets freed up and appears as deleted ingame
  4181. case CHAR_DELETE_UNLINK:
  4182. {
  4183. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_DELETE_INFO);
  4184. stmt->setUInt32(0, guid);
  4185. CharacterDatabase.Execute(stmt);
  4186. break;
  4187. }
  4188. default:
  4189. TC_LOG_ERROR("entities.player", "Player::DeleteFromDB: Unsupported delete method: %u.", charDelete_method);
  4190. return;
  4191. }
  4192. if (updateRealmChars)
  4193. sWorld->UpdateRealmCharCount(accountId);
  4194. sWorld->DeleteCharacterNameData(playerguid);
  4195. }
  4196. /**
  4197. * Characters which were kept back in the database after being deleted and are now too old (see config option "CharDelete.KeepDays"), will be completely deleted.
  4198. *
  4199. * @see Player::DeleteFromDB
  4200. */
  4201. void Player::DeleteOldCharacters()
  4202. {
  4203. uint32 keepDays = sWorld->getIntConfig(CONFIG_CHARDELETE_KEEP_DAYS);
  4204. if (!keepDays)
  4205. return;
  4206. Player::DeleteOldCharacters(keepDays);
  4207. }
  4208. /**
  4209. * Characters which were kept back in the database after being deleted and are older than the specified amount of days, will be completely deleted.
  4210. *
  4211. * @see Player::DeleteFromDB
  4212. *
  4213. * @param keepDays overrite the config option by another amount of days
  4214. */
  4215. void Player::DeleteOldCharacters(uint32 keepDays)
  4216. {
  4217. TC_LOG_INFO("entities.player", "Player::DeleteOldChars: Deleting all characters which have been deleted %u days before...", keepDays);
  4218. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_OLD_CHARS);
  4219. stmt->setUInt32(0, uint32(time(NULL) - time_t(keepDays * DAY)));
  4220. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  4221. if (result)
  4222. {
  4223. TC_LOG_DEBUG("entities.player", "Player::DeleteOldChars: Found " UI64FMTD " character(s) to delete", result->GetRowCount());
  4224. do
  4225. {
  4226. Field* fields = result->Fetch();
  4227. Player::DeleteFromDB(ObjectGuid(HIGHGUID_PLAYER, fields[0].GetUInt32()), fields[1].GetUInt32(), true, true);
  4228. }
  4229. while (result->NextRow());
  4230. }
  4231. }
  4232. /* Preconditions:
  4233. - a resurrectable corpse must not be loaded for the player (only bones)
  4234. - the player must be in world
  4235. */
  4236. void Player::BuildPlayerRepop()
  4237. {
  4238. WorldPacket data(SMSG_PRE_RESURRECT, GetPackGUID().size());
  4239. data << GetPackGUID();
  4240. GetSession()->SendPacket(&data);
  4241. if (getRace() == RACE_NIGHTELF)
  4242. CastSpell(this, 20584, true);
  4243. CastSpell(this, 8326, true);
  4244. // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK
  4245. // there must be SMSG.STOP_MIRROR_TIMER
  4246. // there we must send 888 opcode
  4247. // the player cannot have a corpse already, only bones which are not returned by GetCorpse
  4248. if (GetCorpse())
  4249. {
  4250. TC_LOG_ERROR("entities.player", "BuildPlayerRepop: player %s(%d) already has a corpse", GetName().c_str(), GetGUIDLow());
  4251. return;
  4252. }
  4253. // create a corpse and place it at the player's location
  4254. CreateCorpse();
  4255. Corpse* corpse = GetCorpse();
  4256. if (!corpse)
  4257. {
  4258. TC_LOG_ERROR("entities.player", "Error creating corpse for Player %s [%u]", GetName().c_str(), GetGUIDLow());
  4259. return;
  4260. }
  4261. GetMap()->AddToMap(corpse);
  4262. // convert player body to ghost
  4263. SetHealth(1);
  4264. SetWaterWalking(true);
  4265. if (!GetSession()->isLogingOut() && !HasUnitState(UNIT_STATE_STUNNED))
  4266. SetRooted(false);
  4267. // BG - remove insignia related
  4268. RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
  4269. int32 corpseReclaimDelay = CalculateCorpseReclaimDelay();
  4270. if (corpseReclaimDelay >= 0)
  4271. SendCorpseReclaimDelay(corpseReclaimDelay);
  4272. // to prevent cheating
  4273. corpse->ResetGhostTime();
  4274. StopMirrorTimers(); //disable timers(bars)
  4275. // set and clear other
  4276. SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND);
  4277. }
  4278. void Player::ResurrectPlayer(float restore_percent, bool applySickness)
  4279. {
  4280. WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // remove spirit healer position
  4281. data << uint32(-1);
  4282. data << float(0);
  4283. data << float(0);
  4284. data << float(0);
  4285. GetSession()->SendPacket(&data);
  4286. // speed change, land walk
  4287. // remove death flag + set aura
  4288. SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00);
  4289. // This must be called always even on Players with race != RACE_NIGHTELF in case of faction change
  4290. RemoveAurasDueToSpell(20584); // RACE_NIGHTELF speed bonuses
  4291. RemoveAurasDueToSpell(8326); // SPELL_AURA_GHOST
  4292. if (GetSession()->IsARecruiter() || (GetSession()->GetRecruiterId() != 0))
  4293. SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_REFER_A_FRIEND);
  4294. setDeathState(ALIVE);
  4295. SetWaterWalking(false);
  4296. if (!HasUnitState(UNIT_STATE_STUNNED))
  4297. SetRooted(false);
  4298. m_deathTimer = 0;
  4299. // set health/powers (0- will be set in caller)
  4300. if (restore_percent > 0.0f)
  4301. {
  4302. SetHealth(uint32(GetMaxHealth()*restore_percent));
  4303. SetPower(POWER_MANA, uint32(GetMaxPower(POWER_MANA)*restore_percent));
  4304. SetPower(POWER_RAGE, 0);
  4305. SetPower(POWER_ENERGY, uint32(GetMaxPower(POWER_ENERGY)*restore_percent));
  4306. SetPower(POWER_FOCUS, uint32(GetMaxPower(POWER_FOCUS)*restore_percent));
  4307. SetPower(POWER_ECLIPSE, 0);
  4308. }
  4309. // trigger update zone for alive state zone updates
  4310. uint32 newzone, newarea;
  4311. GetZoneAndAreaId(newzone, newarea);
  4312. UpdateZone(newzone, newarea);
  4313. sOutdoorPvPMgr->HandlePlayerResurrects(this, newzone);
  4314. if (InBattleground())
  4315. {
  4316. if (Battleground* bg = GetBattleground())
  4317. bg->HandlePlayerResurrect(this);
  4318. }
  4319. // update visibility
  4320. UpdateObjectVisibility();
  4321. if (!applySickness)
  4322. return;
  4323. //Characters from level 1-10 are not affected by resurrection sickness.
  4324. //Characters from level 11-19 will suffer from one minute of sickness
  4325. //for each level they are above 10.
  4326. //Characters level 20 and up suffer from ten minutes of sickness.
  4327. int32 startLevel = sWorld->getIntConfig(CONFIG_DEATH_SICKNESS_LEVEL);
  4328. if (int32(getLevel()) >= startLevel)
  4329. {
  4330. // set resurrection sickness
  4331. CastSpell(this, 15007, true);
  4332. // not full duration
  4333. if (int32(getLevel()) < startLevel+9)
  4334. {
  4335. int32 delta = (int32(getLevel()) - startLevel + 1)*MINUTE;
  4336. if (Aura* aur = GetAura(15007, GetGUID()))
  4337. {
  4338. aur->SetDuration(delta*IN_MILLISECONDS);
  4339. }
  4340. }
  4341. }
  4342. }
  4343. void Player::KillPlayer()
  4344. {
  4345. if (IsFlying() && !GetTransport())
  4346. GetMotionMaster()->MoveFall();
  4347. SetRooted(true);
  4348. StopMirrorTimers(); //disable timers(bars)
  4349. setDeathState(CORPSE);
  4350. //SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_IN_PVP);
  4351. SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE);
  4352. ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(GetMapId())->Instanceable() && !HasAuraType(SPELL_AURA_PREVENT_RESURRECTION));
  4353. // 6 minutes until repop at graveyard
  4354. m_deathTimer = 6 * MINUTE * IN_MILLISECONDS;
  4355. UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill
  4356. int32 corpseReclaimDelay = CalculateCorpseReclaimDelay();
  4357. if (corpseReclaimDelay >= 0)
  4358. SendCorpseReclaimDelay(corpseReclaimDelay);
  4359. // don't create corpse at this moment, player might be falling
  4360. // update visibility
  4361. UpdateObjectVisibility();
  4362. }
  4363. void Player::CreateCorpse()
  4364. {
  4365. // prevent existence 2 corpse for player
  4366. SpawnCorpseBones();
  4367. uint32 _uf, _pb, _pb2, _cfb1, _cfb2;
  4368. Corpse* corpse = new Corpse((m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE);
  4369. SetPvPDeath(false);
  4370. if (!corpse->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_CORPSE), this))
  4371. {
  4372. delete corpse;
  4373. return;
  4374. }
  4375. _uf = GetUInt32Value(UNIT_FIELD_BYTES_0);
  4376. _pb = GetUInt32Value(PLAYER_BYTES);
  4377. _pb2 = GetUInt32Value(PLAYER_BYTES_2);
  4378. uint8 race = (uint8)(_uf);
  4379. uint8 skin = (uint8)(_pb);
  4380. uint8 face = (uint8)(_pb >> 8);
  4381. uint8 hairstyle = (uint8)(_pb >> 16);
  4382. uint8 haircolor = (uint8)(_pb >> 24);
  4383. uint8 facialhair = (uint8)(_pb2);
  4384. _cfb1 = ((0x00) | (race << 8) | (getGender() << 16) | (skin << 24));
  4385. _cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));
  4386. corpse->SetUInt32Value(CORPSE_FIELD_BYTES_1, _cfb1);
  4387. corpse->SetUInt32Value(CORPSE_FIELD_BYTES_2, _cfb2);
  4388. uint32 flags = CORPSE_FLAG_UNK2;
  4389. if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
  4390. flags |= CORPSE_FLAG_HIDE_HELM;
  4391. if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
  4392. flags |= CORPSE_FLAG_HIDE_CLOAK;
  4393. if (InBattleground() && !InArena())
  4394. flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia
  4395. corpse->SetUInt32Value(CORPSE_FIELD_FLAGS, flags);
  4396. corpse->SetUInt32Value(CORPSE_FIELD_DISPLAY_ID, GetNativeDisplayId());
  4397. uint32 iDisplayID;
  4398. uint32 iIventoryType;
  4399. uint32 _cfi;
  4400. for (uint8 i = 0; i < EQUIPMENT_SLOT_END; i++)
  4401. {
  4402. if (m_items[i])
  4403. {
  4404. iDisplayID = m_items[i]->GetTemplate()->DisplayInfoID;
  4405. iIventoryType = m_items[i]->GetTemplate()->InventoryType;
  4406. _cfi = iDisplayID | (iIventoryType << 24);
  4407. corpse->SetUInt32Value(CORPSE_FIELD_ITEM + i, _cfi);
  4408. }
  4409. }
  4410. // we do not need to save corpses for BG/arenas
  4411. if (!GetMap()->IsBattlegroundOrArena())
  4412. corpse->SaveToDB();
  4413. // register for player, but not show
  4414. sObjectAccessor->AddCorpse(corpse);
  4415. }
  4416. void Player::SpawnCorpseBones()
  4417. {
  4418. if (sObjectAccessor->ConvertCorpseForPlayer(GetGUID()))
  4419. if (!GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player
  4420. SaveToDB(); // prevent loading as ghost without corpse
  4421. }
  4422. Corpse* Player::GetCorpse() const
  4423. {
  4424. return sObjectAccessor->GetCorpseForPlayerGUID(GetGUID());
  4425. }
  4426. void Player::DurabilityLossAll(double percent, bool inventory)
  4427. {
  4428. for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
  4429. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  4430. DurabilityLoss(pItem, percent);
  4431. if (inventory)
  4432. {
  4433. // bags not have durability
  4434. // for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  4435. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
  4436. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  4437. DurabilityLoss(pItem, percent);
  4438. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  4439. if (Bag* pBag = GetBagByPos(i))
  4440. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  4441. if (Item* pItem = GetItemByPos(i, j))
  4442. DurabilityLoss(pItem, percent);
  4443. }
  4444. }
  4445. void Player::DurabilityLoss(Item* item, double percent)
  4446. {
  4447. if (!item)
  4448. return;
  4449. uint32 pMaxDurability = item ->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
  4450. if (!pMaxDurability)
  4451. return;
  4452. percent /= GetTotalAuraMultiplier(SPELL_AURA_MOD_DURABILITY_LOSS);
  4453. uint32 pDurabilityLoss = uint32(pMaxDurability*percent);
  4454. if (pDurabilityLoss < 1)
  4455. pDurabilityLoss = 1;
  4456. DurabilityPointsLoss(item, pDurabilityLoss);
  4457. }
  4458. void Player::DurabilityPointsLossAll(int32 points, bool inventory)
  4459. {
  4460. for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
  4461. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  4462. DurabilityPointsLoss(pItem, points);
  4463. if (inventory)
  4464. {
  4465. // bags not have durability
  4466. // for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  4467. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
  4468. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  4469. DurabilityPointsLoss(pItem, points);
  4470. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  4471. if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  4472. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  4473. if (Item* pItem = GetItemByPos(i, j))
  4474. DurabilityPointsLoss(pItem, points);
  4475. }
  4476. }
  4477. void Player::DurabilityPointsLoss(Item* item, int32 points)
  4478. {
  4479. int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
  4480. int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
  4481. int32 pNewDurability = pOldDurability - points;
  4482. if (pNewDurability < 0)
  4483. pNewDurability = 0;
  4484. else if (pNewDurability > pMaxDurability)
  4485. pNewDurability = pMaxDurability;
  4486. if (pOldDurability != pNewDurability)
  4487. {
  4488. // modify item stats _before_ Durability set to 0 to pass _ApplyItemMods internal check
  4489. if (pNewDurability == 0 && pOldDurability > 0 && item->IsEquipped())
  4490. _ApplyItemMods(item, item->GetSlot(), false);
  4491. item->SetUInt32Value(ITEM_FIELD_DURABILITY, pNewDurability);
  4492. // modify item stats _after_ restore durability to pass _ApplyItemMods internal check
  4493. if (pNewDurability > 0 && pOldDurability == 0 && item->IsEquipped())
  4494. _ApplyItemMods(item, item->GetSlot(), true);
  4495. item->SetState(ITEM_CHANGED, this);
  4496. }
  4497. }
  4498. void Player::DurabilityPointLossForEquipSlot(EquipmentSlots slot)
  4499. {
  4500. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
  4501. DurabilityPointsLoss(pItem, 1);
  4502. }
  4503. uint32 Player::DurabilityRepairAll(bool cost, float discountMod, bool guildBank)
  4504. {
  4505. uint32 TotalCost = 0;
  4506. // equipped, backpack, bags itself
  4507. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
  4508. TotalCost += DurabilityRepair(((INVENTORY_SLOT_BAG_0 << 8) | i), cost, discountMod, guildBank);
  4509. // bank, buyback and keys not repaired
  4510. // items in inventory bags
  4511. for (uint8 j = INVENTORY_SLOT_BAG_START; j < INVENTORY_SLOT_BAG_END; j++)
  4512. for (uint8 i = 0; i < MAX_BAG_SIZE; i++)
  4513. TotalCost += DurabilityRepair(((j << 8) | i), cost, discountMod, guildBank);
  4514. return TotalCost;
  4515. }
  4516. uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank)
  4517. {
  4518. Item* item = GetItemByPos(pos);
  4519. uint32 TotalCost = 0;
  4520. if (!item)
  4521. return TotalCost;
  4522. uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
  4523. if (!maxDurability)
  4524. return TotalCost;
  4525. uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
  4526. if (cost)
  4527. {
  4528. uint32 LostDurability = maxDurability - curDurability;
  4529. if (LostDurability>0)
  4530. {
  4531. ItemTemplate const* ditemProto = item->GetTemplate();
  4532. DurabilityCostsEntry const* dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
  4533. if (!dcost)
  4534. {
  4535. TC_LOG_ERROR("entities.player.items", "RepairDurability: Wrong item lvl %u", ditemProto->ItemLevel);
  4536. return TotalCost;
  4537. }
  4538. uint32 dQualitymodEntryId = (ditemProto->Quality+1)*2;
  4539. DurabilityQualityEntry const* dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId);
  4540. if (!dQualitymodEntry)
  4541. {
  4542. TC_LOG_ERROR("entities.player.items", "RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntryId);
  4543. return TotalCost;
  4544. }
  4545. uint32 dmultiplier = dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class, ditemProto->SubClass)];
  4546. uint32 costs = uint32(LostDurability*dmultiplier*double(dQualitymodEntry->quality_mod));
  4547. costs = uint32(costs * discountMod * sWorld->getRate(RATE_REPAIRCOST));
  4548. if (costs == 0) //fix for ITEM_QUALITY_ARTIFACT
  4549. costs = 1;
  4550. if (guildBank)
  4551. {
  4552. if (GetGuildId() == 0)
  4553. {
  4554. TC_LOG_DEBUG("entities.player.items", "You are not member of a guild");
  4555. return TotalCost;
  4556. }
  4557. Guild* guild = sGuildMgr->GetGuildById(GetGuildId());
  4558. if (!guild)
  4559. return TotalCost;
  4560. if (!guild->HandleMemberWithdrawMoney(GetSession(), costs, true))
  4561. return TotalCost;
  4562. TotalCost = costs;
  4563. }
  4564. else if (!HasEnoughMoney(uint64(costs)))
  4565. {
  4566. TC_LOG_DEBUG("entities.player.items", "You do not have enough money");
  4567. return TotalCost;
  4568. }
  4569. else
  4570. ModifyMoney(-int64(costs));
  4571. }
  4572. }
  4573. item->SetUInt32Value(ITEM_FIELD_DURABILITY, maxDurability);
  4574. item->SetState(ITEM_CHANGED, this);
  4575. // reapply mods for total broken and repaired item if equipped
  4576. if (IsEquipmentPos(pos) && !curDurability)
  4577. _ApplyItemMods(item, pos & 255, true);
  4578. return TotalCost;
  4579. }
  4580. void Player::RepopAtGraveyard()
  4581. {
  4582. // note: this can be called also when the player is alive
  4583. // for example from WorldSession::HandleMovementOpcodes
  4584. AreaTableEntry const* zone = GetAreaEntryByAreaID(GetAreaId());
  4585. // Such zones are considered unreachable as a ghost and the player must be automatically revived
  4586. if ((!IsAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY) || GetTransport() || GetPositionZ() < (zone ? zone->MaxDepth : -500.0f))
  4587. {
  4588. ResurrectPlayer(0.5f);
  4589. SpawnCorpseBones();
  4590. }
  4591. WorldSafeLocsEntry const* ClosestGrave = NULL;
  4592. // Special handle for battleground maps
  4593. if (Battleground* bg = GetBattleground())
  4594. ClosestGrave = bg->GetClosestGraveYard(this);
  4595. else
  4596. {
  4597. if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(GetZoneId()))
  4598. ClosestGrave = bf->GetClosestGraveYard(this);
  4599. else
  4600. ClosestGrave = sObjectMgr->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam());
  4601. }
  4602. // stop countdown until repop
  4603. m_deathTimer = 0;
  4604. // if no grave found, stay at the current location
  4605. // and don't show spirit healer location
  4606. if (ClosestGrave)
  4607. {
  4608. float const* orientation = sObjectMgr->GetGraveyardOrientation(ClosestGrave->ID);
  4609. TeleportTo(ClosestGrave->map_id, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, orientation ? *orientation : GetOrientation());
  4610. if (isDead()) // not send if alive, because it used in TeleportTo()
  4611. {
  4612. WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // show spirit healer position on minimap
  4613. data << ClosestGrave->map_id;
  4614. data << ClosestGrave->x;
  4615. data << ClosestGrave->y;
  4616. data << ClosestGrave->z;
  4617. GetSession()->SendPacket(&data);
  4618. }
  4619. }
  4620. else if (GetPositionZ() < zone->MaxDepth)
  4621. TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation());
  4622. }
  4623. bool Player::CanJoinConstantChannelInZone(ChatChannelsEntry const* channel, AreaTableEntry const* zone)
  4624. {
  4625. if (channel->flags & CHANNEL_DBC_FLAG_ZONE_DEP && zone->flags & AREA_FLAG_ARENA_INSTANCE)
  4626. return false;
  4627. if ((channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY) && (!(zone->flags & AREA_FLAG_SLAVE_CAPITAL)))
  4628. return false;
  4629. if ((channel->flags & CHANNEL_DBC_FLAG_GUILD_REQ) && GetGuildId())
  4630. return false;
  4631. return true;
  4632. }
  4633. void Player::JoinedChannel(Channel* c)
  4634. {
  4635. m_channels.push_back(c);
  4636. }
  4637. void Player::LeftChannel(Channel* c)
  4638. {
  4639. m_channels.remove(c);
  4640. }
  4641. void Player::CleanupChannels()
  4642. {
  4643. while (!m_channels.empty())
  4644. {
  4645. Channel* ch = *m_channels.begin();
  4646. m_channels.erase(m_channels.begin()); // remove from player's channel list
  4647. ch->LeaveChannel(this, false); // not send to client, not remove from player's channel list
  4648. if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetTeam()))
  4649. cMgr->LeftChannel(ch->GetName()); // deleted channel if empty
  4650. }
  4651. TC_LOG_DEBUG("chat.system", "Player %s: channels cleaned up!", GetName().c_str());
  4652. }
  4653. void Player::UpdateLocalChannels(uint32 newZone)
  4654. {
  4655. if (GetSession()->PlayerLoading() && !IsBeingTeleportedFar())
  4656. return; // The client handles it automatically after loading, but not after teleporting
  4657. AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone);
  4658. if (!current_zone)
  4659. return;
  4660. ChannelMgr* cMgr = ChannelMgr::forTeam(GetTeam());
  4661. if (!cMgr)
  4662. return;
  4663. std::string current_zone_name = current_zone->area_name;
  4664. for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i)
  4665. {
  4666. if (ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i))
  4667. {
  4668. Channel* usedChannel = NULL;
  4669. for (JoinedChannelsList::iterator itr = m_channels.begin(); itr != m_channels.end(); ++itr)
  4670. {
  4671. if ((*itr)->GetChannelId() == i)
  4672. {
  4673. usedChannel = *itr;
  4674. break;
  4675. }
  4676. }
  4677. Channel* removeChannel = NULL;
  4678. Channel* joinChannel = NULL;
  4679. bool sendRemove = true;
  4680. if (CanJoinConstantChannelInZone(channel, current_zone))
  4681. {
  4682. if (!(channel->flags & CHANNEL_DBC_FLAG_GLOBAL))
  4683. {
  4684. if (channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY && usedChannel)
  4685. continue; // Already on the channel, as city channel names are not changing
  4686. char new_channel_name_buf[100];
  4687. char const* currentNameExt;
  4688. if (channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY)
  4689. currentNameExt = sObjectMgr->GetTrinityStringForDBCLocale(LANG_CHANNEL_CITY);
  4690. else
  4691. currentNameExt = current_zone_name.c_str();
  4692. snprintf(new_channel_name_buf, 100, channel->pattern, currentNameExt);
  4693. joinChannel = cMgr->GetJoinChannel(new_channel_name_buf, channel->ChannelID);
  4694. if (usedChannel)
  4695. {
  4696. if (joinChannel != usedChannel)
  4697. {
  4698. removeChannel = usedChannel;
  4699. sendRemove = false; // Do not send leave channel, it already replaced at client
  4700. }
  4701. else
  4702. joinChannel = NULL;
  4703. }
  4704. }
  4705. else
  4706. joinChannel = cMgr->GetJoinChannel(channel->pattern, channel->ChannelID);
  4707. }
  4708. else
  4709. removeChannel = usedChannel;
  4710. if (joinChannel)
  4711. joinChannel->JoinChannel(this, ""); // Changed Channel: ... or Joined Channel: ...
  4712. if (removeChannel)
  4713. {
  4714. removeChannel->LeaveChannel(this, sendRemove); // Leave old channel
  4715. std::string name = removeChannel->GetName(); // Store name, (*i)erase in LeftChannel
  4716. LeftChannel(removeChannel); // Remove from player's channel list
  4717. cMgr->LeftChannel(name); // Delete if empty
  4718. }
  4719. }
  4720. }
  4721. }
  4722. void Player::LeaveLFGChannel()
  4723. {
  4724. for (JoinedChannelsList::iterator i = m_channels.begin(); i != m_channels.end(); ++i)
  4725. {
  4726. if ((*i)->IsLFG())
  4727. {
  4728. (*i)->LeaveChannel(this);
  4729. break;
  4730. }
  4731. }
  4732. }
  4733. void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply)
  4734. {
  4735. if (modGroup >= BASEMOD_END || modType >= MOD_END)
  4736. {
  4737. TC_LOG_ERROR("spells", "ERROR in HandleBaseModValue(): non existed BaseModGroup of wrong BaseModType!");
  4738. return;
  4739. }
  4740. if (modType == FLAT_MOD)
  4741. m_auraBaseMod[modGroup][modType] += apply ? amount : -amount;
  4742. else // PCT_MOD
  4743. ApplyPercentModFloatVar(m_auraBaseMod[modGroup][modType], amount, apply);
  4744. if (!CanModifyStats())
  4745. return;
  4746. switch (modGroup)
  4747. {
  4748. case CRIT_PERCENTAGE: UpdateCritPercentage(BASE_ATTACK); break;
  4749. case RANGED_CRIT_PERCENTAGE: UpdateCritPercentage(RANGED_ATTACK); break;
  4750. case OFFHAND_CRIT_PERCENTAGE: UpdateCritPercentage(OFF_ATTACK); break;
  4751. default: break;
  4752. }
  4753. }
  4754. float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const
  4755. {
  4756. if (modGroup >= BASEMOD_END || modType >= MOD_END)
  4757. {
  4758. TC_LOG_ERROR("spells", "trial to access non existed BaseModGroup or wrong BaseModType!");
  4759. return 0.0f;
  4760. }
  4761. if (modType == PCT_MOD && m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
  4762. return 0.0f;
  4763. return m_auraBaseMod[modGroup][modType];
  4764. }
  4765. float Player::GetTotalBaseModValue(BaseModGroup modGroup) const
  4766. {
  4767. if (modGroup >= BASEMOD_END)
  4768. {
  4769. TC_LOG_ERROR("spells", "wrong BaseModGroup in GetTotalBaseModValue()!");
  4770. return 0.0f;
  4771. }
  4772. if (m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
  4773. return 0.0f;
  4774. return m_auraBaseMod[modGroup][FLAT_MOD] * m_auraBaseMod[modGroup][PCT_MOD];
  4775. }
  4776. float Player::GetMeleeCritFromAgility()
  4777. {
  4778. uint8 level = getLevel();
  4779. uint32 pclass = getClass();
  4780. if (level > GT_MAX_LEVEL)
  4781. level = GT_MAX_LEVEL;
  4782. GtChanceToMeleeCritBaseEntry const* critBase = sGtChanceToMeleeCritBaseStore.LookupEntry(pclass-1);
  4783. GtChanceToMeleeCritEntry const* critRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
  4784. if (critBase == NULL || critRatio == NULL)
  4785. return 0.0f;
  4786. float crit = critBase->base + GetStat(STAT_AGILITY)*critRatio->ratio;
  4787. return crit*100.0f;
  4788. }
  4789. void Player::GetDodgeFromAgility(float &diminishing, float &nondiminishing)
  4790. {
  4791. // Table for base dodge values
  4792. const float dodge_base[MAX_CLASSES] =
  4793. {
  4794. 0.037580f, // Warrior
  4795. 0.036520f, // Paladin
  4796. -0.054500f, // Hunter
  4797. -0.005900f, // Rogue
  4798. 0.031830f, // Priest
  4799. 0.036640f, // DK
  4800. 0.016750f, // Shaman
  4801. 0.034575f, // Mage
  4802. 0.020350f, // Warlock
  4803. 0.0f, // ??
  4804. 0.049510f // Druid
  4805. };
  4806. // Crit/agility to dodge/agility coefficient multipliers; 3.2.0 increased required agility by 15%
  4807. const float crit_to_dodge[MAX_CLASSES] =
  4808. {
  4809. 0.85f/1.15f, // Warrior
  4810. 1.00f/1.15f, // Paladin
  4811. 1.11f/1.15f, // Hunter
  4812. 2.00f/1.15f, // Rogue
  4813. 1.00f/1.15f, // Priest
  4814. 0.85f/1.15f, // DK
  4815. 1.60f/1.15f, // Shaman
  4816. 1.00f/1.15f, // Mage
  4817. 0.97f/1.15f, // Warlock (?)
  4818. 0.0f, // ??
  4819. 2.00f/1.15f // Druid
  4820. };
  4821. uint8 level = getLevel();
  4822. uint32 pclass = getClass();
  4823. if (level > GT_MAX_LEVEL)
  4824. level = GT_MAX_LEVEL;
  4825. // Dodge per agility is proportional to crit per agility, which is available from DBC files
  4826. GtChanceToMeleeCritEntry const* dodgeRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
  4827. if (dodgeRatio == NULL || pclass > MAX_CLASSES)
  4828. return;
  4829. /// @todo research if talents/effects that increase total agility by x% should increase non-diminishing part
  4830. float base_agility = GetCreateStat(STAT_AGILITY) * m_auraModifiersGroup[UNIT_MOD_STAT_START + STAT_AGILITY][BASE_PCT];
  4831. float bonus_agility = GetStat(STAT_AGILITY) - base_agility;
  4832. // calculate diminishing (green in char screen) and non-diminishing (white) contribution
  4833. diminishing = 100.0f * bonus_agility * dodgeRatio->ratio * crit_to_dodge[pclass-1];
  4834. nondiminishing = 100.0f * (dodge_base[pclass-1] + base_agility * dodgeRatio->ratio * crit_to_dodge[pclass-1]);
  4835. }
  4836. float Player::GetSpellCritFromIntellect()
  4837. {
  4838. uint8 level = getLevel();
  4839. uint32 pclass = getClass();
  4840. if (level > GT_MAX_LEVEL)
  4841. level = GT_MAX_LEVEL;
  4842. GtChanceToSpellCritBaseEntry const* critBase = sGtChanceToSpellCritBaseStore.LookupEntry(pclass - 1);
  4843. GtChanceToSpellCritEntry const* critRatio = sGtChanceToSpellCritStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1);
  4844. if (critBase == NULL || critRatio == NULL)
  4845. return 0.0f;
  4846. float crit = critBase->base + GetStat(STAT_INTELLECT) * critRatio->ratio;
  4847. return crit * 100.0f;
  4848. }
  4849. float Player::GetRatingMultiplier(CombatRating cr) const
  4850. {
  4851. uint8 level = getLevel();
  4852. if (level > GT_MAX_LEVEL)
  4853. level = GT_MAX_LEVEL;
  4854. GtCombatRatingsEntry const* Rating = sGtCombatRatingsStore.LookupEntry(cr*GT_MAX_LEVEL+level-1);
  4855. // gtOCTClassCombatRatingScalarStore.dbc starts with 1, CombatRating with zero, so cr+1
  4856. GtOCTClassCombatRatingScalarEntry const* classRating = sGtOCTClassCombatRatingScalarStore.LookupEntry((getClass()-1)*GT_MAX_RATING+cr+1);
  4857. if (!Rating || !classRating)
  4858. return 1.0f; // By default use minimum coefficient (not must be called)
  4859. return classRating->ratio / Rating->ratio;
  4860. }
  4861. float Player::GetRatingBonusValue(CombatRating cr) const
  4862. {
  4863. float baseResult = float(GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr)) * GetRatingMultiplier(cr);
  4864. if (cr != CR_RESILIENCE_PLAYER_DAMAGE_TAKEN)
  4865. return baseResult;
  4866. return float(1.0f - pow(0.99f, baseResult)) * 100.0f;
  4867. }
  4868. float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const
  4869. {
  4870. switch (attType)
  4871. {
  4872. case BASE_ATTACK:
  4873. return GetUInt32Value(PLAYER_EXPERTISE) / 4.0f;
  4874. case OFF_ATTACK:
  4875. return GetUInt32Value(PLAYER_OFFHAND_EXPERTISE) / 4.0f;
  4876. default:
  4877. break;
  4878. }
  4879. return 0.0f;
  4880. }
  4881. float Player::OCTRegenMPPerSpirit()
  4882. {
  4883. uint8 level = getLevel();
  4884. uint32 pclass = getClass();
  4885. if (level > GT_MAX_LEVEL)
  4886. level = GT_MAX_LEVEL;
  4887. // GtOCTRegenMPEntry const* baseRatio = sGtOCTRegenMPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
  4888. GtRegenMPPerSptEntry const* moreRatio = sGtRegenMPPerSptStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
  4889. if (moreRatio == NULL)
  4890. return 0.0f;
  4891. // Formula get from PaperDollFrame script
  4892. float spirit = GetStat(STAT_SPIRIT);
  4893. float regen = spirit * moreRatio->ratio;
  4894. return regen;
  4895. }
  4896. void Player::ApplyRatingMod(CombatRating combatRating, int32 value, bool apply)
  4897. {
  4898. float oldRating = m_baseRatingValue[combatRating];
  4899. m_baseRatingValue[combatRating] += (apply ? value : -value);
  4900. // explicit affected values
  4901. float const multiplier = GetRatingMultiplier(combatRating);
  4902. float const oldVal = oldRating * multiplier;
  4903. float const newVal = m_baseRatingValue[combatRating] * multiplier;
  4904. switch (combatRating)
  4905. {
  4906. case CR_HASTE_MELEE:
  4907. ApplyAttackTimePercentMod(BASE_ATTACK, oldVal, false);
  4908. ApplyAttackTimePercentMod(OFF_ATTACK, oldVal, false);
  4909. ApplyAttackTimePercentMod(BASE_ATTACK, newVal, true);
  4910. ApplyAttackTimePercentMod(OFF_ATTACK, newVal, true);
  4911. if (getClass() == CLASS_DEATH_KNIGHT)
  4912. UpdateAllRunesRegen();
  4913. break;
  4914. case CR_HASTE_RANGED:
  4915. ApplyAttackTimePercentMod(RANGED_ATTACK, oldVal, false);
  4916. ApplyAttackTimePercentMod(RANGED_ATTACK, newVal, true);
  4917. break;
  4918. case CR_HASTE_SPELL:
  4919. ApplyCastTimePercentMod(oldVal, false);
  4920. ApplyCastTimePercentMod(newVal, true);
  4921. break;
  4922. default:
  4923. break;
  4924. }
  4925. UpdateRating(combatRating);
  4926. }
  4927. void Player::UpdateRating(CombatRating cr)
  4928. {
  4929. int32 amount = m_baseRatingValue[cr];
  4930. // Apply bonus from SPELL_AURA_MOD_RATING_FROM_STAT
  4931. // stat used stored in miscValueB for this aura
  4932. AuraEffectList const& modRatingFromStat = GetAuraEffectsByType(SPELL_AURA_MOD_RATING_FROM_STAT);
  4933. for (AuraEffectList::const_iterator i = modRatingFromStat.begin(); i != modRatingFromStat.end(); ++i)
  4934. if ((*i)->GetMiscValue() & (1<<cr))
  4935. amount += int32(CalculatePct(GetStat(Stats((*i)->GetMiscValueB())), (*i)->GetAmount()));
  4936. if (amount < 0)
  4937. amount = 0;
  4938. SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, uint32(amount));
  4939. bool affectStats = CanModifyStats();
  4940. switch (cr)
  4941. {
  4942. case CR_WEAPON_SKILL:
  4943. case CR_DEFENSE_SKILL:
  4944. break;
  4945. case CR_DODGE:
  4946. UpdateDodgePercentage();
  4947. break;
  4948. case CR_PARRY:
  4949. UpdateParryPercentage();
  4950. break;
  4951. case CR_BLOCK:
  4952. UpdateBlockPercentage();
  4953. break;
  4954. case CR_HIT_MELEE:
  4955. UpdateMeleeHitChances();
  4956. break;
  4957. case CR_HIT_RANGED:
  4958. UpdateRangedHitChances();
  4959. break;
  4960. case CR_HIT_SPELL:
  4961. UpdateSpellHitChances();
  4962. break;
  4963. case CR_CRIT_MELEE:
  4964. if (affectStats)
  4965. {
  4966. UpdateCritPercentage(BASE_ATTACK);
  4967. UpdateCritPercentage(OFF_ATTACK);
  4968. }
  4969. break;
  4970. case CR_CRIT_RANGED:
  4971. if (affectStats)
  4972. UpdateCritPercentage(RANGED_ATTACK);
  4973. break;
  4974. case CR_CRIT_SPELL:
  4975. if (affectStats)
  4976. UpdateAllSpellCritChances();
  4977. break;
  4978. case CR_HIT_TAKEN_MELEE: // Deprecated since Cataclysm
  4979. case CR_HIT_TAKEN_RANGED: // Deprecated since Cataclysm
  4980. case CR_HIT_TAKEN_SPELL: // Deprecated since Cataclysm
  4981. case CR_RESILIENCE_PLAYER_DAMAGE_TAKEN:
  4982. case CR_RESILIENCE_CRIT_TAKEN:
  4983. case CR_CRIT_TAKEN_SPELL: // Deprecated since Cataclysm
  4984. break;
  4985. case CR_HASTE_MELEE: // Implemented in Player::ApplyRatingMod
  4986. case CR_HASTE_RANGED:
  4987. case CR_HASTE_SPELL:
  4988. break;
  4989. case CR_WEAPON_SKILL_MAINHAND: // Implemented in Unit::RollMeleeOutcomeAgainst
  4990. case CR_WEAPON_SKILL_OFFHAND:
  4991. case CR_WEAPON_SKILL_RANGED:
  4992. break;
  4993. case CR_EXPERTISE:
  4994. if (affectStats)
  4995. {
  4996. UpdateExpertise(BASE_ATTACK);
  4997. UpdateExpertise(OFF_ATTACK);
  4998. }
  4999. break;
  5000. case CR_ARMOR_PENETRATION:
  5001. if (affectStats)
  5002. UpdateArmorPenetration(amount);
  5003. break;
  5004. case CR_MASTERY:
  5005. UpdateMastery();
  5006. break;
  5007. }
  5008. }
  5009. void Player::UpdateAllRatings()
  5010. {
  5011. for (int cr = 0; cr < MAX_COMBAT_RATING; ++cr)
  5012. UpdateRating(CombatRating(cr));
  5013. }
  5014. void Player::SetRegularAttackTime()
  5015. {
  5016. for (uint8 i = 0; i < MAX_ATTACK; ++i)
  5017. {
  5018. Item* tmpitem = GetWeaponForAttack(WeaponAttackType(i), true);
  5019. if (tmpitem && !tmpitem->IsBroken())
  5020. {
  5021. ItemTemplate const* proto = tmpitem->GetTemplate();
  5022. if (proto->Delay)
  5023. SetAttackTime(WeaponAttackType(i), proto->Delay);
  5024. }
  5025. else
  5026. SetAttackTime(WeaponAttackType(i), BASE_ATTACK_TIME); // If there is no weapon reset attack time to base (might have been changed from forms)
  5027. }
  5028. }
  5029. //skill+step, checking for max value
  5030. bool Player::UpdateSkill(uint32 skill_id, uint32 step)
  5031. {
  5032. if (!skill_id)
  5033. return false;
  5034. SkillStatusMap::iterator itr = mSkillStatus.find(skill_id);
  5035. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5036. return false;
  5037. uint16 field = itr->second.pos / 2;
  5038. uint8 offset = itr->second.pos & 1; // itr->second.pos % 2
  5039. uint16 value = GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset);
  5040. uint16 max = GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset);
  5041. if (!max || !value || value >= max)
  5042. return false;
  5043. if (value < max)
  5044. {
  5045. uint32 new_value = value + step;
  5046. if (new_value > max)
  5047. new_value = max;
  5048. SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, new_value);
  5049. if (itr->second.uState != SKILL_NEW)
  5050. itr->second.uState = SKILL_CHANGED;
  5051. UpdateSkillEnchantments(skill_id, value, new_value);
  5052. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skill_id);
  5053. return true;
  5054. }
  5055. return false;
  5056. }
  5057. inline int SkillGainChance(uint32 SkillValue, uint32 GrayLevel, uint32 GreenLevel, uint32 YellowLevel)
  5058. {
  5059. if (SkillValue >= GrayLevel)
  5060. return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_GREY)*10;
  5061. if (SkillValue >= GreenLevel)
  5062. return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_GREEN)*10;
  5063. if (SkillValue >= YellowLevel)
  5064. return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_YELLOW)*10;
  5065. return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_ORANGE)*10;
  5066. }
  5067. bool Player::UpdateCraftSkill(uint32 spellid)
  5068. {
  5069. TC_LOG_DEBUG("entities.player.skills", "UpdateCraftSkill spellid %d", spellid);
  5070. SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellid);
  5071. for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
  5072. {
  5073. if (_spell_idx->second->skillId)
  5074. {
  5075. uint32 SkillValue = GetPureSkillValue(_spell_idx->second->skillId);
  5076. // Alchemy Discoveries here
  5077. SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spellid);
  5078. if (spellEntry && spellEntry->Mechanic == MECHANIC_DISCOVERY)
  5079. {
  5080. if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this))
  5081. LearnSpell(discoveredSpell, false);
  5082. }
  5083. uint32 craft_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_CRAFTING);
  5084. return UpdateSkillPro(_spell_idx->second->skillId, SkillGainChance(SkillValue,
  5085. _spell_idx->second->max_value,
  5086. (_spell_idx->second->max_value + _spell_idx->second->min_value)/2,
  5087. _spell_idx->second->min_value),
  5088. craft_skill_gain);
  5089. }
  5090. }
  5091. return false;
  5092. }
  5093. bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator)
  5094. {
  5095. TC_LOG_DEBUG("entities.player.skills", "UpdateGatherSkill(SkillId %d SkillLevel %d RedLevel %d)", SkillId, SkillValue, RedLevel);
  5096. uint32 gathering_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_GATHERING);
  5097. // For skinning and Mining chance decrease with level. 1-74 - no decrease, 75-149 - 2 times, 225-299 - 8 times
  5098. switch (SkillId)
  5099. {
  5100. case SKILL_HERBALISM:
  5101. case SKILL_LOCKPICKING:
  5102. case SKILL_JEWELCRAFTING:
  5103. case SKILL_INSCRIPTION:
  5104. return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator, gathering_skill_gain);
  5105. case SKILL_SKINNING:
  5106. if (sWorld->getIntConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS) == 0)
  5107. return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator, gathering_skill_gain);
  5108. else
  5109. return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator) >> (SkillValue/sWorld->getIntConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS)), gathering_skill_gain);
  5110. case SKILL_MINING:
  5111. if (sWorld->getIntConfig(CONFIG_SKILL_CHANCE_MINING_STEPS) == 0)
  5112. return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator, gathering_skill_gain);
  5113. else
  5114. return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator) >> (SkillValue/sWorld->getIntConfig(CONFIG_SKILL_CHANCE_MINING_STEPS)), gathering_skill_gain);
  5115. }
  5116. return false;
  5117. }
  5118. bool Player::UpdateFishingSkill()
  5119. {
  5120. TC_LOG_DEBUG("entities.player.skills", "UpdateFishingSkill");
  5121. uint32 SkillValue = GetPureSkillValue(SKILL_FISHING);
  5122. int32 chance = SkillValue < 75 ? 100 : 2500/(SkillValue-50);
  5123. uint32 gathering_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_GATHERING);
  5124. return UpdateSkillPro(SKILL_FISHING, chance*10, gathering_skill_gain);
  5125. }
  5126. bool Player::UpdateSkillPro(uint16 skillId, int32 chance, uint32 step)
  5127. {
  5128. // levels sync. with spell requirement for skill levels to learn
  5129. // bonus abilities in sSkillLineAbilityStore
  5130. // Used only to avoid scan DBC at each skill grow
  5131. static uint32 bonusSkillLevels[] = { 75, 150, 225, 300, 375, 450, 525 };
  5132. static const size_t bonusSkillLevelsSize = sizeof(bonusSkillLevels) / sizeof(uint32);
  5133. TC_LOG_DEBUG("entities.player.skills", "UpdateSkillPro(SkillId %d, Chance %3.1f%%)", skillId, chance / 10.0f);
  5134. if (!skillId)
  5135. return false;
  5136. if (chance <= 0) // speedup in 0 chance case
  5137. {
  5138. TC_LOG_DEBUG("entities.player.skills", "Player::UpdateSkillPro Chance=%3.1f%% missed", chance / 10.0f);
  5139. return false;
  5140. }
  5141. SkillStatusMap::iterator itr = mSkillStatus.find(skillId);
  5142. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5143. return false;
  5144. uint16 field = itr->second.pos / 2;
  5145. uint8 offset = itr->second.pos & 1; // itr->second.pos % 2
  5146. uint16 value = GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset);
  5147. uint16 max = GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset);
  5148. if (!max || !value || value >= max)
  5149. return false;
  5150. if (irand(1, 1000) > chance)
  5151. {
  5152. TC_LOG_DEBUG("entities.player.skills", "Player::UpdateSkillPro Chance=%3.1f%% missed", chance / 10.0f);
  5153. return false;
  5154. }
  5155. uint16 new_value = value + step;
  5156. if (new_value > max)
  5157. new_value = max;
  5158. SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, new_value);
  5159. if (itr->second.uState != SKILL_NEW)
  5160. itr->second.uState = SKILL_CHANGED;
  5161. for (size_t i = 0; i < bonusSkillLevelsSize; ++i)
  5162. {
  5163. uint32 bsl = bonusSkillLevels[i];
  5164. if (value < bsl && new_value >= bsl)
  5165. {
  5166. LearnSkillRewardedSpells(skillId, new_value);
  5167. break;
  5168. }
  5169. }
  5170. UpdateSkillEnchantments(skillId, value, new_value);
  5171. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skillId);
  5172. TC_LOG_DEBUG("entities.player.skills", "Player::UpdateSkillPro Chance=%3.1f%% taken", chance / 10.0f);
  5173. return true;
  5174. }
  5175. void Player::ModifySkillBonus(uint32 skillid, int32 val, bool talent)
  5176. {
  5177. SkillStatusMap::const_iterator itr = mSkillStatus.find(skillid);
  5178. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5179. return;
  5180. uint16 field = itr->second.pos / 2 + (talent ? PLAYER_SKILL_TALENT_0 : PLAYER_SKILL_MODIFIER_0);
  5181. uint8 offset = itr->second.pos & 1; // itr->second.pos % 2
  5182. uint16 bonus = GetUInt16Value(field, offset);
  5183. SetUInt16Value(field, offset, bonus + val);
  5184. }
  5185. void Player::UpdateSkillsForLevel()
  5186. {
  5187. uint32 maxSkill = GetMaxSkillValueForLevel();
  5188. for (SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr)
  5189. {
  5190. if (itr->second.uState == SKILL_DELETED)
  5191. continue;
  5192. uint32 pskill = itr->first;
  5193. SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pskill, getRace(), getClass());
  5194. if (!rcEntry)
  5195. continue;
  5196. if (GetSkillRangeType(rcEntry) != SKILL_RANGE_LEVEL)
  5197. continue;
  5198. if (IsWeaponSkill(rcEntry->SkillId))
  5199. continue;
  5200. uint16 field = itr->second.pos / 2;
  5201. uint8 offset = itr->second.pos & 1; // itr->second.pos % 2
  5202. //uint16 val = GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset);
  5203. uint16 max = GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset);
  5204. /// update only level dependent max skill values
  5205. if (max != 1)
  5206. {
  5207. SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, maxSkill);
  5208. SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, maxSkill);
  5209. if (itr->second.uState != SKILL_NEW)
  5210. itr->second.uState = SKILL_CHANGED;
  5211. }
  5212. }
  5213. }
  5214. void Player::UpdateSkillsToMaxSkillsForLevel()
  5215. {
  5216. for (SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr)
  5217. {
  5218. if (itr->second.uState == SKILL_DELETED)
  5219. continue;
  5220. uint32 pskill = itr->first;
  5221. SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pskill, getRace(), getClass());
  5222. if (!rcEntry)
  5223. continue;
  5224. if (IsProfessionOrRidingSkill(rcEntry->SkillId))
  5225. continue;
  5226. if (IsWeaponSkill(rcEntry->SkillId))
  5227. continue;
  5228. uint16 field = itr->second.pos / 2;
  5229. uint8 offset = itr->second.pos & 1; // itr->second.pos % 2
  5230. uint16 max = GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset);
  5231. if (max > 1)
  5232. {
  5233. SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, max);
  5234. if (itr->second.uState != SKILL_NEW)
  5235. itr->second.uState = SKILL_CHANGED;
  5236. }
  5237. }
  5238. }
  5239. // This functions sets a skill line value (and adds if doesn't exist yet)
  5240. // To "remove" a skill line, set it's values to zero
  5241. void Player::SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal)
  5242. {
  5243. if (!id)
  5244. return;
  5245. uint16 currVal;
  5246. SkillStatusMap::iterator itr = mSkillStatus.find(id);
  5247. //has skill
  5248. if (itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED)
  5249. {
  5250. uint16 field = itr->second.pos / 2;
  5251. uint8 offset = itr->second.pos & 1; // itr->second.pos % 2
  5252. currVal = GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset);
  5253. if (newVal)
  5254. {
  5255. // if skill value is going down, update enchantments before setting the new value
  5256. if (newVal < currVal)
  5257. UpdateSkillEnchantments(id, currVal, newVal);
  5258. // update step
  5259. SetUInt16Value(PLAYER_SKILL_STEP_0 + field, offset, step);
  5260. // update value
  5261. SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, newVal);
  5262. SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, maxVal);
  5263. if (itr->second.uState != SKILL_NEW)
  5264. itr->second.uState = SKILL_CHANGED;
  5265. LearnSkillRewardedSpells(id, newVal);
  5266. // if skill value is going up, update enchantments after setting the new value
  5267. if (newVal > currVal)
  5268. UpdateSkillEnchantments(id, currVal, newVal);
  5269. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, id);
  5270. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL, id);
  5271. }
  5272. else //remove
  5273. {
  5274. //remove enchantments needing this skill
  5275. UpdateSkillEnchantments(id, currVal, 0);
  5276. // clear skill fields
  5277. SetUInt16Value(PLAYER_SKILL_LINEID_0 + field, offset, 0);
  5278. SetUInt16Value(PLAYER_SKILL_STEP_0 + field, offset, 0);
  5279. SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, 0);
  5280. SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, 0);
  5281. SetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset, 0);
  5282. SetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset, 0);
  5283. // mark as deleted or simply remove from map if not saved yet
  5284. if (itr->second.uState != SKILL_NEW)
  5285. itr->second.uState = SKILL_DELETED;
  5286. else
  5287. mSkillStatus.erase(itr);
  5288. // remove all spells that related to this skill
  5289. for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
  5290. if (SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j))
  5291. if (pAbility->skillId == id)
  5292. RemoveSpell(sSpellMgr->GetFirstSpellInChain(pAbility->spellId));
  5293. // Clear profession lines
  5294. if (GetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1) == id)
  5295. SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1, 0);
  5296. else if (GetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + 1) == id)
  5297. SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + 1, 0);
  5298. }
  5299. }
  5300. else if (newVal) //add
  5301. {
  5302. currVal = 0;
  5303. for (uint32 i = 0; i < PLAYER_MAX_SKILLS; ++i)
  5304. {
  5305. uint16 field = i / 2;
  5306. uint8 offset = i & 1; // i % 2
  5307. if (!GetUInt16Value(PLAYER_SKILL_LINEID_0 + field, offset))
  5308. {
  5309. SkillLineEntry const* skillEntry = sSkillLineStore.LookupEntry(id);
  5310. if (!skillEntry)
  5311. {
  5312. TC_LOG_ERROR("misc", "Skill not found in SkillLineStore: skill #%u", id);
  5313. return;
  5314. }
  5315. SetUInt16Value(PLAYER_SKILL_LINEID_0 + field, offset, id);
  5316. if (skillEntry->categoryId == SKILL_CATEGORY_PROFESSION)
  5317. {
  5318. if (!GetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1))
  5319. SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1, id);
  5320. else if (!GetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + 1))
  5321. SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + 1, id);
  5322. }
  5323. SetUInt16Value(PLAYER_SKILL_STEP_0 + field, offset, step);
  5324. SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, newVal);
  5325. SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, maxVal);
  5326. UpdateSkillEnchantments(id, currVal, newVal);
  5327. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, id);
  5328. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL, id);
  5329. // insert new entry or update if not deleted old entry yet
  5330. if (itr != mSkillStatus.end())
  5331. {
  5332. itr->second.pos = i;
  5333. itr->second.uState = SKILL_CHANGED;
  5334. }
  5335. else
  5336. mSkillStatus.insert(SkillStatusMap::value_type(id, SkillStatusData(i, SKILL_NEW)));
  5337. // apply skill bonuses
  5338. SetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset, 0);
  5339. SetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset, 0);
  5340. // temporary bonuses
  5341. AuraEffectList const& mModSkill = GetAuraEffectsByType(SPELL_AURA_MOD_SKILL);
  5342. for (AuraEffectList::const_iterator j = mModSkill.begin(); j != mModSkill.end(); ++j)
  5343. if ((*j)->GetMiscValue() == int32(id))
  5344. (*j)->HandleEffect(this, AURA_EFFECT_HANDLE_SKILL, true);
  5345. // permanent bonuses
  5346. AuraEffectList const& mModSkillTalent = GetAuraEffectsByType(SPELL_AURA_MOD_SKILL_TALENT);
  5347. for (AuraEffectList::const_iterator j = mModSkillTalent.begin(); j != mModSkillTalent.end(); ++j)
  5348. if ((*j)->GetMiscValue() == int32(id))
  5349. (*j)->HandleEffect(this, AURA_EFFECT_HANDLE_SKILL, true);
  5350. // Learn all spells for skill
  5351. LearnSkillRewardedSpells(id, newVal);
  5352. return;
  5353. }
  5354. }
  5355. }
  5356. }
  5357. bool Player::HasSkill(uint32 skill) const
  5358. {
  5359. if (!skill)
  5360. return false;
  5361. SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
  5362. return (itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED);
  5363. }
  5364. uint16 Player::GetSkillStep(uint16 skill) const
  5365. {
  5366. if (!skill)
  5367. return 0;
  5368. SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
  5369. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5370. return 0;
  5371. return GetUInt16Value(PLAYER_SKILL_STEP_0 + itr->second.pos / 2, itr->second.pos & 1);
  5372. }
  5373. uint16 Player::GetSkillValue(uint32 skill) const
  5374. {
  5375. if (!skill)
  5376. return 0;
  5377. SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
  5378. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5379. return 0;
  5380. uint16 field = itr->second.pos / 2;
  5381. uint8 offset = itr->second.pos & 1;
  5382. int32 result = int32(GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset));
  5383. result += int32(GetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset));
  5384. result += int32(GetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset));
  5385. return result < 0 ? 0 : result;
  5386. }
  5387. uint16 Player::GetMaxSkillValue(uint32 skill) const
  5388. {
  5389. if (!skill)
  5390. return 0;
  5391. SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
  5392. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5393. return 0;
  5394. uint16 field = itr->second.pos / 2;
  5395. uint8 offset = itr->second.pos & 1;
  5396. int32 result = int32(GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset));
  5397. result += int32(GetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset));
  5398. result += int32(GetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset));
  5399. return result < 0 ? 0 : result;
  5400. }
  5401. uint16 Player::GetPureMaxSkillValue(uint32 skill) const
  5402. {
  5403. if (!skill)
  5404. return 0;
  5405. SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
  5406. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5407. return 0;
  5408. uint16 field = itr->second.pos / 2;
  5409. uint8 offset = itr->second.pos & 1;
  5410. return GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset);
  5411. }
  5412. uint16 Player::GetBaseSkillValue(uint32 skill) const
  5413. {
  5414. if (!skill)
  5415. return 0;
  5416. SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
  5417. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5418. return 0;
  5419. uint16 field = itr->second.pos / 2;
  5420. uint8 offset = itr->second.pos & 1;
  5421. int32 result = int32(GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset));
  5422. result += int32(GetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset));
  5423. return result < 0 ? 0 : result;
  5424. }
  5425. uint16 Player::GetPureSkillValue(uint32 skill) const
  5426. {
  5427. if (!skill)
  5428. return 0;
  5429. SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
  5430. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5431. return 0;
  5432. uint16 field = itr->second.pos / 2;
  5433. uint8 offset = itr->second.pos & 1;
  5434. return GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset);
  5435. }
  5436. int16 Player::GetSkillPermBonusValue(uint32 skill) const
  5437. {
  5438. if (!skill)
  5439. return 0;
  5440. SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
  5441. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5442. return 0;
  5443. uint16 field = itr->second.pos / 2;
  5444. uint8 offset = itr->second.pos & 1;
  5445. return GetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset);
  5446. }
  5447. int16 Player::GetSkillTempBonusValue(uint32 skill) const
  5448. {
  5449. if (!skill)
  5450. return 0;
  5451. SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
  5452. if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
  5453. return 0;
  5454. uint16 field = itr->second.pos / 2;
  5455. uint8 offset = itr->second.pos & 1;
  5456. return GetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset);
  5457. }
  5458. void Player::SendActionButtons(uint32 state) const
  5459. {
  5460. WorldPacket data(SMSG_ACTION_BUTTONS, 1+(MAX_ACTION_BUTTONS*4));
  5461. /*
  5462. state can be 0, 1, 2
  5463. 0 - Sends initial action buttons, client does not validate if we have the spell or not
  5464. 1 - Used used after spec swaps, client validates if a spell is known.
  5465. 2 - Clears the action bars client sided. This is sent during spec swap before unlearning and before sending the new buttons
  5466. */
  5467. if (state != 2)
  5468. {
  5469. for (uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button)
  5470. {
  5471. ActionButtonList::const_iterator itr = m_actionButtons.find(button);
  5472. if (itr != m_actionButtons.end() && itr->second.uState != ACTIONBUTTON_DELETED)
  5473. data << uint32(itr->second.packedData);
  5474. else
  5475. data << uint32(0);
  5476. }
  5477. }
  5478. else
  5479. data.resize(MAX_ACTION_BUTTONS * 4); // insert crap, client doesnt even parse this for state == 2
  5480. data << uint8(state);
  5481. GetSession()->SendPacket(&data);
  5482. TC_LOG_INFO("network", "Action Buttons for '%u' spec '%u' Sent", GetGUIDLow(), GetActiveSpec());
  5483. }
  5484. bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type)
  5485. {
  5486. if (button >= MAX_ACTION_BUTTONS)
  5487. {
  5488. TC_LOG_ERROR("entities.player", "Action %u not added into button %u for player %s (GUID: %u): button must be < %u", action, button, GetName().c_str(), GetGUIDLow(), MAX_ACTION_BUTTONS );
  5489. return false;
  5490. }
  5491. if (action >= MAX_ACTION_BUTTON_ACTION_VALUE)
  5492. {
  5493. TC_LOG_ERROR("entities.player", "Action %u not added into button %u for player %s (GUID: %u): action must be < %u", action, button, GetName().c_str(), GetGUIDLow(), MAX_ACTION_BUTTON_ACTION_VALUE);
  5494. return false;
  5495. }
  5496. switch (type)
  5497. {
  5498. case ACTION_BUTTON_SPELL:
  5499. if (!sSpellMgr->GetSpellInfo(action))
  5500. {
  5501. TC_LOG_ERROR("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): spell not exist", action, button, GetName().c_str(), GetGUIDLow());
  5502. return false;
  5503. }
  5504. if (!HasSpell(action))
  5505. {
  5506. TC_LOG_ERROR("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): player don't known this spell", action, button, GetName().c_str(), GetGUIDLow());
  5507. return false;
  5508. }
  5509. break;
  5510. case ACTION_BUTTON_ITEM:
  5511. if (!sObjectMgr->GetItemTemplate(action))
  5512. {
  5513. TC_LOG_ERROR("entities.player", "Item action %u not added into button %u for player %s (GUID: %u): item not exist", action, button, GetName().c_str(), GetGUIDLow());
  5514. return false;
  5515. }
  5516. break;
  5517. case ACTION_BUTTON_C:
  5518. case ACTION_BUTTON_CMACRO:
  5519. case ACTION_BUTTON_MACRO:
  5520. case ACTION_BUTTON_EQSET:
  5521. case ACTION_BUTTON_DROPDOWN:
  5522. break;
  5523. default:
  5524. TC_LOG_ERROR("entities.player", "Unknown action type %u", type);
  5525. return false; // other cases not checked at this moment
  5526. }
  5527. return true;
  5528. }
  5529. ActionButton* Player::addActionButton(uint8 button, uint32 action, uint8 type)
  5530. {
  5531. if (!IsActionButtonDataValid(button, action, type))
  5532. return NULL;
  5533. // it create new button (NEW state) if need or return existed
  5534. ActionButton& ab = m_actionButtons[button];
  5535. // set data and update to CHANGED if not NEW
  5536. ab.SetActionAndType(action, ActionButtonType(type));
  5537. TC_LOG_DEBUG("entities.player", "Player '%u' Added Action '%u' (type %u) to Button '%u'", GetGUIDLow(), action, type, button);
  5538. return &ab;
  5539. }
  5540. void Player::removeActionButton(uint8 button)
  5541. {
  5542. ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
  5543. if (buttonItr == m_actionButtons.end() || buttonItr->second.uState == ACTIONBUTTON_DELETED)
  5544. return;
  5545. if (buttonItr->second.uState == ACTIONBUTTON_NEW)
  5546. m_actionButtons.erase(buttonItr); // new and not saved
  5547. else
  5548. buttonItr->second.uState = ACTIONBUTTON_DELETED; // saved, will deleted at next save
  5549. TC_LOG_DEBUG("entities.player", "Action Button '%u' Removed from Player '%u'", button, GetGUIDLow());
  5550. }
  5551. ActionButton const* Player::GetActionButton(uint8 button)
  5552. {
  5553. ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
  5554. if (buttonItr == m_actionButtons.end() || buttonItr->second.uState == ACTIONBUTTON_DELETED)
  5555. return NULL;
  5556. return &buttonItr->second;
  5557. }
  5558. bool Player::UpdatePosition(float x, float y, float z, float orientation, bool teleport)
  5559. {
  5560. if (!Unit::UpdatePosition(x, y, z, orientation, teleport))
  5561. return false;
  5562. //if (movementInfo.flags & MOVEMENTFLAG_MOVING)
  5563. // mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE);
  5564. //if (movementInfo.flags & MOVEMENTFLAG_TURNING)
  5565. // mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);
  5566. //AURA_INTERRUPT_FLAG_JUMP not sure
  5567. // Update player zone if needed
  5568. if (m_needsZoneUpdate)
  5569. {
  5570. uint32 newZone, newArea;
  5571. GetZoneAndAreaId(newZone, newArea);
  5572. UpdateZone(newZone, newArea);
  5573. m_needsZoneUpdate = false;
  5574. }
  5575. // group update
  5576. if (GetGroup())
  5577. SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
  5578. CheckAreaExploreAndOutdoor();
  5579. return true;
  5580. }
  5581. void Player::SaveRecallPosition()
  5582. {
  5583. m_recallMap = GetMapId();
  5584. m_recallX = GetPositionX();
  5585. m_recallY = GetPositionY();
  5586. m_recallZ = GetPositionZ();
  5587. m_recallO = GetOrientation();
  5588. }
  5589. void Player::SendMessageToSetInRange(WorldPacket* data, float dist, bool self)
  5590. {
  5591. if (self)
  5592. GetSession()->SendPacket(data);
  5593. Trinity::MessageDistDeliverer notifier(this, data, dist);
  5594. VisitNearbyWorldObject(dist, notifier);
  5595. }
  5596. void Player::SendMessageToSetInRange(WorldPacket* data, float dist, bool self, bool own_team_only)
  5597. {
  5598. if (self)
  5599. GetSession()->SendPacket(data);
  5600. Trinity::MessageDistDeliverer notifier(this, data, dist, own_team_only);
  5601. VisitNearbyWorldObject(dist, notifier);
  5602. }
  5603. void Player::SendMessageToSet(WorldPacket* data, Player const* skipped_rcvr)
  5604. {
  5605. if (skipped_rcvr != this)
  5606. GetSession()->SendPacket(data);
  5607. // we use World::GetMaxVisibleDistance() because i cannot see why not use a distance
  5608. // update: replaced by GetMap()->GetVisibilityDistance()
  5609. Trinity::MessageDistDeliverer notifier(this, data, GetVisibilityRange(), false, skipped_rcvr);
  5610. VisitNearbyWorldObject(GetVisibilityRange(), notifier);
  5611. }
  5612. void Player::SendDirectMessage(WorldPacket* data)
  5613. {
  5614. m_session->SendPacket(data);
  5615. }
  5616. void Player::SendCinematicStart(uint32 CinematicSequenceId)
  5617. {
  5618. WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
  5619. data << uint32(CinematicSequenceId);
  5620. SendDirectMessage(&data);
  5621. }
  5622. void Player::SendMovieStart(uint32 MovieId)
  5623. {
  5624. WorldPacket data(SMSG_TRIGGER_MOVIE, 4);
  5625. data << uint32(MovieId);
  5626. SendDirectMessage(&data);
  5627. }
  5628. void Player::CheckAreaExploreAndOutdoor()
  5629. {
  5630. if (!IsAlive())
  5631. return;
  5632. if (IsInFlight())
  5633. return;
  5634. bool isOutdoor;
  5635. uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor);
  5636. if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor)
  5637. RemoveAurasWithAttribute(SPELL_ATTR0_OUTDOORS_ONLY);
  5638. if (areaFlag == 0xffff)
  5639. return;
  5640. int offset = areaFlag / 32;
  5641. if (offset >= PLAYER_EXPLORED_ZONES_SIZE)
  5642. {
  5643. TC_LOG_ERROR("entities.player", "Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).", areaFlag, GetPositionX(), GetPositionY(), offset, offset, PLAYER_EXPLORED_ZONES_SIZE);
  5644. return;
  5645. }
  5646. uint32 val = (uint32)(1 << (areaFlag % 32));
  5647. uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
  5648. if (!(currFields & val))
  5649. {
  5650. SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val));
  5651. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA);
  5652. AreaTableEntry const* areaEntry = GetAreaEntryByAreaFlagAndMap(areaFlag, GetMapId());
  5653. if (!areaEntry)
  5654. {
  5655. TC_LOG_ERROR("entities.player", "Player %u discovered unknown area (x: %f y: %f z: %f map: %u", GetGUIDLow(), GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId());
  5656. return;
  5657. }
  5658. if (areaEntry->area_level > 0)
  5659. {
  5660. uint32 area = areaEntry->ID;
  5661. if (getLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  5662. {
  5663. SendExplorationExperience(area, 0);
  5664. }
  5665. else
  5666. {
  5667. int32 diff = int32(getLevel()) - areaEntry->area_level;
  5668. uint32 XP = 0;
  5669. if (diff < -5)
  5670. {
  5671. XP = uint32(sObjectMgr->GetBaseXP(getLevel()+5)*sWorld->getRate(RATE_XP_EXPLORE));
  5672. }
  5673. else if (diff > 5)
  5674. {
  5675. int32 exploration_percent = 100 - ((diff - 5) * 5);
  5676. if (exploration_percent < 0)
  5677. exploration_percent = 0;
  5678. XP = uint32(sObjectMgr->GetBaseXP(areaEntry->area_level)*exploration_percent/100*sWorld->getRate(RATE_XP_EXPLORE));
  5679. }
  5680. else
  5681. {
  5682. XP = uint32(sObjectMgr->GetBaseXP(areaEntry->area_level)*sWorld->getRate(RATE_XP_EXPLORE));
  5683. }
  5684. GiveXP(XP, NULL);
  5685. SendExplorationExperience(area, XP);
  5686. }
  5687. TC_LOG_INFO("entities.player", "Player %u discovered a new area: %u", GetGUIDLow(), area);
  5688. }
  5689. }
  5690. }
  5691. uint32 Player::TeamForRace(uint8 race)
  5692. {
  5693. if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race))
  5694. {
  5695. switch (rEntry->TeamID)
  5696. {
  5697. case 1: return HORDE;
  5698. case 7: return ALLIANCE;
  5699. }
  5700. TC_LOG_ERROR("entities.player", "Race (%u) has wrong teamid (%u) in DBC: wrong DBC files?", uint32(race), rEntry->TeamID);
  5701. }
  5702. else
  5703. TC_LOG_ERROR("entities.player", "Race (%u) not found in DBC: wrong DBC files?", uint32(race));
  5704. return ALLIANCE;
  5705. }
  5706. void Player::setFactionForRace(uint8 race)
  5707. {
  5708. m_team = TeamForRace(race);
  5709. ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
  5710. setFaction(rEntry ? rEntry->FactionID : 0);
  5711. }
  5712. ReputationRank Player::GetReputationRank(uint32 faction) const
  5713. {
  5714. FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction);
  5715. return GetReputationMgr().GetRank(factionEntry);
  5716. }
  5717. // Calculate total reputation percent player gain with quest/creature level
  5718. int32 Player::CalculateReputationGain(ReputationSource source, uint32 creatureOrQuestLevel, int32 rep, int32 faction, bool noQuestBonus)
  5719. {
  5720. float percent = 100.0f;
  5721. float repMod = noQuestBonus ? 0.0f : float(GetTotalAuraModifier(SPELL_AURA_MOD_REPUTATION_GAIN));
  5722. // faction specific auras only seem to apply to kills
  5723. if (source == REPUTATION_SOURCE_KILL)
  5724. repMod += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_FACTION_REPUTATION_GAIN, faction);
  5725. percent += rep > 0 ? repMod : -repMod;
  5726. float rate;
  5727. switch (source)
  5728. {
  5729. case REPUTATION_SOURCE_KILL:
  5730. rate = sWorld->getRate(RATE_REPUTATION_LOWLEVEL_KILL);
  5731. break;
  5732. case REPUTATION_SOURCE_QUEST:
  5733. case REPUTATION_SOURCE_DAILY_QUEST:
  5734. case REPUTATION_SOURCE_WEEKLY_QUEST:
  5735. case REPUTATION_SOURCE_MONTHLY_QUEST:
  5736. case REPUTATION_SOURCE_REPEATABLE_QUEST:
  5737. rate = sWorld->getRate(RATE_REPUTATION_LOWLEVEL_QUEST);
  5738. break;
  5739. case REPUTATION_SOURCE_SPELL:
  5740. default:
  5741. rate = 1.0f;
  5742. break;
  5743. }
  5744. if (rate != 1.0f && creatureOrQuestLevel <= Trinity::XP::GetGrayLevel(getLevel()))
  5745. percent *= rate;
  5746. if (percent <= 0.0f)
  5747. return 0;
  5748. // Multiply result with the faction specific rate
  5749. if (RepRewardRate const* repData = sObjectMgr->GetRepRewardRate(faction))
  5750. {
  5751. float repRate = 0.0f;
  5752. switch (source)
  5753. {
  5754. case REPUTATION_SOURCE_KILL:
  5755. repRate = repData->creatureRate;
  5756. break;
  5757. case REPUTATION_SOURCE_QUEST:
  5758. repRate = repData->questRate;
  5759. break;
  5760. case REPUTATION_SOURCE_DAILY_QUEST:
  5761. repRate = repData->questDailyRate;
  5762. break;
  5763. case REPUTATION_SOURCE_WEEKLY_QUEST:
  5764. repRate = repData->questWeeklyRate;
  5765. break;
  5766. case REPUTATION_SOURCE_MONTHLY_QUEST:
  5767. repRate = repData->questMonthlyRate;
  5768. break;
  5769. case REPUTATION_SOURCE_REPEATABLE_QUEST:
  5770. repRate = repData->questRepeatableRate;
  5771. break;
  5772. case REPUTATION_SOURCE_SPELL:
  5773. repRate = repData->spellRate;
  5774. break;
  5775. }
  5776. // for custom, a rate of 0.0 will totally disable reputation gain for this faction/type
  5777. if (repRate <= 0.0f)
  5778. return 0;
  5779. percent *= repRate;
  5780. }
  5781. if (source != REPUTATION_SOURCE_SPELL && GetsRecruitAFriendBonus(false))
  5782. percent *= 1.0f + sWorld->getRate(RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS);
  5783. return CalculatePct(rep, percent);
  5784. }
  5785. // Calculates how many reputation points player gains in victim's enemy factions
  5786. void Player::RewardReputation(Unit* victim, float rate)
  5787. {
  5788. if (!victim || victim->GetTypeId() == TYPEID_PLAYER)
  5789. return;
  5790. if (victim->ToCreature()->IsReputationGainDisabled())
  5791. return;
  5792. ReputationOnKillEntry const* Rep = sObjectMgr->GetReputationOnKilEntry(victim->ToCreature()->GetCreatureTemplate()->Entry);
  5793. if (!Rep)
  5794. return;
  5795. uint32 ChampioningFaction = 0;
  5796. if (GetChampioningFaction())
  5797. {
  5798. // support for: Championing - http://www.wowwiki.com/Championing
  5799. Map const* map = GetMap();
  5800. if (map && map->IsNonRaidDungeon())
  5801. if (LFGDungeonEntry const* dungeon = GetLFGDungeon(map->GetId(), map->GetDifficulty()))
  5802. if (dungeon->reclevel == 80)
  5803. ChampioningFaction = GetChampioningFaction();
  5804. }
  5805. uint32 team = GetTeam();
  5806. if (Rep->RepFaction1 && (!Rep->TeamDependent || team == ALLIANCE))
  5807. {
  5808. int32 donerep1 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->getLevel(), Rep->RepValue1, ChampioningFaction ? ChampioningFaction : Rep->RepFaction1);
  5809. donerep1 = int32(donerep1 * rate);
  5810. FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction1);
  5811. uint32 current_reputation_rank1 = GetReputationMgr().GetRank(factionEntry1);
  5812. if (factionEntry1 && current_reputation_rank1 <= Rep->ReputationMaxCap1)
  5813. GetReputationMgr().ModifyReputation(factionEntry1, donerep1);
  5814. }
  5815. if (Rep->RepFaction2 && (!Rep->TeamDependent || team == HORDE))
  5816. {
  5817. int32 donerep2 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->getLevel(), Rep->RepValue2, ChampioningFaction ? ChampioningFaction : Rep->RepFaction2);
  5818. donerep2 = int32(donerep2 * rate);
  5819. FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction2);
  5820. uint32 current_reputation_rank2 = GetReputationMgr().GetRank(factionEntry2);
  5821. if (factionEntry2 && current_reputation_rank2 <= Rep->ReputationMaxCap2)
  5822. GetReputationMgr().ModifyReputation(factionEntry2, donerep2);
  5823. }
  5824. }
  5825. // Calculate how many reputation points player gain with the quest
  5826. void Player::RewardReputation(Quest const* quest)
  5827. {
  5828. for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
  5829. {
  5830. if (!quest->RewardFactionId[i])
  5831. continue;
  5832. int32 rep = 0;
  5833. bool noQuestBonus = false;
  5834. if (quest->RewardFactionValueIdOverride[i])
  5835. {
  5836. rep = quest->RewardFactionValueIdOverride[i] / 100;
  5837. noQuestBonus = true;
  5838. }
  5839. else
  5840. {
  5841. uint32 row = ((quest->RewardFactionValueId[i] < 0) ? 1 : 0) + 1;
  5842. if (QuestFactionRewEntry const* questFactionRewEntry = sQuestFactionRewardStore.LookupEntry(row))
  5843. {
  5844. uint32 field = abs(quest->RewardFactionValueId[i]);
  5845. rep = questFactionRewEntry->QuestRewFactionValue[field];
  5846. }
  5847. }
  5848. if (!rep)
  5849. continue;
  5850. if (quest->IsDaily())
  5851. rep = CalculateReputationGain(REPUTATION_SOURCE_DAILY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], noQuestBonus);
  5852. else if (quest->IsWeekly())
  5853. rep = CalculateReputationGain(REPUTATION_SOURCE_WEEKLY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], noQuestBonus);
  5854. else if (quest->IsMonthly())
  5855. rep = CalculateReputationGain(REPUTATION_SOURCE_MONTHLY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], noQuestBonus);
  5856. else if (quest->IsRepeatable())
  5857. rep = CalculateReputationGain(REPUTATION_SOURCE_REPEATABLE_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], noQuestBonus);
  5858. else
  5859. rep = CalculateReputationGain(REPUTATION_SOURCE_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], noQuestBonus);
  5860. if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(quest->RewardFactionId[i]))
  5861. GetReputationMgr().ModifyReputation(factionEntry, rep);
  5862. }
  5863. }
  5864. void Player::UpdateHonorFields()
  5865. {
  5866. /// called when rewarding honor and at each save
  5867. time_t now = time_t(time(NULL));
  5868. time_t today = time_t(time(NULL) / DAY) * DAY;
  5869. if (m_lastHonorUpdateTime < today)
  5870. {
  5871. time_t yesterday = today - DAY;
  5872. uint16 kills_today = PAIR32_LOPART(GetUInt32Value(PLAYER_FIELD_KILLS));
  5873. // update yesterday's contribution
  5874. if (m_lastHonorUpdateTime >= yesterday)
  5875. {
  5876. // this is the first update today, reset today's contribution
  5877. SetUInt32Value(PLAYER_FIELD_KILLS, MAKE_PAIR32(0, kills_today));
  5878. }
  5879. else
  5880. {
  5881. // no honor/kills yesterday or today, reset
  5882. SetUInt32Value(PLAYER_FIELD_KILLS, 0);
  5883. }
  5884. }
  5885. m_lastHonorUpdateTime = now;
  5886. }
  5887. ///Calculate the amount of honor gained based on the victim
  5888. ///and the size of the group for which the honor is divided
  5889. ///An exact honor value can also be given (overriding the calcs)
  5890. bool Player::RewardHonor(Unit* victim, uint32 groupsize, int32 honor, bool pvptoken)
  5891. {
  5892. // do not reward honor in arenas, but enable onkill spellproc
  5893. if (InArena())
  5894. {
  5895. if (!victim || victim == this || victim->GetTypeId() != TYPEID_PLAYER)
  5896. return false;
  5897. if (GetBGTeam() == victim->ToPlayer()->GetBGTeam())
  5898. return false;
  5899. return true;
  5900. }
  5901. // 'Inactive' this aura prevents the player from gaining honor points and battleground Tokenizer
  5902. if (HasAura(SPELL_AURA_PLAYER_INACTIVE))
  5903. return false;
  5904. ObjectGuid victim_guid;
  5905. uint32 victim_rank = 0;
  5906. // need call before fields update to have chance move yesterday data to appropriate fields before today data change.
  5907. UpdateHonorFields();
  5908. // do not reward honor in arenas, but return true to enable onkill spellproc
  5909. if (InBattleground() && GetBattleground() && GetBattleground()->isArena())
  5910. return true;
  5911. // Promote to float for calculations
  5912. float honor_f = (float)honor;
  5913. if (honor_f <= 0)
  5914. {
  5915. if (!victim || victim == this || victim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
  5916. return false;
  5917. victim_guid = victim->GetGUID();
  5918. if (Player* plrVictim = victim->ToPlayer())
  5919. {
  5920. if (GetTeam() == plrVictim->GetTeam() && !sWorld->IsFFAPvPRealm())
  5921. return false;
  5922. uint8 k_level = getLevel();
  5923. uint8 k_grey = Trinity::XP::GetGrayLevel(k_level);
  5924. uint8 v_level = victim->getLevel();
  5925. if (v_level <= k_grey)
  5926. return false;
  5927. // PLAYER_CHOSEN_TITLE VALUES DESCRIPTION
  5928. // [0] Just name
  5929. // [1..14] Alliance honor titles and player name
  5930. // [15..28] Horde honor titles and player name
  5931. // [29..38] Other title and player name
  5932. // [39+] Nothing
  5933. uint32 victim_title = victim->GetUInt32Value(PLAYER_CHOSEN_TITLE);
  5934. // Get Killer titles, CharTitlesEntry::bit_index
  5935. // Ranks:
  5936. // title[1..14] -> rank[5..18]
  5937. // title[15..28] -> rank[5..18]
  5938. // title[other] -> 0
  5939. if (victim_title == 0)
  5940. victim_guid.Clear(); // Don't show HK: <rank> message, only log.
  5941. else if (victim_title < 15)
  5942. victim_rank = victim_title + 4;
  5943. else if (victim_title < 29)
  5944. victim_rank = victim_title - 14 + 4;
  5945. else
  5946. victim_guid.Clear(); // Don't show HK: <rank> message, only log.
  5947. honor_f = std::ceil(Trinity::Honor::hk_honor_at_level_f(k_level) * (v_level - k_grey) / (k_level - k_grey));
  5948. // count the number of playerkills in one day
  5949. ApplyModUInt32Value(PLAYER_FIELD_KILLS, 1, true);
  5950. // and those in a lifetime
  5951. ApplyModUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, 1, true);
  5952. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL);
  5953. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS, victim->getClass());
  5954. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_RACE, victim->getRace());
  5955. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA, GetAreaId());
  5956. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL, 1, 0, 0, victim);
  5957. }
  5958. else
  5959. {
  5960. if (!victim->ToCreature()->IsRacialLeader())
  5961. return false;
  5962. honor_f = 100.0f; // ??? need more info
  5963. victim_rank = 19; // HK: Leader
  5964. }
  5965. }
  5966. if (victim != NULL)
  5967. {
  5968. if (groupsize > 1)
  5969. honor_f /= groupsize;
  5970. // apply honor multiplier from aura (not stacking-get highest)
  5971. AddPct(honor_f, GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HONOR_GAIN_PCT));
  5972. }
  5973. honor_f *= sWorld->getRate(RATE_HONOR);
  5974. // Back to int now
  5975. honor = int32(honor_f);
  5976. // honor - for show honor points in log
  5977. // victim_guid - for show victim name in log
  5978. // victim_rank [1..4] HK: <dishonored rank>
  5979. // victim_rank [5..19] HK: <alliance\horde rank>
  5980. // victim_rank [0, 20+] HK: <>
  5981. WorldPacket data(SMSG_PVP_CREDIT, 4+8+4);
  5982. data << uint32(honor);
  5983. data << uint64(victim_guid);
  5984. data << uint32(victim_rank);
  5985. GetSession()->SendPacket(&data);
  5986. // add honor points
  5987. ModifyCurrency(CURRENCY_TYPE_HONOR_POINTS, int32(honor));
  5988. if (InBattleground() && honor > 0)
  5989. {
  5990. if (Battleground* bg = GetBattleground())
  5991. {
  5992. bg->UpdatePlayerScore(this, SCORE_BONUS_HONOR, honor, false); //false: prevent looping
  5993. }
  5994. }
  5995. if (sWorld->getBoolConfig(CONFIG_PVP_TOKEN_ENABLE) && pvptoken)
  5996. {
  5997. if (!victim || victim == this || victim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
  5998. return true;
  5999. if (victim->GetTypeId() == TYPEID_PLAYER)
  6000. {
  6001. // Check if allowed to receive it in current map
  6002. uint8 MapType = sWorld->getIntConfig(CONFIG_PVP_TOKEN_MAP_TYPE);
  6003. if ((MapType == 1 && !InBattleground() && !IsFFAPvP())
  6004. || (MapType == 2 && !IsFFAPvP())
  6005. || (MapType == 3 && !InBattleground()))
  6006. return true;
  6007. uint32 itemId = sWorld->getIntConfig(CONFIG_PVP_TOKEN_ID);
  6008. int32 count = sWorld->getIntConfig(CONFIG_PVP_TOKEN_COUNT);
  6009. if (AddItem(itemId, count))
  6010. ChatHandler(GetSession()).PSendSysMessage("You have been awarded a token for slaying another player.");
  6011. }
  6012. }
  6013. return true;
  6014. }
  6015. void Player::_LoadCurrency(PreparedQueryResult result)
  6016. {
  6017. if (!result)
  6018. return;
  6019. do
  6020. {
  6021. Field* fields = result->Fetch();
  6022. uint16 currencyID = fields[0].GetUInt16();
  6023. CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(currencyID);
  6024. if (!currency)
  6025. continue;
  6026. PlayerCurrency cur;
  6027. cur.state = PLAYERCURRENCY_UNCHANGED;
  6028. cur.weekCount = fields[1].GetUInt32();
  6029. cur.totalCount = fields[2].GetUInt32();
  6030. _currencyStorage.insert(PlayerCurrenciesMap::value_type(currencyID, cur));
  6031. } while (result->NextRow());
  6032. }
  6033. void Player::_SaveCurrency(SQLTransaction& trans)
  6034. {
  6035. PreparedStatement* stmt = NULL;
  6036. for (PlayerCurrenciesMap::iterator itr = _currencyStorage.begin(); itr != _currencyStorage.end(); ++itr)
  6037. {
  6038. CurrencyTypesEntry const* entry = sCurrencyTypesStore.LookupEntry(itr->first);
  6039. if (!entry) // should never happen
  6040. continue;
  6041. switch (itr->second.state)
  6042. {
  6043. case PLAYERCURRENCY_NEW:
  6044. stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_PLAYER_CURRENCY);
  6045. stmt->setUInt32(0, GetGUIDLow());
  6046. stmt->setUInt16(1, itr->first);
  6047. stmt->setUInt32(2, itr->second.weekCount);
  6048. stmt->setUInt32(3, itr->second.totalCount);
  6049. trans->Append(stmt);
  6050. break;
  6051. case PLAYERCURRENCY_CHANGED:
  6052. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PLAYER_CURRENCY);
  6053. stmt->setUInt32(0, itr->second.weekCount);
  6054. stmt->setUInt32(1, itr->second.totalCount);
  6055. stmt->setUInt32(2, GetGUIDLow());
  6056. stmt->setUInt16(3, itr->first);
  6057. trans->Append(stmt);
  6058. break;
  6059. default:
  6060. break;
  6061. }
  6062. itr->second.state = PLAYERCURRENCY_UNCHANGED;
  6063. }
  6064. }
  6065. void Player::SendNewCurrency(uint32 id) const
  6066. {
  6067. PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id);
  6068. if (itr == _currencyStorage.end())
  6069. return;
  6070. ByteBuffer currencyData;
  6071. WorldPacket packet(SMSG_INIT_CURRENCY, 4 + (5*4 + 1));
  6072. packet.WriteBits(1, 23);
  6073. CurrencyTypesEntry const* entry = sCurrencyTypesStore.LookupEntry(id);
  6074. if (!entry) // should never happen
  6075. return;
  6076. uint32 precision = (entry->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
  6077. uint32 weekCount = itr->second.weekCount / precision;
  6078. uint32 weekCap = GetCurrencyWeekCap(entry) / precision;
  6079. packet.WriteBit(weekCount);
  6080. packet.WriteBits(0, 4); // some flags
  6081. packet.WriteBit(weekCap);
  6082. packet.WriteBit(0); // season total earned
  6083. currencyData << uint32(itr->second.totalCount / precision);
  6084. if (weekCap)
  6085. currencyData << uint32(weekCap);
  6086. //if (seasonTotal)
  6087. // currencyData << uint32(seasonTotal / precision);
  6088. currencyData << uint32(entry->ID);
  6089. if (weekCount)
  6090. currencyData << uint32(weekCount);
  6091. packet.FlushBits();
  6092. packet.append(currencyData);
  6093. GetSession()->SendPacket(&packet);
  6094. }
  6095. void Player::SendCurrencies() const
  6096. {
  6097. ByteBuffer currencyData;
  6098. WorldPacket packet(SMSG_INIT_CURRENCY, 4 + _currencyStorage.size()*(5*4 + 1));
  6099. size_t count_pos = packet.bitwpos();
  6100. packet.WriteBits(_currencyStorage.size(), 23);
  6101. size_t count = 0;
  6102. for (PlayerCurrenciesMap::const_iterator itr = _currencyStorage.begin(); itr != _currencyStorage.end(); ++itr)
  6103. {
  6104. CurrencyTypesEntry const* entry = sCurrencyTypesStore.LookupEntry(itr->first);
  6105. // not send init meta currencies.
  6106. if (!entry || entry->Category == CURRENCY_CATEGORY_META_CONQUEST)
  6107. continue;
  6108. uint32 precision = (entry->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
  6109. uint32 weekCount = itr->second.weekCount / precision;
  6110. uint32 weekCap = GetCurrencyWeekCap(entry) / precision;
  6111. packet.WriteBit(weekCount);
  6112. packet.WriteBits(0, 4); // some flags
  6113. packet.WriteBit(weekCap);
  6114. packet.WriteBit(0); // season total earned
  6115. currencyData << uint32(itr->second.totalCount / precision);
  6116. if (weekCap)
  6117. currencyData << uint32(weekCap);
  6118. //if (seasonTotal)
  6119. // currencyData << uint32(seasonTotal / precision);
  6120. currencyData << uint32(entry->ID);
  6121. if (weekCount)
  6122. currencyData << uint32(weekCount);
  6123. ++count;
  6124. }
  6125. packet.FlushBits();
  6126. packet.append(currencyData);
  6127. packet.PutBits(count_pos, count, 23);
  6128. GetSession()->SendPacket(&packet);
  6129. }
  6130. void Player::SendPvpRewards() const
  6131. {
  6132. WorldPacket packet(SMSG_REQUEST_PVP_REWARDS_RESPONSE, 24);
  6133. packet << GetCurrencyWeekCap(CURRENCY_TYPE_CONQUEST_POINTS, true);
  6134. packet << GetCurrencyOnWeek(CURRENCY_TYPE_CONQUEST_POINTS, true);
  6135. packet << GetCurrencyWeekCap(CURRENCY_TYPE_CONQUEST_META_ARENA, true);
  6136. packet << GetCurrencyOnWeek(CURRENCY_TYPE_CONQUEST_META_RBG, true);
  6137. packet << GetCurrencyOnWeek(CURRENCY_TYPE_CONQUEST_META_ARENA, true);
  6138. packet << GetCurrencyWeekCap(CURRENCY_TYPE_CONQUEST_POINTS, true);
  6139. GetSession()->SendPacket(&packet);
  6140. }
  6141. uint32 Player::GetCurrency(uint32 id, bool usePrecision) const
  6142. {
  6143. PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id);
  6144. if (itr == _currencyStorage.end())
  6145. return 0;
  6146. CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(id);
  6147. uint32 precision = (usePrecision && currency->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
  6148. return itr->second.totalCount / precision;
  6149. }
  6150. uint32 Player::GetCurrencyOnWeek(uint32 id, bool usePrecision) const
  6151. {
  6152. PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id);
  6153. if (itr == _currencyStorage.end())
  6154. return 0;
  6155. CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(id);
  6156. uint32 precision = (usePrecision && currency->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
  6157. return itr->second.weekCount / precision;
  6158. }
  6159. bool Player::HasCurrency(uint32 id, uint32 count) const
  6160. {
  6161. PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id);
  6162. return itr != _currencyStorage.end() && itr->second.totalCount >= count;
  6163. }
  6164. void Player::ModifyCurrency(uint32 id, int32 count, bool printLog/* = true*/, bool ignoreMultipliers/* = false*/)
  6165. {
  6166. if (!count)
  6167. return;
  6168. CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(id);
  6169. ASSERT(currency);
  6170. if (!ignoreMultipliers)
  6171. count *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_CURRENCY_GAIN, id);
  6172. int32 precision = currency->Flags & CURRENCY_FLAG_HIGH_PRECISION ? CURRENCY_PRECISION : 1;
  6173. uint32 oldTotalCount = 0;
  6174. uint32 oldWeekCount = 0;
  6175. PlayerCurrenciesMap::iterator itr = _currencyStorage.find(id);
  6176. if (itr == _currencyStorage.end())
  6177. {
  6178. PlayerCurrency cur;
  6179. cur.state = PLAYERCURRENCY_NEW;
  6180. cur.totalCount = 0;
  6181. cur.weekCount = 0;
  6182. _currencyStorage[id] = cur;
  6183. itr = _currencyStorage.find(id);
  6184. }
  6185. else
  6186. {
  6187. oldTotalCount = itr->second.totalCount;
  6188. oldWeekCount = itr->second.weekCount;
  6189. }
  6190. // count can't be more then weekCap if used (weekCap > 0)
  6191. uint32 weekCap = GetCurrencyWeekCap(currency);
  6192. if (weekCap && count > int32(weekCap))
  6193. count = weekCap;
  6194. // count can't be more then totalCap if used (totalCap > 0)
  6195. uint32 totalCap = GetCurrencyTotalCap(currency);
  6196. if (totalCap && count > int32(totalCap))
  6197. count = totalCap;
  6198. int32 newTotalCount = int32(oldTotalCount) + count;
  6199. if (newTotalCount < 0)
  6200. newTotalCount = 0;
  6201. int32 newWeekCount = int32(oldWeekCount) + (count > 0 ? count : 0);
  6202. if (newWeekCount < 0)
  6203. newWeekCount = 0;
  6204. // if we get more then weekCap just set to limit
  6205. if (weekCap && int32(weekCap) < newWeekCount)
  6206. {
  6207. newWeekCount = int32(weekCap);
  6208. // weekCap - oldWeekCount always >= 0 as we set limit before!
  6209. newTotalCount = oldTotalCount + (weekCap - oldWeekCount);
  6210. }
  6211. // if we get more then totalCap set to maximum;
  6212. if (totalCap && int32(totalCap) < newTotalCount)
  6213. {
  6214. newTotalCount = int32(totalCap);
  6215. newWeekCount = weekCap;
  6216. }
  6217. if (uint32(newTotalCount) != oldTotalCount)
  6218. {
  6219. if (itr->second.state != PLAYERCURRENCY_NEW)
  6220. itr->second.state = PLAYERCURRENCY_CHANGED;
  6221. itr->second.totalCount = newTotalCount;
  6222. itr->second.weekCount = newWeekCount;
  6223. if (count > 0)
  6224. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CURRENCY, id, count);
  6225. if (currency->Category == CURRENCY_CATEGORY_META_CONQUEST)
  6226. {
  6227. // count was changed to week limit, now we can modify original points.
  6228. ModifyCurrency(CURRENCY_TYPE_CONQUEST_POINTS, count, printLog);
  6229. return;
  6230. }
  6231. WorldPacket packet(SMSG_UPDATE_CURRENCY, 12);
  6232. packet.WriteBit(weekCap != 0);
  6233. packet.WriteBit(0); // hasSeasonCount
  6234. packet.WriteBit(!printLog); // print in log
  6235. // if hasSeasonCount packet << uint32(seasontotalearned); TODO: save this in character DB and use it
  6236. packet << uint32(newTotalCount / precision);
  6237. packet << uint32(id);
  6238. if (weekCap)
  6239. packet << uint32(newWeekCount / precision);
  6240. GetSession()->SendPacket(&packet);
  6241. }
  6242. }
  6243. void Player::SetCurrency(uint32 id, uint32 count, bool /*printLog*/ /*= true*/)
  6244. {
  6245. PlayerCurrenciesMap::iterator itr = _currencyStorage.find(id);
  6246. if (itr == _currencyStorage.end())
  6247. {
  6248. PlayerCurrency cur;
  6249. cur.state = PLAYERCURRENCY_NEW;
  6250. cur.totalCount = count;
  6251. cur.weekCount = 0;
  6252. _currencyStorage[id] = cur;
  6253. }
  6254. }
  6255. uint32 Player::GetCurrencyWeekCap(uint32 id, bool usePrecision) const
  6256. {
  6257. CurrencyTypesEntry const* entry = sCurrencyTypesStore.LookupEntry(id);
  6258. if (!entry)
  6259. return 0;
  6260. uint32 precision = (usePrecision && entry->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
  6261. return GetCurrencyWeekCap(entry) / precision;
  6262. }
  6263. void Player::ResetCurrencyWeekCap()
  6264. {
  6265. for (uint32 arenaSlot = 0; arenaSlot < MAX_ARENA_SLOT; arenaSlot++)
  6266. {
  6267. if (uint32 arenaTeamId = GetArenaTeamId(arenaSlot))
  6268. {
  6269. ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
  6270. arenaTeam->FinishWeek(); // set played this week etc values to 0 in memory, too
  6271. arenaTeam->SaveToDB(); // save changes
  6272. arenaTeam->NotifyStatsChanged(); // notify the players of the changes
  6273. }
  6274. }
  6275. for (PlayerCurrenciesMap::iterator itr = _currencyStorage.begin(); itr != _currencyStorage.end(); ++itr)
  6276. {
  6277. itr->second.weekCount = 0;
  6278. itr->second.state = PLAYERCURRENCY_CHANGED;
  6279. }
  6280. WorldPacket data(SMSG_WEEKLY_RESET_CURRENCY, 0);
  6281. SendDirectMessage(&data);
  6282. }
  6283. uint32 Player::GetCurrencyWeekCap(CurrencyTypesEntry const* currency) const
  6284. {
  6285. switch (currency->ID)
  6286. {
  6287. //original conquest not have week cap
  6288. case CURRENCY_TYPE_CONQUEST_POINTS:
  6289. return std::max(GetCurrencyWeekCap(CURRENCY_TYPE_CONQUEST_META_ARENA, false), GetCurrencyWeekCap(CURRENCY_TYPE_CONQUEST_META_RBG, false));
  6290. case CURRENCY_TYPE_CONQUEST_META_ARENA:
  6291. // should add precision mod = 100
  6292. return Trinity::Currency::ConquestRatingCalculator(_maxPersonalArenaRate) * CURRENCY_PRECISION;
  6293. case CURRENCY_TYPE_CONQUEST_META_RBG:
  6294. // should add precision mod = 100
  6295. return Trinity::Currency::BgConquestRatingCalculator(GetRBGPersonalRating()) * CURRENCY_PRECISION;
  6296. }
  6297. return currency->WeekCap;
  6298. }
  6299. uint32 Player::GetCurrencyTotalCap(CurrencyTypesEntry const* currency) const
  6300. {
  6301. uint32 cap = currency->TotalCap;
  6302. switch (currency->ID)
  6303. {
  6304. case CURRENCY_TYPE_HONOR_POINTS:
  6305. {
  6306. uint32 honorcap = sWorld->getIntConfig(CONFIG_CURRENCY_MAX_HONOR_POINTS);
  6307. if (honorcap > 0)
  6308. cap = honorcap;
  6309. break;
  6310. }
  6311. case CURRENCY_TYPE_JUSTICE_POINTS:
  6312. {
  6313. uint32 justicecap = sWorld->getIntConfig(CONFIG_CURRENCY_MAX_JUSTICE_POINTS);
  6314. if (justicecap > 0)
  6315. cap = justicecap;
  6316. break;
  6317. }
  6318. }
  6319. return cap;
  6320. }
  6321. void Player::UpdateConquestCurrencyCap(uint32 currency)
  6322. {
  6323. uint32 currenciesToUpdate[2] = { currency, CURRENCY_TYPE_CONQUEST_POINTS };
  6324. for (uint32 i = 0; i < 2; ++i)
  6325. {
  6326. CurrencyTypesEntry const* currencyEntry = sCurrencyTypesStore.LookupEntry(currenciesToUpdate[i]);
  6327. if (!currencyEntry)
  6328. continue;
  6329. uint32 precision = (currencyEntry->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? 100 : 1;
  6330. uint32 cap = GetCurrencyWeekCap(currencyEntry);
  6331. WorldPacket packet(SMSG_UPDATE_CURRENCY_WEEK_LIMIT, 8);
  6332. packet << uint32(cap / precision);
  6333. packet << uint32(currenciesToUpdate[i]);
  6334. GetSession()->SendPacket(&packet);
  6335. }
  6336. }
  6337. void Player::SetInGuild(uint32 guildId)
  6338. {
  6339. if (guildId)
  6340. SetGuidValue(OBJECT_FIELD_DATA, ObjectGuid(HIGHGUID_GUILD, guildId));
  6341. else
  6342. SetGuidValue(OBJECT_FIELD_DATA, ObjectGuid::Empty);
  6343. ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_GUILD_LEVEL_ENABLED, guildId != 0 && sWorld->getBoolConfig(CONFIG_GUILD_LEVELING_ENABLED));
  6344. SetUInt16Value(OBJECT_FIELD_TYPE, 1, guildId != 0);
  6345. }
  6346. uint32 Player::GetGuildIdFromDB(ObjectGuid guid)
  6347. {
  6348. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_MEMBER);
  6349. stmt->setUInt32(0, guid.GetCounter());
  6350. if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
  6351. return result->Fetch()[0].GetUInt32();
  6352. return 0;
  6353. }
  6354. uint8 Player::GetRankFromDB(ObjectGuid guid)
  6355. {
  6356. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_MEMBER);
  6357. stmt->setUInt32(0, guid.GetCounter());
  6358. if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
  6359. return result->Fetch()[1].GetUInt8();
  6360. return 0;
  6361. }
  6362. void Player::SetArenaTeamInfoField(uint8 slot, ArenaTeamInfoType type, uint32 value)
  6363. {
  6364. SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * ARENA_TEAM_END) + type, value);
  6365. if (type == ARENA_TEAM_PERSONAL_RATING && value > _maxPersonalArenaRate)
  6366. {
  6367. _maxPersonalArenaRate = value;
  6368. UpdateConquestCurrencyCap(CURRENCY_TYPE_CONQUEST_META_ARENA);
  6369. }
  6370. }
  6371. void Player::SetInArenaTeam(uint32 ArenaTeamId, uint8 slot, uint8 type)
  6372. {
  6373. SetArenaTeamInfoField(slot, ARENA_TEAM_ID, ArenaTeamId);
  6374. SetArenaTeamInfoField(slot, ARENA_TEAM_TYPE, type);
  6375. }
  6376. uint32 Player::GetArenaTeamIdFromDB(ObjectGuid guid, uint8 type)
  6377. {
  6378. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ARENA_TEAM_ID_BY_PLAYER_GUID);
  6379. stmt->setUInt32(0, guid.GetCounter());
  6380. stmt->setUInt8(1, type);
  6381. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  6382. if (!result)
  6383. return 0;
  6384. uint32 id = (*result)[0].GetUInt32();
  6385. return id;
  6386. }
  6387. uint32 Player::GetZoneIdFromDB(ObjectGuid guid)
  6388. {
  6389. uint32 guidLow = guid.GetCounter();
  6390. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_ZONE);
  6391. stmt->setUInt32(0, guidLow);
  6392. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  6393. if (!result)
  6394. return 0;
  6395. Field* fields = result->Fetch();
  6396. uint32 zone = fields[0].GetUInt16();
  6397. if (!zone)
  6398. {
  6399. // stored zone is zero, use generic and slow zone detection
  6400. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_POSITION_XYZ);
  6401. stmt->setUInt32(0, guidLow);
  6402. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  6403. if (!result)
  6404. return 0;
  6405. fields = result->Fetch();
  6406. uint32 map = fields[0].GetUInt16();
  6407. float posx = fields[1].GetFloat();
  6408. float posy = fields[2].GetFloat();
  6409. float posz = fields[3].GetFloat();
  6410. if (!sMapStore.LookupEntry(map))
  6411. return 0;
  6412. zone = sMapMgr->GetZoneId(map, posx, posy, posz);
  6413. if (zone > 0)
  6414. {
  6415. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ZONE);
  6416. stmt->setUInt16(0, uint16(zone));
  6417. stmt->setUInt32(1, guidLow);
  6418. CharacterDatabase.Execute(stmt);
  6419. }
  6420. }
  6421. return zone;
  6422. }
  6423. uint32 Player::GetLevelFromDB(ObjectGuid guid)
  6424. {
  6425. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_LEVEL);
  6426. stmt->setUInt32(0, guid.GetCounter());
  6427. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  6428. if (!result)
  6429. return 0;
  6430. Field* fields = result->Fetch();
  6431. uint8 level = fields[0].GetUInt8();
  6432. return level;
  6433. }
  6434. void Player::UpdateArea(uint32 newArea)
  6435. {
  6436. // FFA_PVP flags are area and not zone id dependent
  6437. // so apply them accordingly
  6438. m_areaUpdateId = newArea;
  6439. AreaTableEntry const* area = GetAreaEntryByAreaID(newArea);
  6440. pvpInfo.IsInFFAPvPArea = area && (area->flags & AREA_FLAG_ARENA);
  6441. UpdatePvPState(true);
  6442. UpdateAreaDependentAuras(newArea);
  6443. // previously this was in UpdateZone (but after UpdateArea) so nothing will break
  6444. pvpInfo.IsInNoPvPArea = false;
  6445. if (area && area->IsSanctuary()) // in sanctuary
  6446. {
  6447. SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
  6448. pvpInfo.IsInNoPvPArea = true;
  6449. if (!duel)
  6450. CombatStopWithPets();
  6451. }
  6452. else
  6453. RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
  6454. uint32 const areaRestFlag = (GetTeam() == ALLIANCE) ? AREA_FLAG_REST_ZONE_ALLIANCE : AREA_FLAG_REST_ZONE_HORDE;
  6455. if (area && area->flags & areaRestFlag)
  6456. {
  6457. SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
  6458. SetRestType(REST_TYPE_IN_FACTION_AREA);
  6459. InnEnter(time(0), GetMapId(), 0, 0, 0);
  6460. }
  6461. else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) && GetRestType() == REST_TYPE_IN_FACTION_AREA)
  6462. {
  6463. RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
  6464. SetRestType(REST_TYPE_NO);
  6465. }
  6466. }
  6467. void Player::UpdateZone(uint32 newZone, uint32 newArea)
  6468. {
  6469. if (m_zoneUpdateId != newZone)
  6470. {
  6471. sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
  6472. sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone);
  6473. sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
  6474. sBattlefieldMgr->HandlePlayerEnterZone(this, newZone);
  6475. SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange...
  6476. if (Guild* guild = GetGuild())
  6477. guild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone);
  6478. }
  6479. // group update
  6480. if (GetGroup())
  6481. SetGroupUpdateFlag(GROUP_UPDATE_FULL);
  6482. m_zoneUpdateId = newZone;
  6483. m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
  6484. // zone changed, so area changed as well, update it
  6485. UpdateArea(newArea);
  6486. AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone);
  6487. if (!zone)
  6488. return;
  6489. if (sWorld->getBoolConfig(CONFIG_WEATHER) && !HasAuraType(SPELL_AURA_FORCE_WEATHER))
  6490. {
  6491. if (Weather* weather = WeatherMgr::FindWeather(zone->ID))
  6492. weather->SendWeatherUpdateToPlayer(this);
  6493. else if (!WeatherMgr::AddWeather(zone->ID))
  6494. // send fine weather packet to remove old zone's weather
  6495. WeatherMgr::SendFineWeatherUpdateToPlayer(this);
  6496. }
  6497. sScriptMgr->OnPlayerUpdateZone(this, newZone, newArea);
  6498. // in PvP, any not controlled zone (except zone->team == 6, default case)
  6499. // in PvE, only opposition team capital
  6500. switch (zone->team)
  6501. {
  6502. case AREATEAM_ALLY:
  6503. pvpInfo.IsInHostileArea = GetTeam() != ALLIANCE && (sWorld->IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
  6504. break;
  6505. case AREATEAM_HORDE:
  6506. pvpInfo.IsInHostileArea = GetTeam() != HORDE && (sWorld->IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
  6507. break;
  6508. case AREATEAM_NONE:
  6509. // overwrite for battlegrounds, maybe batter some zone flags but current known not 100% fit to this
  6510. pvpInfo.IsInHostileArea = sWorld->IsPvPRealm() || InBattleground() || zone->flags & AREA_FLAG_WINTERGRASP;
  6511. break;
  6512. default: // 6 in fact
  6513. pvpInfo.IsInHostileArea = false;
  6514. break;
  6515. }
  6516. // Treat players having a quest flagging for PvP as always in hostile area
  6517. pvpInfo.IsHostile = pvpInfo.IsInHostileArea || HasPvPForcingQuest();
  6518. if (zone->flags & AREA_FLAG_CAPITAL) // Is in a capital city
  6519. {
  6520. if (!pvpInfo.IsHostile || zone->IsSanctuary())
  6521. {
  6522. SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
  6523. SetRestType(REST_TYPE_IN_CITY);
  6524. InnEnter(time(0), GetMapId(), 0, 0, 0);
  6525. }
  6526. pvpInfo.IsInNoPvPArea = true;
  6527. }
  6528. else
  6529. {
  6530. if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
  6531. {
  6532. if (GetRestType() == REST_TYPE_IN_TAVERN) // Still inside a tavern or has recently left
  6533. {
  6534. // Remove rest state if we have recently left a tavern.
  6535. if (GetMapId() != GetInnPosMapId() || GetExactDist(GetInnPosX(), GetInnPosY(), GetInnPosZ()) > 1.0f)
  6536. {
  6537. RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
  6538. SetRestType(REST_TYPE_NO);
  6539. }
  6540. }
  6541. else if (GetRestType() != REST_TYPE_IN_FACTION_AREA) // handled in UpdateArea
  6542. {
  6543. // Recently left a capital city
  6544. RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
  6545. SetRestType(REST_TYPE_NO);
  6546. }
  6547. }
  6548. }
  6549. UpdatePvPState();
  6550. // remove items with area/map limitations (delete only for alive player to allow back in ghost mode)
  6551. // if player resurrected at teleport this will be applied in resurrect code
  6552. if (IsAlive())
  6553. DestroyZoneLimitedItem(true, newZone);
  6554. // check some item equip limitations (in result lost CanTitanGrip at talent reset, for example)
  6555. AutoUnequipOffhandIfNeed();
  6556. // recent client version not send leave/join channel packets for built-in local channels
  6557. UpdateLocalChannels(newZone);
  6558. UpdateZoneDependentAuras(newZone);
  6559. }
  6560. //If players are too far away from the duel flag... they lose the duel
  6561. void Player::CheckDuelDistance(time_t currTime)
  6562. {
  6563. if (!duel)
  6564. return;
  6565. ObjectGuid duelFlagGUID = GetGuidValue(PLAYER_DUEL_ARBITER);
  6566. GameObject* obj = GetMap()->GetGameObject(duelFlagGUID);
  6567. if (!obj)
  6568. return;
  6569. if (duel->outOfBound == 0)
  6570. {
  6571. if (!IsWithinDistInMap(obj, 50))
  6572. {
  6573. duel->outOfBound = currTime;
  6574. WorldPacket data(SMSG_DUEL_OUTOFBOUNDS, 0);
  6575. GetSession()->SendPacket(&data);
  6576. }
  6577. }
  6578. else
  6579. {
  6580. if (IsWithinDistInMap(obj, 40))
  6581. {
  6582. duel->outOfBound = 0;
  6583. WorldPacket data(SMSG_DUEL_INBOUNDS, 0);
  6584. GetSession()->SendPacket(&data);
  6585. }
  6586. else if (currTime >= (duel->outOfBound+10))
  6587. DuelComplete(DUEL_FLED);
  6588. }
  6589. }
  6590. bool Player::IsOutdoorPvPActive()
  6591. {
  6592. return IsAlive() && !HasInvisibilityAura() && !HasStealthAura() && IsPvP() && !HasUnitMovementFlag(MOVEMENTFLAG_FLYING) && !IsInFlight();
  6593. }
  6594. void Player::DuelComplete(DuelCompleteType type)
  6595. {
  6596. // duel not requested
  6597. if (!duel)
  6598. return;
  6599. TC_LOG_DEBUG("entities.unit", "Duel Complete %s %s", GetName().c_str(), duel->opponent->GetName().c_str());
  6600. WorldPacket data(SMSG_DUEL_COMPLETE, (1));
  6601. data << (uint8)((type != DUEL_INTERRUPTED) ? 1 : 0);
  6602. GetSession()->SendPacket(&data);
  6603. if (duel->opponent->GetSession())
  6604. duel->opponent->GetSession()->SendPacket(&data);
  6605. if (type != DUEL_INTERRUPTED)
  6606. {
  6607. data.Initialize(SMSG_DUEL_WINNER, (1+20)); // we guess size
  6608. data << uint8(type == DUEL_WON ? 0 : 1); // 0 = just won; 1 = fled
  6609. data << duel->opponent->GetName();
  6610. data << GetName();
  6611. SendMessageToSet(&data, true);
  6612. }
  6613. sScriptMgr->OnPlayerDuelEnd(duel->opponent, this, type);
  6614. switch (type)
  6615. {
  6616. case DUEL_FLED:
  6617. // if initiator and opponent are on the same team
  6618. // or initiator and opponent are not PvP enabled, forcibly stop attacking
  6619. if (duel->initiator->GetTeam() == duel->opponent->GetTeam())
  6620. {
  6621. duel->initiator->AttackStop();
  6622. duel->opponent->AttackStop();
  6623. }
  6624. else
  6625. {
  6626. if (!duel->initiator->IsPvP())
  6627. duel->initiator->AttackStop();
  6628. if (!duel->opponent->IsPvP())
  6629. duel->opponent->AttackStop();
  6630. }
  6631. break;
  6632. case DUEL_WON:
  6633. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL, 1);
  6634. duel->opponent->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1);
  6635. // Credit for quest Death's Challenge
  6636. if (getClass() == CLASS_DEATH_KNIGHT && duel->opponent->GetQuestStatus(12733) == QUEST_STATUS_INCOMPLETE)
  6637. duel->opponent->CastSpell(duel->opponent, 52994, true);
  6638. // Honor points after duel (the winner) - ImpConfig
  6639. if (uint32 amount = sWorld->getIntConfig(CONFIG_HONOR_AFTER_DUEL))
  6640. duel->opponent->RewardHonor(NULL, 1, amount);
  6641. break;
  6642. default:
  6643. break;
  6644. }
  6645. // Victory emote spell
  6646. if (type != DUEL_INTERRUPTED)
  6647. duel->opponent->CastSpell(duel->opponent, 52852, true);
  6648. //Remove Duel Flag object
  6649. GameObject* obj = GetMap()->GetGameObject(GetGuidValue(PLAYER_DUEL_ARBITER));
  6650. if (obj)
  6651. duel->initiator->RemoveGameObject(obj, true);
  6652. /* remove auras */
  6653. AuraApplicationMap &itsAuras = duel->opponent->GetAppliedAuras();
  6654. for (AuraApplicationMap::iterator i = itsAuras.begin(); i != itsAuras.end();)
  6655. {
  6656. Aura const* aura = i->second->GetBase();
  6657. if (!i->second->IsPositive() && aura->GetCasterGUID() == GetGUID() && aura->GetApplyTime() >= duel->startTime)
  6658. duel->opponent->RemoveAura(i);
  6659. else
  6660. ++i;
  6661. }
  6662. AuraApplicationMap &myAuras = GetAppliedAuras();
  6663. for (AuraApplicationMap::iterator i = myAuras.begin(); i != myAuras.end();)
  6664. {
  6665. Aura const* aura = i->second->GetBase();
  6666. if (!i->second->IsPositive() && aura->GetCasterGUID() == duel->opponent->GetGUID() && aura->GetApplyTime() >= duel->startTime)
  6667. RemoveAura(i);
  6668. else
  6669. ++i;
  6670. }
  6671. // cleanup combo points
  6672. if (GetComboTarget() == duel->opponent->GetGUID())
  6673. ClearComboPoints();
  6674. else if (GetComboTarget() == duel->opponent->GetPetGUID())
  6675. ClearComboPoints();
  6676. if (duel->opponent->GetComboTarget() == GetGUID())
  6677. duel->opponent->ClearComboPoints();
  6678. else if (duel->opponent->GetComboTarget() == GetPetGUID())
  6679. duel->opponent->ClearComboPoints();
  6680. //cleanups
  6681. SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid::Empty);
  6682. SetUInt32Value(PLAYER_DUEL_TEAM, 0);
  6683. duel->opponent->SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid::Empty);
  6684. duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0);
  6685. delete duel->opponent->duel;
  6686. duel->opponent->duel = NULL;
  6687. delete duel;
  6688. duel = NULL;
  6689. }
  6690. //---------------------------------------------------------//
  6691. void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply)
  6692. {
  6693. if (slot >= INVENTORY_SLOT_BAG_END || !item)
  6694. return;
  6695. ItemTemplate const* proto = item->GetTemplate();
  6696. if (!proto)
  6697. return;
  6698. // not apply/remove mods for broken item
  6699. if (item->IsBroken())
  6700. return;
  6701. TC_LOG_INFO("entities.player.items", "applying mods for item %u ", item->GetGUIDLow());
  6702. uint8 attacktype = Player::GetAttackBySlot(slot);
  6703. if (proto->Socket[0].Color) //only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items
  6704. CorrectMetaGemEnchants(slot, apply);
  6705. if (attacktype < MAX_ATTACK)
  6706. _ApplyWeaponDependentAuraMods(item, WeaponAttackType(attacktype), apply);
  6707. _ApplyItemBonuses(proto, slot, apply);
  6708. ApplyItemEquipSpell(item, apply);
  6709. ApplyEnchantment(item, apply);
  6710. TC_LOG_DEBUG("entities.player.items", "_ApplyItemMods complete.");
  6711. }
  6712. void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply, bool only_level_scale /*= false*/)
  6713. {
  6714. if (slot >= INVENTORY_SLOT_BAG_END || !proto)
  6715. return;
  6716. ScalingStatDistributionEntry const* ssd = proto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(proto->ScalingStatDistribution) : NULL;
  6717. if (only_level_scale && !ssd)
  6718. return;
  6719. // req. check at equip, but allow use for extended range if range limit max level, set proper level
  6720. uint32 ssd_level = getLevel();
  6721. if (ssd && ssd_level > ssd->MaxLevel)
  6722. ssd_level = ssd->MaxLevel;
  6723. ScalingStatValuesEntry const* ssv = ssd ? sScalingStatValuesStore.LookupEntry(ssd_level) : NULL;
  6724. if (only_level_scale && !ssv)
  6725. return;
  6726. for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
  6727. {
  6728. uint32 statType = 0;
  6729. int32 val = 0;
  6730. // If set ScalingStatDistribution need get stats and values from it
  6731. if (ssd && ssv)
  6732. {
  6733. if (ssd->StatMod[i] < 0)
  6734. continue;
  6735. statType = ssd->StatMod[i];
  6736. val = (ssv->GetStatMultiplier(proto->InventoryType) * ssd->Modifier[i]) / 10000;
  6737. }
  6738. else
  6739. {
  6740. statType = proto->ItemStat[i].ItemStatType;
  6741. val = proto->ItemStat[i].ItemStatValue;
  6742. }
  6743. if (val == 0)
  6744. continue;
  6745. switch (statType)
  6746. {
  6747. case ITEM_MOD_MANA:
  6748. HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply);
  6749. break;
  6750. case ITEM_MOD_HEALTH: // modify HP
  6751. HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply);
  6752. break;
  6753. case ITEM_MOD_AGILITY: // modify agility
  6754. HandleStatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply);
  6755. ApplyStatBuffMod(STAT_AGILITY, float(val), apply);
  6756. break;
  6757. case ITEM_MOD_STRENGTH: //modify strength
  6758. HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply);
  6759. ApplyStatBuffMod(STAT_STRENGTH, float(val), apply);
  6760. break;
  6761. case ITEM_MOD_INTELLECT: //modify intellect
  6762. HandleStatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply);
  6763. ApplyStatBuffMod(STAT_INTELLECT, float(val), apply);
  6764. break;
  6765. case ITEM_MOD_SPIRIT: //modify spirit
  6766. HandleStatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply);
  6767. ApplyStatBuffMod(STAT_SPIRIT, float(val), apply);
  6768. break;
  6769. case ITEM_MOD_STAMINA: //modify stamina
  6770. HandleStatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply);
  6771. ApplyStatBuffMod(STAT_STAMINA, float(val), apply);
  6772. break;
  6773. case ITEM_MOD_DEFENSE_SKILL_RATING:
  6774. ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply);
  6775. break;
  6776. case ITEM_MOD_DODGE_RATING:
  6777. ApplyRatingMod(CR_DODGE, int32(val), apply);
  6778. break;
  6779. case ITEM_MOD_PARRY_RATING:
  6780. ApplyRatingMod(CR_PARRY, int32(val), apply);
  6781. break;
  6782. case ITEM_MOD_BLOCK_RATING:
  6783. ApplyRatingMod(CR_BLOCK, int32(val), apply);
  6784. break;
  6785. case ITEM_MOD_HIT_MELEE_RATING:
  6786. ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
  6787. break;
  6788. case ITEM_MOD_HIT_RANGED_RATING:
  6789. ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
  6790. break;
  6791. case ITEM_MOD_HIT_SPELL_RATING:
  6792. ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
  6793. break;
  6794. case ITEM_MOD_CRIT_MELEE_RATING:
  6795. ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
  6796. break;
  6797. case ITEM_MOD_CRIT_RANGED_RATING:
  6798. ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
  6799. break;
  6800. case ITEM_MOD_CRIT_SPELL_RATING:
  6801. ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
  6802. break;
  6803. // case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
  6804. // ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
  6805. // break;
  6806. // case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
  6807. // ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
  6808. // break;
  6809. // case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
  6810. // ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
  6811. // break;
  6812. // case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
  6813. // ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
  6814. // break;
  6815. case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
  6816. ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE_TAKEN, int32(val), apply);
  6817. break;
  6818. // case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
  6819. // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
  6820. // break;
  6821. case ITEM_MOD_HASTE_MELEE_RATING:
  6822. ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
  6823. break;
  6824. case ITEM_MOD_HASTE_RANGED_RATING:
  6825. ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
  6826. break;
  6827. case ITEM_MOD_HASTE_SPELL_RATING:
  6828. ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
  6829. break;
  6830. case ITEM_MOD_HIT_RATING:
  6831. ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
  6832. ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
  6833. ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
  6834. break;
  6835. case ITEM_MOD_CRIT_RATING:
  6836. ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
  6837. ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
  6838. ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
  6839. break;
  6840. // case ITEM_MOD_HIT_TAKEN_RATING: // Unused since 3.3.5
  6841. // ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
  6842. // ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
  6843. // ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
  6844. // break;
  6845. // case ITEM_MOD_CRIT_TAKEN_RATING: // Unused since 3.3.5
  6846. // ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
  6847. // ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
  6848. // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
  6849. // break;
  6850. case ITEM_MOD_RESILIENCE_RATING:
  6851. ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE_TAKEN, int32(val), apply);
  6852. break;
  6853. case ITEM_MOD_HASTE_RATING:
  6854. ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
  6855. ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
  6856. ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
  6857. break;
  6858. case ITEM_MOD_EXPERTISE_RATING:
  6859. ApplyRatingMod(CR_EXPERTISE, int32(val), apply);
  6860. break;
  6861. case ITEM_MOD_ATTACK_POWER:
  6862. HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(val), apply);
  6863. HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply);
  6864. break;
  6865. case ITEM_MOD_RANGED_ATTACK_POWER:
  6866. HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply);
  6867. break;
  6868. case ITEM_MOD_MANA_REGENERATION:
  6869. ApplyManaRegenBonus(int32(val), apply);
  6870. break;
  6871. case ITEM_MOD_ARMOR_PENETRATION_RATING:
  6872. ApplyRatingMod(CR_ARMOR_PENETRATION, int32(val), apply);
  6873. break;
  6874. case ITEM_MOD_SPELL_POWER:
  6875. ApplySpellPowerBonus(int32(val), apply);
  6876. break;
  6877. case ITEM_MOD_HEALTH_REGEN:
  6878. ApplyHealthRegenBonus(int32(val), apply);
  6879. break;
  6880. case ITEM_MOD_SPELL_PENETRATION:
  6881. ApplySpellPenetrationBonus(val, apply);
  6882. break;
  6883. case ITEM_MOD_MASTERY_RATING:
  6884. ApplyRatingMod(CR_MASTERY, int32(val), apply);
  6885. break;
  6886. case ITEM_MOD_FIRE_RESISTANCE:
  6887. HandleStatModifier(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(val), apply);
  6888. break;
  6889. case ITEM_MOD_FROST_RESISTANCE:
  6890. HandleStatModifier(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(val), apply);
  6891. break;
  6892. case ITEM_MOD_HOLY_RESISTANCE:
  6893. HandleStatModifier(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(val), apply);
  6894. break;
  6895. case ITEM_MOD_SHADOW_RESISTANCE:
  6896. HandleStatModifier(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(val), apply);
  6897. break;
  6898. case ITEM_MOD_NATURE_RESISTANCE:
  6899. HandleStatModifier(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(val), apply);
  6900. break;
  6901. case ITEM_MOD_ARCANE_RESISTANCE:
  6902. HandleStatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(val), apply);
  6903. break;
  6904. }
  6905. }
  6906. // Apply Spell Power from ScalingStatValue if set
  6907. if (ssv && proto->Flags2 & ITEM_FLAGS_EXTRA_CASTER_WEAPON)
  6908. if (int32 spellbonus = int32(ssv->Spellpower))
  6909. ApplySpellPowerBonus(spellbonus, apply);
  6910. // If set ScalingStatValue armor get it or use item armor
  6911. uint32 armor = proto->Armor;
  6912. if (ssv && proto->Class == ITEM_CLASS_ARMOR)
  6913. armor = ssv->GetArmor(proto->InventoryType, proto->SubClass - 1);
  6914. else if (armor && proto->ArmorDamageModifier)
  6915. armor -= uint32(proto->ArmorDamageModifier);
  6916. if (armor)
  6917. {
  6918. UnitModifierType modType = TOTAL_VALUE;
  6919. if (proto->Class == ITEM_CLASS_ARMOR)
  6920. {
  6921. switch (proto->SubClass)
  6922. {
  6923. case ITEM_SUBCLASS_ARMOR_CLOTH:
  6924. case ITEM_SUBCLASS_ARMOR_LEATHER:
  6925. case ITEM_SUBCLASS_ARMOR_MAIL:
  6926. case ITEM_SUBCLASS_ARMOR_PLATE:
  6927. case ITEM_SUBCLASS_ARMOR_SHIELD:
  6928. modType = BASE_VALUE;
  6929. break;
  6930. }
  6931. }
  6932. HandleStatModifier(UNIT_MOD_ARMOR, modType, float(armor), apply);
  6933. }
  6934. // Add armor bonus from ArmorDamageModifier if > 0
  6935. if (proto->ArmorDamageModifier > 0)
  6936. HandleStatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE, float(proto->ArmorDamageModifier), apply);
  6937. WeaponAttackType attType = BASE_ATTACK;
  6938. if (slot == EQUIPMENT_SLOT_RANGED && (
  6939. proto->InventoryType == INVTYPE_RANGED || proto->InventoryType == INVTYPE_THROWN ||
  6940. proto->InventoryType == INVTYPE_RANGEDRIGHT))
  6941. {
  6942. attType = RANGED_ATTACK;
  6943. }
  6944. else if (slot == EQUIPMENT_SLOT_OFFHAND)
  6945. {
  6946. attType = OFF_ATTACK;
  6947. }
  6948. if (CanUseAttackType(attType))
  6949. _ApplyWeaponDamage(slot, proto, ssv, apply);
  6950. }
  6951. void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingStatValuesEntry const* ssv, bool apply)
  6952. {
  6953. WeaponAttackType attType = BASE_ATTACK;
  6954. float damage = 0.0f;
  6955. if (slot == EQUIPMENT_SLOT_RANGED && (
  6956. proto->InventoryType == INVTYPE_RANGED || proto->InventoryType == INVTYPE_THROWN ||
  6957. proto->InventoryType == INVTYPE_RANGEDRIGHT))
  6958. {
  6959. attType = RANGED_ATTACK;
  6960. }
  6961. else if (slot == EQUIPMENT_SLOT_OFFHAND)
  6962. {
  6963. attType = OFF_ATTACK;
  6964. }
  6965. float minDamage = proto->DamageMin;
  6966. float maxDamage = proto->DamageMax;
  6967. // If set dpsMod in ScalingStatValue use it for min (70% from average), max (130% from average) damage
  6968. int32 extraDPS = 0;
  6969. if (ssv)
  6970. {
  6971. float damageMultiplier = 0.0f;
  6972. extraDPS = ssv->GetDPSAndDamageMultiplier(proto->SubClass, (proto->Flags2 & ITEM_FLAGS_EXTRA_CASTER_WEAPON) != 0, &damageMultiplier);
  6973. if (extraDPS)
  6974. {
  6975. float average = extraDPS * proto->Delay / 1000.0f;
  6976. minDamage = (1.0f - damageMultiplier) * average;
  6977. maxDamage = (1.0f + damageMultiplier) * average;
  6978. }
  6979. }
  6980. if (minDamage > 0)
  6981. {
  6982. damage = apply ? minDamage : BASE_MINDAMAGE;
  6983. SetBaseWeaponDamage(attType, MINDAMAGE, damage);
  6984. }
  6985. if (maxDamage > 0)
  6986. {
  6987. damage = apply ? maxDamage : BASE_MAXDAMAGE;
  6988. SetBaseWeaponDamage(attType, MAXDAMAGE, damage);
  6989. }
  6990. if (proto->Delay && !IsInFeralForm())
  6991. {
  6992. if (slot == EQUIPMENT_SLOT_RANGED)
  6993. SetAttackTime(RANGED_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
  6994. else if (slot == EQUIPMENT_SLOT_MAINHAND)
  6995. SetAttackTime(BASE_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
  6996. else if (slot == EQUIPMENT_SLOT_OFFHAND)
  6997. SetAttackTime(OFF_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
  6998. }
  6999. if (CanModifyStats() && (damage || proto->Delay))
  7000. UpdateDamagePhysical(attType);
  7001. }
  7002. void Player::_ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply)
  7003. {
  7004. AuraEffectList const& auraCritList = GetAuraEffectsByType(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT);
  7005. for (AuraEffectList::const_iterator itr = auraCritList.begin(); itr != auraCritList.end(); ++itr)
  7006. _ApplyWeaponDependentAuraCritMod(item, attackType, *itr, apply);
  7007. AuraEffectList const& auraDamageFlatList = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE);
  7008. for (AuraEffectList::const_iterator itr = auraDamageFlatList.begin(); itr != auraDamageFlatList.end(); ++itr)
  7009. _ApplyWeaponDependentAuraDamageMod(item, attackType, *itr, apply);
  7010. AuraEffectList const& auraDamagePctList = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
  7011. for (AuraEffectList::const_iterator itr = auraDamagePctList.begin(); itr != auraDamagePctList.end(); ++itr)
  7012. _ApplyWeaponDependentAuraDamageMod(item, attackType, *itr, apply);
  7013. }
  7014. void Player::_ApplyWeaponDependentAuraCritMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply)
  7015. {
  7016. // don't apply mod if item is broken or cannot be used
  7017. if (item->IsBroken() || !CanUseAttackType(attackType))
  7018. return;
  7019. // generic not weapon specific case processes in aura code
  7020. if (aura->GetSpellInfo()->EquippedItemClass == -1)
  7021. return;
  7022. BaseModGroup mod = BASEMOD_END;
  7023. switch (attackType)
  7024. {
  7025. case BASE_ATTACK: mod = CRIT_PERCENTAGE; break;
  7026. case OFF_ATTACK: mod = OFFHAND_CRIT_PERCENTAGE;break;
  7027. case RANGED_ATTACK: mod = RANGED_CRIT_PERCENTAGE; break;
  7028. default: return;
  7029. }
  7030. if (item->IsFitToSpellRequirements(aura->GetSpellInfo()))
  7031. HandleBaseModValue(mod, FLAT_MOD, float (aura->GetAmount()), apply);
  7032. }
  7033. void Player::_ApplyWeaponDependentAuraDamageMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply)
  7034. {
  7035. // don't apply mod if item is broken or cannot be used
  7036. if (item->IsBroken() || !CanUseAttackType(attackType))
  7037. return;
  7038. // ignore spell mods for not wands
  7039. if ((aura->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) == 0 && (getClassMask() & CLASSMASK_WAND_USERS) == 0)
  7040. return;
  7041. // generic not weapon specific case processes in aura code
  7042. if (aura->GetSpellInfo()->EquippedItemClass == -1)
  7043. return;
  7044. UnitMods unitMod = UNIT_MOD_END;
  7045. switch (attackType)
  7046. {
  7047. case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break;
  7048. case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break;
  7049. case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break;
  7050. default: return;
  7051. }
  7052. UnitModifierType unitModType = TOTAL_VALUE;
  7053. switch (aura->GetAuraType())
  7054. {
  7055. case SPELL_AURA_MOD_DAMAGE_DONE: unitModType = TOTAL_VALUE; break;
  7056. case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: unitModType = TOTAL_PCT; break;
  7057. default: return;
  7058. }
  7059. if (item->IsFitToSpellRequirements(aura->GetSpellInfo()))
  7060. {
  7061. HandleStatModifier(unitMod, unitModType, float(aura->GetAmount()), apply);
  7062. if (unitModType == TOTAL_VALUE)
  7063. {
  7064. if (aura->GetAmount() > 0)
  7065. ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS, aura->GetAmount(), apply);
  7066. else
  7067. ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG, aura->GetAmount(), apply);
  7068. }
  7069. }
  7070. }
  7071. void Player::ApplyItemEquipSpell(Item* item, bool apply, bool form_change)
  7072. {
  7073. if (!item)
  7074. return;
  7075. ItemTemplate const* proto = item->GetTemplate();
  7076. if (!proto)
  7077. return;
  7078. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  7079. {
  7080. _Spell const& spellData = proto->Spells[i];
  7081. // no spell
  7082. if (!spellData.SpellId)
  7083. continue;
  7084. // wrong triggering type
  7085. if (apply && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP)
  7086. continue;
  7087. // check if it is valid spell
  7088. SpellInfo const* spellproto = sSpellMgr->GetSpellInfo(spellData.SpellId);
  7089. if (!spellproto)
  7090. continue;
  7091. ApplyEquipSpell(spellproto, item, apply, form_change);
  7092. }
  7093. }
  7094. void Player::ApplyEquipSpell(SpellInfo const* spellInfo, Item* item, bool apply, bool form_change)
  7095. {
  7096. if (apply)
  7097. {
  7098. // Cannot be used in this stance/form
  7099. if (spellInfo->CheckShapeshift(GetShapeshiftForm()) != SPELL_CAST_OK)
  7100. return;
  7101. if (form_change) // check aura active state from other form
  7102. {
  7103. AuraApplicationMapBounds range = GetAppliedAuras().equal_range(spellInfo->Id);
  7104. for (AuraApplicationMap::const_iterator itr = range.first; itr != range.second; ++itr)
  7105. if (!item || itr->second->GetBase()->GetCastItemGUID() == item->GetGUID())
  7106. return;
  7107. }
  7108. TC_LOG_DEBUG("entities.player", "WORLD: cast %s Equip spellId - %i", (item ? "item" : "itemset"), spellInfo->Id);
  7109. CastSpell(this, spellInfo, true, item);
  7110. }
  7111. else
  7112. {
  7113. if (form_change) // check aura compatibility
  7114. {
  7115. // Cannot be used in this stance/form
  7116. if (spellInfo->CheckShapeshift(GetShapeshiftForm()) == SPELL_CAST_OK)
  7117. return; // and remove only not compatible at form change
  7118. }
  7119. if (item)
  7120. RemoveAurasDueToItemSpell(spellInfo->Id, item->GetGUID()); // un-apply all spells, not only at-equipped
  7121. else
  7122. RemoveAurasDueToSpell(spellInfo->Id); // un-apply spell (item set case)
  7123. }
  7124. }
  7125. void Player::UpdateEquipSpellsAtFormChange()
  7126. {
  7127. for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
  7128. {
  7129. if (m_items[i] && !m_items[i]->IsBroken() && CanUseAttackType(GetAttackBySlot(i)))
  7130. {
  7131. ApplyItemEquipSpell(m_items[i], false, true); // remove spells that not fit to form
  7132. ApplyItemEquipSpell(m_items[i], true, true); // add spells that fit form but not active
  7133. }
  7134. }
  7135. // item set bonuses not dependent from item broken state
  7136. for (size_t setindex = 0; setindex < ItemSetEff.size(); ++setindex)
  7137. {
  7138. ItemSetEffect* eff = ItemSetEff[setindex];
  7139. if (!eff)
  7140. continue;
  7141. for (uint32 y = 0; y < MAX_ITEM_SET_SPELLS; ++y)
  7142. {
  7143. SpellInfo const* spellInfo = eff->spells[y];
  7144. if (!spellInfo)
  7145. continue;
  7146. ApplyEquipSpell(spellInfo, NULL, false, true); // remove spells that not fit to form
  7147. ApplyEquipSpell(spellInfo, NULL, true, true); // add spells that fit form but not active
  7148. }
  7149. }
  7150. }
  7151. void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx)
  7152. {
  7153. if (!target || !target->IsAlive() || target == this)
  7154. return;
  7155. for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
  7156. {
  7157. // If usable, try to cast item spell
  7158. if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  7159. if (!item->IsBroken() && CanUseAttackType(attType))
  7160. if (ItemTemplate const* proto = item->GetTemplate())
  7161. {
  7162. // Additional check for weapons
  7163. if (proto->Class == ITEM_CLASS_WEAPON)
  7164. {
  7165. // offhand item cannot proc from main hand hit etc
  7166. EquipmentSlots slot;
  7167. switch (attType)
  7168. {
  7169. case BASE_ATTACK: slot = EQUIPMENT_SLOT_MAINHAND; break;
  7170. case OFF_ATTACK: slot = EQUIPMENT_SLOT_OFFHAND; break;
  7171. case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break;
  7172. default: slot = EQUIPMENT_SLOT_END; break;
  7173. }
  7174. if (slot != i)
  7175. continue;
  7176. // Check if item is useable (forms or disarm)
  7177. if (attType == BASE_ATTACK)
  7178. if (!IsUseEquipedWeapon(true) && !IsInFeralForm())
  7179. continue;
  7180. }
  7181. CastItemCombatSpell(target, attType, procVictim, procEx, item, proto);
  7182. }
  7183. }
  7184. }
  7185. void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item* item, ItemTemplate const* proto)
  7186. {
  7187. // Can do effect if any damage done to target
  7188. if (procVictim & PROC_FLAG_TAKEN_DAMAGE)
  7189. //if (damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE)
  7190. {
  7191. for (uint8 i = 0; i < MAX_ITEM_SPELLS; ++i)
  7192. {
  7193. _Spell const& spellData = proto->Spells[i];
  7194. // no spell
  7195. if (!spellData.SpellId)
  7196. continue;
  7197. // wrong triggering type
  7198. if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_CHANCE_ON_HIT)
  7199. continue;
  7200. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellData.SpellId);
  7201. if (!spellInfo)
  7202. {
  7203. TC_LOG_ERROR("entities.player.items", "WORLD: unknown Item spellid %i", spellData.SpellId);
  7204. continue;
  7205. }
  7206. // not allow proc extra attack spell at extra attack
  7207. if (m_extraAttacks && spellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
  7208. return;
  7209. float chance = (float)spellInfo->ProcChance;
  7210. if (proto->SpellPPMRate)
  7211. {
  7212. uint32 WeaponSpeed = GetAttackTime(attType);
  7213. chance = GetPPMProcChance(WeaponSpeed, proto->SpellPPMRate, spellInfo);
  7214. }
  7215. else if (chance > 100.0f)
  7216. chance = GetWeaponProcChance();
  7217. if (roll_chance_f(chance))
  7218. CastSpell(target, spellInfo->Id, true, item);
  7219. }
  7220. }
  7221. // item combat enchantments
  7222. for (uint8 e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot)
  7223. {
  7224. if (e_slot > PRISMATIC_ENCHANTMENT_SLOT && e_slot < PROP_ENCHANTMENT_SLOT_0) // not holding enchantment id
  7225. continue;
  7226. uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot));
  7227. SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  7228. if (!pEnchant)
  7229. continue;
  7230. for (uint8 s = 0; s < MAX_ITEM_ENCHANTMENT_EFFECTS; ++s)
  7231. {
  7232. if (pEnchant->type[s] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
  7233. continue;
  7234. SpellEnchantProcEntry const* entry = sSpellMgr->GetSpellEnchantProcEvent(enchant_id);
  7235. if (entry && entry->procEx)
  7236. {
  7237. // Check hit/crit/dodge/parry requirement
  7238. if ((entry->procEx & procEx) == 0)
  7239. continue;
  7240. }
  7241. else
  7242. {
  7243. // Can do effect if any damage done to target
  7244. if (!(procVictim & PROC_FLAG_TAKEN_DAMAGE))
  7245. //if (!(damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE))
  7246. continue;
  7247. }
  7248. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(pEnchant->spellid[s]);
  7249. if (!spellInfo)
  7250. {
  7251. TC_LOG_ERROR("entities.player.items", "Player::CastItemCombatSpell(GUID: %u, name: %s, enchant: %i): unknown spell %i is cast, ignoring...",
  7252. GetGUIDLow(), GetName().c_str(), pEnchant->ID, pEnchant->spellid[s]);
  7253. continue;
  7254. }
  7255. float chance = pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance();
  7256. if (entry)
  7257. {
  7258. if (entry->PPMChance)
  7259. chance = GetPPMProcChance(proto->Delay, entry->PPMChance, spellInfo);
  7260. else if (entry->customChance)
  7261. chance = (float)entry->customChance;
  7262. }
  7263. // Apply spell mods
  7264. ApplySpellMod(pEnchant->spellid[s], SPELLMOD_CHANCE_OF_SUCCESS, chance);
  7265. // Shiv has 100% chance to apply the poison
  7266. if (FindCurrentSpellBySpellId(5938) && e_slot == TEMP_ENCHANTMENT_SLOT)
  7267. chance = 100.0f;
  7268. if (roll_chance_f(chance))
  7269. {
  7270. if (spellInfo->IsPositive())
  7271. CastSpell(this, spellInfo, true, item);
  7272. else
  7273. CastSpell(target, spellInfo, true, item);
  7274. }
  7275. }
  7276. }
  7277. }
  7278. void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 cast_count, uint32 glyphIndex)
  7279. {
  7280. ItemTemplate const* proto = item->GetTemplate();
  7281. // special learning case
  7282. if (proto->Spells[0].SpellId == 483 || proto->Spells[0].SpellId == 55884)
  7283. {
  7284. uint32 learn_spell_id = proto->Spells[0].SpellId;
  7285. uint32 learning_spell_id = proto->Spells[1].SpellId;
  7286. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(learn_spell_id);
  7287. if (!spellInfo)
  7288. {
  7289. TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring ", proto->ItemId, learn_spell_id);
  7290. SendEquipError(EQUIP_ERR_INTERNAL_BAG_ERROR, item, NULL);
  7291. return;
  7292. }
  7293. Spell* spell = new Spell(this, spellInfo, TRIGGERED_NONE);
  7294. spell->m_CastItem = item;
  7295. spell->m_cast_count = cast_count; //set count of casts
  7296. spell->SetSpellValue(SPELLVALUE_BASE_POINT0, learning_spell_id);
  7297. spell->prepare(&targets);
  7298. return;
  7299. }
  7300. // use triggered flag only for items with many spell casts and for not first cast
  7301. uint8 count = 0;
  7302. // item spells cast at use
  7303. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  7304. {
  7305. _Spell const& spellData = proto->Spells[i];
  7306. // no spell
  7307. if (!spellData.SpellId)
  7308. continue;
  7309. // wrong triggering type
  7310. if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
  7311. continue;
  7312. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellData.SpellId);
  7313. if (!spellInfo)
  7314. {
  7315. TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring", proto->ItemId, spellData.SpellId);
  7316. continue;
  7317. }
  7318. Spell* spell = new Spell(this, spellInfo, (count > 0) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
  7319. spell->m_CastItem = item;
  7320. spell->m_cast_count = cast_count; // set count of casts
  7321. spell->m_glyphIndex = glyphIndex; // glyph index
  7322. spell->prepare(&targets);
  7323. ++count;
  7324. }
  7325. // Item enchantments spells cast at use
  7326. for (uint8 e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot)
  7327. {
  7328. if (e_slot > PRISMATIC_ENCHANTMENT_SLOT && e_slot < PROP_ENCHANTMENT_SLOT_0) // not holding enchantment id
  7329. continue;
  7330. uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot));
  7331. SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  7332. if (!pEnchant)
  7333. continue;
  7334. for (uint8 s = 0; s < MAX_ITEM_ENCHANTMENT_EFFECTS; ++s)
  7335. {
  7336. if (pEnchant->type[s] != ITEM_ENCHANTMENT_TYPE_USE_SPELL)
  7337. continue;
  7338. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(pEnchant->spellid[s]);
  7339. if (!spellInfo)
  7340. {
  7341. TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]);
  7342. continue;
  7343. }
  7344. Spell* spell = new Spell(this, spellInfo, (count > 0) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
  7345. spell->m_CastItem = item;
  7346. spell->m_cast_count = cast_count; // set count of casts
  7347. spell->m_glyphIndex = glyphIndex; // glyph index
  7348. spell->prepare(&targets);
  7349. ++count;
  7350. }
  7351. }
  7352. }
  7353. void Player::_RemoveAllItemMods()
  7354. {
  7355. TC_LOG_DEBUG("entities.player.items", "_RemoveAllItemMods start.");
  7356. for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
  7357. {
  7358. if (m_items[i])
  7359. {
  7360. ItemTemplate const* proto = m_items[i]->GetTemplate();
  7361. if (!proto)
  7362. continue;
  7363. // item set bonuses not dependent from item broken state
  7364. if (proto->ItemSet)
  7365. RemoveItemsSetItem(this, proto);
  7366. if (m_items[i]->IsBroken() || !CanUseAttackType(GetAttackBySlot(i)))
  7367. continue;
  7368. ApplyItemEquipSpell(m_items[i], false);
  7369. ApplyEnchantment(m_items[i], false);
  7370. }
  7371. }
  7372. for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
  7373. {
  7374. if (m_items[i])
  7375. {
  7376. if (m_items[i]->IsBroken() || !CanUseAttackType(GetAttackBySlot(i)))
  7377. continue;
  7378. ItemTemplate const* proto = m_items[i]->GetTemplate();
  7379. if (!proto)
  7380. continue;
  7381. uint32 attacktype = Player::GetAttackBySlot(i);
  7382. if (attacktype < MAX_ATTACK)
  7383. _ApplyWeaponDependentAuraMods(m_items[i], WeaponAttackType(attacktype), false);
  7384. _ApplyItemBonuses(proto, i, false);
  7385. }
  7386. }
  7387. TC_LOG_DEBUG("entities.player.items", "_RemoveAllItemMods complete.");
  7388. }
  7389. void Player::_ApplyAllItemMods()
  7390. {
  7391. TC_LOG_DEBUG("entities.player.items", "_ApplyAllItemMods start.");
  7392. for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
  7393. {
  7394. if (m_items[i])
  7395. {
  7396. if (m_items[i]->IsBroken() || !CanUseAttackType(GetAttackBySlot(i)))
  7397. continue;
  7398. ItemTemplate const* proto = m_items[i]->GetTemplate();
  7399. if (!proto)
  7400. continue;
  7401. uint32 attacktype = Player::GetAttackBySlot(i);
  7402. if (attacktype < MAX_ATTACK)
  7403. _ApplyWeaponDependentAuraMods(m_items[i], WeaponAttackType(attacktype), true);
  7404. _ApplyItemBonuses(proto, i, true);
  7405. }
  7406. }
  7407. for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
  7408. {
  7409. if (m_items[i])
  7410. {
  7411. ItemTemplate const* proto = m_items[i]->GetTemplate();
  7412. if (!proto)
  7413. continue;
  7414. // item set bonuses not dependent from item broken state
  7415. if (proto->ItemSet)
  7416. AddItemsSetItem(this, m_items[i]);
  7417. if (m_items[i]->IsBroken() || !CanUseAttackType(GetAttackBySlot(i)))
  7418. continue;
  7419. ApplyItemEquipSpell(m_items[i], true);
  7420. ApplyEnchantment(m_items[i], true);
  7421. }
  7422. }
  7423. TC_LOG_DEBUG("entities.player.items", "_ApplyAllItemMods complete.");
  7424. }
  7425. void Player::_ApplyAllLevelScaleItemMods(bool apply)
  7426. {
  7427. for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
  7428. {
  7429. if (m_items[i])
  7430. {
  7431. if (m_items[i]->IsBroken() || !CanUseAttackType(GetAttackBySlot(i)))
  7432. continue;
  7433. ItemTemplate const* proto = m_items[i]->GetTemplate();
  7434. if (!proto)
  7435. continue;
  7436. _ApplyItemBonuses(proto, i, apply, true);
  7437. }
  7438. }
  7439. }
  7440. /* If in a battleground a player dies, and an enemy removes the insignia, the player's bones is lootable
  7441. Called by remove insignia spell effect */
  7442. void Player::RemovedInsignia(Player* looterPlr)
  7443. {
  7444. if (!GetBattlegroundId())
  7445. return;
  7446. // If not released spirit, do it !
  7447. if (m_deathTimer > 0)
  7448. {
  7449. m_deathTimer = 0;
  7450. BuildPlayerRepop();
  7451. RepopAtGraveyard();
  7452. }
  7453. // We have to convert player corpse to bones, not to be able to resurrect there
  7454. // SpawnCorpseBones isn't handy, 'cos it saves player while he in BG
  7455. Corpse* bones = sObjectAccessor->ConvertCorpseForPlayer(GetGUID(), true);
  7456. if (!bones)
  7457. return;
  7458. // Now we must make bones lootable, and send player loot
  7459. bones->SetFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);
  7460. // We store the level of our player in the gold field
  7461. // We retrieve this information at Player::SendLoot()
  7462. bones->loot.gold = getLevel();
  7463. bones->lootRecipient = looterPlr;
  7464. looterPlr->SendLoot(bones->GetGUID(), LOOT_INSIGNIA);
  7465. }
  7466. void Player::SendLootRelease(ObjectGuid guid)
  7467. {
  7468. WorldPacket data(SMSG_LOOT_RELEASE_RESPONSE, (8+1));
  7469. data << uint64(guid) << uint8(1);
  7470. SendDirectMessage(&data);
  7471. }
  7472. void Player::SendLoot(ObjectGuid guid, LootType loot_type)
  7473. {
  7474. if (ObjectGuid lguid = GetLootGUID())
  7475. m_session->DoLootRelease(lguid);
  7476. Loot* loot = 0;
  7477. PermissionTypes permission = ALL_PERMISSION;
  7478. TC_LOG_DEBUG("loot", "Player::SendLoot");
  7479. if (guid.IsGameObject())
  7480. {
  7481. TC_LOG_DEBUG("loot", "IS_GAMEOBJECT_GUID(guid)");
  7482. GameObject* go = GetMap()->GetGameObject(guid);
  7483. // not check distance for GO in case owned GO (fishing bobber case, for example)
  7484. // And permit out of range GO with no owner in case fishing hole
  7485. if (!go || (loot_type != LOOT_FISHINGHOLE && ((loot_type != LOOT_FISHING && loot_type != LOOT_FISHING_JUNK) || go->GetOwnerGUID() != GetGUID()) && !go->IsWithinDistInMap(this, INTERACTION_DISTANCE)) || (loot_type == LOOT_CORPSE && go->GetRespawnTime() && go->isSpawnedByDefault()))
  7486. {
  7487. SendLootRelease(guid);
  7488. return;
  7489. }
  7490. loot = &go->loot;
  7491. if (go->getLootState() == GO_READY)
  7492. {
  7493. uint32 lootid = go->GetGOInfo()->GetLootId();
  7494. if (Battleground* bg = GetBattleground())
  7495. if (!bg->CanActivateGO(go->GetEntry(), GetTeam()))
  7496. {
  7497. SendLootRelease(guid);
  7498. return;
  7499. }
  7500. if (lootid)
  7501. {
  7502. loot->clear();
  7503. Group* group = GetGroup();
  7504. bool groupRules = (group && go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules);
  7505. // check current RR player and get next if necessary
  7506. if (groupRules)
  7507. group->UpdateLooterGuid(go, true);
  7508. loot->FillLoot(lootid, LootTemplates_Gameobject, this, !groupRules, false, go->GetLootMode());
  7509. // get next RR player (for next loot)
  7510. if (groupRules && !go->loot.empty())
  7511. group->UpdateLooterGuid(go);
  7512. }
  7513. if (loot_type == LOOT_FISHING)
  7514. go->getFishLoot(loot, this);
  7515. else if (loot_type == LOOT_FISHING_JUNK)
  7516. go->getFishLootJunk(loot, this);
  7517. if (go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules)
  7518. {
  7519. if (Group* group = GetGroup())
  7520. {
  7521. switch (group->GetLootMethod())
  7522. {
  7523. case GROUP_LOOT:
  7524. // GroupLoot: rolls items over threshold. Items with quality < threshold, round robin
  7525. group->GroupLoot(loot, go);
  7526. break;
  7527. case NEED_BEFORE_GREED:
  7528. group->NeedBeforeGreed(loot, go);
  7529. break;
  7530. case MASTER_LOOT:
  7531. group->MasterLoot(loot, go);
  7532. break;
  7533. default:
  7534. break;
  7535. }
  7536. }
  7537. }
  7538. go->SetLootState(GO_ACTIVATED, this);
  7539. }
  7540. if (go->getLootState() == GO_ACTIVATED)
  7541. {
  7542. if (Group* group = GetGroup())
  7543. {
  7544. switch (group->GetLootMethod())
  7545. {
  7546. case MASTER_LOOT:
  7547. permission = group->GetMasterLooterGuid() == GetGUID() ? MASTER_PERMISSION : RESTRICTED_PERMISSION;
  7548. break;
  7549. case FREE_FOR_ALL:
  7550. permission = ALL_PERMISSION;
  7551. break;
  7552. case ROUND_ROBIN:
  7553. permission = ROUND_ROBIN_PERMISSION;
  7554. break;
  7555. default:
  7556. permission = GROUP_PERMISSION;
  7557. break;
  7558. }
  7559. }
  7560. else
  7561. permission = ALL_PERMISSION;
  7562. }
  7563. }
  7564. else if (guid.IsItem())
  7565. {
  7566. Item* item = GetItemByGuid(guid);
  7567. if (!item)
  7568. {
  7569. SendLootRelease(guid);
  7570. return;
  7571. }
  7572. permission = OWNER_PERMISSION;
  7573. loot = &item->loot;
  7574. // If item doesn't already have loot, attempt to load it. If that
  7575. // fails then this is first time opening, generate loot
  7576. if (!item->m_lootGenerated && !item->ItemContainerLoadLootFromDB())
  7577. {
  7578. item->m_lootGenerated = true;
  7579. loot->clear();
  7580. switch (loot_type)
  7581. {
  7582. case LOOT_DISENCHANTING:
  7583. loot->FillLoot(item->GetTemplate()->DisenchantID, LootTemplates_Disenchant, this, true);
  7584. break;
  7585. case LOOT_PROSPECTING:
  7586. loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this, true);
  7587. break;
  7588. case LOOT_MILLING:
  7589. loot->FillLoot(item->GetEntry(), LootTemplates_Milling, this, true);
  7590. break;
  7591. default:
  7592. loot->generateMoneyLoot(item->GetTemplate()->MinMoneyLoot, item->GetTemplate()->MaxMoneyLoot);
  7593. loot->FillLoot(item->GetEntry(), LootTemplates_Item, this, true, loot->gold != 0);
  7594. // Force save the loot and money items that were just rolled
  7595. // Also saves the container item ID in Loot struct (not to DB)
  7596. if (loot->gold > 0 || loot->unlootedCount > 0)
  7597. item->ItemContainerSaveLootToDB();
  7598. break;
  7599. }
  7600. }
  7601. }
  7602. else if (guid.IsCorpse()) // remove insignia
  7603. {
  7604. Corpse* bones = ObjectAccessor::GetCorpse(*this, guid);
  7605. if (!bones || !(loot_type == LOOT_CORPSE || loot_type == LOOT_INSIGNIA) || bones->GetType() != CORPSE_BONES)
  7606. {
  7607. SendLootRelease(guid);
  7608. return;
  7609. }
  7610. loot = &bones->loot;
  7611. if (!bones->lootForBody)
  7612. {
  7613. bones->lootForBody = true;
  7614. uint32 pLevel = bones->loot.gold;
  7615. bones->loot.clear();
  7616. if (Battleground* bg = GetBattleground())
  7617. if (bg->GetTypeID(true) == BATTLEGROUND_AV)
  7618. loot->FillLoot(1, LootTemplates_Creature, this, true);
  7619. // It may need a better formula
  7620. // Now it works like this: lvl10: ~6copper, lvl70: ~9silver
  7621. bones->loot.gold = uint32(urand(50, 150) * 0.016f * std::pow(float(pLevel) / 5.76f, 2.5f) * sWorld->getRate(RATE_DROP_MONEY));
  7622. }
  7623. if (bones->lootRecipient != this)
  7624. permission = NONE_PERMISSION;
  7625. else
  7626. permission = OWNER_PERMISSION;
  7627. }
  7628. else
  7629. {
  7630. Creature* creature = GetMap()->GetCreature(guid);
  7631. // must be in range and creature must be alive for pickpocket and must be dead for another loot
  7632. if (!creature || creature->IsAlive() != (loot_type == LOOT_PICKPOCKETING) || !creature->IsWithinDistInMap(this, INTERACTION_DISTANCE))
  7633. {
  7634. SendLootRelease(guid);
  7635. return;
  7636. }
  7637. if (loot_type == LOOT_PICKPOCKETING && IsFriendlyTo(creature))
  7638. {
  7639. SendLootRelease(guid);
  7640. return;
  7641. }
  7642. loot = &creature->loot;
  7643. if (loot_type == LOOT_PICKPOCKETING)
  7644. {
  7645. if (loot->loot_type != LOOT_PICKPOCKETING)
  7646. {
  7647. if (creature->CanGeneratePickPocketLoot())
  7648. {
  7649. creature->StartPickPocketRefillTimer();
  7650. loot->clear();
  7651. if (uint32 lootid = creature->GetCreatureTemplate()->pickpocketLootId)
  7652. loot->FillLoot(lootid, LootTemplates_Pickpocketing, this, true);
  7653. // Generate extra money for pick pocket loot
  7654. const uint32 a = urand(0, creature->getLevel() / 2);
  7655. const uint32 b = urand(0, getLevel() / 2);
  7656. loot->gold = uint32(10 * (a + b) * sWorld->getRate(RATE_DROP_MONEY));
  7657. permission = OWNER_PERMISSION;
  7658. }
  7659. else
  7660. {
  7661. permission = NONE_PERMISSION;
  7662. SendLootError(guid, LOOT_ERROR_ALREADY_PICKPOCKETED);
  7663. return;
  7664. }
  7665. } // else - still has pickpocket loot generated & not fully taken
  7666. }
  7667. else
  7668. {
  7669. // the player whose group may loot the corpse
  7670. Player* recipient = creature->GetLootRecipient();
  7671. if (!recipient)
  7672. return;
  7673. if (loot->loot_type == LOOT_NONE)
  7674. {
  7675. // for creature, loot is filled when creature is killed.
  7676. if (Group* group = recipient->GetGroup())
  7677. {
  7678. switch (group->GetLootMethod())
  7679. {
  7680. case GROUP_LOOT:
  7681. // GroupLoot: rolls items over threshold. Items with quality < threshold, round robin
  7682. group->GroupLoot(loot, creature);
  7683. break;
  7684. case NEED_BEFORE_GREED:
  7685. group->NeedBeforeGreed(loot, creature);
  7686. break;
  7687. case MASTER_LOOT:
  7688. group->MasterLoot(loot, creature);
  7689. break;
  7690. default:
  7691. break;
  7692. }
  7693. }
  7694. }
  7695. // if loot is already skinning loot then don't do anything else
  7696. if (loot->loot_type == LOOT_SKINNING)
  7697. {
  7698. loot_type = LOOT_SKINNING;
  7699. permission = creature->GetSkinner() == GetGUID() ? OWNER_PERMISSION : NONE_PERMISSION;
  7700. }
  7701. else if (loot_type == LOOT_SKINNING)
  7702. {
  7703. loot->clear();
  7704. loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, this, true);
  7705. creature->SetSkinner(GetGUID());
  7706. permission = OWNER_PERMISSION;
  7707. }
  7708. // set group rights only for loot_type != LOOT_SKINNING
  7709. else
  7710. {
  7711. if (Group* group = GetGroup())
  7712. {
  7713. if (group == recipient->GetGroup())
  7714. {
  7715. switch (group->GetLootMethod())
  7716. {
  7717. case MASTER_LOOT:
  7718. permission = group->GetMasterLooterGuid() == GetGUID() ? MASTER_PERMISSION : RESTRICTED_PERMISSION;
  7719. break;
  7720. case FREE_FOR_ALL:
  7721. permission = ALL_PERMISSION;
  7722. break;
  7723. case ROUND_ROBIN:
  7724. permission = ROUND_ROBIN_PERMISSION;
  7725. break;
  7726. default:
  7727. permission = GROUP_PERMISSION;
  7728. break;
  7729. }
  7730. }
  7731. else
  7732. permission = NONE_PERMISSION;
  7733. }
  7734. else if (recipient == this)
  7735. permission = OWNER_PERMISSION;
  7736. else
  7737. permission = NONE_PERMISSION;
  7738. }
  7739. }
  7740. }
  7741. // LOOT_INSIGNIA and LOOT_FISHINGHOLE unsupported by client
  7742. switch (loot_type)
  7743. {
  7744. case LOOT_INSIGNIA: loot_type = LOOT_SKINNING; break;
  7745. case LOOT_FISHINGHOLE: loot_type = LOOT_FISHING; break;
  7746. case LOOT_FISHING_JUNK: loot_type = LOOT_FISHING; break;
  7747. default: break;
  7748. }
  7749. // need know merged fishing/corpse loot type for achievements
  7750. loot->loot_type = loot_type;
  7751. if (permission != NONE_PERMISSION)
  7752. {
  7753. SetLootGUID(guid);
  7754. WorldPacket data(SMSG_LOOT_RESPONSE, (9 + 50)); // we guess size
  7755. data << uint64(guid);
  7756. data << uint8(loot_type);
  7757. data << LootView(*loot, this, permission);
  7758. SendDirectMessage(&data);
  7759. // add 'this' player as one of the players that are looting 'loot'
  7760. loot->AddLooter(GetGUID());
  7761. }
  7762. else
  7763. SendLootError(GetLootGUID(), LOOT_ERROR_DIDNT_KILL);
  7764. if (loot_type == LOOT_CORPSE && !guid.IsItem())
  7765. SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
  7766. }
  7767. void Player::SendLootError(ObjectGuid guid, LootError error)
  7768. {
  7769. WorldPacket data(SMSG_LOOT_RESPONSE, 10);
  7770. data << uint64(guid);
  7771. data << uint8(LOOT_NONE);
  7772. data << uint8(error);
  7773. SendDirectMessage(&data);
  7774. }
  7775. void Player::SendNotifyLootMoneyRemoved()
  7776. {
  7777. WorldPacket data(SMSG_LOOT_CLEAR_MONEY, 0);
  7778. GetSession()->SendPacket(&data);
  7779. }
  7780. void Player::SendNotifyLootItemRemoved(uint8 lootSlot)
  7781. {
  7782. WorldPacket data(SMSG_LOOT_REMOVED, 1);
  7783. data << uint8(lootSlot);
  7784. GetSession()->SendPacket(&data);
  7785. }
  7786. void Player::SendUpdateWorldState(uint32 Field, uint32 Value)
  7787. {
  7788. WorldPacket data(SMSG_UPDATE_WORLD_STATE, 4+4+1);
  7789. data << Field;
  7790. data << Value;
  7791. data << uint8(0);
  7792. GetSession()->SendPacket(&data);
  7793. }
  7794. void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
  7795. {
  7796. // data depends on zoneid/mapid...
  7797. Battleground* bg = GetBattleground();
  7798. uint32 mapid = GetMapId();
  7799. OutdoorPvP* pvp = sOutdoorPvPMgr->GetOutdoorPvPToZoneId(zoneid);
  7800. InstanceScript* instance = GetInstanceScript();
  7801. Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(zoneid);
  7802. TC_LOG_DEBUG("network", "Sending SMSG_INIT_WORLD_STATES to Map: %u, Zone: %u", mapid, zoneid);
  7803. WorldPacket data(SMSG_INIT_WORLD_STATES, (4+4+4+2+(12*8)));
  7804. data << uint32(mapid); // mapid
  7805. data << uint32(zoneid); // zone id
  7806. data << uint32(areaid); // area id, new 2.1.0
  7807. size_t countPos = data.wpos();
  7808. data << uint16(0); // count of uint64 blocks
  7809. data << uint32(0x8d8) << uint32(0x0); // 1
  7810. data << uint32(0x8d7) << uint32(0x0); // 2
  7811. data << uint32(0x8d6) << uint32(0x0); // 3
  7812. data << uint32(0x8d5) << uint32(0x0); // 4
  7813. data << uint32(0x8d4) << uint32(0x0); // 5
  7814. data << uint32(0x8d3) << uint32(0x0); // 6
  7815. // 7 1 - Arena season in progress, 0 - end of season
  7816. data << uint32(0xC77) << uint32(sWorld->getBoolConfig(CONFIG_ARENA_SEASON_IN_PROGRESS));
  7817. // 8 Arena season id
  7818. data << uint32(0xF3D) << uint32(sWorld->getIntConfig(CONFIG_ARENA_SEASON_ID));
  7819. if (mapid == 530) // Outland
  7820. {
  7821. data << uint32(0x9bf) << uint32(0x0); // 7
  7822. data << uint32(0x9bd) << uint32(0xF); // 8
  7823. data << uint32(0x9bb) << uint32(0xF); // 9
  7824. }
  7825. // insert <field> <value>
  7826. switch (zoneid)
  7827. {
  7828. case 1: // Dun Morogh
  7829. case 11: // Wetlands
  7830. case 12: // Elwynn Forest
  7831. case 38: // Loch Modan
  7832. case 40: // Westfall
  7833. case 51: // Searing Gorge
  7834. case 1519: // Stormwind City
  7835. case 1537: // Ironforge
  7836. case 2257: // Deeprun Tram
  7837. case 3703: // Shattrath City
  7838. break;
  7839. case 1377: // Silithus
  7840. if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_SI)
  7841. pvp->FillInitialWorldStates(data);
  7842. else
  7843. {
  7844. // states are always shown
  7845. data << uint32(2313) << uint32(0x0); // 7 ally silityst gathered
  7846. data << uint32(2314) << uint32(0x0); // 8 horde silityst gathered
  7847. data << uint32(2317) << uint32(0x0); // 9 max silithyst
  7848. }
  7849. // dunno about these... aq opening event maybe?
  7850. data << uint32(2322) << uint32(0x0); // 10 sandworm N
  7851. data << uint32(2323) << uint32(0x0); // 11 sandworm S
  7852. data << uint32(2324) << uint32(0x0); // 12 sandworm SW
  7853. data << uint32(2325) << uint32(0x0); // 13 sandworm E
  7854. break;
  7855. case 2597: // Alterac Valley
  7856. if (bg && bg->GetTypeID(true) == BATTLEGROUND_AV)
  7857. bg->FillInitialWorldStates(data);
  7858. else
  7859. {
  7860. data << uint32(0x7ae) << uint32(0x1); // 7 snowfall n
  7861. data << uint32(0x532) << uint32(0x1); // 8 frostwolfhut hc
  7862. data << uint32(0x531) << uint32(0x0); // 9 frostwolfhut ac
  7863. data << uint32(0x52e) << uint32(0x0); // 10 stormpike firstaid a_a
  7864. data << uint32(0x571) << uint32(0x0); // 11 east frostwolf tower horde assaulted -unused
  7865. data << uint32(0x570) << uint32(0x0); // 12 west frostwolf tower horde assaulted - unused
  7866. data << uint32(0x567) << uint32(0x1); // 13 frostwolfe c
  7867. data << uint32(0x566) << uint32(0x1); // 14 frostwolfw c
  7868. data << uint32(0x550) << uint32(0x1); // 15 irondeep (N) ally
  7869. data << uint32(0x544) << uint32(0x0); // 16 ice grave a_a
  7870. data << uint32(0x536) << uint32(0x0); // 17 stormpike grave h_c
  7871. data << uint32(0x535) << uint32(0x1); // 18 stormpike grave a_c
  7872. data << uint32(0x518) << uint32(0x0); // 19 stoneheart grave a_a
  7873. data << uint32(0x517) << uint32(0x0); // 20 stoneheart grave h_a
  7874. data << uint32(0x574) << uint32(0x0); // 21 1396 unk
  7875. data << uint32(0x573) << uint32(0x0); // 22 iceblood tower horde assaulted -unused
  7876. data << uint32(0x572) << uint32(0x0); // 23 towerpoint horde assaulted - unused
  7877. data << uint32(0x56f) << uint32(0x0); // 24 1391 unk
  7878. data << uint32(0x56e) << uint32(0x0); // 25 iceblood a
  7879. data << uint32(0x56d) << uint32(0x0); // 26 towerp a
  7880. data << uint32(0x56c) << uint32(0x0); // 27 frostwolfe a
  7881. data << uint32(0x56b) << uint32(0x0); // 28 froswolfw a
  7882. data << uint32(0x56a) << uint32(0x1); // 29 1386 unk
  7883. data << uint32(0x569) << uint32(0x1); // 30 iceblood c
  7884. data << uint32(0x568) << uint32(0x1); // 31 towerp c
  7885. data << uint32(0x565) << uint32(0x0); // 32 stoneh tower a
  7886. data << uint32(0x564) << uint32(0x0); // 33 icewing tower a
  7887. data << uint32(0x563) << uint32(0x0); // 34 dunn a
  7888. data << uint32(0x562) << uint32(0x0); // 35 duns a
  7889. data << uint32(0x561) << uint32(0x0); // 36 stoneheart bunker alliance assaulted - unused
  7890. data << uint32(0x560) << uint32(0x0); // 37 icewing bunker alliance assaulted - unused
  7891. data << uint32(0x55f) << uint32(0x0); // 38 dunbaldar south alliance assaulted - unused
  7892. data << uint32(0x55e) << uint32(0x0); // 39 dunbaldar north alliance assaulted - unused
  7893. data << uint32(0x55d) << uint32(0x0); // 40 stone tower d
  7894. data << uint32(0x3c6) << uint32(0x0); // 41 966 unk
  7895. data << uint32(0x3c4) << uint32(0x0); // 42 964 unk
  7896. data << uint32(0x3c2) << uint32(0x0); // 43 962 unk
  7897. data << uint32(0x516) << uint32(0x1); // 44 stoneheart grave a_c
  7898. data << uint32(0x515) << uint32(0x0); // 45 stonheart grave h_c
  7899. data << uint32(0x3b6) << uint32(0x0); // 46 950 unk
  7900. data << uint32(0x55c) << uint32(0x0); // 47 icewing tower d
  7901. data << uint32(0x55b) << uint32(0x0); // 48 dunn d
  7902. data << uint32(0x55a) << uint32(0x0); // 49 duns d
  7903. data << uint32(0x559) << uint32(0x0); // 50 1369 unk
  7904. data << uint32(0x558) << uint32(0x0); // 51 iceblood d
  7905. data << uint32(0x557) << uint32(0x0); // 52 towerp d
  7906. data << uint32(0x556) << uint32(0x0); // 53 frostwolfe d
  7907. data << uint32(0x555) << uint32(0x0); // 54 frostwolfw d
  7908. data << uint32(0x554) << uint32(0x1); // 55 stoneh tower c
  7909. data << uint32(0x553) << uint32(0x1); // 56 icewing tower c
  7910. data << uint32(0x552) << uint32(0x1); // 57 dunn c
  7911. data << uint32(0x551) << uint32(0x1); // 58 duns c
  7912. data << uint32(0x54f) << uint32(0x0); // 59 irondeep (N) horde
  7913. data << uint32(0x54e) << uint32(0x0); // 60 irondeep (N) ally
  7914. data << uint32(0x54d) << uint32(0x1); // 61 mine (S) neutral
  7915. data << uint32(0x54c) << uint32(0x0); // 62 mine (S) horde
  7916. data << uint32(0x54b) << uint32(0x0); // 63 mine (S) ally
  7917. data << uint32(0x545) << uint32(0x0); // 64 iceblood h_a
  7918. data << uint32(0x543) << uint32(0x1); // 65 iceblod h_c
  7919. data << uint32(0x542) << uint32(0x0); // 66 iceblood a_c
  7920. data << uint32(0x540) << uint32(0x0); // 67 snowfall h_a
  7921. data << uint32(0x53f) << uint32(0x0); // 68 snowfall a_a
  7922. data << uint32(0x53e) << uint32(0x0); // 69 snowfall h_c
  7923. data << uint32(0x53d) << uint32(0x0); // 70 snowfall a_c
  7924. data << uint32(0x53c) << uint32(0x0); // 71 frostwolf g h_a
  7925. data << uint32(0x53b) << uint32(0x0); // 72 frostwolf g a_a
  7926. data << uint32(0x53a) << uint32(0x1); // 73 frostwolf g h_c
  7927. data << uint32(0x539) << uint32(0x0); // 74 frostwolf g a_c
  7928. data << uint32(0x538) << uint32(0x0); // 75 stormpike grave h_a
  7929. data << uint32(0x537) << uint32(0x0); // 76 stormpike grave a_a
  7930. data << uint32(0x534) << uint32(0x0); // 77 frostwolf hut h_a
  7931. data << uint32(0x533) << uint32(0x0); // 78 frostwolf hut a_a
  7932. data << uint32(0x530) << uint32(0x0); // 79 stormpike first aid h_a
  7933. data << uint32(0x52f) << uint32(0x0); // 80 stormpike first aid h_c
  7934. data << uint32(0x52d) << uint32(0x1); // 81 stormpike first aid a_c
  7935. }
  7936. break;
  7937. case 3277: // Warsong Gulch
  7938. if (bg && bg->GetTypeID(true) == BATTLEGROUND_WS)
  7939. bg->FillInitialWorldStates(data);
  7940. else
  7941. {
  7942. data << uint32(0x62d) << uint32(0x0); // 7 1581 alliance flag captures
  7943. data << uint32(0x62e) << uint32(0x0); // 8 1582 horde flag captures
  7944. data << uint32(0x609) << uint32(0x0); // 9 1545 unk, set to 1 on alliance flag pickup...
  7945. data << uint32(0x60a) << uint32(0x0); // 10 1546 unk, set to 1 on horde flag pickup, after drop it's -1
  7946. data << uint32(0x60b) << uint32(0x2); // 11 1547 unk
  7947. data << uint32(0x641) << uint32(0x3); // 12 1601 unk (max flag captures?)
  7948. data << uint32(0x922) << uint32(0x1); // 13 2338 horde (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
  7949. data << uint32(0x923) << uint32(0x1); // 14 2339 alliance (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
  7950. }
  7951. break;
  7952. case 3358: // Arathi Basin
  7953. if (bg && bg->GetTypeID(true) == BATTLEGROUND_AB)
  7954. bg->FillInitialWorldStates(data);
  7955. else
  7956. {
  7957. data << uint32(0x6e7) << uint32(0x0); // 7 1767 stables alliance
  7958. data << uint32(0x6e8) << uint32(0x0); // 8 1768 stables horde
  7959. data << uint32(0x6e9) << uint32(0x0); // 9 1769 unk, ST?
  7960. data << uint32(0x6ea) << uint32(0x0); // 10 1770 stables (show/hide)
  7961. data << uint32(0x6ec) << uint32(0x0); // 11 1772 farm (0 - horde controlled, 1 - alliance controlled)
  7962. data << uint32(0x6ed) << uint32(0x0); // 12 1773 farm (show/hide)
  7963. data << uint32(0x6ee) << uint32(0x0); // 13 1774 farm color
  7964. data << uint32(0x6ef) << uint32(0x0); // 14 1775 gold mine color, may be FM?
  7965. data << uint32(0x6f0) << uint32(0x0); // 15 1776 alliance resources
  7966. data << uint32(0x6f1) << uint32(0x0); // 16 1777 horde resources
  7967. data << uint32(0x6f2) << uint32(0x0); // 17 1778 horde bases
  7968. data << uint32(0x6f3) << uint32(0x0); // 18 1779 alliance bases
  7969. data << uint32(0x6f4) << uint32(0x7d0); // 19 1780 max resources (2000)
  7970. data << uint32(0x6f6) << uint32(0x0); // 20 1782 blacksmith color
  7971. data << uint32(0x6f7) << uint32(0x0); // 21 1783 blacksmith (show/hide)
  7972. data << uint32(0x6f8) << uint32(0x0); // 22 1784 unk, bs?
  7973. data << uint32(0x6f9) << uint32(0x0); // 23 1785 unk, bs?
  7974. data << uint32(0x6fb) << uint32(0x0); // 24 1787 gold mine (0 - horde contr, 1 - alliance contr)
  7975. data << uint32(0x6fc) << uint32(0x0); // 25 1788 gold mine (0 - conflict, 1 - horde)
  7976. data << uint32(0x6fd) << uint32(0x0); // 26 1789 gold mine (1 - show/0 - hide)
  7977. data << uint32(0x6fe) << uint32(0x0); // 27 1790 gold mine color
  7978. data << uint32(0x700) << uint32(0x0); // 28 1792 gold mine color, wtf?, may be LM?
  7979. data << uint32(0x701) << uint32(0x0); // 29 1793 lumber mill color (0 - conflict, 1 - horde contr)
  7980. data << uint32(0x702) << uint32(0x0); // 30 1794 lumber mill (show/hide)
  7981. data << uint32(0x703) << uint32(0x0); // 31 1795 lumber mill color color
  7982. data << uint32(0x732) << uint32(0x1); // 32 1842 stables (1 - uncontrolled)
  7983. data << uint32(0x733) << uint32(0x1); // 33 1843 gold mine (1 - uncontrolled)
  7984. data << uint32(0x734) << uint32(0x1); // 34 1844 lumber mill (1 - uncontrolled)
  7985. data << uint32(0x735) << uint32(0x1); // 35 1845 farm (1 - uncontrolled)
  7986. data << uint32(0x736) << uint32(0x1); // 36 1846 blacksmith (1 - uncontrolled)
  7987. data << uint32(0x745) << uint32(0x2); // 37 1861 unk
  7988. data << uint32(0x7a3) << uint32(0x708); // 38 1955 warning limit (1800)
  7989. }
  7990. break;
  7991. case 3820: // Eye of the Storm
  7992. if (bg && bg->GetTypeID(true) == BATTLEGROUND_EY)
  7993. bg->FillInitialWorldStates(data);
  7994. else
  7995. {
  7996. data << uint32(0xac1) << uint32(0x0); // 7 2753 Horde Bases
  7997. data << uint32(0xac0) << uint32(0x0); // 8 2752 Alliance Bases
  7998. data << uint32(0xab6) << uint32(0x0); // 9 2742 Mage Tower - Horde conflict
  7999. data << uint32(0xab5) << uint32(0x0); // 10 2741 Mage Tower - Alliance conflict
  8000. data << uint32(0xab4) << uint32(0x0); // 11 2740 Fel Reaver - Horde conflict
  8001. data << uint32(0xab3) << uint32(0x0); // 12 2739 Fel Reaver - Alliance conflict
  8002. data << uint32(0xab2) << uint32(0x0); // 13 2738 Draenei - Alliance conflict
  8003. data << uint32(0xab1) << uint32(0x0); // 14 2737 Draenei - Horde conflict
  8004. data << uint32(0xab0) << uint32(0x0); // 15 2736 unk // 0 at start
  8005. data << uint32(0xaaf) << uint32(0x0); // 16 2735 unk // 0 at start
  8006. data << uint32(0xaad) << uint32(0x0); // 17 2733 Draenei - Horde control
  8007. data << uint32(0xaac) << uint32(0x0); // 18 2732 Draenei - Alliance control
  8008. data << uint32(0xaab) << uint32(0x1); // 19 2731 Draenei uncontrolled (1 - yes, 0 - no)
  8009. data << uint32(0xaaa) << uint32(0x0); // 20 2730 Mage Tower - Alliance control
  8010. data << uint32(0xaa9) << uint32(0x0); // 21 2729 Mage Tower - Horde control
  8011. data << uint32(0xaa8) << uint32(0x1); // 22 2728 Mage Tower uncontrolled (1 - yes, 0 - no)
  8012. data << uint32(0xaa7) << uint32(0x0); // 23 2727 Fel Reaver - Horde control
  8013. data << uint32(0xaa6) << uint32(0x0); // 24 2726 Fel Reaver - Alliance control
  8014. data << uint32(0xaa5) << uint32(0x1); // 25 2725 Fel Reaver uncontrolled (1 - yes, 0 - no)
  8015. data << uint32(0xaa4) << uint32(0x0); // 26 2724 Boold Elf - Horde control
  8016. data << uint32(0xaa3) << uint32(0x0); // 27 2723 Boold Elf - Alliance control
  8017. data << uint32(0xaa2) << uint32(0x1); // 28 2722 Boold Elf uncontrolled (1 - yes, 0 - no)
  8018. data << uint32(0xac5) << uint32(0x1); // 29 2757 Flag (1 - show, 0 - hide) - doesn't work exactly this way!
  8019. data << uint32(0xad2) << uint32(0x1); // 30 2770 Horde top-stats (1 - show, 0 - hide) // 02 -> horde picked up the flag
  8020. data << uint32(0xad1) << uint32(0x1); // 31 2769 Alliance top-stats (1 - show, 0 - hide) // 02 -> alliance picked up the flag
  8021. data << uint32(0xabe) << uint32(0x0); // 32 2750 Horde resources
  8022. data << uint32(0xabd) << uint32(0x0); // 33 2749 Alliance resources
  8023. data << uint32(0xa05) << uint32(0x8e); // 34 2565 unk, constant?
  8024. data << uint32(0xaa0) << uint32(0x0); // 35 2720 Capturing progress-bar (100 -> empty (only grey), 0 -> blue|red (no grey), default 0)
  8025. data << uint32(0xa9f) << uint32(0x0); // 36 2719 Capturing progress-bar (0 - left, 100 - right)
  8026. data << uint32(0xa9e) << uint32(0x0); // 37 2718 Capturing progress-bar (1 - show, 0 - hide)
  8027. data << uint32(0xc0d) << uint32(0x17b); // 38 3085 unk
  8028. // and some more ... unknown
  8029. }
  8030. break;
  8031. // any of these needs change! the client remembers the prev setting!
  8032. // ON EVERY ZONE LEAVE, RESET THE OLD ZONE'S WORLD STATE, BUT AT LEAST THE UI STUFF!
  8033. case 3483: // Hellfire Peninsula
  8034. if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_HP)
  8035. pvp->FillInitialWorldStates(data);
  8036. else
  8037. {
  8038. data << uint32(0x9ba) << uint32(0x1); // 10 // add ally tower main gui icon // maybe should be sent only on login?
  8039. data << uint32(0x9b9) << uint32(0x1); // 11 // add horde tower main gui icon // maybe should be sent only on login?
  8040. data << uint32(0x9b5) << uint32(0x0); // 12 // show neutral broken hill icon // 2485
  8041. data << uint32(0x9b4) << uint32(0x1); // 13 // show icon above broken hill // 2484
  8042. data << uint32(0x9b3) << uint32(0x0); // 14 // show ally broken hill icon // 2483
  8043. data << uint32(0x9b2) << uint32(0x0); // 15 // show neutral overlook icon // 2482
  8044. data << uint32(0x9b1) << uint32(0x1); // 16 // show the overlook arrow // 2481
  8045. data << uint32(0x9b0) << uint32(0x0); // 17 // show ally overlook icon // 2480
  8046. data << uint32(0x9ae) << uint32(0x0); // 18 // horde pvp objectives captured // 2478
  8047. data << uint32(0x9ac) << uint32(0x0); // 19 // ally pvp objectives captured // 2476
  8048. data << uint32(2475) << uint32(100); //: ally / horde slider grey area // show only in direct vicinity!
  8049. data << uint32(2474) << uint32(50); //: ally / horde slider percentage, 100 for ally, 0 for horde // show only in direct vicinity!
  8050. data << uint32(2473) << uint32(0); //: ally / horde slider display // show only in direct vicinity!
  8051. data << uint32(0x9a8) << uint32(0x0); // 20 // show the neutral stadium icon // 2472
  8052. data << uint32(0x9a7) << uint32(0x0); // 21 // show the ally stadium icon // 2471
  8053. data << uint32(0x9a6) << uint32(0x1); // 22 // show the horde stadium icon // 2470
  8054. }
  8055. break;
  8056. case 3518: // Nagrand
  8057. if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_NA)
  8058. pvp->FillInitialWorldStates(data);
  8059. else
  8060. {
  8061. data << uint32(2503) << uint32(0x0); // 10
  8062. data << uint32(2502) << uint32(0x0); // 11
  8063. data << uint32(2493) << uint32(0x0); // 12
  8064. data << uint32(2491) << uint32(0x0); // 13
  8065. data << uint32(2495) << uint32(0x0); // 14
  8066. data << uint32(2494) << uint32(0x0); // 15
  8067. data << uint32(2497) << uint32(0x0); // 16
  8068. data << uint32(2762) << uint32(0x0); // 17
  8069. data << uint32(2662) << uint32(0x0); // 18
  8070. data << uint32(2663) << uint32(0x0); // 19
  8071. data << uint32(2664) << uint32(0x0); // 20
  8072. data << uint32(2760) << uint32(0x0); // 21
  8073. data << uint32(2670) << uint32(0x0); // 22
  8074. data << uint32(2668) << uint32(0x0); // 23
  8075. data << uint32(2669) << uint32(0x0); // 24
  8076. data << uint32(2761) << uint32(0x0); // 25
  8077. data << uint32(2667) << uint32(0x0); // 26
  8078. data << uint32(2665) << uint32(0x0); // 27
  8079. data << uint32(2666) << uint32(0x0); // 28
  8080. data << uint32(2763) << uint32(0x0); // 29
  8081. data << uint32(2659) << uint32(0x0); // 30
  8082. data << uint32(2660) << uint32(0x0); // 31
  8083. data << uint32(2661) << uint32(0x0); // 32
  8084. data << uint32(2671) << uint32(0x0); // 33
  8085. data << uint32(2676) << uint32(0x0); // 34
  8086. data << uint32(2677) << uint32(0x0); // 35
  8087. data << uint32(2672) << uint32(0x0); // 36
  8088. data << uint32(2673) << uint32(0x0); // 37
  8089. }
  8090. break;
  8091. case 3519: // Terokkar Forest
  8092. if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_TF)
  8093. pvp->FillInitialWorldStates(data);
  8094. else
  8095. {
  8096. data << uint32(0xa41) << uint32(0x0); // 10 // 2625 capture bar pos
  8097. data << uint32(0xa40) << uint32(0x14); // 11 // 2624 capture bar neutral
  8098. data << uint32(0xa3f) << uint32(0x0); // 12 // 2623 show capture bar
  8099. data << uint32(0xa3e) << uint32(0x0); // 13 // 2622 horde towers controlled
  8100. data << uint32(0xa3d) << uint32(0x5); // 14 // 2621 ally towers controlled
  8101. data << uint32(0xa3c) << uint32(0x0); // 15 // 2620 show towers controlled
  8102. data << uint32(0xa88) << uint32(0x0); // 16 // 2696 SE Neu
  8103. data << uint32(0xa87) << uint32(0x0); // 17 // SE Horde
  8104. data << uint32(0xa86) << uint32(0x0); // 18 // SE Ally
  8105. data << uint32(0xa85) << uint32(0x0); // 19 //S Neu
  8106. data << uint32(0xa84) << uint32(0x0); // 20 S Horde
  8107. data << uint32(0xa83) << uint32(0x0); // 21 S Ally
  8108. data << uint32(0xa82) << uint32(0x0); // 22 NE Neu
  8109. data << uint32(0xa81) << uint32(0x0); // 23 NE Horde
  8110. data << uint32(0xa80) << uint32(0x0); // 24 NE Ally
  8111. data << uint32(0xa7e) << uint32(0x0); // 25 // 2686 N Neu
  8112. data << uint32(0xa7d) << uint32(0x0); // 26 N Horde
  8113. data << uint32(0xa7c) << uint32(0x0); // 27 N Ally
  8114. data << uint32(0xa7b) << uint32(0x0); // 28 NW Ally
  8115. data << uint32(0xa7a) << uint32(0x0); // 29 NW Horde
  8116. data << uint32(0xa79) << uint32(0x0); // 30 NW Neutral
  8117. data << uint32(0x9d0) << uint32(0x5); // 31 // 2512 locked time remaining seconds first digit
  8118. data << uint32(0x9ce) << uint32(0x0); // 32 // 2510 locked time remaining seconds second digit
  8119. data << uint32(0x9cd) << uint32(0x0); // 33 // 2509 locked time remaining minutes
  8120. data << uint32(0x9cc) << uint32(0x0); // 34 // 2508 neutral locked time show
  8121. data << uint32(0xad0) << uint32(0x0); // 35 // 2768 horde locked time show
  8122. data << uint32(0xacf) << uint32(0x1); // 36 // 2767 ally locked time show
  8123. }
  8124. break;
  8125. case 3521: // Zangarmarsh
  8126. if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_ZM)
  8127. pvp->FillInitialWorldStates(data);
  8128. else
  8129. {
  8130. data << uint32(0x9e1) << uint32(0x0); // 10 //2529
  8131. data << uint32(0x9e0) << uint32(0x0); // 11
  8132. data << uint32(0x9df) << uint32(0x0); // 12
  8133. data << uint32(0xa5d) << uint32(0x1); // 13 //2653
  8134. data << uint32(0xa5c) << uint32(0x0); // 14 //2652 east beacon neutral
  8135. data << uint32(0xa5b) << uint32(0x1); // 15 horde
  8136. data << uint32(0xa5a) << uint32(0x0); // 16 ally
  8137. data << uint32(0xa59) << uint32(0x1); // 17 // 2649 Twin spire graveyard horde 12???
  8138. data << uint32(0xa58) << uint32(0x0); // 18 ally 14 ???
  8139. data << uint32(0xa57) << uint32(0x0); // 19 neutral 7???
  8140. data << uint32(0xa56) << uint32(0x0); // 20 // 2646 west beacon neutral
  8141. data << uint32(0xa55) << uint32(0x1); // 21 horde
  8142. data << uint32(0xa54) << uint32(0x0); // 22 ally
  8143. data << uint32(0x9e7) << uint32(0x0); // 23 // 2535
  8144. data << uint32(0x9e6) << uint32(0x0); // 24
  8145. data << uint32(0x9e5) << uint32(0x0); // 25
  8146. data << uint32(0xa00) << uint32(0x0); // 26 // 2560
  8147. data << uint32(0x9ff) << uint32(0x1); // 27
  8148. data << uint32(0x9fe) << uint32(0x0); // 28
  8149. data << uint32(0x9fd) << uint32(0x0); // 29
  8150. data << uint32(0x9fc) << uint32(0x1); // 30
  8151. data << uint32(0x9fb) << uint32(0x0); // 31
  8152. data << uint32(0xa62) << uint32(0x0); // 32 // 2658
  8153. data << uint32(0xa61) << uint32(0x1); // 33
  8154. data << uint32(0xa60) << uint32(0x1); // 34
  8155. data << uint32(0xa5f) << uint32(0x0); // 35
  8156. }
  8157. break;
  8158. case 3698: // Nagrand Arena
  8159. if (bg && bg->GetTypeID(true) == BATTLEGROUND_NA)
  8160. bg->FillInitialWorldStates(data);
  8161. else
  8162. {
  8163. data << uint32(0xa0f) << uint32(0x0); // 7
  8164. data << uint32(0xa10) << uint32(0x0); // 8
  8165. data << uint32(0xa11) << uint32(0x0); // 9 show
  8166. }
  8167. break;
  8168. case 3702: // Blade's Edge Arena
  8169. if (bg && bg->GetTypeID(true) == BATTLEGROUND_BE)
  8170. bg->FillInitialWorldStates(data);
  8171. else
  8172. {
  8173. data << uint32(0x9f0) << uint32(0x0); // 7 gold
  8174. data << uint32(0x9f1) << uint32(0x0); // 8 green
  8175. data << uint32(0x9f3) << uint32(0x0); // 9 show
  8176. }
  8177. break;
  8178. case 3968: // Ruins of Lordaeron
  8179. if (bg && bg->GetTypeID(true) == BATTLEGROUND_RL)
  8180. bg->FillInitialWorldStates(data);
  8181. else
  8182. {
  8183. data << uint32(0xbb8) << uint32(0x0); // 7 gold
  8184. data << uint32(0xbb9) << uint32(0x0); // 8 green
  8185. data << uint32(0xbba) << uint32(0x0); // 9 show
  8186. }
  8187. break;
  8188. case 4378: // Dalaran Sewers
  8189. if (bg && bg->GetTypeID(true) == BATTLEGROUND_DS)
  8190. bg->FillInitialWorldStates(data);
  8191. else
  8192. {
  8193. data << uint32(3601) << uint32(0x0); // 7 gold
  8194. data << uint32(3600) << uint32(0x0); // 8 green
  8195. data << uint32(3610) << uint32(0x0); // 9 show
  8196. }
  8197. break;
  8198. case 4384: // Strand of the Ancients
  8199. if (bg && bg->GetTypeID(true) == BATTLEGROUND_SA)
  8200. bg->FillInitialWorldStates(data);
  8201. else
  8202. {
  8203. // 1-3 A defend, 4-6 H defend, 7-9 unk defend, 1 - ok, 2 - half destroyed, 3 - destroyed
  8204. data << uint32(0xf09) << uint32(0x0); // 7 3849 Gate of Temple
  8205. data << uint32(0xe36) << uint32(0x0); // 8 3638 Gate of Yellow Moon
  8206. data << uint32(0xe27) << uint32(0x0); // 9 3623 Gate of Green Emerald
  8207. data << uint32(0xe24) << uint32(0x0); // 10 3620 Gate of Blue Sapphire
  8208. data << uint32(0xe21) << uint32(0x0); // 11 3617 Gate of Red Sun
  8209. data << uint32(0xe1e) << uint32(0x0); // 12 3614 Gate of Purple Ametyst
  8210. data << uint32(0xdf3) << uint32(0x0); // 13 3571 bonus timer (1 - on, 0 - off)
  8211. data << uint32(0xded) << uint32(0x0); // 14 3565 Horde Attacker
  8212. data << uint32(0xdec) << uint32(0x0); // 15 3564 Alliance Attacker
  8213. // End Round (timer), better explain this by example, eg. ends in 19:59 -> A:BC
  8214. data << uint32(0xde9) << uint32(0x0); // 16 3561 C
  8215. data << uint32(0xde8) << uint32(0x0); // 17 3560 B
  8216. data << uint32(0xde7) << uint32(0x0); // 18 3559 A
  8217. data << uint32(0xe35) << uint32(0x0); // 19 3637 East g - Horde control
  8218. data << uint32(0xe34) << uint32(0x0); // 20 3636 West g - Horde control
  8219. data << uint32(0xe33) << uint32(0x0); // 21 3635 South g - Horde control
  8220. data << uint32(0xe32) << uint32(0x0); // 22 3634 East g - Alliance control
  8221. data << uint32(0xe31) << uint32(0x0); // 23 3633 West g - Alliance control
  8222. data << uint32(0xe30) << uint32(0x0); // 24 3632 South g - Alliance control
  8223. data << uint32(0xe2f) << uint32(0x0); // 25 3631 Chamber of Ancients - Horde control
  8224. data << uint32(0xe2e) << uint32(0x0); // 26 3630 Chamber of Ancients - Alliance control
  8225. data << uint32(0xe2d) << uint32(0x0); // 27 3629 Beach1 - Horde control
  8226. data << uint32(0xe2c) << uint32(0x0); // 28 3628 Beach2 - Horde control
  8227. data << uint32(0xe2b) << uint32(0x0); // 29 3627 Beach1 - Alliance control
  8228. data << uint32(0xe2a) << uint32(0x0); // 30 3626 Beach2 - Alliance control
  8229. // and many unks...
  8230. }
  8231. break;
  8232. case 4406: // Ring of Valor
  8233. if (bg && bg->GetTypeID(true) == BATTLEGROUND_RV)
  8234. bg->FillInitialWorldStates(data);
  8235. else
  8236. {
  8237. data << uint32(0xe10) << uint32(0x0); // 7 gold
  8238. data << uint32(0xe11) << uint32(0x0); // 8 green
  8239. data << uint32(0xe1a) << uint32(0x0); // 9 show
  8240. }
  8241. break;
  8242. case 4710:
  8243. if (bg && bg->GetTypeID(true) == BATTLEGROUND_IC)
  8244. bg->FillInitialWorldStates(data);
  8245. else
  8246. {
  8247. data << uint32(4221) << uint32(1); // 7 BG_IC_ALLIANCE_RENFORT_SET
  8248. data << uint32(4222) << uint32(1); // 8 BG_IC_HORDE_RENFORT_SET
  8249. data << uint32(4226) << uint32(300); // 9 BG_IC_ALLIANCE_RENFORT
  8250. data << uint32(4227) << uint32(300); // 10 BG_IC_HORDE_RENFORT
  8251. data << uint32(4322) << uint32(1); // 11 BG_IC_GATE_FRONT_H_WS_OPEN
  8252. data << uint32(4321) << uint32(1); // 12 BG_IC_GATE_WEST_H_WS_OPEN
  8253. data << uint32(4320) << uint32(1); // 13 BG_IC_GATE_EAST_H_WS_OPEN
  8254. data << uint32(4323) << uint32(1); // 14 BG_IC_GATE_FRONT_A_WS_OPEN
  8255. data << uint32(4324) << uint32(1); // 15 BG_IC_GATE_WEST_A_WS_OPEN
  8256. data << uint32(4325) << uint32(1); // 16 BG_IC_GATE_EAST_A_WS_OPEN
  8257. data << uint32(4317) << uint32(1); // 17 unknown
  8258. data << uint32(4301) << uint32(1); // 18 BG_IC_DOCKS_UNCONTROLLED
  8259. data << uint32(4296) << uint32(1); // 19 BG_IC_HANGAR_UNCONTROLLED
  8260. data << uint32(4306) << uint32(1); // 20 BG_IC_QUARRY_UNCONTROLLED
  8261. data << uint32(4311) << uint32(1); // 21 BG_IC_REFINERY_UNCONTROLLED
  8262. data << uint32(4294) << uint32(1); // 22 BG_IC_WORKSHOP_UNCONTROLLED
  8263. data << uint32(4243) << uint32(1); // 23 unknown
  8264. data << uint32(4345) << uint32(1); // 24 unknown
  8265. }
  8266. break;
  8267. // The Ruby Sanctum
  8268. case 4987:
  8269. if (instance && mapid == 724)
  8270. instance->FillInitialWorldStates(data);
  8271. else
  8272. {
  8273. data << uint32(5049) << uint32(50); // 9 WORLDSTATE_CORPOREALITY_MATERIAL
  8274. data << uint32(5050) << uint32(50); // 10 WORLDSTATE_CORPOREALITY_TWILIGHT
  8275. data << uint32(5051) << uint32(0); // 11 WORLDSTATE_CORPOREALITY_TOGGLE
  8276. }
  8277. break;
  8278. // Icecrown Citadel
  8279. case 4812:
  8280. if (instance && mapid == 631)
  8281. instance->FillInitialWorldStates(data);
  8282. else
  8283. {
  8284. data << uint32(4903) << uint32(0); // 9 WORLDSTATE_SHOW_TIMER (Blood Quickening weekly)
  8285. data << uint32(4904) << uint32(30); // 10 WORLDSTATE_EXECUTION_TIME
  8286. data << uint32(4940) << uint32(0); // 11 WORLDSTATE_SHOW_ATTEMPTS
  8287. data << uint32(4941) << uint32(50); // 12 WORLDSTATE_ATTEMPTS_REMAINING
  8288. data << uint32(4942) << uint32(50); // 13 WORLDSTATE_ATTEMPTS_MAX
  8289. }
  8290. break;
  8291. // The Culling of Stratholme
  8292. case 4100:
  8293. if (instance && mapid == 595)
  8294. instance->FillInitialWorldStates(data);
  8295. else
  8296. {
  8297. data << uint32(3479) << uint32(0); // 9 WORLDSTATE_SHOW_CRATES
  8298. data << uint32(3480) << uint32(0); // 10 WORLDSTATE_CRATES_REVEALED
  8299. data << uint32(3504) << uint32(0); // 11 WORLDSTATE_WAVE_COUNT
  8300. data << uint32(3931) << uint32(25); // 12 WORLDSTATE_TIME_GUARDIAN
  8301. data << uint32(3932) << uint32(0); // 13 WORLDSTATE_TIME_GUARDIAN_SHOW
  8302. }
  8303. break;
  8304. // The Oculus
  8305. case 4228:
  8306. if (instance && mapid == 578)
  8307. instance->FillInitialWorldStates(data);
  8308. else
  8309. {
  8310. data << uint32(3524) << uint32(0); // 9 WORLD_STATE_CENTRIFUGE_CONSTRUCT_SHOW
  8311. data << uint32(3486) << uint32(0); // 10 WORLD_STATE_CENTRIFUGE_CONSTRUCT_AMOUNT
  8312. }
  8313. break;
  8314. // Ulduar
  8315. case 4273:
  8316. if (instance && mapid == 603)
  8317. instance->FillInitialWorldStates(data);
  8318. else
  8319. {
  8320. data << uint32(4132) << uint32(0); // 9 WORLDSTATE_ALGALON_TIMER_ENABLED
  8321. data << uint32(4131) << uint32(0); // 10 WORLDSTATE_ALGALON_DESPAWN_TIMER
  8322. }
  8323. break;
  8324. // Zul Aman
  8325. case 3805:
  8326. if (instance && mapid == 568)
  8327. instance->FillInitialWorldStates(data);
  8328. else
  8329. {
  8330. data << uint32(3104) << uint32(0); // 9 WORLD_STATE_ZULAMAN_TIMER_ENABLED
  8331. data << uint32(3106) << uint32(0); // 10 WORLD_STATE_ZULAMAN_TIMER
  8332. }
  8333. break;
  8334. // Twin Peaks
  8335. case 5031:
  8336. if (bg && bg->GetTypeID(true) == BATTLEGROUND_TP)
  8337. bg->FillInitialWorldStates(data);
  8338. else
  8339. {
  8340. data << uint32(0x62d) << uint32(0x0); // 7 1581 alliance flag captures
  8341. data << uint32(0x62e) << uint32(0x0); // 8 1582 horde flag captures
  8342. data << uint32(0x609) << uint32(0x0); // 9 1545 unk
  8343. data << uint32(0x60a) << uint32(0x0); // 10 1546 unk
  8344. data << uint32(0x60b) << uint32(0x2); // 11 1547 unk
  8345. data << uint32(0x641) << uint32(0x3); // 12 1601 unk
  8346. data << uint32(0x922) << uint32(0x1); // 13 2338 horde (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
  8347. data << uint32(0x923) << uint32(0x1); // 14 2339 alliance (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
  8348. }
  8349. break;
  8350. // Battle for Gilneas
  8351. case 5449:
  8352. if (bg && bg->GetTypeID(true) == BATTLEGROUND_BFG)
  8353. bg->FillInitialWorldStates(data);
  8354. break;
  8355. // Wintergrasp
  8356. case 4197:
  8357. if (bf && bf->GetTypeId() == BATTLEFIELD_WG)
  8358. {
  8359. bf->FillInitialWorldStates(data);
  8360. break;
  8361. }
  8362. // Halls of Refection
  8363. case 4820:
  8364. if (instance && mapid == 668)
  8365. instance->FillInitialWorldStates(data);
  8366. else
  8367. {
  8368. data << uint32(4884) << uint32(0); // 9 WORLD_STATE_HOR_WAVES_ENABLED
  8369. data << uint32(4882) << uint32(0); // 10 WORLD_STATE_HOR_WAVE_COUNT
  8370. }
  8371. break;
  8372. // No break here, intended.
  8373. default:
  8374. data << uint32(0x914) << uint32(0x0); // 7
  8375. data << uint32(0x913) << uint32(0x0); // 8
  8376. data << uint32(0x912) << uint32(0x0); // 9
  8377. data << uint32(0x915) << uint32(0x0); // 10
  8378. break;
  8379. }
  8380. uint16 length = (data.wpos() - countPos) / 8;
  8381. data.put<uint16>(countPos, length);
  8382. GetSession()->SendPacket(&data);
  8383. SendBGWeekendWorldStates();
  8384. SendBattlefieldWorldStates();
  8385. }
  8386. void Player::SendBGWeekendWorldStates()
  8387. {
  8388. for (uint32 i = 1; i < sBattlemasterListStore.GetNumRows(); ++i)
  8389. {
  8390. BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(i);
  8391. if (bl && bl->HolidayWorldStateId)
  8392. {
  8393. if (BattlegroundMgr::IsBGWeekend((BattlegroundTypeId)bl->id))
  8394. SendUpdateWorldState(bl->HolidayWorldStateId, 1);
  8395. else
  8396. SendUpdateWorldState(bl->HolidayWorldStateId, 0);
  8397. }
  8398. }
  8399. }
  8400. void Player::SendBattlefieldWorldStates()
  8401. {
  8402. /// Send misc stuff that needs to be sent on every login, like the battle timers.
  8403. if (sWorld->getBoolConfig(CONFIG_WINTERGRASP_ENABLE))
  8404. {
  8405. if (Battlefield* wg = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG))
  8406. {
  8407. if (wg->IsWarTime())
  8408. SendUpdateWorldState(ClockWorldState[1], uint32(time(NULL)));
  8409. else // Time to next battle
  8410. {
  8411. uint32 timer = wg->GetTimer() / 1000;
  8412. SendUpdateWorldState(ClockWorldState[1], time(NULL) + timer);
  8413. }
  8414. }
  8415. }
  8416. }
  8417. uint32 Player::GetXPRestBonus(uint32 xp)
  8418. {
  8419. uint32 rested_bonus = (uint32)GetRestBonus(); // xp for each rested bonus
  8420. if (rested_bonus > xp) // max rested_bonus == xp or (r+x) = 200% xp
  8421. rested_bonus = xp;
  8422. SetRestBonus(GetRestBonus() - rested_bonus);
  8423. TC_LOG_INFO("entities.player", "Player gain %u xp (+ %u Rested Bonus). Rested points=%f", xp+rested_bonus, rested_bonus, GetRestBonus());
  8424. return rested_bonus;
  8425. }
  8426. void Player::SetBindPoint(ObjectGuid guid)
  8427. {
  8428. WorldPacket data(SMSG_BINDER_CONFIRM, 8);
  8429. data << uint64(guid);
  8430. GetSession()->SendPacket(&data);
  8431. }
  8432. void Player::SendTalentWipeConfirm(ObjectGuid guid)
  8433. {
  8434. WorldPacket data(MSG_TALENT_WIPE_CONFIRM, 8 + 4);
  8435. data << uint64(guid);
  8436. uint32 cost = sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST) ? 0 : GetNextResetTalentsCost();
  8437. data << cost;
  8438. GetSession()->SendPacket(&data);
  8439. }
  8440. void Player::ResetPetTalents()
  8441. {
  8442. // This needs another gossip option + NPC text as a confirmation.
  8443. // The confirmation gossip listid has the text: "Yes, please do."
  8444. Pet* pet = GetPet();
  8445. if (!pet || pet->getPetType() != HUNTER_PET || pet->m_usedTalentCount == 0)
  8446. return;
  8447. CharmInfo* charmInfo = pet->GetCharmInfo();
  8448. if (!charmInfo)
  8449. {
  8450. TC_LOG_ERROR("entities.player", "Object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
  8451. return;
  8452. }
  8453. pet->resetTalents();
  8454. SendTalentsInfoData(true);
  8455. }
  8456. /*********************************************************/
  8457. /*** STORAGE SYSTEM ***/
  8458. /*********************************************************/
  8459. void Player::SetVirtualItemSlot(uint8 i, Item* item)
  8460. {
  8461. ASSERT(i < 3);
  8462. if (i < 2 && item)
  8463. {
  8464. if (!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
  8465. return;
  8466. uint32 charges = item->GetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT);
  8467. if (charges == 0)
  8468. return;
  8469. if (charges > 1)
  8470. item->SetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT, charges-1);
  8471. else if (charges <= 1)
  8472. {
  8473. ApplyEnchantment(item, TEMP_ENCHANTMENT_SLOT, false);
  8474. item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
  8475. }
  8476. }
  8477. }
  8478. void Player::SetSheath(SheathState sheathed)
  8479. {
  8480. switch (sheathed)
  8481. {
  8482. case SHEATH_STATE_UNARMED: // no prepared weapon
  8483. SetVirtualItemSlot(0, NULL);
  8484. SetVirtualItemSlot(1, NULL);
  8485. SetVirtualItemSlot(2, NULL);
  8486. break;
  8487. case SHEATH_STATE_MELEE: // prepared melee weapon
  8488. SetVirtualItemSlot(0, GetWeaponForAttack(BASE_ATTACK, true));
  8489. SetVirtualItemSlot(1, GetWeaponForAttack(OFF_ATTACK, true));
  8490. SetVirtualItemSlot(2, NULL);
  8491. break;
  8492. case SHEATH_STATE_RANGED: // prepared ranged weapon
  8493. SetVirtualItemSlot(0, NULL);
  8494. SetVirtualItemSlot(1, NULL);
  8495. SetVirtualItemSlot(2, GetWeaponForAttack(RANGED_ATTACK, true));
  8496. break;
  8497. default:
  8498. SetVirtualItemSlot(0, NULL);
  8499. SetVirtualItemSlot(1, NULL);
  8500. SetVirtualItemSlot(2, NULL);
  8501. break;
  8502. }
  8503. Unit::SetSheath(sheathed); // this must visualize Sheath changing for other players...
  8504. }
  8505. uint8 Player::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) const
  8506. {
  8507. uint8 playerClass = getClass();
  8508. uint8 slots[4];
  8509. slots[0] = NULL_SLOT;
  8510. slots[1] = NULL_SLOT;
  8511. slots[2] = NULL_SLOT;
  8512. slots[3] = NULL_SLOT;
  8513. switch (proto->InventoryType)
  8514. {
  8515. case INVTYPE_HEAD:
  8516. slots[0] = EQUIPMENT_SLOT_HEAD;
  8517. break;
  8518. case INVTYPE_NECK:
  8519. slots[0] = EQUIPMENT_SLOT_NECK;
  8520. break;
  8521. case INVTYPE_SHOULDERS:
  8522. slots[0] = EQUIPMENT_SLOT_SHOULDERS;
  8523. break;
  8524. case INVTYPE_BODY:
  8525. slots[0] = EQUIPMENT_SLOT_BODY;
  8526. break;
  8527. case INVTYPE_CHEST:
  8528. slots[0] = EQUIPMENT_SLOT_CHEST;
  8529. break;
  8530. case INVTYPE_ROBE:
  8531. slots[0] = EQUIPMENT_SLOT_CHEST;
  8532. break;
  8533. case INVTYPE_WAIST:
  8534. slots[0] = EQUIPMENT_SLOT_WAIST;
  8535. break;
  8536. case INVTYPE_LEGS:
  8537. slots[0] = EQUIPMENT_SLOT_LEGS;
  8538. break;
  8539. case INVTYPE_FEET:
  8540. slots[0] = EQUIPMENT_SLOT_FEET;
  8541. break;
  8542. case INVTYPE_WRISTS:
  8543. slots[0] = EQUIPMENT_SLOT_WRISTS;
  8544. break;
  8545. case INVTYPE_HANDS:
  8546. slots[0] = EQUIPMENT_SLOT_HANDS;
  8547. break;
  8548. case INVTYPE_FINGER:
  8549. slots[0] = EQUIPMENT_SLOT_FINGER1;
  8550. slots[1] = EQUIPMENT_SLOT_FINGER2;
  8551. break;
  8552. case INVTYPE_TRINKET:
  8553. slots[0] = EQUIPMENT_SLOT_TRINKET1;
  8554. slots[1] = EQUIPMENT_SLOT_TRINKET2;
  8555. break;
  8556. case INVTYPE_CLOAK:
  8557. slots[0] = EQUIPMENT_SLOT_BACK;
  8558. break;
  8559. case INVTYPE_WEAPON:
  8560. {
  8561. slots[0] = EQUIPMENT_SLOT_MAINHAND;
  8562. // suggest offhand slot only if know dual wielding
  8563. // (this will be replace mainhand weapon at auto equip instead unwonted "you don't known dual wielding" ...
  8564. if (CanDualWield())
  8565. slots[1] = EQUIPMENT_SLOT_OFFHAND;
  8566. break;
  8567. }
  8568. case INVTYPE_SHIELD:
  8569. slots[0] = EQUIPMENT_SLOT_OFFHAND;
  8570. break;
  8571. case INVTYPE_RANGED:
  8572. slots[0] = EQUIPMENT_SLOT_RANGED;
  8573. break;
  8574. case INVTYPE_2HWEAPON:
  8575. slots[0] = EQUIPMENT_SLOT_MAINHAND;
  8576. if (Item* mhWeapon = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND))
  8577. {
  8578. if (ItemTemplate const* mhWeaponProto = mhWeapon->GetTemplate())
  8579. {
  8580. if (mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF)
  8581. {
  8582. const_cast<Player*>(this)->AutoUnequipOffhandIfNeed(true);
  8583. break;
  8584. }
  8585. }
  8586. }
  8587. if (GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
  8588. {
  8589. if (proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF)
  8590. {
  8591. const_cast<Player*>(this)->AutoUnequipOffhandIfNeed(true);
  8592. break;
  8593. }
  8594. }
  8595. if (CanDualWield() && CanTitanGrip() && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF)
  8596. slots[1] = EQUIPMENT_SLOT_OFFHAND;
  8597. break;
  8598. case INVTYPE_TABARD:
  8599. slots[0] = EQUIPMENT_SLOT_TABARD;
  8600. break;
  8601. case INVTYPE_WEAPONMAINHAND:
  8602. slots[0] = EQUIPMENT_SLOT_MAINHAND;
  8603. break;
  8604. case INVTYPE_WEAPONOFFHAND:
  8605. slots[0] = EQUIPMENT_SLOT_OFFHAND;
  8606. break;
  8607. case INVTYPE_HOLDABLE:
  8608. slots[0] = EQUIPMENT_SLOT_OFFHAND;
  8609. break;
  8610. case INVTYPE_THROWN:
  8611. slots[0] = EQUIPMENT_SLOT_RANGED;
  8612. break;
  8613. case INVTYPE_RANGEDRIGHT:
  8614. slots[0] = EQUIPMENT_SLOT_RANGED;
  8615. break;
  8616. case INVTYPE_BAG:
  8617. slots[0] = INVENTORY_SLOT_BAG_START + 0;
  8618. slots[1] = INVENTORY_SLOT_BAG_START + 1;
  8619. slots[2] = INVENTORY_SLOT_BAG_START + 2;
  8620. slots[3] = INVENTORY_SLOT_BAG_START + 3;
  8621. break;
  8622. case INVTYPE_RELIC:
  8623. {
  8624. if (playerClass == CLASS_PALADIN || playerClass == CLASS_DRUID ||
  8625. playerClass == CLASS_SHAMAN || playerClass == CLASS_DEATH_KNIGHT)
  8626. slots[0] = EQUIPMENT_SLOT_RANGED;
  8627. break;
  8628. }
  8629. default:
  8630. return NULL_SLOT;
  8631. }
  8632. if (slot != NULL_SLOT)
  8633. {
  8634. if (swap || !GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
  8635. for (uint8 i = 0; i < 4; ++i)
  8636. if (slots[i] == slot)
  8637. return slot;
  8638. }
  8639. else
  8640. {
  8641. // search free slot at first
  8642. for (uint8 i = 0; i < 4; ++i)
  8643. if (slots[i] != NULL_SLOT && !GetItemByPos(INVENTORY_SLOT_BAG_0, slots[i]))
  8644. // in case 2hand equipped weapon (without titan grip) offhand slot empty but not free
  8645. if (slots[i] != EQUIPMENT_SLOT_OFFHAND || !IsTwoHandUsed())
  8646. return slots[i];
  8647. // if not found free and can swap return first appropriate from used
  8648. for (uint8 i = 0; i < 4; ++i)
  8649. if (slots[i] != NULL_SLOT && swap)
  8650. return slots[i];
  8651. }
  8652. // no free position
  8653. return NULL_SLOT;
  8654. }
  8655. InventoryResult Player::CanUnequipItems(uint32 item, uint32 count) const
  8656. {
  8657. uint32 tempcount = 0;
  8658. InventoryResult res = EQUIP_ERR_OK;
  8659. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
  8660. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  8661. if (pItem->GetEntry() == item)
  8662. {
  8663. InventoryResult ires = CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false);
  8664. if (ires == EQUIP_ERR_OK)
  8665. {
  8666. tempcount += pItem->GetCount();
  8667. if (tempcount >= count)
  8668. return EQUIP_ERR_OK;
  8669. }
  8670. else
  8671. res = ires;
  8672. }
  8673. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  8674. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  8675. if (pItem->GetEntry() == item)
  8676. {
  8677. tempcount += pItem->GetCount();
  8678. if (tempcount >= count)
  8679. return EQUIP_ERR_OK;
  8680. }
  8681. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  8682. if (Bag* pBag = GetBagByPos(i))
  8683. for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
  8684. if (Item* pItem = GetItemByPos(i, j))
  8685. if (pItem->GetEntry() == item)
  8686. {
  8687. tempcount += pItem->GetCount();
  8688. if (tempcount >= count)
  8689. return EQUIP_ERR_OK;
  8690. }
  8691. // not found req. item count and have unequippable items
  8692. return res;
  8693. }
  8694. uint32 Player::GetItemCount(uint32 item, bool inBankAlso, Item* skipItem) const
  8695. {
  8696. uint32 count = 0;
  8697. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
  8698. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  8699. if (pItem != skipItem && pItem->GetEntry() == item)
  8700. count += pItem->GetCount();
  8701. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  8702. if (Bag* pBag = GetBagByPos(i))
  8703. count += pBag->GetItemCount(item, skipItem);
  8704. if (skipItem && skipItem->GetTemplate()->GemProperties)
  8705. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  8706. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  8707. if (pItem != skipItem && pItem->GetTemplate()->Socket[0].Color)
  8708. count += pItem->GetGemCountWithID(item);
  8709. if (inBankAlso)
  8710. {
  8711. // checking every item from 39 to 74 (including bank bags)
  8712. for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_BAG_END; ++i)
  8713. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  8714. if (pItem != skipItem && pItem->GetEntry() == item)
  8715. count += pItem->GetCount();
  8716. for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
  8717. if (Bag* pBag = GetBagByPos(i))
  8718. count += pBag->GetItemCount(item, skipItem);
  8719. if (skipItem && skipItem->GetTemplate()->GemProperties)
  8720. for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
  8721. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  8722. if (pItem != skipItem && pItem->GetTemplate()->Socket[0].Color)
  8723. count += pItem->GetGemCountWithID(item);
  8724. }
  8725. return count;
  8726. }
  8727. uint32 Player::GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipItem) const
  8728. {
  8729. uint32 count = 0;
  8730. for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  8731. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  8732. if (pItem != skipItem)
  8733. if (ItemTemplate const* pProto = pItem->GetTemplate())
  8734. if (pProto->ItemLimitCategory == limitCategory)
  8735. count += pItem->GetCount();
  8736. for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  8737. if (Bag* pBag = GetBagByPos(i))
  8738. count += pBag->GetItemCountWithLimitCategory(limitCategory, skipItem);
  8739. for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_BAG_END; ++i)
  8740. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  8741. if (pItem != skipItem)
  8742. if (ItemTemplate const* pProto = pItem->GetTemplate())
  8743. if (pProto->ItemLimitCategory == limitCategory)
  8744. count += pItem->GetCount();
  8745. for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
  8746. if (Bag* pBag = GetBagByPos(i))
  8747. count += pBag->GetItemCountWithLimitCategory(limitCategory, skipItem);
  8748. return count;
  8749. }
  8750. Item* Player::GetItemByGuid(ObjectGuid guid) const
  8751. {
  8752. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  8753. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  8754. if (pItem->GetGUID() == guid)
  8755. return pItem;
  8756. for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_BAG_END; ++i)
  8757. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  8758. if (pItem->GetGUID() == guid)
  8759. return pItem;
  8760. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  8761. if (Bag* pBag = GetBagByPos(i))
  8762. for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
  8763. if (Item* pItem = pBag->GetItemByPos(j))
  8764. if (pItem->GetGUID() == guid)
  8765. return pItem;
  8766. for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
  8767. if (Bag* pBag = GetBagByPos(i))
  8768. for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
  8769. if (Item* pItem = pBag->GetItemByPos(j))
  8770. if (pItem->GetGUID() == guid)
  8771. return pItem;
  8772. return NULL;
  8773. }
  8774. Item* Player::GetItemByPos(uint16 pos) const
  8775. {
  8776. uint8 bag = pos >> 8;
  8777. uint8 slot = pos & 255;
  8778. return GetItemByPos(bag, slot);
  8779. }
  8780. Item* Player::GetItemByPos(uint8 bag, uint8 slot) const
  8781. {
  8782. if (bag == INVENTORY_SLOT_BAG_0 && slot < BANK_SLOT_BAG_END)
  8783. return m_items[slot];
  8784. else if (Bag* pBag = GetBagByPos(bag))
  8785. return pBag->GetItemByPos(slot);
  8786. return NULL;
  8787. }
  8788. //Does additional check for disarmed weapons
  8789. Item* Player::GetUseableItemByPos(uint8 bag, uint8 slot) const
  8790. {
  8791. if (!CanUseAttackType(GetAttackBySlot(slot)))
  8792. return NULL;
  8793. return GetItemByPos(bag, slot);
  8794. }
  8795. Bag* Player::GetBagByPos(uint8 bag) const
  8796. {
  8797. if ((bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)
  8798. || (bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END))
  8799. if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, bag))
  8800. return item->ToBag();
  8801. return NULL;
  8802. }
  8803. Item* Player::GetWeaponForAttack(WeaponAttackType attackType, bool useable /*= false*/) const
  8804. {
  8805. uint8 slot;
  8806. switch (attackType)
  8807. {
  8808. case BASE_ATTACK: slot = EQUIPMENT_SLOT_MAINHAND; break;
  8809. case OFF_ATTACK: slot = EQUIPMENT_SLOT_OFFHAND; break;
  8810. case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break;
  8811. default: return NULL;
  8812. }
  8813. Item* item = NULL;
  8814. if (useable)
  8815. item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, slot);
  8816. else
  8817. item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
  8818. if (!item || item->GetTemplate()->Class != ITEM_CLASS_WEAPON)
  8819. return NULL;
  8820. if (!useable)
  8821. return item;
  8822. if (item->IsBroken() || IsInFeralForm())
  8823. return NULL;
  8824. return item;
  8825. }
  8826. Item* Player::GetShield(bool useable) const
  8827. {
  8828. Item* item = NULL;
  8829. if (useable)
  8830. item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
  8831. else
  8832. item = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
  8833. if (!item || item->GetTemplate()->Class != ITEM_CLASS_ARMOR)
  8834. return NULL;
  8835. if (!useable)
  8836. return item;
  8837. if (item->IsBroken())
  8838. return NULL;
  8839. return item;
  8840. }
  8841. uint8 Player::GetAttackBySlot(uint8 slot)
  8842. {
  8843. switch (slot)
  8844. {
  8845. case EQUIPMENT_SLOT_MAINHAND: return BASE_ATTACK;
  8846. case EQUIPMENT_SLOT_OFFHAND: return OFF_ATTACK;
  8847. case EQUIPMENT_SLOT_RANGED: return RANGED_ATTACK;
  8848. default: return MAX_ATTACK;
  8849. }
  8850. }
  8851. bool Player::IsInventoryPos(uint8 bag, uint8 slot)
  8852. {
  8853. if (bag == INVENTORY_SLOT_BAG_0 && slot == NULL_SLOT)
  8854. return true;
  8855. if (bag == INVENTORY_SLOT_BAG_0 && (slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END))
  8856. return true;
  8857. if (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)
  8858. return true;
  8859. return false;
  8860. }
  8861. bool Player::IsEquipmentPos(uint8 bag, uint8 slot)
  8862. {
  8863. if (bag == INVENTORY_SLOT_BAG_0 && (slot < EQUIPMENT_SLOT_END))
  8864. return true;
  8865. if (bag == INVENTORY_SLOT_BAG_0 && (slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END))
  8866. return true;
  8867. return false;
  8868. }
  8869. bool Player::IsBankPos(uint8 bag, uint8 slot)
  8870. {
  8871. if (bag == INVENTORY_SLOT_BAG_0 && (slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END))
  8872. return true;
  8873. if (bag == INVENTORY_SLOT_BAG_0 && (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END))
  8874. return true;
  8875. if (bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END)
  8876. return true;
  8877. return false;
  8878. }
  8879. bool Player::IsBagPos(uint16 pos)
  8880. {
  8881. uint8 bag = pos >> 8;
  8882. uint8 slot = pos & 255;
  8883. if (bag == INVENTORY_SLOT_BAG_0 && (slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END))
  8884. return true;
  8885. if (bag == INVENTORY_SLOT_BAG_0 && (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END))
  8886. return true;
  8887. return false;
  8888. }
  8889. bool Player::IsValidPos(uint8 bag, uint8 slot, bool explicit_pos)
  8890. {
  8891. // post selected
  8892. if (bag == NULL_BAG && !explicit_pos)
  8893. return true;
  8894. if (bag == INVENTORY_SLOT_BAG_0)
  8895. {
  8896. // any post selected
  8897. if (slot == NULL_SLOT && !explicit_pos)
  8898. return true;
  8899. // equipment
  8900. if (slot < EQUIPMENT_SLOT_END)
  8901. return true;
  8902. // bag equip slots
  8903. if (slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END)
  8904. return true;
  8905. // backpack slots
  8906. if (slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END)
  8907. return true;
  8908. // bank main slots
  8909. if (slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END)
  8910. return true;
  8911. // bank bag slots
  8912. if (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END)
  8913. return true;
  8914. return false;
  8915. }
  8916. // bag content slots
  8917. // bank bag content slots
  8918. if (Bag* pBag = GetBagByPos(bag))
  8919. {
  8920. // any post selected
  8921. if (slot == NULL_SLOT && !explicit_pos)
  8922. return true;
  8923. return slot < pBag->GetBagSize();
  8924. }
  8925. // where this?
  8926. return false;
  8927. }
  8928. bool Player::HasItemCount(uint32 item, uint32 count, bool inBankAlso) const
  8929. {
  8930. uint32 tempcount = 0;
  8931. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
  8932. {
  8933. Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
  8934. if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade())
  8935. {
  8936. tempcount += pItem->GetCount();
  8937. if (tempcount >= count)
  8938. return true;
  8939. }
  8940. }
  8941. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  8942. {
  8943. if (Bag* pBag = GetBagByPos(i))
  8944. {
  8945. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  8946. {
  8947. Item* pItem = GetItemByPos(i, j);
  8948. if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade())
  8949. {
  8950. tempcount += pItem->GetCount();
  8951. if (tempcount >= count)
  8952. return true;
  8953. }
  8954. }
  8955. }
  8956. }
  8957. if (inBankAlso)
  8958. {
  8959. for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
  8960. {
  8961. Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
  8962. if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade())
  8963. {
  8964. tempcount += pItem->GetCount();
  8965. if (tempcount >= count)
  8966. return true;
  8967. }
  8968. }
  8969. for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
  8970. {
  8971. if (Bag* pBag = GetBagByPos(i))
  8972. {
  8973. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  8974. {
  8975. Item* pItem = GetItemByPos(i, j);
  8976. if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade())
  8977. {
  8978. tempcount += pItem->GetCount();
  8979. if (tempcount >= count)
  8980. return true;
  8981. }
  8982. }
  8983. }
  8984. }
  8985. }
  8986. return false;
  8987. }
  8988. bool Player::HasItemOrGemWithIdEquipped(uint32 item, uint32 count, uint8 except_slot) const
  8989. {
  8990. uint32 tempcount = 0;
  8991. for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
  8992. {
  8993. if (i == except_slot)
  8994. continue;
  8995. Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
  8996. if (pItem && pItem->GetEntry() == item)
  8997. {
  8998. tempcount += pItem->GetCount();
  8999. if (tempcount >= count)
  9000. return true;
  9001. }
  9002. }
  9003. ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(item);
  9004. if (pProto && pProto->GemProperties)
  9005. {
  9006. for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
  9007. {
  9008. if (i == except_slot)
  9009. continue;
  9010. Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
  9011. if (pItem && pItem->GetTemplate()->Socket[0].Color)
  9012. {
  9013. tempcount += pItem->GetGemCountWithID(item);
  9014. if (tempcount >= count)
  9015. return true;
  9016. }
  9017. }
  9018. }
  9019. return false;
  9020. }
  9021. bool Player::HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot) const
  9022. {
  9023. uint32 tempcount = 0;
  9024. for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
  9025. {
  9026. if (i == except_slot)
  9027. continue;
  9028. Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
  9029. if (!pItem)
  9030. continue;
  9031. ItemTemplate const* pProto = pItem->GetTemplate();
  9032. if (!pProto)
  9033. continue;
  9034. if (pProto->ItemLimitCategory == limitCategory)
  9035. {
  9036. tempcount += pItem->GetCount();
  9037. if (tempcount >= count)
  9038. return true;
  9039. }
  9040. if (pProto->Socket[0].Color || pItem->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT))
  9041. {
  9042. tempcount += pItem->GetGemCountWithLimitCategory(limitCategory);
  9043. if (tempcount >= count)
  9044. return true;
  9045. }
  9046. }
  9047. return false;
  9048. }
  9049. InventoryResult Player::CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count /*= NULL*/, uint32* itemLimitCategory /*= NULL*/) const
  9050. {
  9051. ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(entry);
  9052. if (!pProto)
  9053. {
  9054. if (no_space_count)
  9055. *no_space_count = count;
  9056. return EQUIP_ERR_ITEM_MAX_COUNT;
  9057. }
  9058. if (pItem && pItem->m_lootGenerated)
  9059. return EQUIP_ERR_LOOT_GONE;
  9060. // no maximum
  9061. if ((pProto->MaxCount <= 0 && pProto->ItemLimitCategory == 0) || pProto->MaxCount == 2147483647)
  9062. return EQUIP_ERR_OK;
  9063. if (pProto->MaxCount > 0)
  9064. {
  9065. uint32 curcount = GetItemCount(pProto->ItemId, true, pItem);
  9066. if (curcount + count > uint32(pProto->MaxCount))
  9067. {
  9068. if (no_space_count)
  9069. *no_space_count = count + curcount - pProto->MaxCount;
  9070. return EQUIP_ERR_ITEM_MAX_COUNT;
  9071. }
  9072. }
  9073. // check unique-equipped limit
  9074. if (pProto->ItemLimitCategory)
  9075. {
  9076. ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(pProto->ItemLimitCategory);
  9077. if (!limitEntry)
  9078. {
  9079. if (no_space_count)
  9080. *no_space_count = count;
  9081. return EQUIP_ERR_NOT_EQUIPPABLE;
  9082. }
  9083. if (limitEntry->mode == ITEM_LIMIT_CATEGORY_MODE_HAVE)
  9084. {
  9085. uint32 curcount = GetItemCountWithLimitCategory(pProto->ItemLimitCategory, pItem);
  9086. if (curcount + count > uint32(limitEntry->maxCount))
  9087. {
  9088. if (no_space_count)
  9089. *no_space_count = count + curcount - limitEntry->maxCount;
  9090. if (itemLimitCategory)
  9091. *itemLimitCategory = pProto->ItemLimitCategory;
  9092. return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS;
  9093. }
  9094. }
  9095. }
  9096. return EQUIP_ERR_OK;
  9097. }
  9098. InventoryResult Player::CanStoreNewItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count /*= NULL*/) const
  9099. {
  9100. return CanStoreItem(bag, slot, dest, item, count, NULL, false, no_space_count);
  9101. }
  9102. InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, Item* pItem, bool swap /*= false*/) const
  9103. {
  9104. if (!pItem)
  9105. return EQUIP_ERR_ITEM_NOT_FOUND;
  9106. uint32 count = pItem->GetCount();
  9107. return CanStoreItem(bag, slot, dest, pItem->GetEntry(), count, pItem, swap, NULL);
  9108. }
  9109. InventoryResult Player::CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot, ItemPosCountVec &dest, ItemTemplate const* pProto, uint32& count, bool swap, Item* pSrcItem) const
  9110. {
  9111. Item* pItem2 = GetItemByPos(bag, slot);
  9112. // ignore move item (this slot will be empty at move)
  9113. if (pItem2 == pSrcItem)
  9114. pItem2 = NULL;
  9115. uint32 need_space;
  9116. if (pSrcItem && pSrcItem->IsNotEmptyBag() && !IsBagPos(uint16(bag) << 8 | slot))
  9117. return EQUIP_ERR_DESTROY_NONEMPTY_BAG;
  9118. // empty specific slot - check item fit to slot
  9119. if (!pItem2 || swap)
  9120. {
  9121. if (bag == INVENTORY_SLOT_BAG_0)
  9122. {
  9123. // prevent cheating
  9124. if ((slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END) || slot >= PLAYER_SLOT_END)
  9125. return EQUIP_ERR_WRONG_BAG_TYPE;
  9126. }
  9127. else
  9128. {
  9129. Bag* pBag = GetBagByPos(bag);
  9130. if (!pBag)
  9131. return EQUIP_ERR_WRONG_BAG_TYPE;
  9132. ItemTemplate const* pBagProto = pBag->GetTemplate();
  9133. if (!pBagProto)
  9134. return EQUIP_ERR_WRONG_BAG_TYPE;
  9135. if (slot >= pBagProto->ContainerSlots)
  9136. return EQUIP_ERR_WRONG_BAG_TYPE;
  9137. if (!ItemCanGoIntoBag(pProto, pBagProto))
  9138. return EQUIP_ERR_WRONG_BAG_TYPE;
  9139. }
  9140. // non empty stack with space
  9141. need_space = pProto->GetMaxStackSize();
  9142. }
  9143. // non empty slot, check item type
  9144. else
  9145. {
  9146. // can be merged at least partly
  9147. InventoryResult res = pItem2->CanBeMergedPartlyWith(pProto);
  9148. if (res != EQUIP_ERR_OK)
  9149. return res;
  9150. // free stack space or infinity
  9151. need_space = pProto->GetMaxStackSize() - pItem2->GetCount();
  9152. }
  9153. if (need_space > count)
  9154. need_space = count;
  9155. ItemPosCount newPosition = ItemPosCount((bag << 8) | slot, need_space);
  9156. if (!newPosition.isContainedIn(dest))
  9157. {
  9158. dest.push_back(newPosition);
  9159. count -= need_space;
  9160. }
  9161. return EQUIP_ERR_OK;
  9162. }
  9163. InventoryResult Player::CanStoreItem_InBag(uint8 bag, ItemPosCountVec &dest, ItemTemplate const* pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const
  9164. {
  9165. // skip specific bag already processed in first called CanStoreItem_InBag
  9166. if (bag == skip_bag)
  9167. return EQUIP_ERR_WRONG_BAG_TYPE;
  9168. // skip not existed bag or self targeted bag
  9169. Bag* pBag = GetBagByPos(bag);
  9170. if (!pBag || pBag == pSrcItem)
  9171. return EQUIP_ERR_WRONG_BAG_TYPE;
  9172. if (pSrcItem && pSrcItem->IsNotEmptyBag())
  9173. return EQUIP_ERR_DESTROY_NONEMPTY_BAG;
  9174. ItemTemplate const* pBagProto = pBag->GetTemplate();
  9175. if (!pBagProto)
  9176. return EQUIP_ERR_WRONG_BAG_TYPE;
  9177. // specialized bag mode or non-specilized
  9178. if (non_specialized != (pBagProto->Class == ITEM_CLASS_CONTAINER && pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER))
  9179. return EQUIP_ERR_WRONG_BAG_TYPE;
  9180. if (!ItemCanGoIntoBag(pProto, pBagProto))
  9181. return EQUIP_ERR_WRONG_BAG_TYPE;
  9182. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  9183. {
  9184. // skip specific slot already processed in first called CanStoreItem_InSpecificSlot
  9185. if (j == skip_slot)
  9186. continue;
  9187. Item* pItem2 = GetItemByPos(bag, j);
  9188. // ignore move item (this slot will be empty at move)
  9189. if (pItem2 == pSrcItem)
  9190. pItem2 = NULL;
  9191. // if merge skip empty, if !merge skip non-empty
  9192. if ((pItem2 != NULL) != merge)
  9193. continue;
  9194. uint32 need_space = pProto->GetMaxStackSize();
  9195. if (pItem2)
  9196. {
  9197. // can be merged at least partly
  9198. uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
  9199. if (res != EQUIP_ERR_OK)
  9200. continue;
  9201. // descrease at current stacksize
  9202. need_space -= pItem2->GetCount();
  9203. }
  9204. if (need_space > count)
  9205. need_space = count;
  9206. ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space);
  9207. if (!newPosition.isContainedIn(dest))
  9208. {
  9209. dest.push_back(newPosition);
  9210. count -= need_space;
  9211. if (count==0)
  9212. return EQUIP_ERR_OK;
  9213. }
  9214. }
  9215. return EQUIP_ERR_OK;
  9216. }
  9217. InventoryResult Player::CanStoreItem_InInventorySlots(uint8 slot_begin, uint8 slot_end, ItemPosCountVec &dest, ItemTemplate const* pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const
  9218. {
  9219. //this is never called for non-bag slots so we can do this
  9220. if (pSrcItem && pSrcItem->IsNotEmptyBag())
  9221. return EQUIP_ERR_DESTROY_NONEMPTY_BAG;
  9222. for (uint32 j = slot_begin; j < slot_end; j++)
  9223. {
  9224. // skip specific slot already processed in first called CanStoreItem_InSpecificSlot
  9225. if (INVENTORY_SLOT_BAG_0 == skip_bag && j == skip_slot)
  9226. continue;
  9227. Item* pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, j);
  9228. // ignore move item (this slot will be empty at move)
  9229. if (pItem2 == pSrcItem)
  9230. pItem2 = NULL;
  9231. // if merge skip empty, if !merge skip non-empty
  9232. if ((pItem2 != NULL) != merge)
  9233. continue;
  9234. uint32 need_space = pProto->GetMaxStackSize();
  9235. if (pItem2)
  9236. {
  9237. // can be merged at least partly
  9238. uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
  9239. if (res != EQUIP_ERR_OK)
  9240. continue;
  9241. // descrease at current stacksize
  9242. need_space -= pItem2->GetCount();
  9243. }
  9244. if (need_space > count)
  9245. need_space = count;
  9246. ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space);
  9247. if (!newPosition.isContainedIn(dest))
  9248. {
  9249. dest.push_back(newPosition);
  9250. count -= need_space;
  9251. if (count==0)
  9252. return EQUIP_ERR_OK;
  9253. }
  9254. }
  9255. return EQUIP_ERR_OK;
  9256. }
  9257. InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 entry, uint32 count, Item* pItem, bool swap, uint32* no_space_count) const
  9258. {
  9259. TC_LOG_DEBUG("entities.player.items", "STORAGE: CanStoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, entry, count);
  9260. ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(entry);
  9261. if (!pProto)
  9262. {
  9263. if (no_space_count)
  9264. *no_space_count = count;
  9265. return swap ? EQUIP_ERR_CANT_SWAP : EQUIP_ERR_ITEM_NOT_FOUND;
  9266. }
  9267. if (pItem)
  9268. {
  9269. // item used
  9270. if (pItem->m_lootGenerated)
  9271. {
  9272. if (no_space_count)
  9273. *no_space_count = count;
  9274. return EQUIP_ERR_LOOT_GONE;
  9275. }
  9276. if (pItem->IsBindedNotWith(this))
  9277. {
  9278. if (no_space_count)
  9279. *no_space_count = count;
  9280. return EQUIP_ERR_NOT_OWNER;
  9281. }
  9282. }
  9283. // check count of items (skip for auto move for same player from bank)
  9284. uint32 no_similar_count = 0; // can't store this amount similar items
  9285. InventoryResult res = CanTakeMoreSimilarItems(entry, count, pItem, &no_similar_count);
  9286. if (res != EQUIP_ERR_OK)
  9287. {
  9288. if (count == no_similar_count)
  9289. {
  9290. if (no_space_count)
  9291. *no_space_count = no_similar_count;
  9292. return res;
  9293. }
  9294. count -= no_similar_count;
  9295. }
  9296. // in specific slot
  9297. if (bag != NULL_BAG && slot != NULL_SLOT)
  9298. {
  9299. res = CanStoreItem_InSpecificSlot(bag, slot, dest, pProto, count, swap, pItem);
  9300. if (res != EQUIP_ERR_OK)
  9301. {
  9302. if (no_space_count)
  9303. *no_space_count = count + no_similar_count;
  9304. return res;
  9305. }
  9306. if (count == 0)
  9307. {
  9308. if (no_similar_count == 0)
  9309. return EQUIP_ERR_OK;
  9310. if (no_space_count)
  9311. *no_space_count = count + no_similar_count;
  9312. return EQUIP_ERR_ITEM_MAX_COUNT;
  9313. }
  9314. }
  9315. // not specific slot or have space for partly store only in specific slot
  9316. // in specific bag
  9317. if (bag != NULL_BAG)
  9318. {
  9319. // search stack in bag for merge to
  9320. if (pProto->Stackable != 1)
  9321. {
  9322. if (bag == INVENTORY_SLOT_BAG_0) // inventory
  9323. {
  9324. res = CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag, slot);
  9325. if (res != EQUIP_ERR_OK)
  9326. {
  9327. if (no_space_count)
  9328. *no_space_count = count + no_similar_count;
  9329. return res;
  9330. }
  9331. if (count == 0)
  9332. {
  9333. if (no_similar_count == 0)
  9334. return EQUIP_ERR_OK;
  9335. if (no_space_count)
  9336. *no_space_count = count + no_similar_count;
  9337. return EQUIP_ERR_ITEM_MAX_COUNT;
  9338. }
  9339. }
  9340. else // equipped bag
  9341. {
  9342. // we need check 2 time (specialized/non_specialized), use NULL_BAG to prevent skipping bag
  9343. res = CanStoreItem_InBag(bag, dest, pProto, count, true, false, pItem, NULL_BAG, slot);
  9344. if (res != EQUIP_ERR_OK)
  9345. res = CanStoreItem_InBag(bag, dest, pProto, count, true, true, pItem, NULL_BAG, slot);
  9346. if (res != EQUIP_ERR_OK)
  9347. {
  9348. if (no_space_count)
  9349. *no_space_count = count + no_similar_count;
  9350. return res;
  9351. }
  9352. if (count == 0)
  9353. {
  9354. if (no_similar_count == 0)
  9355. return EQUIP_ERR_OK;
  9356. if (no_space_count)
  9357. *no_space_count = count + no_similar_count;
  9358. return EQUIP_ERR_ITEM_MAX_COUNT;
  9359. }
  9360. }
  9361. }
  9362. // search free slot in bag for place to
  9363. if (bag == INVENTORY_SLOT_BAG_0) // inventory
  9364. {
  9365. res = CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot);
  9366. if (res != EQUIP_ERR_OK)
  9367. {
  9368. if (no_space_count)
  9369. *no_space_count = count + no_similar_count;
  9370. return res;
  9371. }
  9372. if (count == 0)
  9373. {
  9374. if (no_similar_count == 0)
  9375. return EQUIP_ERR_OK;
  9376. if (no_space_count)
  9377. *no_space_count = count + no_similar_count;
  9378. return EQUIP_ERR_ITEM_MAX_COUNT;
  9379. }
  9380. }
  9381. else // equipped bag
  9382. {
  9383. res = CanStoreItem_InBag(bag, dest, pProto, count, false, false, pItem, NULL_BAG, slot);
  9384. if (res != EQUIP_ERR_OK)
  9385. res = CanStoreItem_InBag(bag, dest, pProto, count, false, true, pItem, NULL_BAG, slot);
  9386. if (res != EQUIP_ERR_OK)
  9387. {
  9388. if (no_space_count)
  9389. *no_space_count = count + no_similar_count;
  9390. return res;
  9391. }
  9392. if (count == 0)
  9393. {
  9394. if (no_similar_count == 0)
  9395. return EQUIP_ERR_OK;
  9396. if (no_space_count)
  9397. *no_space_count = count + no_similar_count;
  9398. return EQUIP_ERR_ITEM_MAX_COUNT;
  9399. }
  9400. }
  9401. }
  9402. // not specific bag or have space for partly store only in specific bag
  9403. // search stack for merge to
  9404. if (pProto->Stackable != 1)
  9405. {
  9406. res = CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag, slot);
  9407. if (res != EQUIP_ERR_OK)
  9408. {
  9409. if (no_space_count)
  9410. *no_space_count = count + no_similar_count;
  9411. return res;
  9412. }
  9413. if (count == 0)
  9414. {
  9415. if (no_similar_count == 0)
  9416. return EQUIP_ERR_OK;
  9417. if (no_space_count)
  9418. *no_space_count = count + no_similar_count;
  9419. return EQUIP_ERR_ITEM_MAX_COUNT;
  9420. }
  9421. if (pProto->BagFamily)
  9422. {
  9423. for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  9424. {
  9425. res = CanStoreItem_InBag(i, dest, pProto, count, true, false, pItem, bag, slot);
  9426. if (res != EQUIP_ERR_OK)
  9427. continue;
  9428. if (count == 0)
  9429. {
  9430. if (no_similar_count == 0)
  9431. return EQUIP_ERR_OK;
  9432. if (no_space_count)
  9433. *no_space_count = count + no_similar_count;
  9434. return EQUIP_ERR_ITEM_MAX_COUNT;
  9435. }
  9436. }
  9437. }
  9438. for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  9439. {
  9440. res = CanStoreItem_InBag(i, dest, pProto, count, true, true, pItem, bag, slot);
  9441. if (res != EQUIP_ERR_OK)
  9442. continue;
  9443. if (count == 0)
  9444. {
  9445. if (no_similar_count == 0)
  9446. return EQUIP_ERR_OK;
  9447. if (no_space_count)
  9448. *no_space_count = count + no_similar_count;
  9449. return EQUIP_ERR_ITEM_MAX_COUNT;
  9450. }
  9451. }
  9452. }
  9453. // search free slot - special bag case
  9454. if (pProto->BagFamily)
  9455. {
  9456. for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  9457. {
  9458. res = CanStoreItem_InBag(i, dest, pProto, count, false, false, pItem, bag, slot);
  9459. if (res != EQUIP_ERR_OK)
  9460. continue;
  9461. if (count == 0)
  9462. {
  9463. if (no_similar_count == 0)
  9464. return EQUIP_ERR_OK;
  9465. if (no_space_count)
  9466. *no_space_count = count + no_similar_count;
  9467. return EQUIP_ERR_ITEM_MAX_COUNT;
  9468. }
  9469. }
  9470. }
  9471. if (pItem && pItem->IsNotEmptyBag())
  9472. return EQUIP_ERR_BAG_IN_BAG;
  9473. // search free slot
  9474. res = CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot);
  9475. if (res != EQUIP_ERR_OK)
  9476. {
  9477. if (no_space_count)
  9478. *no_space_count = count + no_similar_count;
  9479. return res;
  9480. }
  9481. if (count == 0)
  9482. {
  9483. if (no_similar_count == 0)
  9484. return EQUIP_ERR_OK;
  9485. if (no_space_count)
  9486. *no_space_count = count + no_similar_count;
  9487. return EQUIP_ERR_ITEM_MAX_COUNT;
  9488. }
  9489. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  9490. {
  9491. res = CanStoreItem_InBag(i, dest, pProto, count, false, true, pItem, bag, slot);
  9492. if (res != EQUIP_ERR_OK)
  9493. continue;
  9494. if (count == 0)
  9495. {
  9496. if (no_similar_count == 0)
  9497. return EQUIP_ERR_OK;
  9498. if (no_space_count)
  9499. *no_space_count = count + no_similar_count;
  9500. return EQUIP_ERR_ITEM_MAX_COUNT;
  9501. }
  9502. }
  9503. if (no_space_count)
  9504. *no_space_count = count + no_similar_count;
  9505. return EQUIP_ERR_INV_FULL;
  9506. }
  9507. //////////////////////////////////////////////////////////////////////////
  9508. InventoryResult Player::CanStoreItems(Item** items, int count, uint32* itemLimitCategory) const
  9509. {
  9510. Item* item2;
  9511. // fill space table
  9512. uint32 inventoryCounts[INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START];
  9513. uint32 bagCounts[INVENTORY_SLOT_BAG_END - INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE];
  9514. memset(inventoryCounts, 0, sizeof(uint32) * (INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START));
  9515. memset(bagCounts, 0, sizeof(uint32) * (INVENTORY_SLOT_BAG_END - INVENTORY_SLOT_BAG_START) * MAX_BAG_SIZE);
  9516. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
  9517. {
  9518. item2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
  9519. if (item2 && !item2->IsInTrade())
  9520. inventoryCounts[i - INVENTORY_SLOT_ITEM_START] = item2->GetCount();
  9521. }
  9522. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  9523. if (Bag* pBag = GetBagByPos(i))
  9524. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  9525. {
  9526. item2 = GetItemByPos(i, j);
  9527. if (item2 && !item2->IsInTrade())
  9528. bagCounts[i - INVENTORY_SLOT_BAG_START][j] = item2->GetCount();
  9529. }
  9530. // check free space for all items
  9531. for (int k = 0; k < count; ++k)
  9532. {
  9533. Item* item = items[k];
  9534. // no item
  9535. if (!item)
  9536. continue;
  9537. TC_LOG_DEBUG("entities.player.items", "STORAGE: CanStoreItems %i. item = %u, count = %u", k + 1, item->GetEntry(), item->GetCount());
  9538. ItemTemplate const* pProto = item->GetTemplate();
  9539. // strange item
  9540. if (!pProto)
  9541. return EQUIP_ERR_ITEM_NOT_FOUND;
  9542. // item used
  9543. if (item->m_lootGenerated)
  9544. return EQUIP_ERR_LOOT_GONE;
  9545. // item it 'bind'
  9546. if (item->IsBindedNotWith(this))
  9547. return EQUIP_ERR_NOT_OWNER;
  9548. ItemTemplate const* pBagProto;
  9549. // item is 'one item only'
  9550. InventoryResult res = CanTakeMoreSimilarItems(item, itemLimitCategory);
  9551. if (res != EQUIP_ERR_OK)
  9552. return res;
  9553. // search stack for merge to
  9554. if (pProto->Stackable != 1)
  9555. {
  9556. bool b_found = false;
  9557. for (int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t)
  9558. {
  9559. item2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t);
  9560. if (item2 && item2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inventoryCounts[t-INVENTORY_SLOT_ITEM_START] + item->GetCount() <= pProto->GetMaxStackSize())
  9561. {
  9562. inventoryCounts[t-INVENTORY_SLOT_ITEM_START] += item->GetCount();
  9563. b_found = true;
  9564. break;
  9565. }
  9566. }
  9567. if (b_found)
  9568. continue;
  9569. for (int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
  9570. {
  9571. if (Bag* bag = GetBagByPos(t))
  9572. {
  9573. if (ItemCanGoIntoBag(item->GetTemplate(), bag->GetTemplate()))
  9574. {
  9575. for (uint32 j = 0; j < bag->GetBagSize(); j++)
  9576. {
  9577. item2 = GetItemByPos(t, j);
  9578. if (item2 && item2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && bagCounts[t-INVENTORY_SLOT_BAG_START][j] + item->GetCount() <= pProto->GetMaxStackSize())
  9579. {
  9580. bagCounts[t-INVENTORY_SLOT_BAG_START][j] += item->GetCount();
  9581. b_found = true;
  9582. break;
  9583. }
  9584. }
  9585. }
  9586. }
  9587. }
  9588. if (b_found)
  9589. continue;
  9590. }
  9591. // special bag case
  9592. if (pProto->BagFamily)
  9593. {
  9594. bool b_found = false;
  9595. for (int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
  9596. {
  9597. if (Bag* bag = GetBagByPos(t))
  9598. {
  9599. pBagProto = bag->GetTemplate();
  9600. // not plain container check
  9601. if (pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER) &&
  9602. ItemCanGoIntoBag(pProto, pBagProto))
  9603. {
  9604. for (uint32 j = 0; j < bag->GetBagSize(); j++)
  9605. {
  9606. if (bagCounts[t-INVENTORY_SLOT_BAG_START][j] == 0)
  9607. {
  9608. bagCounts[t-INVENTORY_SLOT_BAG_START][j] = 1;
  9609. b_found = true;
  9610. break;
  9611. }
  9612. }
  9613. }
  9614. }
  9615. }
  9616. if (b_found)
  9617. continue;
  9618. }
  9619. // search free slot
  9620. bool b_found = false;
  9621. for (int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t)
  9622. {
  9623. if (inventoryCounts[t-INVENTORY_SLOT_ITEM_START] == 0)
  9624. {
  9625. inventoryCounts[t-INVENTORY_SLOT_ITEM_START] = 1;
  9626. b_found = true;
  9627. break;
  9628. }
  9629. }
  9630. if (b_found)
  9631. continue;
  9632. // search free slot in bags
  9633. for (int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
  9634. {
  9635. if (Bag* bag = GetBagByPos(t))
  9636. {
  9637. pBagProto = bag->GetTemplate();
  9638. // special bag already checked
  9639. if (pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER))
  9640. continue;
  9641. for (uint32 j = 0; j < bag->GetBagSize(); j++)
  9642. {
  9643. if (bagCounts[t-INVENTORY_SLOT_BAG_START][j] == 0)
  9644. {
  9645. bagCounts[t-INVENTORY_SLOT_BAG_START][j] = 1;
  9646. b_found = true;
  9647. break;
  9648. }
  9649. }
  9650. }
  9651. }
  9652. // no free slot found?
  9653. if (!b_found)
  9654. return EQUIP_ERR_BAG_FULL;
  9655. }
  9656. return EQUIP_ERR_OK;
  9657. }
  9658. //////////////////////////////////////////////////////////////////////////
  9659. InventoryResult Player::CanEquipNewItem(uint8 slot, uint16 &dest, uint32 item, bool swap) const
  9660. {
  9661. dest = 0;
  9662. Item* pItem = Item::CreateItem(item, 1, this);
  9663. if (pItem)
  9664. {
  9665. InventoryResult result = CanEquipItem(slot, dest, pItem, swap);
  9666. delete pItem;
  9667. return result;
  9668. }
  9669. return EQUIP_ERR_ITEM_NOT_FOUND;
  9670. }
  9671. InventoryResult Player::CanEquipItem(uint8 slot, uint16 &dest, Item* pItem, bool swap, bool not_loading) const
  9672. {
  9673. dest = 0;
  9674. if (pItem)
  9675. {
  9676. TC_LOG_DEBUG("entities.player.items", "STORAGE: CanEquipItem slot = %u, item = %u, count = %u", slot, pItem->GetEntry(), pItem->GetCount());
  9677. ItemTemplate const* pProto = pItem->GetTemplate();
  9678. if (pProto)
  9679. {
  9680. // item used
  9681. if (pItem->m_lootGenerated)
  9682. return EQUIP_ERR_LOOT_GONE;
  9683. if (pItem->IsBindedNotWith(this))
  9684. return EQUIP_ERR_NOT_OWNER;
  9685. // check count of items (skip for auto move for same player from bank)
  9686. InventoryResult res = CanTakeMoreSimilarItems(pItem);
  9687. if (res != EQUIP_ERR_OK)
  9688. return res;
  9689. // check this only in game
  9690. if (not_loading)
  9691. {
  9692. // May be here should be more stronger checks; STUNNED checked
  9693. // ROOT, CONFUSED, DISTRACTED, FLEEING this needs to be checked.
  9694. if (HasUnitState(UNIT_STATE_STUNNED))
  9695. return EQUIP_ERR_GENERIC_STUNNED;
  9696. // do not allow equipping gear except weapons, offhands, projectiles, relics in
  9697. // - combat
  9698. // - in-progress arenas
  9699. if (!pProto->CanChangeEquipStateInCombat())
  9700. {
  9701. if (IsInCombat())
  9702. return EQUIP_ERR_NOT_IN_COMBAT;
  9703. if (Battleground* bg = GetBattleground())
  9704. if (bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS)
  9705. return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
  9706. }
  9707. if (IsInCombat()&& (pProto->Class == ITEM_CLASS_WEAPON || pProto->InventoryType == INVTYPE_RELIC) && m_weaponChangeTimer != 0)
  9708. return EQUIP_ERR_CLIENT_LOCKED_OUT; // maybe exist better err
  9709. if (IsNonMeleeSpellCast(false))
  9710. return EQUIP_ERR_CLIENT_LOCKED_OUT;
  9711. }
  9712. ScalingStatDistributionEntry const* ssd = pProto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(pProto->ScalingStatDistribution) : 0;
  9713. // check allowed level (extend range to upper values if MaxLevel more or equal max player level, this let GM set high level with 1...max range items)
  9714. if (ssd && ssd->MaxLevel < DEFAULT_MAX_LEVEL && ssd->MaxLevel < getLevel())
  9715. return EQUIP_ERR_NOT_EQUIPPABLE;
  9716. uint8 eslot = FindEquipSlot(pProto, slot, swap);
  9717. if (eslot == NULL_SLOT)
  9718. return EQUIP_ERR_NOT_EQUIPPABLE;
  9719. res = CanUseItem(pItem, not_loading);
  9720. if (res != EQUIP_ERR_OK)
  9721. return res;
  9722. if (!swap && GetItemByPos(INVENTORY_SLOT_BAG_0, eslot))
  9723. return EQUIP_ERR_NO_SLOT_AVAILABLE;
  9724. // if we are swapping 2 equiped items, CanEquipUniqueItem check
  9725. // should ignore the item we are trying to swap, and not the
  9726. // destination item. CanEquipUniqueItem should ignore destination
  9727. // item only when we are swapping weapon from bag
  9728. uint8 ignore = uint8(NULL_SLOT);
  9729. switch (eslot)
  9730. {
  9731. case EQUIPMENT_SLOT_MAINHAND:
  9732. ignore = EQUIPMENT_SLOT_OFFHAND;
  9733. break;
  9734. case EQUIPMENT_SLOT_OFFHAND:
  9735. ignore = EQUIPMENT_SLOT_MAINHAND;
  9736. break;
  9737. case EQUIPMENT_SLOT_FINGER1:
  9738. ignore = EQUIPMENT_SLOT_FINGER2;
  9739. break;
  9740. case EQUIPMENT_SLOT_FINGER2:
  9741. ignore = EQUIPMENT_SLOT_FINGER1;
  9742. break;
  9743. case EQUIPMENT_SLOT_TRINKET1:
  9744. ignore = EQUIPMENT_SLOT_TRINKET2;
  9745. break;
  9746. case EQUIPMENT_SLOT_TRINKET2:
  9747. ignore = EQUIPMENT_SLOT_TRINKET1;
  9748. break;
  9749. }
  9750. if (ignore == uint8(NULL_SLOT) || pItem != GetItemByPos(INVENTORY_SLOT_BAG_0, ignore))
  9751. ignore = eslot;
  9752. InventoryResult res2 = CanEquipUniqueItem(pItem, swap ? ignore : uint8(NULL_SLOT));
  9753. if (res2 != EQUIP_ERR_OK)
  9754. return res2;
  9755. // check unique-equipped special item classes
  9756. if (pProto->Class == ITEM_CLASS_QUIVER)
  9757. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  9758. if (Item* pBag = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  9759. if (pBag != pItem)
  9760. if (ItemTemplate const* pBagProto = pBag->GetTemplate())
  9761. if (pBagProto->Class == pProto->Class && (!swap || pBag->GetSlot() != eslot))
  9762. return (pBagProto->SubClass == ITEM_SUBCLASS_AMMO_POUCH)
  9763. ? EQUIP_ERR_ONLY_ONE_AMMO
  9764. : EQUIP_ERR_ONLY_ONE_QUIVER;
  9765. uint32 type = pProto->InventoryType;
  9766. if (eslot == EQUIPMENT_SLOT_OFFHAND)
  9767. {
  9768. // Do not allow polearm to be equipped in the offhand (rare case for the only 1h polearm 41750)
  9769. if (type == INVTYPE_WEAPON && pProto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM)
  9770. return EQUIP_ERR_2HSKILLNOTFOUND;
  9771. else if (type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND)
  9772. {
  9773. if (!CanDualWield())
  9774. return EQUIP_ERR_2HSKILLNOTFOUND;
  9775. }
  9776. else if (type == INVTYPE_2HWEAPON)
  9777. {
  9778. if (!CanDualWield() || !CanTitanGrip())
  9779. return EQUIP_ERR_2HSKILLNOTFOUND;
  9780. }
  9781. if (IsTwoHandUsed())
  9782. return EQUIP_ERR_2HANDED_EQUIPPED;
  9783. }
  9784. // equip two-hand weapon case (with possible unequip 2 items)
  9785. if (type == INVTYPE_2HWEAPON)
  9786. {
  9787. if (eslot == EQUIPMENT_SLOT_OFFHAND)
  9788. {
  9789. if (!CanTitanGrip())
  9790. return EQUIP_ERR_NOT_EQUIPPABLE;
  9791. }
  9792. else if (eslot != EQUIPMENT_SLOT_MAINHAND)
  9793. return EQUIP_ERR_NOT_EQUIPPABLE;
  9794. if (!CanTitanGrip())
  9795. {
  9796. // offhand item must can be stored in inventory for offhand item and it also must be unequipped
  9797. Item* offItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
  9798. ItemPosCountVec off_dest;
  9799. if (offItem && (!not_loading ||
  9800. CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0) << 8 | EQUIPMENT_SLOT_OFFHAND, false) != EQUIP_ERR_OK ||
  9801. CanStoreItem(NULL_BAG, NULL_SLOT, off_dest, offItem, false) != EQUIP_ERR_OK))
  9802. return swap ? EQUIP_ERR_CANT_SWAP : EQUIP_ERR_INV_FULL;
  9803. }
  9804. }
  9805. dest = ((INVENTORY_SLOT_BAG_0 << 8) | eslot);
  9806. return EQUIP_ERR_OK;
  9807. }
  9808. }
  9809. return !swap ? EQUIP_ERR_ITEM_NOT_FOUND : EQUIP_ERR_CANT_SWAP;
  9810. }
  9811. InventoryResult Player::CanUnequipItem(uint16 pos, bool swap) const
  9812. {
  9813. // Applied only to equipped items and bank bags
  9814. if (!IsEquipmentPos(pos) && !IsBagPos(pos))
  9815. return EQUIP_ERR_OK;
  9816. Item* pItem = GetItemByPos(pos);
  9817. // Applied only to existed equipped item
  9818. if (!pItem)
  9819. return EQUIP_ERR_OK;
  9820. TC_LOG_DEBUG("entities.player.items", "STORAGE: CanUnequipItem slot = %u, item = %u, count = %u", pos, pItem->GetEntry(), pItem->GetCount());
  9821. ItemTemplate const* pProto = pItem->GetTemplate();
  9822. if (!pProto)
  9823. return EQUIP_ERR_ITEM_NOT_FOUND;
  9824. // item used
  9825. if (pItem->m_lootGenerated)
  9826. return EQUIP_ERR_LOOT_GONE;
  9827. // do not allow unequipping gear except weapons, offhands, projectiles, relics in
  9828. // - combat
  9829. // - in-progress arenas
  9830. if (!pProto->CanChangeEquipStateInCombat())
  9831. {
  9832. if (IsInCombat())
  9833. return EQUIP_ERR_NOT_IN_COMBAT;
  9834. if (Battleground* bg = GetBattleground())
  9835. if (bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS)
  9836. return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
  9837. }
  9838. if (!swap && pItem->IsNotEmptyBag())
  9839. return EQUIP_ERR_DESTROY_NONEMPTY_BAG;
  9840. return EQUIP_ERR_OK;
  9841. }
  9842. InventoryResult Player::CanBankItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, Item* pItem, bool swap, bool not_loading) const
  9843. {
  9844. if (!pItem)
  9845. return swap ? EQUIP_ERR_CANT_SWAP : EQUIP_ERR_ITEM_NOT_FOUND;
  9846. uint32 count = pItem->GetCount();
  9847. TC_LOG_DEBUG("entities.player.items", "STORAGE: CanBankItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), pItem->GetCount());
  9848. ItemTemplate const* pProto = pItem->GetTemplate();
  9849. if (!pProto)
  9850. return swap ? EQUIP_ERR_CANT_SWAP : EQUIP_ERR_ITEM_NOT_FOUND;
  9851. // item used
  9852. if (pItem->m_lootGenerated)
  9853. return EQUIP_ERR_LOOT_GONE;
  9854. if (pItem->IsBindedNotWith(this))
  9855. return EQUIP_ERR_NOT_OWNER;
  9856. // Currency Tokenizer are not supposed to be swapped out of their hidden bag
  9857. if (pItem->IsCurrencyToken())
  9858. {
  9859. TC_LOG_ERROR("entities.player", "Possible hacking attempt: Player %s [guid: %u] tried to move token [guid: %u, entry: %u] out of the currency bag!",
  9860. GetName().c_str(), GetGUIDLow(), pItem->GetGUIDLow(), pProto->ItemId);
  9861. return EQUIP_ERR_CANT_SWAP;
  9862. }
  9863. // check count of items (skip for auto move for same player from bank)
  9864. InventoryResult res = CanTakeMoreSimilarItems(pItem);
  9865. if (res != EQUIP_ERR_OK)
  9866. return res;
  9867. // in specific slot
  9868. if (bag != NULL_BAG && slot != NULL_SLOT)
  9869. {
  9870. if (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END)
  9871. {
  9872. if (!pItem->IsBag())
  9873. return EQUIP_ERR_WRONG_SLOT;
  9874. if (slot - BANK_SLOT_BAG_START >= GetBankBagSlotCount())
  9875. return EQUIP_ERR_NO_BANK_SLOT;
  9876. res = CanUseItem(pItem, not_loading);
  9877. if (res != EQUIP_ERR_OK)
  9878. return res;
  9879. }
  9880. res = CanStoreItem_InSpecificSlot(bag, slot, dest, pProto, count, swap, pItem);
  9881. if (res != EQUIP_ERR_OK)
  9882. return res;
  9883. if (count == 0)
  9884. return EQUIP_ERR_OK;
  9885. }
  9886. // not specific slot or have space for partly store only in specific slot
  9887. // in specific bag
  9888. if (bag != NULL_BAG)
  9889. {
  9890. if (pItem->IsNotEmptyBag())
  9891. return EQUIP_ERR_BAG_IN_BAG;
  9892. // search stack in bag for merge to
  9893. if (pProto->Stackable != 1)
  9894. {
  9895. if (bag == INVENTORY_SLOT_BAG_0)
  9896. {
  9897. res = CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START, BANK_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag, slot);
  9898. if (res != EQUIP_ERR_OK)
  9899. return res;
  9900. if (count == 0)
  9901. return EQUIP_ERR_OK;
  9902. }
  9903. else
  9904. {
  9905. res = CanStoreItem_InBag(bag, dest, pProto, count, true, false, pItem, NULL_BAG, slot);
  9906. if (res != EQUIP_ERR_OK)
  9907. res = CanStoreItem_InBag(bag, dest, pProto, count, true, true, pItem, NULL_BAG, slot);
  9908. if (res != EQUIP_ERR_OK)
  9909. return res;
  9910. if (count == 0)
  9911. return EQUIP_ERR_OK;
  9912. }
  9913. }
  9914. // search free slot in bag
  9915. if (bag == INVENTORY_SLOT_BAG_0)
  9916. {
  9917. res = CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START, BANK_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot);
  9918. if (res != EQUIP_ERR_OK)
  9919. return res;
  9920. if (count == 0)
  9921. return EQUIP_ERR_OK;
  9922. }
  9923. else
  9924. {
  9925. res = CanStoreItem_InBag(bag, dest, pProto, count, false, false, pItem, NULL_BAG, slot);
  9926. if (res != EQUIP_ERR_OK)
  9927. res = CanStoreItem_InBag(bag, dest, pProto, count, false, true, pItem, NULL_BAG, slot);
  9928. if (res != EQUIP_ERR_OK)
  9929. return res;
  9930. if (count == 0)
  9931. return EQUIP_ERR_OK;
  9932. }
  9933. }
  9934. // not specific bag or have space for partly store only in specific bag
  9935. // search stack for merge to
  9936. if (pProto->Stackable != 1)
  9937. {
  9938. // in slots
  9939. res = CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START, BANK_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag, slot);
  9940. if (res != EQUIP_ERR_OK)
  9941. return res;
  9942. if (count == 0)
  9943. return EQUIP_ERR_OK;
  9944. // in special bags
  9945. if (pProto->BagFamily)
  9946. {
  9947. for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
  9948. {
  9949. res = CanStoreItem_InBag(i, dest, pProto, count, true, false, pItem, bag, slot);
  9950. if (res != EQUIP_ERR_OK)
  9951. continue;
  9952. if (count == 0)
  9953. return EQUIP_ERR_OK;
  9954. }
  9955. }
  9956. for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
  9957. {
  9958. res = CanStoreItem_InBag(i, dest, pProto, count, true, true, pItem, bag, slot);
  9959. if (res != EQUIP_ERR_OK)
  9960. continue;
  9961. if (count == 0)
  9962. return EQUIP_ERR_OK;
  9963. }
  9964. }
  9965. // search free place in special bag
  9966. if (pProto->BagFamily)
  9967. {
  9968. for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
  9969. {
  9970. res = CanStoreItem_InBag(i, dest, pProto, count, false, false, pItem, bag, slot);
  9971. if (res != EQUIP_ERR_OK)
  9972. continue;
  9973. if (count == 0)
  9974. return EQUIP_ERR_OK;
  9975. }
  9976. }
  9977. // search free space
  9978. res = CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START, BANK_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot);
  9979. if (res != EQUIP_ERR_OK)
  9980. return res;
  9981. if (count == 0)
  9982. return EQUIP_ERR_OK;
  9983. for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
  9984. {
  9985. res = CanStoreItem_InBag(i, dest, pProto, count, false, true, pItem, bag, slot);
  9986. if (res != EQUIP_ERR_OK)
  9987. continue;
  9988. if (count == 0)
  9989. return EQUIP_ERR_OK;
  9990. }
  9991. return EQUIP_ERR_BANK_FULL;
  9992. }
  9993. InventoryResult Player::CanUseItem(Item* pItem, bool not_loading) const
  9994. {
  9995. if (pItem)
  9996. {
  9997. TC_LOG_DEBUG("entities.player.items", "STORAGE: CanUseItem item = %u", pItem->GetEntry());
  9998. if (!IsAlive() && not_loading)
  9999. return EQUIP_ERR_PLAYER_DEAD;
  10000. //if (isStunned())
  10001. // return EQUIP_ERR_GENERIC_STUNNED;
  10002. ItemTemplate const* pProto = pItem->GetTemplate();
  10003. if (pProto)
  10004. {
  10005. if (pItem->IsBindedNotWith(this))
  10006. return EQUIP_ERR_NOT_OWNER;
  10007. InventoryResult res = CanUseItem(pProto);
  10008. if (res != EQUIP_ERR_OK)
  10009. return res;
  10010. if (pItem->GetSkill() != 0)
  10011. {
  10012. bool allowEquip = false;
  10013. uint32 itemSkill = pItem->GetSkill();
  10014. // Armor that is binded to account can "morph" from plate to mail, etc. if skill is not learned yet.
  10015. if (pProto->Quality == ITEM_QUALITY_HEIRLOOM && pProto->Class == ITEM_CLASS_ARMOR && !HasSkill(itemSkill))
  10016. {
  10017. /// @todo when you right-click already equipped item it throws EQUIP_ERR_PROFICIENCY_NEEDED.
  10018. // In fact it's a visual bug, everything works properly... I need sniffs of operations with
  10019. // binded to account items from off server.
  10020. switch (getClass())
  10021. {
  10022. case CLASS_HUNTER:
  10023. case CLASS_SHAMAN:
  10024. allowEquip = (itemSkill == SKILL_MAIL);
  10025. break;
  10026. case CLASS_PALADIN:
  10027. case CLASS_WARRIOR:
  10028. allowEquip = (itemSkill == SKILL_PLATE_MAIL);
  10029. break;
  10030. }
  10031. }
  10032. if (!allowEquip && GetSkillValue(itemSkill) == 0)
  10033. return EQUIP_ERR_PROFICIENCY_NEEDED;
  10034. }
  10035. if (pProto->RequiredReputationFaction && uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank)
  10036. return EQUIP_ERR_CANT_EQUIP_REPUTATION;
  10037. return EQUIP_ERR_OK;
  10038. }
  10039. }
  10040. return EQUIP_ERR_ITEM_NOT_FOUND;
  10041. }
  10042. InventoryResult Player::CanUseItem(ItemTemplate const* proto) const
  10043. {
  10044. // Used by group, function NeedBeforeGreed, to know if a prototype can be used by a player
  10045. if (!proto)
  10046. return EQUIP_ERR_ITEM_NOT_FOUND;
  10047. if ((proto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && GetTeam() != HORDE)
  10048. return EQUIP_ERR_CANT_EQUIP_EVER;
  10049. if ((proto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && GetTeam() != ALLIANCE)
  10050. return EQUIP_ERR_CANT_EQUIP_EVER;
  10051. if ((proto->AllowableClass & getClassMask()) == 0 || (proto->AllowableRace & getRaceMask()) == 0)
  10052. return EQUIP_ERR_CANT_EQUIP_EVER;
  10053. if (proto->RequiredSkill != 0)
  10054. {
  10055. if (GetSkillValue(proto->RequiredSkill) == 0)
  10056. return EQUIP_ERR_PROFICIENCY_NEEDED;
  10057. else if (GetSkillValue(proto->RequiredSkill) < proto->RequiredSkillRank)
  10058. return EQUIP_ERR_CANT_EQUIP_SKILL;
  10059. }
  10060. if (proto->RequiredSpell != 0 && !HasSpell(proto->RequiredSpell))
  10061. return EQUIP_ERR_PROFICIENCY_NEEDED;
  10062. if (getLevel() < proto->RequiredLevel)
  10063. return EQUIP_ERR_CANT_EQUIP_LEVEL_I;
  10064. // If World Event is not active, prevent using event dependant items
  10065. if (proto->HolidayId && !IsHolidayActive((HolidayIds)proto->HolidayId))
  10066. return EQUIP_ERR_CLIENT_LOCKED_OUT;
  10067. // learning (recipes, mounts, pets, etc.)
  10068. if (proto->Spells[0].SpellId == 483 || proto->Spells[0].SpellId == 55884)
  10069. if (HasSpell(proto->Spells[1].SpellId))
  10070. return EQUIP_ERR_INTERNAL_BAG_ERROR;
  10071. return EQUIP_ERR_OK;
  10072. }
  10073. InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObject const* lootedObject) const
  10074. {
  10075. if (!GetGroup() || !GetGroup()->isLFGGroup())
  10076. return EQUIP_ERR_OK; // not in LFG group
  10077. // check if looted object is inside the lfg dungeon
  10078. Map const* map = lootedObject->GetMap();
  10079. if (!sLFGMgr->inLfgDungeonMap(GetGroup()->GetGUID(), map->GetId(), map->GetDifficulty()))
  10080. return EQUIP_ERR_OK;
  10081. if (!proto)
  10082. return EQUIP_ERR_ITEM_NOT_FOUND;
  10083. // Used by group, function NeedBeforeGreed, to know if a prototype can be used by a player
  10084. const static uint32 item_weapon_skills[MAX_ITEM_SUBCLASS_WEAPON] =
  10085. {
  10086. SKILL_AXES, SKILL_2H_AXES, SKILL_BOWS, SKILL_GUNS, SKILL_MACES,
  10087. SKILL_2H_MACES, SKILL_POLEARMS, SKILL_SWORDS, SKILL_2H_SWORDS, 0,
  10088. SKILL_STAVES, 0, 0, SKILL_FIST_WEAPONS, 0,
  10089. SKILL_DAGGERS, SKILL_THROWN, SKILL_ASSASSINATION, SKILL_CROSSBOWS, SKILL_WANDS,
  10090. SKILL_FISHING
  10091. }; //Copy from function Item::GetSkill()
  10092. if ((proto->AllowableClass & getClassMask()) == 0 || (proto->AllowableRace & getRaceMask()) == 0)
  10093. return EQUIP_ERR_CANT_EQUIP_EVER;
  10094. if (proto->RequiredSpell != 0 && !HasSpell(proto->RequiredSpell))
  10095. return EQUIP_ERR_PROFICIENCY_NEEDED;
  10096. if (proto->RequiredSkill != 0)
  10097. {
  10098. if (!GetSkillValue(proto->RequiredSkill))
  10099. return EQUIP_ERR_PROFICIENCY_NEEDED;
  10100. else if (GetSkillValue(proto->RequiredSkill) < proto->RequiredSkillRank)
  10101. return EQUIP_ERR_CANT_EQUIP_SKILL;
  10102. }
  10103. uint8 _class = getClass();
  10104. if (proto->Class == ITEM_CLASS_WEAPON && GetSkillValue(item_weapon_skills[proto->SubClass]) == 0)
  10105. return EQUIP_ERR_PROFICIENCY_NEEDED;
  10106. if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass > ITEM_SUBCLASS_ARMOR_MISCELLANEOUS && proto->SubClass < ITEM_SUBCLASS_ARMOR_BUCKLER && proto->InventoryType != INVTYPE_CLOAK)
  10107. {
  10108. if (_class == CLASS_WARRIOR || _class == CLASS_PALADIN || _class == CLASS_DEATH_KNIGHT)
  10109. {
  10110. if (getLevel() < 40)
  10111. {
  10112. if (proto->SubClass != ITEM_SUBCLASS_ARMOR_MAIL)
  10113. return EQUIP_ERR_CLIENT_LOCKED_OUT;
  10114. }
  10115. else if (proto->SubClass != ITEM_SUBCLASS_ARMOR_PLATE)
  10116. return EQUIP_ERR_CLIENT_LOCKED_OUT;
  10117. }
  10118. else if (_class == CLASS_HUNTER || _class == CLASS_SHAMAN)
  10119. {
  10120. if (getLevel() < 40)
  10121. {
  10122. if (proto->SubClass != ITEM_SUBCLASS_ARMOR_LEATHER)
  10123. return EQUIP_ERR_CLIENT_LOCKED_OUT;
  10124. }
  10125. else if (proto->SubClass != ITEM_SUBCLASS_ARMOR_MAIL)
  10126. return EQUIP_ERR_CLIENT_LOCKED_OUT;
  10127. }
  10128. if (_class == CLASS_ROGUE || _class == CLASS_DRUID)
  10129. if (proto->SubClass != ITEM_SUBCLASS_ARMOR_LEATHER)
  10130. return EQUIP_ERR_CLIENT_LOCKED_OUT;
  10131. if (_class == CLASS_MAGE || _class == CLASS_PRIEST || _class == CLASS_WARLOCK)
  10132. if (proto->SubClass != ITEM_SUBCLASS_ARMOR_CLOTH)
  10133. return EQUIP_ERR_CLIENT_LOCKED_OUT;
  10134. }
  10135. return EQUIP_ERR_OK;
  10136. }
  10137. // Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
  10138. Item* Player::StoreNewItem(ItemPosCountVec const& dest, uint32 item, bool update, int32 randomPropertyId, AllowedLooterSet const& allowedLooters)
  10139. {
  10140. uint32 count = 0;
  10141. for (ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ++itr)
  10142. count += itr->count;
  10143. Item* pItem = Item::CreateItem(item, count, this);
  10144. if (pItem)
  10145. {
  10146. ItemAddedQuestCheck(item, count);
  10147. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, item, count);
  10148. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM, item, 1);
  10149. if (randomPropertyId)
  10150. pItem->SetItemRandomProperties(randomPropertyId);
  10151. pItem = StoreItem(dest, pItem, update);
  10152. if (allowedLooters.size() > 1 && pItem->GetTemplate()->GetMaxStackSize() == 1 && pItem->IsSoulBound())
  10153. {
  10154. pItem->SetSoulboundTradeable(allowedLooters);
  10155. pItem->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, GetTotalPlayedTime());
  10156. AddTradeableItem(pItem);
  10157. // save data
  10158. std::ostringstream ss;
  10159. AllowedLooterSet::const_iterator itr = allowedLooters.begin();
  10160. ss << *itr;
  10161. for (++itr; itr != allowedLooters.end(); ++itr)
  10162. ss << ' ' << *itr;
  10163. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_BOP_TRADE);
  10164. stmt->setUInt32(0, pItem->GetGUIDLow());
  10165. stmt->setString(1, ss.str());
  10166. CharacterDatabase.Execute(stmt);
  10167. }
  10168. }
  10169. return pItem;
  10170. }
  10171. Item* Player::StoreItem(ItemPosCountVec const& dest, Item* pItem, bool update)
  10172. {
  10173. if (!pItem)
  10174. return NULL;
  10175. Item* lastItem = pItem;
  10176. for (ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end();)
  10177. {
  10178. uint16 pos = itr->pos;
  10179. uint32 count = itr->count;
  10180. ++itr;
  10181. if (itr == dest.end())
  10182. {
  10183. lastItem = _StoreItem(pos, pItem, count, false, update);
  10184. break;
  10185. }
  10186. lastItem = _StoreItem(pos, pItem, count, true, update);
  10187. }
  10188. return lastItem;
  10189. }
  10190. // Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
  10191. Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool update)
  10192. {
  10193. if (!pItem)
  10194. return NULL;
  10195. uint8 bag = pos >> 8;
  10196. uint8 slot = pos & 255;
  10197. TC_LOG_DEBUG("entities.player.items", "STORAGE: StoreItem bag = %u, slot = %u, item = %u, count = %u, guid = %u", bag, slot, pItem->GetEntry(), count, pItem->GetGUIDLow());
  10198. Item* pItem2 = GetItemByPos(bag, slot);
  10199. if (!pItem2)
  10200. {
  10201. if (clone)
  10202. pItem = pItem->CloneItem(count, this);
  10203. else
  10204. pItem->SetCount(count);
  10205. if (!pItem)
  10206. return NULL;
  10207. if (pItem->GetTemplate()->Bonding == BIND_WHEN_PICKED_UP ||
  10208. pItem->GetTemplate()->Bonding == BIND_QUEST_ITEM ||
  10209. (pItem->GetTemplate()->Bonding == BIND_WHEN_EQUIPED && IsBagPos(pos)))
  10210. pItem->SetBinding(true);
  10211. Bag* pBag = (bag == INVENTORY_SLOT_BAG_0) ? NULL : GetBagByPos(bag);
  10212. if (!pBag)
  10213. {
  10214. m_items[slot] = pItem;
  10215. SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), pItem->GetGUID());
  10216. pItem->SetGuidValue(ITEM_FIELD_CONTAINED, GetGUID());
  10217. pItem->SetGuidValue(ITEM_FIELD_OWNER, GetGUID());
  10218. pItem->SetSlot(slot);
  10219. pItem->SetContainer(NULL);
  10220. }
  10221. else
  10222. pBag->StoreItem(slot, pItem, update);
  10223. if (IsInWorld() && update)
  10224. {
  10225. pItem->AddToWorld();
  10226. pItem->SendUpdateToPlayer(this);
  10227. }
  10228. pItem->SetState(ITEM_CHANGED, this);
  10229. if (pBag)
  10230. pBag->SetState(ITEM_CHANGED, this);
  10231. AddEnchantmentDurations(pItem);
  10232. AddItemDurations(pItem);
  10233. const ItemTemplate* proto = pItem->GetTemplate();
  10234. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  10235. if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger
  10236. if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END))
  10237. if (!HasAura(proto->Spells[i].SpellId))
  10238. CastSpell(this, proto->Spells[i].SpellId, true, pItem);
  10239. return pItem;
  10240. }
  10241. else
  10242. {
  10243. if (pItem2->GetTemplate()->Bonding == BIND_WHEN_PICKED_UP ||
  10244. pItem2->GetTemplate()->Bonding == BIND_QUEST_ITEM ||
  10245. (pItem2->GetTemplate()->Bonding == BIND_WHEN_EQUIPED && IsBagPos(pos)))
  10246. pItem2->SetBinding(true);
  10247. pItem2->SetCount(pItem2->GetCount() + count);
  10248. if (IsInWorld() && update)
  10249. pItem2->SendUpdateToPlayer(this);
  10250. if (!clone)
  10251. {
  10252. // delete item (it not in any slot currently)
  10253. if (IsInWorld() && update)
  10254. {
  10255. pItem->RemoveFromWorld();
  10256. pItem->DestroyForPlayer(this);
  10257. }
  10258. RemoveEnchantmentDurations(pItem);
  10259. RemoveItemDurations(pItem);
  10260. pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
  10261. pItem->SetNotRefundable(this);
  10262. pItem->ClearSoulboundTradeable(this);
  10263. RemoveTradeableItem(pItem);
  10264. pItem->SetState(ITEM_REMOVED, this);
  10265. }
  10266. AddEnchantmentDurations(pItem2);
  10267. pItem2->SetState(ITEM_CHANGED, this);
  10268. const ItemTemplate* proto = pItem2->GetTemplate();
  10269. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  10270. if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger
  10271. if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END))
  10272. if (!HasAura(proto->Spells[i].SpellId))
  10273. CastSpell(this, proto->Spells[i].SpellId, true, pItem2);
  10274. return pItem2;
  10275. }
  10276. }
  10277. Item* Player::EquipNewItem(uint16 pos, uint32 item, bool update)
  10278. {
  10279. if (Item* pItem = Item::CreateItem(item, 1, this))
  10280. {
  10281. ItemAddedQuestCheck(item, 1);
  10282. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, item, 1);
  10283. return EquipItem(pos, pItem, update);
  10284. }
  10285. return NULL;
  10286. }
  10287. Item* Player::EquipItem(uint16 pos, Item* pItem, bool update)
  10288. {
  10289. AddEnchantmentDurations(pItem);
  10290. AddItemDurations(pItem);
  10291. uint8 bag = pos >> 8;
  10292. uint8 slot = pos & 255;
  10293. Item* pItem2 = GetItemByPos(bag, slot);
  10294. if (!pItem2)
  10295. {
  10296. VisualizeItem(slot, pItem);
  10297. if (IsAlive())
  10298. {
  10299. ItemTemplate const* pProto = pItem->GetTemplate();
  10300. // item set bonuses applied only at equip and removed at unequip, and still active for broken items
  10301. if (pProto && pProto->ItemSet)
  10302. AddItemsSetItem(this, pItem);
  10303. _ApplyItemMods(pItem, slot, true);
  10304. if (pProto && IsInCombat() && (pProto->Class == ITEM_CLASS_WEAPON || pProto->InventoryType == INVTYPE_RELIC) && m_weaponChangeTimer == 0)
  10305. {
  10306. uint32 cooldownSpell = getClass() == CLASS_ROGUE ? 6123 : 6119;
  10307. SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(cooldownSpell);
  10308. if (!spellProto)
  10309. TC_LOG_ERROR("entities.player", "Weapon switch cooldown spell %u couldn't be found in Spell.dbc", cooldownSpell);
  10310. else
  10311. {
  10312. m_weaponChangeTimer = spellProto->StartRecoveryTime;
  10313. GetGlobalCooldownMgr().AddGlobalCooldown(spellProto, m_weaponChangeTimer);
  10314. WorldPacket data;
  10315. BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_INCLUDE_GCD, cooldownSpell, 0);
  10316. GetSession()->SendPacket(&data);
  10317. }
  10318. }
  10319. }
  10320. if (IsInWorld() && update)
  10321. {
  10322. pItem->AddToWorld();
  10323. pItem->SendUpdateToPlayer(this);
  10324. }
  10325. ApplyEquipCooldown(pItem);
  10326. // update expertise and armor penetration - passive auras may need it
  10327. if (slot == EQUIPMENT_SLOT_MAINHAND)
  10328. UpdateExpertise(BASE_ATTACK);
  10329. else if (slot == EQUIPMENT_SLOT_OFFHAND)
  10330. UpdateExpertise(OFF_ATTACK);
  10331. switch (slot)
  10332. {
  10333. case EQUIPMENT_SLOT_MAINHAND:
  10334. case EQUIPMENT_SLOT_OFFHAND:
  10335. case EQUIPMENT_SLOT_RANGED:
  10336. RecalculateRating(CR_ARMOR_PENETRATION);
  10337. default:
  10338. break;
  10339. }
  10340. }
  10341. else
  10342. {
  10343. pItem2->SetCount(pItem2->GetCount() + pItem->GetCount());
  10344. if (IsInWorld() && update)
  10345. pItem2->SendUpdateToPlayer(this);
  10346. // delete item (it not in any slot currently)
  10347. //pItem->DeleteFromDB();
  10348. if (IsInWorld() && update)
  10349. {
  10350. pItem->RemoveFromWorld();
  10351. pItem->DestroyForPlayer(this);
  10352. }
  10353. RemoveEnchantmentDurations(pItem);
  10354. RemoveItemDurations(pItem);
  10355. pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
  10356. pItem->SetNotRefundable(this);
  10357. pItem->ClearSoulboundTradeable(this);
  10358. RemoveTradeableItem(pItem);
  10359. pItem->SetState(ITEM_REMOVED, this);
  10360. pItem2->SetState(ITEM_CHANGED, this);
  10361. ApplyEquipCooldown(pItem2);
  10362. return pItem2;
  10363. }
  10364. // only for full equip instead adding to stack
  10365. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry());
  10366. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM, pItem->GetEntry(), slot);
  10367. return pItem;
  10368. }
  10369. void Player::QuickEquipItem(uint16 pos, Item* pItem)
  10370. {
  10371. if (pItem)
  10372. {
  10373. AddEnchantmentDurations(pItem);
  10374. AddItemDurations(pItem);
  10375. uint8 slot = pos & 255;
  10376. VisualizeItem(slot, pItem);
  10377. if (IsInWorld())
  10378. {
  10379. pItem->AddToWorld();
  10380. pItem->SendUpdateToPlayer(this);
  10381. }
  10382. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry());
  10383. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM, pItem->GetEntry(), slot);
  10384. }
  10385. }
  10386. void Player::SetVisibleItemSlot(uint8 slot, Item* pItem)
  10387. {
  10388. if (pItem)
  10389. {
  10390. SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), pItem->GetVisibleEntry());
  10391. SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0, pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT));
  10392. SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 1, pItem->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT));
  10393. }
  10394. else
  10395. {
  10396. SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), 0);
  10397. SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0);
  10398. }
  10399. }
  10400. void Player::VisualizeItem(uint8 slot, Item* pItem)
  10401. {
  10402. if (!pItem)
  10403. return;
  10404. // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
  10405. if (pItem->GetTemplate()->Bonding == BIND_WHEN_EQUIPED || pItem->GetTemplate()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetTemplate()->Bonding == BIND_QUEST_ITEM)
  10406. pItem->SetBinding(true);
  10407. TC_LOG_DEBUG("entities.player.items", "STORAGE: EquipItem slot = %u, item = %u", slot, pItem->GetEntry());
  10408. m_items[slot] = pItem;
  10409. SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), pItem->GetGUID());
  10410. pItem->SetGuidValue(ITEM_FIELD_CONTAINED, GetGUID());
  10411. pItem->SetGuidValue(ITEM_FIELD_OWNER, GetGUID());
  10412. pItem->SetSlot(slot);
  10413. pItem->SetContainer(NULL);
  10414. if (slot < EQUIPMENT_SLOT_END)
  10415. SetVisibleItemSlot(slot, pItem);
  10416. pItem->SetState(ITEM_CHANGED, this);
  10417. }
  10418. Item* Player::BankItem(ItemPosCountVec const& dest, Item* pItem, bool update)
  10419. {
  10420. return StoreItem(dest, pItem, update);
  10421. }
  10422. void Player::RemoveItem(uint8 bag, uint8 slot, bool update)
  10423. {
  10424. // note: removeitem does not actually change the item
  10425. // it only takes the item out of storage temporarily
  10426. // note2: if removeitem is to be used for delinking
  10427. // the item must be removed from the player's updatequeue
  10428. Item* pItem = GetItemByPos(bag, slot);
  10429. if (pItem)
  10430. {
  10431. TC_LOG_DEBUG("entities.player.items", "STORAGE: RemoveItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
  10432. RemoveEnchantmentDurations(pItem);
  10433. RemoveItemDurations(pItem);
  10434. RemoveTradeableItem(pItem);
  10435. if (bag == INVENTORY_SLOT_BAG_0)
  10436. {
  10437. if (slot < INVENTORY_SLOT_BAG_END)
  10438. {
  10439. ItemTemplate const* pProto = pItem->GetTemplate();
  10440. // item set bonuses applied only at equip and removed at unequip, and still active for broken items
  10441. if (pProto && pProto->ItemSet)
  10442. RemoveItemsSetItem(this, pProto);
  10443. _ApplyItemMods(pItem, slot, false);
  10444. // remove item dependent auras and casts (only weapon and armor slots)
  10445. if (slot < EQUIPMENT_SLOT_END)
  10446. {
  10447. RemoveItemDependentAurasAndCasts(pItem);
  10448. // remove held enchantments, update expertise
  10449. if (slot == EQUIPMENT_SLOT_MAINHAND)
  10450. {
  10451. if (pItem->GetItemSuffixFactor())
  10452. {
  10453. pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_3);
  10454. pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_4);
  10455. }
  10456. else
  10457. {
  10458. pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0);
  10459. pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1);
  10460. }
  10461. UpdateExpertise(BASE_ATTACK);
  10462. }
  10463. else if (slot == EQUIPMENT_SLOT_OFFHAND)
  10464. UpdateExpertise(OFF_ATTACK);
  10465. // update armor penetration - passive auras may need it
  10466. switch (slot)
  10467. {
  10468. case EQUIPMENT_SLOT_MAINHAND:
  10469. case EQUIPMENT_SLOT_OFFHAND:
  10470. case EQUIPMENT_SLOT_RANGED:
  10471. RecalculateRating(CR_ARMOR_PENETRATION);
  10472. default:
  10473. break;
  10474. }
  10475. }
  10476. }
  10477. m_items[slot] = NULL;
  10478. SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), ObjectGuid::Empty);
  10479. if (slot < EQUIPMENT_SLOT_END)
  10480. SetVisibleItemSlot(slot, NULL);
  10481. }
  10482. else if (Bag* pBag = GetBagByPos(bag))
  10483. pBag->RemoveItem(slot, update);
  10484. pItem->SetGuidValue(ITEM_FIELD_CONTAINED, ObjectGuid::Empty);
  10485. // pItem->SetUInt64Value(ITEM_FIELD_OWNER, 0); not clear owner at remove (it will be set at store). This used in mail and auction code
  10486. pItem->SetSlot(NULL_SLOT);
  10487. if (IsInWorld() && update)
  10488. pItem->SendUpdateToPlayer(this);
  10489. }
  10490. }
  10491. // Common operation need to remove item from inventory without delete in trade, auction, guild bank, mail....
  10492. void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
  10493. {
  10494. if (Item* it = GetItemByPos(bag, slot))
  10495. {
  10496. ItemRemovedQuestCheck(it->GetEntry(), it->GetCount());
  10497. RemoveItem(bag, slot, update);
  10498. it->SetNotRefundable(this, false);
  10499. it->RemoveFromUpdateQueueOf(this);
  10500. if (it->IsInWorld())
  10501. {
  10502. it->RemoveFromWorld();
  10503. it->DestroyForPlayer(this);
  10504. }
  10505. }
  10506. }
  10507. // Common operation need to add item from inventory without delete in trade, guild bank, mail....
  10508. void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB)
  10509. {
  10510. // update quest counters
  10511. ItemAddedQuestCheck(pItem->GetEntry(), pItem->GetCount());
  10512. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, pItem->GetEntry(), pItem->GetCount());
  10513. // store item
  10514. Item* pLastItem = StoreItem(dest, pItem, update);
  10515. // only set if not merged to existed stack (pItem can be deleted already but we can compare pointers any way)
  10516. if (pLastItem == pItem)
  10517. {
  10518. // update owner for last item (this can be original item with wrong owner
  10519. if (pLastItem->GetOwnerGUID() != GetGUID())
  10520. pLastItem->SetOwnerGUID(GetGUID());
  10521. // if this original item then it need create record in inventory
  10522. // in case trade we already have item in other player inventory
  10523. pLastItem->SetState(in_characterInventoryDB ? ITEM_CHANGED : ITEM_NEW, this);
  10524. if (pLastItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
  10525. AddTradeableItem(pLastItem);
  10526. }
  10527. }
  10528. void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
  10529. {
  10530. Item* pItem = GetItemByPos(bag, slot);
  10531. if (pItem)
  10532. {
  10533. TC_LOG_DEBUG("entities.player.items", "STORAGE: DestroyItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
  10534. // Also remove all contained items if the item is a bag.
  10535. // This if () prevents item saving crashes if the condition for a bag to be empty before being destroyed was bypassed somehow.
  10536. if (pItem->IsNotEmptyBag())
  10537. for (uint8 i = 0; i < MAX_BAG_SIZE; ++i)
  10538. DestroyItem(slot, i, update);
  10539. if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
  10540. {
  10541. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
  10542. stmt->setUInt32(0, pItem->GetGUIDLow());
  10543. CharacterDatabase.Execute(stmt);
  10544. }
  10545. RemoveEnchantmentDurations(pItem);
  10546. RemoveItemDurations(pItem);
  10547. pItem->SetNotRefundable(this);
  10548. pItem->ClearSoulboundTradeable(this);
  10549. RemoveTradeableItem(pItem);
  10550. const ItemTemplate* proto = pItem->GetTemplate();
  10551. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  10552. if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger
  10553. RemoveAurasDueToSpell(proto->Spells[i].SpellId);
  10554. ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
  10555. sScriptMgr->OnItemRemove(this, pItem);
  10556. if (bag == INVENTORY_SLOT_BAG_0)
  10557. {
  10558. SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), ObjectGuid::Empty);
  10559. // equipment and equipped bags can have applied bonuses
  10560. if (slot < INVENTORY_SLOT_BAG_END)
  10561. {
  10562. ItemTemplate const* pProto = pItem->GetTemplate();
  10563. // item set bonuses applied only at equip and removed at unequip, and still active for broken items
  10564. if (pProto && pProto->ItemSet)
  10565. RemoveItemsSetItem(this, pProto);
  10566. _ApplyItemMods(pItem, slot, false);
  10567. }
  10568. if (slot < EQUIPMENT_SLOT_END)
  10569. {
  10570. // remove item dependent auras and casts (only weapon and armor slots)
  10571. RemoveItemDependentAurasAndCasts(pItem);
  10572. // update expertise and armor penetration - passive auras may need it
  10573. switch (slot)
  10574. {
  10575. case EQUIPMENT_SLOT_MAINHAND:
  10576. case EQUIPMENT_SLOT_OFFHAND:
  10577. case EQUIPMENT_SLOT_RANGED:
  10578. RecalculateRating(CR_ARMOR_PENETRATION);
  10579. default:
  10580. break;
  10581. }
  10582. if (slot == EQUIPMENT_SLOT_MAINHAND)
  10583. UpdateExpertise(BASE_ATTACK);
  10584. else if (slot == EQUIPMENT_SLOT_OFFHAND)
  10585. UpdateExpertise(OFF_ATTACK);
  10586. // equipment visual show
  10587. SetVisibleItemSlot(slot, NULL);
  10588. }
  10589. m_items[slot] = NULL;
  10590. }
  10591. else if (Bag* pBag = GetBagByPos(bag))
  10592. pBag->RemoveItem(slot, update);
  10593. // Delete rolled money / loot from db.
  10594. // MUST be done before RemoveFromWorld() or GetTemplate() fails
  10595. if (ItemTemplate const* pTmp = pItem->GetTemplate())
  10596. if (pTmp->Flags & ITEM_PROTO_FLAG_OPENABLE)
  10597. pItem->ItemContainerDeleteLootMoneyAndLootItemsFromDB();
  10598. if (IsInWorld() && update)
  10599. {
  10600. pItem->RemoveFromWorld();
  10601. pItem->DestroyForPlayer(this);
  10602. }
  10603. //pItem->SetOwnerGUID(0);
  10604. pItem->SetGuidValue(ITEM_FIELD_CONTAINED, ObjectGuid::Empty);
  10605. pItem->SetSlot(NULL_SLOT);
  10606. pItem->SetState(ITEM_REMOVED, this);
  10607. }
  10608. }
  10609. void Player::DestroyItemCount(uint32 itemEntry, uint32 count, bool update, bool unequip_check)
  10610. {
  10611. TC_LOG_DEBUG("entities.player.items", "STORAGE: DestroyItemCount item = %u, count = %u", itemEntry, count);
  10612. uint32 remcount = 0;
  10613. // in inventory
  10614. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  10615. {
  10616. if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  10617. {
  10618. if (item->GetEntry() == itemEntry && !item->IsInTrade())
  10619. {
  10620. if (item->GetCount() + remcount <= count)
  10621. {
  10622. // all items in inventory can unequipped
  10623. remcount += item->GetCount();
  10624. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  10625. if (remcount >= count)
  10626. return;
  10627. }
  10628. else
  10629. {
  10630. ItemRemovedQuestCheck(item->GetEntry(), count - remcount);
  10631. item->SetCount(item->GetCount() - count + remcount);
  10632. if (IsInWorld() && update)
  10633. item->SendUpdateToPlayer(this);
  10634. item->SetState(ITEM_CHANGED, this);
  10635. return;
  10636. }
  10637. }
  10638. }
  10639. }
  10640. // in inventory bags
  10641. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  10642. {
  10643. if (Bag* bag = GetBagByPos(i))
  10644. {
  10645. for (uint32 j = 0; j < bag->GetBagSize(); j++)
  10646. {
  10647. if (Item* item = bag->GetItemByPos(j))
  10648. {
  10649. if (item->GetEntry() == itemEntry && !item->IsInTrade())
  10650. {
  10651. // all items in bags can be unequipped
  10652. if (item->GetCount() + remcount <= count)
  10653. {
  10654. remcount += item->GetCount();
  10655. DestroyItem(i, j, update);
  10656. if (remcount >= count)
  10657. return;
  10658. }
  10659. else
  10660. {
  10661. ItemRemovedQuestCheck(item->GetEntry(), count - remcount);
  10662. item->SetCount(item->GetCount() - count + remcount);
  10663. if (IsInWorld() && update)
  10664. item->SendUpdateToPlayer(this);
  10665. item->SetState(ITEM_CHANGED, this);
  10666. return;
  10667. }
  10668. }
  10669. }
  10670. }
  10671. }
  10672. }
  10673. // in equipment and bag list
  10674. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
  10675. {
  10676. if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  10677. {
  10678. if (item && item->GetEntry() == itemEntry && !item->IsInTrade())
  10679. {
  10680. if (item->GetCount() + remcount <= count)
  10681. {
  10682. if (!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false) == EQUIP_ERR_OK)
  10683. {
  10684. remcount += item->GetCount();
  10685. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  10686. if (remcount >= count)
  10687. return;
  10688. }
  10689. }
  10690. else
  10691. {
  10692. ItemRemovedQuestCheck(item->GetEntry(), count - remcount);
  10693. item->SetCount(item->GetCount() - count + remcount);
  10694. if (IsInWorld() && update)
  10695. item->SendUpdateToPlayer(this);
  10696. item->SetState(ITEM_CHANGED, this);
  10697. return;
  10698. }
  10699. }
  10700. }
  10701. }
  10702. // in bank
  10703. for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
  10704. {
  10705. if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  10706. {
  10707. if (item->GetEntry() == itemEntry && !item->IsInTrade())
  10708. {
  10709. if (item->GetCount() + remcount <= count)
  10710. {
  10711. remcount += item->GetCount();
  10712. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  10713. if (remcount >= count)
  10714. return;
  10715. }
  10716. else
  10717. {
  10718. ItemRemovedQuestCheck(item->GetEntry(), count - remcount);
  10719. item->SetCount(item->GetCount() - count + remcount);
  10720. if (IsInWorld() && update)
  10721. item->SendUpdateToPlayer(this);
  10722. item->SetState(ITEM_CHANGED, this);
  10723. return;
  10724. }
  10725. }
  10726. }
  10727. }
  10728. // in bank bags
  10729. for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
  10730. {
  10731. if (Bag* bag = GetBagByPos(i))
  10732. {
  10733. for (uint32 j = 0; j < bag->GetBagSize(); j++)
  10734. {
  10735. if (Item* item = bag->GetItemByPos(j))
  10736. {
  10737. if (item->GetEntry() == itemEntry && !item->IsInTrade())
  10738. {
  10739. // all items in bags can be unequipped
  10740. if (item->GetCount() + remcount <= count)
  10741. {
  10742. remcount += item->GetCount();
  10743. DestroyItem(i, j, update);
  10744. if (remcount >= count)
  10745. return;
  10746. }
  10747. else
  10748. {
  10749. ItemRemovedQuestCheck(item->GetEntry(), count - remcount);
  10750. item->SetCount(item->GetCount() - count + remcount);
  10751. if (IsInWorld() && update)
  10752. item->SendUpdateToPlayer(this);
  10753. item->SetState(ITEM_CHANGED, this);
  10754. return;
  10755. }
  10756. }
  10757. }
  10758. }
  10759. }
  10760. }
  10761. }
  10762. void Player::DestroyZoneLimitedItem(bool update, uint32 new_zone)
  10763. {
  10764. TC_LOG_DEBUG("entities.player.items", "STORAGE: DestroyZoneLimitedItem in map %u and area %u", GetMapId(), new_zone);
  10765. // in inventory
  10766. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
  10767. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  10768. if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
  10769. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  10770. // in inventory bags
  10771. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  10772. if (Bag* pBag = GetBagByPos(i))
  10773. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  10774. if (Item* pItem = pBag->GetItemByPos(j))
  10775. if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
  10776. DestroyItem(i, j, update);
  10777. // in equipment and bag list
  10778. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
  10779. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  10780. if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
  10781. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  10782. }
  10783. void Player::DestroyConjuredItems(bool update)
  10784. {
  10785. // used when entering arena
  10786. // destroys all conjured items
  10787. TC_LOG_DEBUG("entities.player.items", "STORAGE: DestroyConjuredItems");
  10788. // in inventory
  10789. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
  10790. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  10791. if (pItem->IsConjuredConsumable())
  10792. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  10793. // in inventory bags
  10794. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
  10795. if (Bag* pBag = GetBagByPos(i))
  10796. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  10797. if (Item* pItem = pBag->GetItemByPos(j))
  10798. if (pItem->IsConjuredConsumable())
  10799. DestroyItem(i, j, update);
  10800. // in equipment and bag list
  10801. for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
  10802. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  10803. if (pItem->IsConjuredConsumable())
  10804. DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
  10805. }
  10806. Item* Player::GetItemByEntry(uint32 entry) const
  10807. {
  10808. // in inventory
  10809. for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  10810. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  10811. if (pItem->GetEntry() == entry)
  10812. return pItem;
  10813. for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  10814. if (Bag* pBag = GetBagByPos(i))
  10815. for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
  10816. if (Item* pItem = pBag->GetItemByPos(j))
  10817. if (pItem->GetEntry() == entry)
  10818. return pItem;
  10819. for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
  10820. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  10821. if (pItem->GetEntry() == entry)
  10822. return pItem;
  10823. return NULL;
  10824. }
  10825. void Player::DestroyItemCount(Item* pItem, uint32 &count, bool update)
  10826. {
  10827. if (!pItem)
  10828. return;
  10829. TC_LOG_DEBUG("entities.player.items", "STORAGE: DestroyItemCount item (GUID: %u, Entry: %u) count = %u", pItem->GetGUIDLow(), pItem->GetEntry(), count);
  10830. if (pItem->GetCount() <= count)
  10831. {
  10832. count -= pItem->GetCount();
  10833. DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), update);
  10834. }
  10835. else
  10836. {
  10837. ItemRemovedQuestCheck(pItem->GetEntry(), count);
  10838. pItem->SetCount(pItem->GetCount() - count);
  10839. count = 0;
  10840. if (IsInWorld() && update)
  10841. pItem->SendUpdateToPlayer(this);
  10842. pItem->SetState(ITEM_CHANGED, this);
  10843. }
  10844. }
  10845. void Player::SplitItem(uint16 src, uint16 dst, uint32 count)
  10846. {
  10847. uint8 srcbag = src >> 8;
  10848. uint8 srcslot = src & 255;
  10849. uint8 dstbag = dst >> 8;
  10850. uint8 dstslot = dst & 255;
  10851. Item* pSrcItem = GetItemByPos(srcbag, srcslot);
  10852. if (!pSrcItem)
  10853. {
  10854. SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL);
  10855. return;
  10856. }
  10857. if (pSrcItem->m_lootGenerated) // prevent split looting item (item
  10858. {
  10859. //best error message found for attempting to split while looting
  10860. SendEquipError(EQUIP_ERR_SPLIT_FAILED, pSrcItem, NULL);
  10861. return;
  10862. }
  10863. // not let split all items (can be only at cheating)
  10864. if (pSrcItem->GetCount() == count)
  10865. {
  10866. SendEquipError(EQUIP_ERR_SPLIT_FAILED, pSrcItem, NULL);
  10867. return;
  10868. }
  10869. // not let split more existed items (can be only at cheating)
  10870. if (pSrcItem->GetCount() < count)
  10871. {
  10872. SendEquipError(EQUIP_ERR_TOO_FEW_TO_SPLIT, pSrcItem, NULL);
  10873. return;
  10874. }
  10875. //! If trading
  10876. if (TradeData* tradeData = GetTradeData())
  10877. {
  10878. //! If current item is in trade window (only possible with packet spoofing - silent return)
  10879. if (tradeData->GetTradeSlotForItem(pSrcItem->GetGUID()) != TRADE_SLOT_INVALID)
  10880. return;
  10881. }
  10882. TC_LOG_DEBUG("entities.player.items", "STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count);
  10883. Item* pNewItem = pSrcItem->CloneItem(count, this);
  10884. if (!pNewItem)
  10885. {
  10886. SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL);
  10887. return;
  10888. }
  10889. if (IsInventoryPos(dst))
  10890. {
  10891. // change item amount before check (for unique max count check)
  10892. pSrcItem->SetCount(pSrcItem->GetCount() - count);
  10893. ItemPosCountVec dest;
  10894. InventoryResult msg = CanStoreItem(dstbag, dstslot, dest, pNewItem, false);
  10895. if (msg != EQUIP_ERR_OK)
  10896. {
  10897. delete pNewItem;
  10898. pSrcItem->SetCount(pSrcItem->GetCount() + count);
  10899. SendEquipError(msg, pSrcItem, NULL);
  10900. return;
  10901. }
  10902. if (IsInWorld())
  10903. pSrcItem->SendUpdateToPlayer(this);
  10904. pSrcItem->SetState(ITEM_CHANGED, this);
  10905. StoreItem(dest, pNewItem, true);
  10906. }
  10907. else if (IsBankPos(dst))
  10908. {
  10909. // change item amount before check (for unique max count check)
  10910. pSrcItem->SetCount(pSrcItem->GetCount() - count);
  10911. ItemPosCountVec dest;
  10912. InventoryResult msg = CanBankItem(dstbag, dstslot, dest, pNewItem, false);
  10913. if (msg != EQUIP_ERR_OK)
  10914. {
  10915. delete pNewItem;
  10916. pSrcItem->SetCount(pSrcItem->GetCount() + count);
  10917. SendEquipError(msg, pSrcItem, NULL);
  10918. return;
  10919. }
  10920. if (IsInWorld())
  10921. pSrcItem->SendUpdateToPlayer(this);
  10922. pSrcItem->SetState(ITEM_CHANGED, this);
  10923. BankItem(dest, pNewItem, true);
  10924. }
  10925. else if (IsEquipmentPos(dst))
  10926. {
  10927. // change item amount before check (for unique max count check), provide space for splitted items
  10928. pSrcItem->SetCount(pSrcItem->GetCount() - count);
  10929. uint16 dest;
  10930. InventoryResult msg = CanEquipItem(dstslot, dest, pNewItem, false);
  10931. if (msg != EQUIP_ERR_OK)
  10932. {
  10933. delete pNewItem;
  10934. pSrcItem->SetCount(pSrcItem->GetCount() + count);
  10935. SendEquipError(msg, pSrcItem, NULL);
  10936. return;
  10937. }
  10938. if (IsInWorld())
  10939. pSrcItem->SendUpdateToPlayer(this);
  10940. pSrcItem->SetState(ITEM_CHANGED, this);
  10941. EquipItem(dest, pNewItem, true);
  10942. AutoUnequipOffhandIfNeed();
  10943. }
  10944. }
  10945. void Player::SwapItem(uint16 src, uint16 dst)
  10946. {
  10947. uint8 srcbag = src >> 8;
  10948. uint8 srcslot = src & 255;
  10949. uint8 dstbag = dst >> 8;
  10950. uint8 dstslot = dst & 255;
  10951. Item* pSrcItem = GetItemByPos(srcbag, srcslot);
  10952. Item* pDstItem = GetItemByPos(dstbag, dstslot);
  10953. if (!pSrcItem)
  10954. return;
  10955. TC_LOG_DEBUG("entities.player.items", "STORAGE: SwapItem bag = %u, slot = %u, item = %u", dstbag, dstslot, pSrcItem->GetEntry());
  10956. if (!IsAlive())
  10957. {
  10958. SendEquipError(EQUIP_ERR_PLAYER_DEAD, pSrcItem, pDstItem);
  10959. return;
  10960. }
  10961. // SRC checks
  10962. // check unequip potability for equipped items and bank bags
  10963. if (IsEquipmentPos(src) || IsBagPos(src))
  10964. {
  10965. // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later)
  10966. InventoryResult msg = CanUnequipItem(src, !IsBagPos(src) || IsBagPos(dst) || (pDstItem && pDstItem->ToBag() && pDstItem->ToBag()->IsEmpty()));
  10967. if (msg != EQUIP_ERR_OK)
  10968. {
  10969. SendEquipError(msg, pSrcItem, pDstItem);
  10970. return;
  10971. }
  10972. }
  10973. // prevent put equipped/bank bag in self
  10974. if (IsBagPos(src) && srcslot == dstbag)
  10975. {
  10976. SendEquipError(EQUIP_ERR_BAG_IN_BAG, pSrcItem, pDstItem);
  10977. return;
  10978. }
  10979. // prevent equipping bag in the same slot from its inside
  10980. if (IsBagPos(dst) && srcbag == dstslot)
  10981. {
  10982. SendEquipError(EQUIP_ERR_CANT_SWAP, pSrcItem, pDstItem);
  10983. return;
  10984. }
  10985. // DST checks
  10986. if (pDstItem)
  10987. {
  10988. // check unequip potability for equipped items and bank bags
  10989. if (IsEquipmentPos(dst) || IsBagPos(dst))
  10990. {
  10991. // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later)
  10992. InventoryResult msg = CanUnequipItem(dst, !IsBagPos(dst) || IsBagPos(src) || (pSrcItem->ToBag() && pSrcItem->ToBag()->IsEmpty()));
  10993. if (msg != EQUIP_ERR_OK)
  10994. {
  10995. SendEquipError(msg, pSrcItem, pDstItem);
  10996. return;
  10997. }
  10998. }
  10999. }
  11000. // NOW this is or item move (swap with empty), or swap with another item (including bags in bag possitions)
  11001. // or swap empty bag with another empty or not empty bag (with items exchange)
  11002. // Move case
  11003. if (!pDstItem)
  11004. {
  11005. if (IsInventoryPos(dst))
  11006. {
  11007. ItemPosCountVec dest;
  11008. InventoryResult msg = CanStoreItem(dstbag, dstslot, dest, pSrcItem, false);
  11009. if (msg != EQUIP_ERR_OK)
  11010. {
  11011. SendEquipError(msg, pSrcItem, NULL);
  11012. return;
  11013. }
  11014. RemoveItem(srcbag, srcslot, true);
  11015. StoreItem(dest, pSrcItem, true);
  11016. if (IsBankPos(src))
  11017. ItemAddedQuestCheck(pSrcItem->GetEntry(), pSrcItem->GetCount());
  11018. }
  11019. else if (IsBankPos(dst))
  11020. {
  11021. ItemPosCountVec dest;
  11022. InventoryResult msg = CanBankItem(dstbag, dstslot, dest, pSrcItem, false);
  11023. if (msg != EQUIP_ERR_OK)
  11024. {
  11025. SendEquipError(msg, pSrcItem, NULL);
  11026. return;
  11027. }
  11028. RemoveItem(srcbag, srcslot, true);
  11029. BankItem(dest, pSrcItem, true);
  11030. ItemRemovedQuestCheck(pSrcItem->GetEntry(), pSrcItem->GetCount());
  11031. }
  11032. else if (IsEquipmentPos(dst))
  11033. {
  11034. uint16 dest;
  11035. InventoryResult msg = CanEquipItem(dstslot, dest, pSrcItem, false);
  11036. if (msg != EQUIP_ERR_OK)
  11037. {
  11038. SendEquipError(msg, pSrcItem, NULL);
  11039. return;
  11040. }
  11041. RemoveItem(srcbag, srcslot, true);
  11042. EquipItem(dest, pSrcItem, true);
  11043. AutoUnequipOffhandIfNeed();
  11044. }
  11045. return;
  11046. }
  11047. // attempt merge to / fill target item
  11048. if (!pSrcItem->IsBag() && !pDstItem->IsBag())
  11049. {
  11050. InventoryResult msg;
  11051. ItemPosCountVec sDest;
  11052. uint16 eDest = 0;
  11053. if (IsInventoryPos(dst))
  11054. msg = CanStoreItem(dstbag, dstslot, sDest, pSrcItem, false);
  11055. else if (IsBankPos(dst))
  11056. msg = CanBankItem(dstbag, dstslot, sDest, pSrcItem, false);
  11057. else if (IsEquipmentPos(dst))
  11058. msg = CanEquipItem(dstslot, eDest, pSrcItem, false);
  11059. else
  11060. return;
  11061. // can be merge/fill
  11062. if (msg == EQUIP_ERR_OK)
  11063. {
  11064. if (pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetTemplate()->GetMaxStackSize())
  11065. {
  11066. RemoveItem(srcbag, srcslot, true);
  11067. if (IsInventoryPos(dst))
  11068. StoreItem(sDest, pSrcItem, true);
  11069. else if (IsBankPos(dst))
  11070. BankItem(sDest, pSrcItem, true);
  11071. else if (IsEquipmentPos(dst))
  11072. {
  11073. EquipItem(eDest, pSrcItem, true);
  11074. AutoUnequipOffhandIfNeed();
  11075. }
  11076. }
  11077. else
  11078. {
  11079. pSrcItem->SetCount(pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetTemplate()->GetMaxStackSize());
  11080. pDstItem->SetCount(pSrcItem->GetTemplate()->GetMaxStackSize());
  11081. pSrcItem->SetState(ITEM_CHANGED, this);
  11082. pDstItem->SetState(ITEM_CHANGED, this);
  11083. if (IsInWorld())
  11084. {
  11085. pSrcItem->SendUpdateToPlayer(this);
  11086. pDstItem->SendUpdateToPlayer(this);
  11087. }
  11088. }
  11089. SendRefundInfo(pDstItem);
  11090. return;
  11091. }
  11092. }
  11093. // impossible merge/fill, do real swap
  11094. InventoryResult msg = EQUIP_ERR_OK;
  11095. // check src->dest move possibility
  11096. ItemPosCountVec sDest;
  11097. uint16 eDest = 0;
  11098. if (IsInventoryPos(dst))
  11099. msg = CanStoreItem(dstbag, dstslot, sDest, pSrcItem, true);
  11100. else if (IsBankPos(dst))
  11101. msg = CanBankItem(dstbag, dstslot, sDest, pSrcItem, true);
  11102. else if (IsEquipmentPos(dst))
  11103. {
  11104. msg = CanEquipItem(dstslot, eDest, pSrcItem, true);
  11105. if (msg == EQUIP_ERR_OK)
  11106. msg = CanUnequipItem(eDest, true);
  11107. }
  11108. if (msg != EQUIP_ERR_OK)
  11109. {
  11110. SendEquipError(msg, pSrcItem, pDstItem);
  11111. return;
  11112. }
  11113. // check dest->src move possibility
  11114. ItemPosCountVec sDest2;
  11115. uint16 eDest2 = 0;
  11116. if (IsInventoryPos(src))
  11117. msg = CanStoreItem(srcbag, srcslot, sDest2, pDstItem, true);
  11118. else if (IsBankPos(src))
  11119. msg = CanBankItem(srcbag, srcslot, sDest2, pDstItem, true);
  11120. else if (IsEquipmentPos(src))
  11121. {
  11122. msg = CanEquipItem(srcslot, eDest2, pDstItem, true);
  11123. if (msg == EQUIP_ERR_OK)
  11124. msg = CanUnequipItem(eDest2, true);
  11125. }
  11126. if (msg != EQUIP_ERR_OK)
  11127. {
  11128. SendEquipError(msg, pDstItem, pSrcItem);
  11129. return;
  11130. }
  11131. // Check bag swap with item exchange (one from empty in not bag possition (equipped (not possible in fact) or store)
  11132. if (Bag* srcBag = pSrcItem->ToBag())
  11133. {
  11134. if (Bag* dstBag = pDstItem->ToBag())
  11135. {
  11136. Bag* emptyBag = NULL;
  11137. Bag* fullBag = NULL;
  11138. if (srcBag->IsEmpty() && !IsBagPos(src))
  11139. {
  11140. emptyBag = srcBag;
  11141. fullBag = dstBag;
  11142. }
  11143. else if (dstBag->IsEmpty() && !IsBagPos(dst))
  11144. {
  11145. emptyBag = dstBag;
  11146. fullBag = srcBag;
  11147. }
  11148. // bag swap (with items exchange) case
  11149. if (emptyBag && fullBag)
  11150. {
  11151. ItemTemplate const* emptyProto = emptyBag->GetTemplate();
  11152. uint32 count = 0;
  11153. for (uint32 i=0; i < fullBag->GetBagSize(); ++i)
  11154. {
  11155. Item* bagItem = fullBag->GetItemByPos(i);
  11156. if (!bagItem)
  11157. continue;
  11158. ItemTemplate const* bagItemProto = bagItem->GetTemplate();
  11159. if (!bagItemProto || !ItemCanGoIntoBag(bagItemProto, emptyProto))
  11160. {
  11161. // one from items not go to empty target bag
  11162. SendEquipError(EQUIP_ERR_BAG_IN_BAG, pSrcItem, pDstItem);
  11163. return;
  11164. }
  11165. ++count;
  11166. }
  11167. if (count > emptyBag->GetBagSize())
  11168. {
  11169. // too small targeted bag
  11170. SendEquipError(EQUIP_ERR_CANT_SWAP, pSrcItem, pDstItem);
  11171. return;
  11172. }
  11173. // Items swap
  11174. count = 0; // will pos in new bag
  11175. for (uint32 i = 0; i< fullBag->GetBagSize(); ++i)
  11176. {
  11177. Item* bagItem = fullBag->GetItemByPos(i);
  11178. if (!bagItem)
  11179. continue;
  11180. fullBag->RemoveItem(i, true);
  11181. emptyBag->StoreItem(count, bagItem, true);
  11182. bagItem->SetState(ITEM_CHANGED, this);
  11183. ++count;
  11184. }
  11185. }
  11186. }
  11187. }
  11188. // now do moves, remove...
  11189. RemoveItem(dstbag, dstslot, false);
  11190. RemoveItem(srcbag, srcslot, false);
  11191. // add to dest
  11192. if (IsInventoryPos(dst))
  11193. StoreItem(sDest, pSrcItem, true);
  11194. else if (IsBankPos(dst))
  11195. BankItem(sDest, pSrcItem, true);
  11196. else if (IsEquipmentPos(dst))
  11197. EquipItem(eDest, pSrcItem, true);
  11198. // add to src
  11199. if (IsInventoryPos(src))
  11200. StoreItem(sDest2, pDstItem, true);
  11201. else if (IsBankPos(src))
  11202. BankItem(sDest2, pDstItem, true);
  11203. else if (IsEquipmentPos(src))
  11204. EquipItem(eDest2, pDstItem, true);
  11205. // if player is moving bags and is looting an item inside this bag
  11206. // release the loot
  11207. if (GetLootGUID())
  11208. {
  11209. bool released = false;
  11210. if (IsBagPos(src))
  11211. {
  11212. Bag* bag = pSrcItem->ToBag();
  11213. for (uint32 i = 0; i < bag->GetBagSize(); ++i)
  11214. {
  11215. if (Item* bagItem = bag->GetItemByPos(i))
  11216. {
  11217. if (bagItem->m_lootGenerated)
  11218. {
  11219. m_session->DoLootRelease(GetLootGUID());
  11220. released = true; // so we don't need to look at dstBag
  11221. break;
  11222. }
  11223. }
  11224. }
  11225. }
  11226. if (!released && IsBagPos(dst) && pDstItem)
  11227. {
  11228. Bag* bag = pDstItem->ToBag();
  11229. for (uint32 i = 0; i < bag->GetBagSize(); ++i)
  11230. {
  11231. if (Item* bagItem = bag->GetItemByPos(i))
  11232. {
  11233. if (bagItem->m_lootGenerated)
  11234. {
  11235. m_session->DoLootRelease(GetLootGUID());
  11236. released = true; // not realy needed here
  11237. break;
  11238. }
  11239. }
  11240. }
  11241. }
  11242. }
  11243. AutoUnequipOffhandIfNeed();
  11244. }
  11245. void Player::AddItemToBuyBackSlot(Item* pItem)
  11246. {
  11247. if (pItem)
  11248. {
  11249. uint32 slot = m_currentBuybackSlot;
  11250. // if current back slot non-empty search oldest or free
  11251. if (m_items[slot])
  11252. {
  11253. uint32 oldest_time = GetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1);
  11254. uint32 oldest_slot = BUYBACK_SLOT_START;
  11255. for (uint32 i = BUYBACK_SLOT_START+1; i < BUYBACK_SLOT_END; ++i)
  11256. {
  11257. // found empty
  11258. if (!m_items[i])
  11259. {
  11260. oldest_slot = i;
  11261. break;
  11262. }
  11263. uint32 i_time = GetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + i - BUYBACK_SLOT_START);
  11264. if (oldest_time > i_time)
  11265. {
  11266. oldest_time = i_time;
  11267. oldest_slot = i;
  11268. }
  11269. }
  11270. // find oldest
  11271. slot = oldest_slot;
  11272. }
  11273. RemoveItemFromBuyBackSlot(slot, true);
  11274. TC_LOG_DEBUG("entities.player.items", "STORAGE: AddItemToBuyBackSlot item = %u, slot = %u", pItem->GetEntry(), slot);
  11275. m_items[slot] = pItem;
  11276. time_t base = time(NULL);
  11277. uint32 etime = uint32(base - m_logintime + (30 * 3600));
  11278. uint32 eslot = slot - BUYBACK_SLOT_START;
  11279. SetGuidValue(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), pItem->GetGUID());
  11280. if (ItemTemplate const* proto = pItem->GetTemplate())
  11281. SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, proto->SellPrice * pItem->GetCount());
  11282. else
  11283. SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
  11284. SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, (uint32)etime);
  11285. // move to next (for non filled list is move most optimized choice)
  11286. if (m_currentBuybackSlot < BUYBACK_SLOT_END - 1)
  11287. ++m_currentBuybackSlot;
  11288. }
  11289. }
  11290. Item* Player::GetItemFromBuyBackSlot(uint32 slot)
  11291. {
  11292. TC_LOG_DEBUG("entities.player.items", "STORAGE: GetItemFromBuyBackSlot slot = %u", slot);
  11293. if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END)
  11294. return m_items[slot];
  11295. return NULL;
  11296. }
  11297. void Player::RemoveItemFromBuyBackSlot(uint32 slot, bool del)
  11298. {
  11299. TC_LOG_DEBUG("entities.player.items", "STORAGE: RemoveItemFromBuyBackSlot slot = %u", slot);
  11300. if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END)
  11301. {
  11302. Item* pItem = m_items[slot];
  11303. if (pItem)
  11304. {
  11305. pItem->RemoveFromWorld();
  11306. if (del)
  11307. pItem->SetState(ITEM_REMOVED, this);
  11308. }
  11309. m_items[slot] = NULL;
  11310. uint32 eslot = slot - BUYBACK_SLOT_START;
  11311. SetGuidValue(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), ObjectGuid::Empty);
  11312. SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
  11313. SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0);
  11314. // if current backslot is filled set to now free slot
  11315. if (m_items[m_currentBuybackSlot])
  11316. m_currentBuybackSlot = slot;
  11317. }
  11318. }
  11319. void Player::SendEquipError(InventoryResult msg, Item* pItem, Item* pItem2, uint32 itemid)
  11320. {
  11321. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)", msg);
  11322. WorldPacket data(SMSG_INVENTORY_CHANGE_FAILURE, (msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I ? 22 : 18));
  11323. data << uint8(msg);
  11324. if (msg != EQUIP_ERR_OK)
  11325. {
  11326. data << uint64(pItem ? pItem->GetGUID() : ObjectGuid::Empty);
  11327. data << uint64(pItem2 ? pItem2->GetGUID() : ObjectGuid::Empty);
  11328. data << uint8(0); // bag type subclass, used with EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM and EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG2
  11329. switch (msg)
  11330. {
  11331. case EQUIP_ERR_CANT_EQUIP_LEVEL_I:
  11332. case EQUIP_ERR_PURCHASE_LEVEL_TOO_LOW:
  11333. {
  11334. ItemTemplate const* proto = pItem ? pItem->GetTemplate() : sObjectMgr->GetItemTemplate(itemid);
  11335. data << uint32(proto ? proto->RequiredLevel : 0);
  11336. break;
  11337. }
  11338. case EQUIP_ERR_NO_OUTPUT: // no idea about this one...
  11339. {
  11340. data << uint64(0); // item guid
  11341. data << uint32(0); // slot
  11342. data << uint64(0); // container
  11343. break;
  11344. }
  11345. case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS:
  11346. case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED_IS:
  11347. case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS:
  11348. {
  11349. ItemTemplate const* proto = pItem ? pItem->GetTemplate() : sObjectMgr->GetItemTemplate(itemid);
  11350. data << uint32(proto ? proto->ItemLimitCategory : 0);
  11351. break;
  11352. }
  11353. default:
  11354. break;
  11355. }
  11356. }
  11357. GetSession()->SendPacket(&data);
  11358. }
  11359. void Player::SendBuyError(BuyResult msg, Creature* creature, uint32 item, uint32 /*param*/)
  11360. {
  11361. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_BUY_FAILED");
  11362. WorldPacket data(SMSG_BUY_FAILED, (8+4+4+1));
  11363. data << uint64(creature ? creature->GetGUID() : ObjectGuid::Empty);
  11364. data << uint32(item);
  11365. data << uint8(msg);
  11366. GetSession()->SendPacket(&data);
  11367. }
  11368. void Player::SendSellError(SellResult msg, Creature* creature, ObjectGuid guid)
  11369. {
  11370. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_SELL_ITEM");
  11371. WorldPacket data(SMSG_SELL_ITEM, (8+8+1)); // last check 4.3.4
  11372. data << uint64(creature ? creature->GetGUID() : ObjectGuid::Empty);
  11373. data << uint64(guid);
  11374. data << uint8(msg);
  11375. GetSession()->SendPacket(&data);
  11376. }
  11377. bool Player::IsUseEquipedWeapon(bool mainhand) const
  11378. {
  11379. // disarm applied only to mainhand weapon
  11380. return !IsInFeralForm() && (!mainhand || !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED));
  11381. }
  11382. bool Player::IsTwoHandUsed() const
  11383. {
  11384. Item* mainItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
  11385. return mainItem && mainItem->GetTemplate()->InventoryType == INVTYPE_2HWEAPON && !CanTitanGrip();
  11386. }
  11387. void Player::TradeCancel(bool sendback)
  11388. {
  11389. if (m_trade)
  11390. {
  11391. Player* trader = m_trade->GetTrader();
  11392. // send yellow "Trade canceled" message to both traders
  11393. if (sendback)
  11394. GetSession()->SendCancelTrade();
  11395. trader->GetSession()->SendCancelTrade();
  11396. // cleanup
  11397. delete m_trade;
  11398. m_trade = NULL;
  11399. delete trader->m_trade;
  11400. trader->m_trade = NULL;
  11401. }
  11402. }
  11403. void Player::UpdateSoulboundTradeItems()
  11404. {
  11405. if (m_itemSoulboundTradeable.empty())
  11406. return;
  11407. // also checks for garbage data
  11408. for (ItemDurationList::iterator itr = m_itemSoulboundTradeable.begin(); itr != m_itemSoulboundTradeable.end();)
  11409. {
  11410. ASSERT(*itr);
  11411. if ((*itr)->GetOwnerGUID() != GetGUID())
  11412. {
  11413. m_itemSoulboundTradeable.erase(itr++);
  11414. continue;
  11415. }
  11416. if ((*itr)->CheckSoulboundTradeExpire())
  11417. {
  11418. m_itemSoulboundTradeable.erase(itr++);
  11419. continue;
  11420. }
  11421. ++itr;
  11422. }
  11423. }
  11424. void Player::AddTradeableItem(Item* item)
  11425. {
  11426. m_itemSoulboundTradeable.push_back(item);
  11427. }
  11428. /// @todo should never allow an item to be added to m_itemSoulboundTradeable twice
  11429. void Player::RemoveTradeableItem(Item* item)
  11430. {
  11431. m_itemSoulboundTradeable.remove(item);
  11432. }
  11433. void Player::UpdateItemDuration(uint32 time, bool realtimeonly)
  11434. {
  11435. if (m_itemDuration.empty())
  11436. return;
  11437. TC_LOG_DEBUG("entities.player.items", "Player::UpdateItemDuration(%u, %u)", time, realtimeonly);
  11438. for (ItemDurationList::const_iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end();)
  11439. {
  11440. Item* item = *itr;
  11441. ++itr; // current element can be erased in UpdateDuration
  11442. if (!realtimeonly || item->GetTemplate()->FlagsCu & ITEM_FLAGS_CU_DURATION_REAL_TIME)
  11443. item->UpdateDuration(this, time);
  11444. }
  11445. }
  11446. void Player::UpdateEnchantTime(uint32 time)
  11447. {
  11448. for (EnchantDurationList::iterator itr = m_enchantDuration.begin(), next; itr != m_enchantDuration.end(); itr=next)
  11449. {
  11450. ASSERT(itr->item);
  11451. next = itr;
  11452. if (!itr->item->GetEnchantmentId(itr->slot))
  11453. {
  11454. next = m_enchantDuration.erase(itr);
  11455. }
  11456. else if (itr->leftduration <= time)
  11457. {
  11458. ApplyEnchantment(itr->item, itr->slot, false, false);
  11459. itr->item->ClearEnchantment(itr->slot);
  11460. next = m_enchantDuration.erase(itr);
  11461. }
  11462. else if (itr->leftduration > time)
  11463. {
  11464. itr->leftduration -= time;
  11465. ++next;
  11466. }
  11467. }
  11468. }
  11469. void Player::AddEnchantmentDurations(Item* item)
  11470. {
  11471. for (int x = 0; x < MAX_ENCHANTMENT_SLOT; ++x)
  11472. {
  11473. if (x > PRISMATIC_ENCHANTMENT_SLOT && x < PROP_ENCHANTMENT_SLOT_0) // not holding enchantment id
  11474. continue;
  11475. if (!item->GetEnchantmentId(EnchantmentSlot(x)))
  11476. continue;
  11477. uint32 duration = item->GetEnchantmentDuration(EnchantmentSlot(x));
  11478. if (duration > 0)
  11479. AddEnchantmentDuration(item, EnchantmentSlot(x), duration);
  11480. }
  11481. }
  11482. void Player::RemoveEnchantmentDurations(Item* item)
  11483. {
  11484. for (EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end();)
  11485. {
  11486. if (itr->item == item)
  11487. {
  11488. // save duration in item
  11489. item->SetEnchantmentDuration(EnchantmentSlot(itr->slot), itr->leftduration, this);
  11490. itr = m_enchantDuration.erase(itr);
  11491. }
  11492. else
  11493. ++itr;
  11494. }
  11495. }
  11496. void Player::RemoveArenaEnchantments(EnchantmentSlot slot)
  11497. {
  11498. // remove enchantments from equipped items first to clean up the m_enchantDuration list
  11499. for (EnchantDurationList::iterator itr = m_enchantDuration.begin(), next; itr != m_enchantDuration.end(); itr = next)
  11500. {
  11501. next = itr;
  11502. if (itr->slot == slot)
  11503. {
  11504. if (itr->item && itr->item->GetEnchantmentId(slot))
  11505. {
  11506. // Poisons and DK runes are enchants which are allowed on arenas
  11507. if (sSpellMgr->IsArenaAllowedEnchancment(itr->item->GetEnchantmentId(slot)))
  11508. {
  11509. ++next;
  11510. continue;
  11511. }
  11512. // remove from stats
  11513. ApplyEnchantment(itr->item, slot, false, false);
  11514. // remove visual
  11515. itr->item->ClearEnchantment(slot);
  11516. }
  11517. // remove from update list
  11518. next = m_enchantDuration.erase(itr);
  11519. }
  11520. else
  11521. ++next;
  11522. }
  11523. // remove enchants from inventory items
  11524. // NOTE: no need to remove these from stats, since these aren't equipped
  11525. // in inventory
  11526. for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
  11527. if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  11528. if (pItem->GetEnchantmentId(slot))
  11529. pItem->ClearEnchantment(slot);
  11530. // in inventory bags
  11531. for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  11532. if (Bag* pBag = GetBagByPos(i))
  11533. for (uint32 j = 0; j < pBag->GetBagSize(); j++)
  11534. if (Item* pItem = pBag->GetItemByPos(j))
  11535. if (pItem->GetEnchantmentId(slot))
  11536. pItem->ClearEnchantment(slot);
  11537. }
  11538. // duration == 0 will remove item enchant
  11539. void Player::AddEnchantmentDuration(Item* item, EnchantmentSlot slot, uint32 duration)
  11540. {
  11541. if (!item)
  11542. return;
  11543. if (slot >= MAX_ENCHANTMENT_SLOT)
  11544. return;
  11545. for (EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr)
  11546. {
  11547. if (itr->item == item && itr->slot == slot)
  11548. {
  11549. itr->item->SetEnchantmentDuration(itr->slot, itr->leftduration, this);
  11550. m_enchantDuration.erase(itr);
  11551. break;
  11552. }
  11553. }
  11554. if (item && duration > 0)
  11555. {
  11556. GetSession()->SendItemEnchantTimeUpdate(GetGUID(), item->GetGUID(), slot, uint32(duration/1000));
  11557. m_enchantDuration.push_back(EnchantDuration(item, slot, duration));
  11558. }
  11559. }
  11560. void Player::ApplyReforgeEnchantment(Item* item, bool apply)
  11561. {
  11562. if (!item)
  11563. return;
  11564. ItemReforgeEntry const* reforge = sItemReforgeStore.LookupEntry(item->GetEnchantmentId(REFORGE_ENCHANTMENT_SLOT));
  11565. if (!reforge)
  11566. return;
  11567. float removeValue = item->GetReforgableStat(ItemModType(reforge->SourceStat)) * reforge->SourceMultiplier;
  11568. float addValue = removeValue * reforge->FinalMultiplier;
  11569. switch (reforge->SourceStat)
  11570. {
  11571. case ITEM_MOD_MANA:
  11572. HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, -removeValue, apply);
  11573. break;
  11574. case ITEM_MOD_HEALTH:
  11575. HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, -removeValue, apply);
  11576. break;
  11577. case ITEM_MOD_AGILITY:
  11578. HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, -removeValue, apply);
  11579. ApplyStatBuffMod(STAT_AGILITY, -removeValue, apply);
  11580. break;
  11581. case ITEM_MOD_STRENGTH:
  11582. HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, -removeValue, apply);
  11583. ApplyStatBuffMod(STAT_STRENGTH, -removeValue, apply);
  11584. break;
  11585. case ITEM_MOD_INTELLECT:
  11586. HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, -removeValue, apply);
  11587. ApplyStatBuffMod(STAT_INTELLECT, -removeValue, apply);
  11588. break;
  11589. case ITEM_MOD_SPIRIT:
  11590. HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, -removeValue, apply);
  11591. ApplyStatBuffMod(STAT_SPIRIT, -removeValue, apply);
  11592. break;
  11593. case ITEM_MOD_STAMINA:
  11594. HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, -removeValue, apply);
  11595. ApplyStatBuffMod(STAT_STAMINA, -removeValue, apply);
  11596. break;
  11597. case ITEM_MOD_DEFENSE_SKILL_RATING:
  11598. ApplyRatingMod(CR_DEFENSE_SKILL, -int32(removeValue), apply);
  11599. break;
  11600. case ITEM_MOD_DODGE_RATING:
  11601. ApplyRatingMod(CR_DODGE, -int32(removeValue), apply);
  11602. break;
  11603. case ITEM_MOD_PARRY_RATING:
  11604. ApplyRatingMod(CR_PARRY, -int32(removeValue), apply);
  11605. break;
  11606. case ITEM_MOD_BLOCK_RATING:
  11607. ApplyRatingMod(CR_BLOCK, -int32(removeValue), apply);
  11608. break;
  11609. case ITEM_MOD_HIT_MELEE_RATING:
  11610. ApplyRatingMod(CR_HIT_MELEE, -int32(removeValue), apply);
  11611. break;
  11612. case ITEM_MOD_HIT_RANGED_RATING:
  11613. ApplyRatingMod(CR_HIT_RANGED, -int32(removeValue), apply);
  11614. break;
  11615. case ITEM_MOD_HIT_SPELL_RATING:
  11616. ApplyRatingMod(CR_HIT_SPELL, -int32(removeValue), apply);
  11617. break;
  11618. case ITEM_MOD_CRIT_MELEE_RATING:
  11619. ApplyRatingMod(CR_CRIT_MELEE, -int32(removeValue), apply);
  11620. break;
  11621. case ITEM_MOD_CRIT_RANGED_RATING:
  11622. ApplyRatingMod(CR_CRIT_RANGED, -int32(removeValue), apply);
  11623. break;
  11624. case ITEM_MOD_CRIT_SPELL_RATING:
  11625. ApplyRatingMod(CR_CRIT_SPELL, -int32(removeValue), apply);
  11626. break;
  11627. case ITEM_MOD_HASTE_SPELL_RATING:
  11628. ApplyRatingMod(CR_HASTE_SPELL, -int32(removeValue), apply);
  11629. break;
  11630. case ITEM_MOD_HIT_RATING:
  11631. ApplyRatingMod(CR_HIT_MELEE, -int32(removeValue), apply);
  11632. ApplyRatingMod(CR_HIT_RANGED, -int32(removeValue), apply);
  11633. ApplyRatingMod(CR_HIT_SPELL, -int32(removeValue), apply);
  11634. break;
  11635. case ITEM_MOD_CRIT_RATING:
  11636. ApplyRatingMod(CR_CRIT_MELEE, -int32(removeValue), apply);
  11637. ApplyRatingMod(CR_CRIT_RANGED, -int32(removeValue), apply);
  11638. ApplyRatingMod(CR_CRIT_SPELL, -int32(removeValue), apply);
  11639. break;
  11640. case ITEM_MOD_RESILIENCE_RATING:
  11641. ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE_TAKEN, -int32(removeValue), apply);
  11642. break;
  11643. case ITEM_MOD_HASTE_RATING:
  11644. ApplyRatingMod(CR_HASTE_MELEE, -int32(removeValue), apply);
  11645. ApplyRatingMod(CR_HASTE_RANGED, -int32(removeValue), apply);
  11646. ApplyRatingMod(CR_HASTE_SPELL, -int32(removeValue), apply);
  11647. break;
  11648. case ITEM_MOD_EXPERTISE_RATING:
  11649. ApplyRatingMod(CR_EXPERTISE, -int32(removeValue), apply);
  11650. break;
  11651. case ITEM_MOD_ATTACK_POWER:
  11652. HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, -removeValue, apply);
  11653. HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, -removeValue, apply);
  11654. break;
  11655. case ITEM_MOD_RANGED_ATTACK_POWER:
  11656. HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, -removeValue, apply);
  11657. break;
  11658. case ITEM_MOD_MANA_REGENERATION:
  11659. ApplyManaRegenBonus(-int32(removeValue), apply);
  11660. break;
  11661. case ITEM_MOD_ARMOR_PENETRATION_RATING:
  11662. ApplyRatingMod(CR_ARMOR_PENETRATION, -int32(removeValue), apply);
  11663. break;
  11664. case ITEM_MOD_SPELL_POWER:
  11665. ApplySpellPowerBonus(-int32(removeValue), apply);
  11666. break;
  11667. case ITEM_MOD_HEALTH_REGEN:
  11668. ApplyHealthRegenBonus(-int32(removeValue), apply);
  11669. break;
  11670. case ITEM_MOD_SPELL_PENETRATION:
  11671. ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, -int32(removeValue), apply);
  11672. m_spellPenetrationItemMod += apply ? -int32(removeValue) : int32(removeValue);
  11673. break;
  11674. case ITEM_MOD_BLOCK_VALUE:
  11675. HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, -removeValue, apply);
  11676. break;
  11677. case ITEM_MOD_MASTERY_RATING:
  11678. ApplyRatingMod(CR_MASTERY, -int32(removeValue), apply);
  11679. break;
  11680. }
  11681. switch (reforge->FinalStat)
  11682. {
  11683. case ITEM_MOD_MANA:
  11684. HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, addValue, apply);
  11685. break;
  11686. case ITEM_MOD_HEALTH:
  11687. HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, addValue, apply);
  11688. break;
  11689. case ITEM_MOD_AGILITY:
  11690. HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, addValue, apply);
  11691. ApplyStatBuffMod(STAT_AGILITY, addValue, apply);
  11692. break;
  11693. case ITEM_MOD_STRENGTH:
  11694. HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, addValue, apply);
  11695. ApplyStatBuffMod(STAT_STRENGTH, addValue, apply);
  11696. break;
  11697. case ITEM_MOD_INTELLECT:
  11698. HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, addValue, apply);
  11699. ApplyStatBuffMod(STAT_INTELLECT, addValue, apply);
  11700. break;
  11701. case ITEM_MOD_SPIRIT:
  11702. HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, addValue, apply);
  11703. ApplyStatBuffMod(STAT_SPIRIT, addValue, apply);
  11704. break;
  11705. case ITEM_MOD_STAMINA:
  11706. HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, addValue, apply);
  11707. ApplyStatBuffMod(STAT_STAMINA, addValue, apply);
  11708. break;
  11709. case ITEM_MOD_DEFENSE_SKILL_RATING:
  11710. ApplyRatingMod(CR_DEFENSE_SKILL, int32(addValue), apply);
  11711. break;
  11712. case ITEM_MOD_DODGE_RATING:
  11713. ApplyRatingMod(CR_DODGE, int32(addValue), apply);
  11714. break;
  11715. case ITEM_MOD_PARRY_RATING:
  11716. ApplyRatingMod(CR_PARRY, int32(addValue), apply);
  11717. break;
  11718. case ITEM_MOD_BLOCK_RATING:
  11719. ApplyRatingMod(CR_BLOCK, int32(addValue), apply);
  11720. break;
  11721. case ITEM_MOD_HIT_MELEE_RATING:
  11722. ApplyRatingMod(CR_HIT_MELEE, int32(addValue), apply);
  11723. break;
  11724. case ITEM_MOD_HIT_RANGED_RATING:
  11725. ApplyRatingMod(CR_HIT_RANGED, int32(addValue), apply);
  11726. break;
  11727. case ITEM_MOD_HIT_SPELL_RATING:
  11728. ApplyRatingMod(CR_HIT_SPELL, int32(addValue), apply);
  11729. break;
  11730. case ITEM_MOD_CRIT_MELEE_RATING:
  11731. ApplyRatingMod(CR_CRIT_MELEE, int32(addValue), apply);
  11732. break;
  11733. case ITEM_MOD_CRIT_RANGED_RATING:
  11734. ApplyRatingMod(CR_CRIT_RANGED, int32(addValue), apply);
  11735. break;
  11736. case ITEM_MOD_CRIT_SPELL_RATING:
  11737. ApplyRatingMod(CR_CRIT_SPELL, int32(addValue), apply);
  11738. break;
  11739. case ITEM_MOD_HASTE_SPELL_RATING:
  11740. ApplyRatingMod(CR_HASTE_SPELL, int32(addValue), apply);
  11741. break;
  11742. case ITEM_MOD_HIT_RATING:
  11743. ApplyRatingMod(CR_HIT_MELEE, int32(addValue), apply);
  11744. ApplyRatingMod(CR_HIT_RANGED, int32(addValue), apply);
  11745. ApplyRatingMod(CR_HIT_SPELL, int32(addValue), apply);
  11746. break;
  11747. case ITEM_MOD_CRIT_RATING:
  11748. ApplyRatingMod(CR_CRIT_MELEE, int32(addValue), apply);
  11749. ApplyRatingMod(CR_CRIT_RANGED, int32(addValue), apply);
  11750. ApplyRatingMod(CR_CRIT_SPELL, int32(addValue), apply);
  11751. break;
  11752. case ITEM_MOD_RESILIENCE_RATING:
  11753. ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE_TAKEN, int32(addValue), apply);
  11754. break;
  11755. case ITEM_MOD_HASTE_RATING:
  11756. ApplyRatingMod(CR_HASTE_MELEE, int32(addValue), apply);
  11757. ApplyRatingMod(CR_HASTE_RANGED, int32(addValue), apply);
  11758. ApplyRatingMod(CR_HASTE_SPELL, int32(addValue), apply);
  11759. break;
  11760. case ITEM_MOD_EXPERTISE_RATING:
  11761. ApplyRatingMod(CR_EXPERTISE, int32(addValue), apply);
  11762. break;
  11763. case ITEM_MOD_ATTACK_POWER:
  11764. HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, addValue, apply);
  11765. HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, addValue, apply);
  11766. break;
  11767. case ITEM_MOD_RANGED_ATTACK_POWER:
  11768. HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, addValue, apply);
  11769. break;
  11770. case ITEM_MOD_MANA_REGENERATION:
  11771. ApplyManaRegenBonus(int32(addValue), apply);
  11772. break;
  11773. case ITEM_MOD_ARMOR_PENETRATION_RATING:
  11774. ApplyRatingMod(CR_ARMOR_PENETRATION, int32(addValue), apply);
  11775. break;
  11776. case ITEM_MOD_SPELL_POWER:
  11777. ApplySpellPowerBonus(int32(addValue), apply);
  11778. break;
  11779. case ITEM_MOD_HEALTH_REGEN:
  11780. ApplyHealthRegenBonus(int32(addValue), apply);
  11781. break;
  11782. case ITEM_MOD_SPELL_PENETRATION:
  11783. ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, int32(addValue), apply);
  11784. m_spellPenetrationItemMod += apply ? int32(addValue) : -int32(addValue);
  11785. break;
  11786. case ITEM_MOD_BLOCK_VALUE:
  11787. HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, addValue, apply);
  11788. break;
  11789. case ITEM_MOD_MASTERY_RATING:
  11790. ApplyRatingMod(CR_MASTERY, int32(addValue), apply);
  11791. break;
  11792. }
  11793. }
  11794. void Player::ApplyEnchantment(Item* item, bool apply)
  11795. {
  11796. for (uint32 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot)
  11797. {
  11798. // Apply reforge as last enchant
  11799. if (slot == REFORGE_ENCHANTMENT_SLOT)
  11800. continue;
  11801. ApplyEnchantment(item, EnchantmentSlot(slot), apply);
  11802. }
  11803. ApplyEnchantment(item, REFORGE_ENCHANTMENT_SLOT, apply);
  11804. }
  11805. void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool apply_dur, bool ignore_condition)
  11806. {
  11807. if (!item || !item->IsEquipped())
  11808. return;
  11809. if (slot >= MAX_ENCHANTMENT_SLOT)
  11810. return;
  11811. if (slot == TRANSMOGRIFY_ENCHANTMENT_SLOT)
  11812. return;
  11813. if (slot == REFORGE_ENCHANTMENT_SLOT)
  11814. {
  11815. ApplyReforgeEnchantment(item, apply);
  11816. return;
  11817. }
  11818. uint32 enchant_id = item->GetEnchantmentId(slot);
  11819. if (!enchant_id)
  11820. return;
  11821. SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  11822. if (!pEnchant)
  11823. return;
  11824. if (!ignore_condition && pEnchant->EnchantmentCondition && !EnchantmentFitsRequirements(pEnchant->EnchantmentCondition, -1))
  11825. return;
  11826. if (pEnchant->requiredLevel > getLevel())
  11827. return;
  11828. if (pEnchant->requiredSkill > 0 && pEnchant->requiredSkillValue > GetSkillValue(pEnchant->requiredSkill))
  11829. return;
  11830. // Cogwheel gems dont have requirement data set in SpellItemEnchantment.dbc, but they do have it in Item-sparse.db2
  11831. if (ItemTemplate const* gem = sObjectMgr->GetItemTemplate(pEnchant->GemID))
  11832. if (gem->RequiredSkill && GetSkillValue(gem->RequiredSkill) < gem->RequiredSkillRank)
  11833. return;
  11834. // If we're dealing with a gem inside a prismatic socket we need to check the prismatic socket requirements
  11835. // rather than the gem requirements itself. If the socket has no color it is a prismatic socket.
  11836. if ((slot == SOCK_ENCHANTMENT_SLOT || slot == SOCK_ENCHANTMENT_SLOT_2 || slot == SOCK_ENCHANTMENT_SLOT_3)
  11837. && !item->GetTemplate()->Socket[slot-SOCK_ENCHANTMENT_SLOT].Color)
  11838. {
  11839. // Check if the requirements for the prismatic socket are met before applying the gem stats
  11840. SpellItemEnchantmentEntry const* pPrismaticEnchant = sSpellItemEnchantmentStore.LookupEntry(item->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT));
  11841. if (!pPrismaticEnchant || (pPrismaticEnchant->requiredSkill > 0 && pPrismaticEnchant->requiredSkillValue > GetSkillValue(pPrismaticEnchant->requiredSkill)))
  11842. return;
  11843. }
  11844. if (!item->IsBroken())
  11845. {
  11846. for (int s = 0; s < MAX_ITEM_ENCHANTMENT_EFFECTS; ++s)
  11847. {
  11848. uint32 enchant_display_type = pEnchant->type[s];
  11849. uint32 enchant_amount = pEnchant->amount[s];
  11850. uint32 enchant_spell_id = pEnchant->spellid[s];
  11851. switch (enchant_display_type)
  11852. {
  11853. case ITEM_ENCHANTMENT_TYPE_NONE:
  11854. break;
  11855. case ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL:
  11856. // processed in Player::CastItemCombatSpell
  11857. break;
  11858. case ITEM_ENCHANTMENT_TYPE_DAMAGE:
  11859. if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
  11860. HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(enchant_amount), apply);
  11861. else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND)
  11862. HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(enchant_amount), apply);
  11863. else if (item->GetSlot() == EQUIPMENT_SLOT_RANGED)
  11864. HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
  11865. break;
  11866. case ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL:
  11867. if (enchant_spell_id)
  11868. {
  11869. if (apply)
  11870. {
  11871. int32 basepoints = 0;
  11872. // Random Property Exist - try found basepoints for spell (basepoints depends from item suffix factor)
  11873. if (item->GetItemRandomPropertyId() < 0)
  11874. {
  11875. ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
  11876. if (item_rand)
  11877. {
  11878. // Search enchant_amount
  11879. for (int k = 0; k < 5; ++k)
  11880. {
  11881. if (item_rand->enchant_id[k] == enchant_id)
  11882. {
  11883. basepoints = int32((item_rand->prefix[k] * item->GetItemSuffixFactor()) / 10000);
  11884. break;
  11885. }
  11886. }
  11887. }
  11888. }
  11889. // Cast custom spell vs all equal basepoints got from enchant_amount
  11890. if (basepoints)
  11891. CastCustomSpell(this, enchant_spell_id, &basepoints, &basepoints, &basepoints, true, item);
  11892. else
  11893. CastSpell(this, enchant_spell_id, true, item);
  11894. }
  11895. else
  11896. RemoveAurasDueToItemSpell(enchant_spell_id, item->GetGUID());
  11897. }
  11898. break;
  11899. case ITEM_ENCHANTMENT_TYPE_RESISTANCE:
  11900. if (!enchant_amount)
  11901. {
  11902. ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
  11903. if (item_rand)
  11904. {
  11905. for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k)
  11906. {
  11907. if (item_rand->enchant_id[k] == enchant_id)
  11908. {
  11909. enchant_amount = uint32((item_rand->prefix[k] * item->GetItemSuffixFactor()) / 10000);
  11910. break;
  11911. }
  11912. }
  11913. }
  11914. }
  11915. HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply);
  11916. break;
  11917. case ITEM_ENCHANTMENT_TYPE_STAT:
  11918. {
  11919. if (!enchant_amount)
  11920. {
  11921. ItemRandomSuffixEntry const* item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
  11922. if (item_rand_suffix)
  11923. {
  11924. for (int k = 0; k < 5; ++k)
  11925. {
  11926. if (item_rand_suffix->enchant_id[k] == enchant_id)
  11927. {
  11928. enchant_amount = uint32((item_rand_suffix->prefix[k] * item->GetItemSuffixFactor()) / 10000);
  11929. break;
  11930. }
  11931. }
  11932. }
  11933. }
  11934. TC_LOG_DEBUG("entities.player.items", "Adding %u to stat nb %u", enchant_amount, enchant_spell_id);
  11935. switch (enchant_spell_id)
  11936. {
  11937. case ITEM_MOD_MANA:
  11938. TC_LOG_DEBUG("entities.player.items", "+ %u MANA", enchant_amount);
  11939. HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(enchant_amount), apply);
  11940. break;
  11941. case ITEM_MOD_HEALTH:
  11942. TC_LOG_DEBUG("entities.player.items", "+ %u HEALTH", enchant_amount);
  11943. HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(enchant_amount), apply);
  11944. break;
  11945. case ITEM_MOD_AGILITY:
  11946. TC_LOG_DEBUG("entities.player.items", "+ %u AGILITY", enchant_amount);
  11947. HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply);
  11948. ApplyStatBuffMod(STAT_AGILITY, (float)enchant_amount, apply);
  11949. break;
  11950. case ITEM_MOD_STRENGTH:
  11951. TC_LOG_DEBUG("entities.player.items", "+ %u STRENGTH", enchant_amount);
  11952. HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply);
  11953. ApplyStatBuffMod(STAT_STRENGTH, (float)enchant_amount, apply);
  11954. break;
  11955. case ITEM_MOD_INTELLECT:
  11956. TC_LOG_DEBUG("entities.player.items", "+ %u INTELLECT", enchant_amount);
  11957. HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply);
  11958. ApplyStatBuffMod(STAT_INTELLECT, (float)enchant_amount, apply);
  11959. break;
  11960. case ITEM_MOD_SPIRIT:
  11961. TC_LOG_DEBUG("entities.player.items", "+ %u SPIRIT", enchant_amount);
  11962. HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply);
  11963. ApplyStatBuffMod(STAT_SPIRIT, (float)enchant_amount, apply);
  11964. break;
  11965. case ITEM_MOD_STAMINA:
  11966. TC_LOG_DEBUG("entities.player.items", "+ %u STAMINA", enchant_amount);
  11967. HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply);
  11968. ApplyStatBuffMod(STAT_STAMINA, (float)enchant_amount, apply);
  11969. break;
  11970. case ITEM_MOD_DEFENSE_SKILL_RATING:
  11971. ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply);
  11972. TC_LOG_DEBUG("entities.player.items", "+ %u DEFENCE", enchant_amount);
  11973. break;
  11974. case ITEM_MOD_DODGE_RATING:
  11975. ApplyRatingMod(CR_DODGE, enchant_amount, apply);
  11976. TC_LOG_DEBUG("entities.player.items", "+ %u DODGE", enchant_amount);
  11977. break;
  11978. case ITEM_MOD_PARRY_RATING:
  11979. ApplyRatingMod(CR_PARRY, enchant_amount, apply);
  11980. TC_LOG_DEBUG("entities.player.items", "+ %u PARRY", enchant_amount);
  11981. break;
  11982. case ITEM_MOD_BLOCK_RATING:
  11983. ApplyRatingMod(CR_BLOCK, enchant_amount, apply);
  11984. TC_LOG_DEBUG("entities.player.items", "+ %u SHIELD_BLOCK", enchant_amount);
  11985. break;
  11986. case ITEM_MOD_HIT_MELEE_RATING:
  11987. ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
  11988. TC_LOG_DEBUG("entities.player.items", "+ %u MELEE_HIT", enchant_amount);
  11989. break;
  11990. case ITEM_MOD_HIT_RANGED_RATING:
  11991. ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
  11992. TC_LOG_DEBUG("entities.player.items", "+ %u RANGED_HIT", enchant_amount);
  11993. break;
  11994. case ITEM_MOD_HIT_SPELL_RATING:
  11995. ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
  11996. TC_LOG_DEBUG("entities.player.items", "+ %u SPELL_HIT", enchant_amount);
  11997. break;
  11998. case ITEM_MOD_CRIT_MELEE_RATING:
  11999. ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
  12000. TC_LOG_DEBUG("entities.player.items", "+ %u MELEE_CRIT", enchant_amount);
  12001. break;
  12002. case ITEM_MOD_CRIT_RANGED_RATING:
  12003. ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
  12004. TC_LOG_DEBUG("entities.player.items", "+ %u RANGED_CRIT", enchant_amount);
  12005. break;
  12006. case ITEM_MOD_CRIT_SPELL_RATING:
  12007. ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
  12008. TC_LOG_DEBUG("entities.player.items", "+ %u SPELL_CRIT", enchant_amount);
  12009. break;
  12010. // Values from ITEM_STAT_MELEE_HA_RATING to ITEM_MOD_HASTE_RANGED_RATING are never used
  12011. // in Enchantments
  12012. // case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
  12013. // ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
  12014. // break;
  12015. // case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
  12016. // ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
  12017. // break;
  12018. // case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
  12019. // ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
  12020. // break;
  12021. // case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
  12022. // ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
  12023. // break;
  12024. // case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
  12025. // ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
  12026. // break;
  12027. // case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
  12028. // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
  12029. // break;
  12030. // case ITEM_MOD_HASTE_MELEE_RATING:
  12031. // ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
  12032. // break;
  12033. // case ITEM_MOD_HASTE_RANGED_RATING:
  12034. // ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
  12035. // break;
  12036. case ITEM_MOD_HASTE_SPELL_RATING:
  12037. ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
  12038. break;
  12039. case ITEM_MOD_HIT_RATING:
  12040. ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
  12041. ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
  12042. ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
  12043. TC_LOG_DEBUG("entities.player.items", "+ %u HIT", enchant_amount);
  12044. break;
  12045. case ITEM_MOD_CRIT_RATING:
  12046. ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
  12047. ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
  12048. ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
  12049. TC_LOG_DEBUG("entities.player.items", "+ %u CRITICAL", enchant_amount);
  12050. break;
  12051. // case ITEM_MOD_HIT_TAKEN_RATING: // Unused since 3.3.5
  12052. // ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
  12053. // ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
  12054. // ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
  12055. // break;
  12056. // case ITEM_MOD_CRIT_TAKEN_RATING: // Unused since 3.3.5
  12057. // ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
  12058. // ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
  12059. // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
  12060. // break;
  12061. case ITEM_MOD_RESILIENCE_RATING:
  12062. ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE_TAKEN, enchant_amount, apply);
  12063. TC_LOG_DEBUG("entities.player.items", "+ %u RESILIENCE", enchant_amount);
  12064. break;
  12065. case ITEM_MOD_HASTE_RATING:
  12066. ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
  12067. ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
  12068. ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
  12069. TC_LOG_DEBUG("entities.player.items", "+ %u HASTE", enchant_amount);
  12070. break;
  12071. case ITEM_MOD_EXPERTISE_RATING:
  12072. ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply);
  12073. TC_LOG_DEBUG("entities.player.items", "+ %u EXPERTISE", enchant_amount);
  12074. break;
  12075. case ITEM_MOD_ATTACK_POWER:
  12076. HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(enchant_amount), apply);
  12077. HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
  12078. TC_LOG_DEBUG("entities.player.items", "+ %u ATTACK_POWER", enchant_amount);
  12079. break;
  12080. case ITEM_MOD_RANGED_ATTACK_POWER:
  12081. HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
  12082. TC_LOG_DEBUG("entities.player.items", "+ %u RANGED_ATTACK_POWER", enchant_amount);
  12083. break;
  12084. case ITEM_MOD_MANA_REGENERATION:
  12085. ApplyManaRegenBonus(enchant_amount, apply);
  12086. TC_LOG_DEBUG("entities.player.items", "+ %u MANA_REGENERATION", enchant_amount);
  12087. break;
  12088. case ITEM_MOD_ARMOR_PENETRATION_RATING:
  12089. ApplyRatingMod(CR_ARMOR_PENETRATION, enchant_amount, apply);
  12090. TC_LOG_DEBUG("entities.player.items", "+ %u ARMOR PENETRATION", enchant_amount);
  12091. break;
  12092. case ITEM_MOD_SPELL_POWER:
  12093. ApplySpellPowerBonus(enchant_amount, apply);
  12094. TC_LOG_DEBUG("entities.player.items", "+ %u SPELL_POWER", enchant_amount);
  12095. break;
  12096. case ITEM_MOD_HEALTH_REGEN:
  12097. ApplyHealthRegenBonus(enchant_amount, apply);
  12098. TC_LOG_DEBUG("entities.player.items", "+ %u HEALTH_REGENERATION", enchant_amount);
  12099. break;
  12100. case ITEM_MOD_SPELL_PENETRATION:
  12101. ApplySpellPenetrationBonus(enchant_amount, apply);
  12102. TC_LOG_DEBUG("entities.player.items", "+ %u SPELL_PENETRATION", enchant_amount);
  12103. break;
  12104. case ITEM_MOD_BLOCK_VALUE:
  12105. HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(enchant_amount), apply);
  12106. TC_LOG_DEBUG("entities.player.items", "+ %u BLOCK_VALUE", enchant_amount);
  12107. break;
  12108. case ITEM_MOD_MASTERY_RATING:
  12109. ApplyRatingMod(CR_MASTERY, enchant_amount, apply);
  12110. TC_LOG_DEBUG("entities.player.items", "+ %u MASTERY", enchant_amount);
  12111. break;
  12112. default:
  12113. break;
  12114. }
  12115. break;
  12116. }
  12117. case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon
  12118. {
  12119. if (getClass() == CLASS_SHAMAN)
  12120. {
  12121. float addValue = 0.0f;
  12122. if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
  12123. {
  12124. addValue = float(enchant_amount * item->GetTemplate()->Delay / 1000.0f);
  12125. HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, addValue, apply);
  12126. }
  12127. else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND)
  12128. {
  12129. addValue = float(enchant_amount * item->GetTemplate()->Delay / 1000.0f);
  12130. HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, addValue, apply);
  12131. }
  12132. }
  12133. break;
  12134. }
  12135. case ITEM_ENCHANTMENT_TYPE_USE_SPELL:
  12136. // processed in Player::CastItemUseSpell
  12137. break;
  12138. case ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET:
  12139. // nothing do..
  12140. break;
  12141. default:
  12142. TC_LOG_ERROR("entities.player", "Unknown item enchantment (id = %d) display type: %d", enchant_id, enchant_display_type);
  12143. break;
  12144. } /*switch (enchant_display_type)*/
  12145. } /*for*/
  12146. }
  12147. // visualize enchantment at player and equipped items
  12148. if (slot == PERM_ENCHANTMENT_SLOT)
  12149. SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 0, apply ? item->GetEnchantmentId(slot) : 0);
  12150. if (slot == TEMP_ENCHANTMENT_SLOT)
  12151. SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 1, apply ? item->GetEnchantmentId(slot) : 0);
  12152. if (apply_dur)
  12153. {
  12154. if (apply)
  12155. {
  12156. // set duration
  12157. uint32 duration = item->GetEnchantmentDuration(slot);
  12158. if (duration > 0)
  12159. AddEnchantmentDuration(item, slot, duration);
  12160. }
  12161. else
  12162. {
  12163. // duration == 0 will remove EnchantDuration
  12164. AddEnchantmentDuration(item, slot, 0);
  12165. }
  12166. }
  12167. }
  12168. void Player::UpdateSkillEnchantments(uint16 skill_id, uint16 curr_value, uint16 new_value)
  12169. {
  12170. for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
  12171. {
  12172. if (m_items[i])
  12173. {
  12174. for (uint8 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot)
  12175. {
  12176. if (slot > PRISMATIC_ENCHANTMENT_SLOT && slot < PROP_ENCHANTMENT_SLOT_0) // not holding enchantment id
  12177. continue;
  12178. uint32 ench_id = m_items[i]->GetEnchantmentId(EnchantmentSlot(slot));
  12179. if (!ench_id)
  12180. continue;
  12181. SpellItemEnchantmentEntry const* Enchant = sSpellItemEnchantmentStore.LookupEntry(ench_id);
  12182. if (!Enchant)
  12183. return;
  12184. if (Enchant->requiredSkill == skill_id)
  12185. {
  12186. // Checks if the enchantment needs to be applied or removed
  12187. if (curr_value < Enchant->requiredSkillValue && new_value >= Enchant->requiredSkillValue)
  12188. ApplyEnchantment(m_items[i], EnchantmentSlot(slot), true);
  12189. else if (new_value < Enchant->requiredSkillValue && curr_value >= Enchant->requiredSkillValue)
  12190. ApplyEnchantment(m_items[i], EnchantmentSlot(slot), false);
  12191. }
  12192. // If we're dealing with a gem inside a prismatic socket we need to check the prismatic socket requirements
  12193. // rather than the gem requirements itself. If the socket has no color it is a prismatic socket.
  12194. if ((slot == SOCK_ENCHANTMENT_SLOT || slot == SOCK_ENCHANTMENT_SLOT_2 || slot == SOCK_ENCHANTMENT_SLOT_3)
  12195. && !m_items[i]->GetTemplate()->Socket[slot-SOCK_ENCHANTMENT_SLOT].Color)
  12196. {
  12197. SpellItemEnchantmentEntry const* pPrismaticEnchant = sSpellItemEnchantmentStore.LookupEntry(m_items[i]->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT));
  12198. if (pPrismaticEnchant && pPrismaticEnchant->requiredSkill == skill_id)
  12199. {
  12200. if (curr_value < pPrismaticEnchant->requiredSkillValue && new_value >= pPrismaticEnchant->requiredSkillValue)
  12201. ApplyEnchantment(m_items[i], EnchantmentSlot(slot), true);
  12202. else if (new_value < pPrismaticEnchant->requiredSkillValue && curr_value >= pPrismaticEnchant->requiredSkillValue)
  12203. ApplyEnchantment(m_items[i], EnchantmentSlot(slot), false);
  12204. }
  12205. }
  12206. }
  12207. }
  12208. }
  12209. }
  12210. void Player::SendEnchantmentDurations()
  12211. {
  12212. for (EnchantDurationList::const_iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr)
  12213. GetSession()->SendItemEnchantTimeUpdate(GetGUID(), itr->item->GetGUID(), itr->slot, uint32(itr->leftduration) / 1000);
  12214. }
  12215. void Player::SendItemDurations()
  12216. {
  12217. for (ItemDurationList::const_iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end(); ++itr)
  12218. (*itr)->SendTimeUpdate(this);
  12219. }
  12220. void Player::SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast)
  12221. {
  12222. if (!item) // prevent crash
  12223. return;
  12224. // last check 2.0.10
  12225. WorldPacket data(SMSG_ITEM_PUSH_RESULT, (8+4+4+4+1+4+4+4+4+4));
  12226. data << uint64(GetGUID()); // player GUID
  12227. data << uint32(received); // 0=looted, 1=from npc
  12228. data << uint32(created); // 0=received, 1=created
  12229. data << uint32(1); // bool print error to chat
  12230. data << uint8(item->GetBagSlot()); // bagslot
  12231. // item slot, but when added to stack: 0xFFFFFFFF
  12232. data << uint32((item->GetCount() == count) ? item->GetSlot() : -1);
  12233. data << uint32(item->GetEntry()); // item id
  12234. data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
  12235. data << int32(item->GetItemRandomPropertyId()); // random item property id
  12236. data << uint32(count); // count of items
  12237. data << uint32(GetItemCount(item->GetEntry())); // count of items in inventory
  12238. if (broadcast && GetGroup())
  12239. GetGroup()->BroadcastPacket(&data, true);
  12240. else
  12241. GetSession()->SendPacket(&data);
  12242. }
  12243. /*********************************************************/
  12244. /*** GOSSIP SYSTEM ***/
  12245. /*********************************************************/
  12246. void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool showQuests /*= false*/)
  12247. {
  12248. PlayerMenu* menu = PlayerTalkClass;
  12249. menu->ClearMenus();
  12250. menu->GetGossipMenu().SetMenuId(menuId);
  12251. GossipMenuItemsMapBounds menuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(menuId);
  12252. // if default menuId and no menu options exist for this, use options from default options
  12253. if (menuItemBounds.first == menuItemBounds.second && menuId == GetDefaultGossipMenuForSource(source))
  12254. menuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(0);
  12255. uint32 npcflags = 0;
  12256. if (source->GetTypeId() == TYPEID_UNIT)
  12257. {
  12258. npcflags = source->GetUInt32Value(UNIT_NPC_FLAGS);
  12259. if (showQuests && npcflags & UNIT_NPC_FLAG_QUESTGIVER)
  12260. PrepareQuestMenu(source->GetGUID());
  12261. }
  12262. else if (source->GetTypeId() == TYPEID_GAMEOBJECT)
  12263. if (showQuests && source->ToGameObject()->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
  12264. PrepareQuestMenu(source->GetGUID());
  12265. for (GossipMenuItemsContainer::const_iterator itr = menuItemBounds.first; itr != menuItemBounds.second; ++itr)
  12266. {
  12267. bool canTalk = true;
  12268. if (!sConditionMgr->IsObjectMeetToConditions(this, source, itr->second.Conditions))
  12269. continue;
  12270. if (Creature* creature = source->ToCreature())
  12271. {
  12272. if (!(itr->second.OptionNpcflag & npcflags))
  12273. continue;
  12274. switch (itr->second.OptionType)
  12275. {
  12276. case GOSSIP_OPTION_ARMORER:
  12277. canTalk = false; // added in special mode
  12278. break;
  12279. case GOSSIP_OPTION_SPIRITHEALER:
  12280. if (!isDead())
  12281. canTalk = false;
  12282. break;
  12283. case GOSSIP_OPTION_VENDOR:
  12284. {
  12285. VendorItemData const* vendorItems = creature->GetVendorItems();
  12286. if (!vendorItems || vendorItems->Empty())
  12287. {
  12288. TC_LOG_ERROR("sql.sql", "Creature %s (Entry: %u GUID: %u DB GUID: %u) has UNIT_NPC_FLAG_VENDOR set but has an empty trading item list.", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUIDLow(), creature->GetDBTableGUIDLow());
  12289. canTalk = false;
  12290. }
  12291. break;
  12292. }
  12293. case GOSSIP_OPTION_LEARNDUALSPEC:
  12294. if (!(GetSpecsCount() == 1 && creature->isCanTrainingAndResetTalentsOf(this) && !(getLevel() < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL))))
  12295. canTalk = false;
  12296. break;
  12297. case GOSSIP_OPTION_UNLEARNTALENTS:
  12298. if (!creature->isCanTrainingAndResetTalentsOf(this))
  12299. canTalk = false;
  12300. break;
  12301. case GOSSIP_OPTION_UNLEARNPETTALENTS:
  12302. if (!GetPet() || GetPet()->getPetType() != HUNTER_PET || GetPet()->m_spells.size() <= 1 || creature->GetCreatureTemplate()->trainer_type != TRAINER_TYPE_PETS || creature->GetCreatureTemplate()->trainer_class != CLASS_HUNTER)
  12303. canTalk = false;
  12304. break;
  12305. case GOSSIP_OPTION_TAXIVENDOR:
  12306. if (GetSession()->SendLearnNewTaxiNode(creature))
  12307. return;
  12308. break;
  12309. case GOSSIP_OPTION_BATTLEFIELD:
  12310. if (!creature->isCanInteractWithBattleMaster(this, false))
  12311. canTalk = false;
  12312. break;
  12313. case GOSSIP_OPTION_STABLEPET:
  12314. if (getClass() != CLASS_HUNTER)
  12315. canTalk = false;
  12316. break;
  12317. case GOSSIP_OPTION_QUESTGIVER:
  12318. canTalk = false;
  12319. break;
  12320. case GOSSIP_OPTION_TRAINER:
  12321. if (getClass() != creature->GetCreatureTemplate()->trainer_class && creature->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS)
  12322. TC_LOG_ERROR("sql.sql", "GOSSIP_OPTION_TRAINER:: Player %s (GUID: %u) request wrong gossip menu: %u with wrong class: %u at Creature: %s (Entry: %u, Trainer Class: %u)",
  12323. GetName().c_str(), GetGUIDLow(), menu->GetGossipMenu().GetMenuId(), getClass(), creature->GetName().c_str(), creature->GetEntry(), creature->GetCreatureTemplate()->trainer_class);
  12324. // no break;
  12325. case GOSSIP_OPTION_GOSSIP:
  12326. case GOSSIP_OPTION_SPIRITGUIDE:
  12327. case GOSSIP_OPTION_INNKEEPER:
  12328. case GOSSIP_OPTION_BANKER:
  12329. case GOSSIP_OPTION_PETITIONER:
  12330. case GOSSIP_OPTION_TABARDDESIGNER:
  12331. case GOSSIP_OPTION_AUCTIONEER:
  12332. break; // no checks
  12333. case GOSSIP_OPTION_OUTDOORPVP:
  12334. if (!sOutdoorPvPMgr->CanTalkTo(this, creature, itr->second))
  12335. canTalk = false;
  12336. break;
  12337. default:
  12338. TC_LOG_ERROR("sql.sql", "Creature entry %u has unknown gossip option %u for menu %u", creature->GetEntry(), itr->second.OptionType, itr->second.MenuId);
  12339. canTalk = false;
  12340. break;
  12341. }
  12342. }
  12343. else if (GameObject* go = source->ToGameObject())
  12344. {
  12345. switch (itr->second.OptionType)
  12346. {
  12347. case GOSSIP_OPTION_GOSSIP:
  12348. if (go->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER && go->GetGoType() != GAMEOBJECT_TYPE_GOOBER)
  12349. canTalk = false;
  12350. break;
  12351. default:
  12352. canTalk = false;
  12353. break;
  12354. }
  12355. }
  12356. if (canTalk)
  12357. {
  12358. std::string strOptionText, strBoxText;
  12359. BroadcastText const* optionBroadcastText = sObjectMgr->GetBroadcastText(itr->second.OptionBroadcastTextId);
  12360. BroadcastText const* boxBroadcastText = sObjectMgr->GetBroadcastText(itr->second.BoxBroadcastTextId);
  12361. LocaleConstant locale = GetSession()->GetSessionDbLocaleIndex();
  12362. if (optionBroadcastText)
  12363. strOptionText = optionBroadcastText->GetText(locale, getGender());
  12364. else
  12365. strOptionText = itr->second.OptionText;
  12366. if (boxBroadcastText)
  12367. strBoxText = boxBroadcastText->GetText(locale, getGender());
  12368. else
  12369. strBoxText = itr->second.BoxText;
  12370. if (locale != DEFAULT_LOCALE)
  12371. {
  12372. if (!optionBroadcastText)
  12373. {
  12374. /// Find localizations from database.
  12375. if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(MAKE_PAIR32(menuId, menuId)))
  12376. ObjectMgr::GetLocaleString(gossipMenuLocale->OptionText, locale, strOptionText);
  12377. }
  12378. if (!boxBroadcastText)
  12379. {
  12380. /// Find localizations from database.
  12381. if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(MAKE_PAIR32(menuId, menuId)))
  12382. ObjectMgr::GetLocaleString(gossipMenuLocale->BoxText, locale, strBoxText);
  12383. }
  12384. }
  12385. menu->GetGossipMenu().AddMenuItem(itr->second.OptionIndex, itr->second.OptionIcon, strOptionText, 0, itr->second.OptionType, strBoxText, itr->second.BoxMoney, itr->second.BoxCoded);
  12386. menu->GetGossipMenu().AddGossipMenuItemData(itr->second.OptionIndex, itr->second.ActionMenuId, itr->second.ActionPoiId);
  12387. }
  12388. }
  12389. }
  12390. void Player::SendPreparedGossip(WorldObject* source)
  12391. {
  12392. if (!source)
  12393. return;
  12394. if (source->GetTypeId() == TYPEID_UNIT)
  12395. {
  12396. // in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
  12397. if (!source->ToCreature()->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP) && !PlayerTalkClass->GetQuestMenu().Empty())
  12398. {
  12399. SendPreparedQuest(source->GetGUID());
  12400. return;
  12401. }
  12402. }
  12403. else if (source->GetTypeId() == TYPEID_GAMEOBJECT)
  12404. {
  12405. // probably need to find a better way here
  12406. if (!PlayerTalkClass->GetGossipMenu().GetMenuId() && !PlayerTalkClass->GetQuestMenu().Empty())
  12407. {
  12408. SendPreparedQuest(source->GetGUID());
  12409. return;
  12410. }
  12411. }
  12412. // in case non empty gossip menu (that not included quests list size) show it
  12413. // (quest entries from quest menu will be included in list)
  12414. uint32 textId = GetGossipTextId(source);
  12415. if (uint32 menuId = PlayerTalkClass->GetGossipMenu().GetMenuId())
  12416. textId = GetGossipTextId(menuId, source);
  12417. PlayerTalkClass->SendGossipMenu(textId, source->GetGUID());
  12418. }
  12419. void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 menuId)
  12420. {
  12421. GossipMenu& gossipMenu = PlayerTalkClass->GetGossipMenu();
  12422. // if not same, then something funky is going on
  12423. if (menuId != gossipMenu.GetMenuId())
  12424. return;
  12425. GossipMenuItem const* item = gossipMenu.GetItem(gossipListId);
  12426. if (!item)
  12427. return;
  12428. uint32 gossipOptionId = item->OptionType;
  12429. ObjectGuid guid = source->GetGUID();
  12430. if (source->GetTypeId() == TYPEID_GAMEOBJECT)
  12431. {
  12432. if (gossipOptionId > GOSSIP_OPTION_QUESTGIVER)
  12433. {
  12434. TC_LOG_ERROR("entities.player", "Player guid %u request invalid gossip option for GameObject entry %u", GetGUIDLow(), source->GetEntry());
  12435. return;
  12436. }
  12437. }
  12438. GossipMenuItemData const* menuItemData = gossipMenu.GetItemData(gossipListId);
  12439. if (!menuItemData)
  12440. return;
  12441. int32 cost = int32(item->BoxMoney);
  12442. if (!HasEnoughMoney(int64(cost)))
  12443. {
  12444. SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
  12445. PlayerTalkClass->SendCloseGossip();
  12446. return;
  12447. }
  12448. switch (gossipOptionId)
  12449. {
  12450. case GOSSIP_OPTION_GOSSIP:
  12451. {
  12452. if (menuItemData->GossipActionPoi)
  12453. PlayerTalkClass->SendPointOfInterest(menuItemData->GossipActionPoi);
  12454. if (menuItemData->GossipActionMenuId)
  12455. {
  12456. PrepareGossipMenu(source, menuItemData->GossipActionMenuId);
  12457. SendPreparedGossip(source);
  12458. }
  12459. break;
  12460. }
  12461. case GOSSIP_OPTION_OUTDOORPVP:
  12462. sOutdoorPvPMgr->HandleGossipOption(this, source->GetGUID(), gossipListId);
  12463. break;
  12464. case GOSSIP_OPTION_SPIRITHEALER:
  12465. if (isDead())
  12466. source->ToCreature()->CastSpell(source->ToCreature(), 17251, true, NULL, NULL, GetGUID());
  12467. break;
  12468. case GOSSIP_OPTION_QUESTGIVER:
  12469. PrepareQuestMenu(guid);
  12470. SendPreparedQuest(guid);
  12471. break;
  12472. case GOSSIP_OPTION_VENDOR:
  12473. case GOSSIP_OPTION_ARMORER:
  12474. GetSession()->SendListInventory(guid);
  12475. break;
  12476. case GOSSIP_OPTION_STABLEPET:
  12477. GetSession()->SendStablePet(guid);
  12478. break;
  12479. case GOSSIP_OPTION_TRAINER:
  12480. GetSession()->SendTrainerList(guid);
  12481. break;
  12482. case GOSSIP_OPTION_LEARNDUALSPEC:
  12483. if (GetSpecsCount() == 1 && getLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL))
  12484. {
  12485. // Cast spells that teach dual spec
  12486. // Both are also ImplicitTarget self and must be cast by player
  12487. CastSpell(this, 63680, true, NULL, NULL, GetGUID());
  12488. CastSpell(this, 63624, true, NULL, NULL, GetGUID());
  12489. // Should show another Gossip text with "Congratulations..."
  12490. PlayerTalkClass->SendCloseGossip();
  12491. }
  12492. break;
  12493. case GOSSIP_OPTION_UNLEARNTALENTS:
  12494. PlayerTalkClass->SendCloseGossip();
  12495. SendTalentWipeConfirm(guid);
  12496. break;
  12497. case GOSSIP_OPTION_UNLEARNPETTALENTS:
  12498. PlayerTalkClass->SendCloseGossip();
  12499. ResetPetTalents();
  12500. break;
  12501. case GOSSIP_OPTION_TAXIVENDOR:
  12502. GetSession()->SendTaxiMenu(source->ToCreature());
  12503. break;
  12504. case GOSSIP_OPTION_INNKEEPER:
  12505. PlayerTalkClass->SendCloseGossip();
  12506. SetBindPoint(guid);
  12507. break;
  12508. case GOSSIP_OPTION_BANKER:
  12509. GetSession()->SendShowBank(guid);
  12510. break;
  12511. case GOSSIP_OPTION_PETITIONER:
  12512. PlayerTalkClass->SendCloseGossip();
  12513. GetSession()->SendPetitionShowList(guid);
  12514. break;
  12515. case GOSSIP_OPTION_TABARDDESIGNER:
  12516. PlayerTalkClass->SendCloseGossip();
  12517. GetSession()->SendTabardVendorActivate(guid);
  12518. break;
  12519. case GOSSIP_OPTION_AUCTIONEER:
  12520. GetSession()->SendAuctionHello(guid, source->ToCreature());
  12521. break;
  12522. case GOSSIP_OPTION_SPIRITGUIDE:
  12523. PrepareGossipMenu(source);
  12524. SendPreparedGossip(source);
  12525. break;
  12526. case GOSSIP_OPTION_BATTLEFIELD:
  12527. {
  12528. BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(source->GetEntry());
  12529. if (bgTypeId == BATTLEGROUND_TYPE_NONE)
  12530. {
  12531. TC_LOG_ERROR("entities.player", "a user (guid %u) requested battlegroundlist from a npc who is no battlemaster", GetGUIDLow());
  12532. return;
  12533. }
  12534. GetSession()->SendBattleGroundList(guid, bgTypeId);
  12535. break;
  12536. }
  12537. }
  12538. ModifyMoney(-cost);
  12539. }
  12540. uint32 Player::GetGossipTextId(WorldObject* source)
  12541. {
  12542. if (!source)
  12543. return DEFAULT_GOSSIP_MESSAGE;
  12544. return GetGossipTextId(GetDefaultGossipMenuForSource(source), source);
  12545. }
  12546. uint32 Player::GetGossipTextId(uint32 menuId, WorldObject* source)
  12547. {
  12548. uint32 textId = DEFAULT_GOSSIP_MESSAGE;
  12549. if (!menuId)
  12550. return textId;
  12551. GossipMenusMapBounds menuBounds = sObjectMgr->GetGossipMenusMapBounds(menuId);
  12552. for (GossipMenusContainer::const_iterator itr = menuBounds.first; itr != menuBounds.second; ++itr)
  12553. {
  12554. if (sConditionMgr->IsObjectMeetToConditions(this, source, itr->second.conditions))
  12555. textId = itr->second.text_id;
  12556. }
  12557. return textId;
  12558. }
  12559. uint32 Player::GetDefaultGossipMenuForSource(WorldObject* source)
  12560. {
  12561. switch (source->GetTypeId())
  12562. {
  12563. case TYPEID_UNIT:
  12564. return source->ToCreature()->GetCreatureTemplate()->GossipMenuId;
  12565. case TYPEID_GAMEOBJECT:
  12566. return source->ToGameObject()->GetGOInfo()->GetGossipMenuId();
  12567. default:
  12568. break;
  12569. }
  12570. return 0;
  12571. }
  12572. /*********************************************************/
  12573. /*** QUEST SYSTEM ***/
  12574. /*********************************************************/
  12575. void Player::PrepareQuestMenu(ObjectGuid guid)
  12576. {
  12577. QuestRelationBounds objectQR;
  12578. QuestRelationBounds objectQIR;
  12579. // pets also can have quests
  12580. Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid);
  12581. if (creature)
  12582. {
  12583. objectQR = sObjectMgr->GetCreatureQuestRelationBounds(creature->GetEntry());
  12584. objectQIR = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(creature->GetEntry());
  12585. }
  12586. else
  12587. {
  12588. //we should obtain map pointer from GetMap() in 99% of cases. Special case
  12589. //only for quests which cast teleport spells on player
  12590. Map* _map = IsInWorld() ? GetMap() : sMapMgr->FindMap(GetMapId(), GetInstanceId());
  12591. ASSERT(_map);
  12592. GameObject* gameObject = _map->GetGameObject(guid);
  12593. if (gameObject)
  12594. {
  12595. objectQR = sObjectMgr->GetGOQuestRelationBounds(gameObject->GetEntry());
  12596. objectQIR = sObjectMgr->GetGOQuestInvolvedRelationBounds(gameObject->GetEntry());
  12597. }
  12598. else
  12599. return;
  12600. }
  12601. QuestMenu &qm = PlayerTalkClass->GetQuestMenu();
  12602. qm.ClearMenu();
  12603. for (QuestRelations::const_iterator i = objectQIR.first; i != objectQIR.second; ++i)
  12604. {
  12605. uint32 quest_id = i->second;
  12606. QuestStatus status = GetQuestStatus(quest_id);
  12607. if (status == QUEST_STATUS_COMPLETE)
  12608. qm.AddMenuItem(quest_id, 4);
  12609. else if (status == QUEST_STATUS_INCOMPLETE)
  12610. qm.AddMenuItem(quest_id, 4);
  12611. //else if (status == QUEST_STATUS_AVAILABLE)
  12612. // qm.AddMenuItem(quest_id, 2);
  12613. }
  12614. for (QuestRelations::const_iterator i = objectQR.first; i != objectQR.second; ++i)
  12615. {
  12616. uint32 quest_id = i->second;
  12617. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  12618. if (!quest)
  12619. continue;
  12620. if (!CanTakeQuest(quest, false))
  12621. continue;
  12622. if (quest->IsAutoComplete())
  12623. qm.AddMenuItem(quest_id, 4);
  12624. else if (GetQuestStatus(quest_id) == QUEST_STATUS_NONE)
  12625. qm.AddMenuItem(quest_id, 2);
  12626. }
  12627. }
  12628. void Player::SendPreparedQuest(ObjectGuid guid)
  12629. {
  12630. QuestMenu& questMenu = PlayerTalkClass->GetQuestMenu();
  12631. if (questMenu.Empty())
  12632. return;
  12633. // single element case
  12634. if (questMenu.GetMenuItemCount() == 1)
  12635. {
  12636. QuestMenuItem const& qmi0 = questMenu.GetItem(0);
  12637. uint32 questId = qmi0.QuestId;
  12638. // Auto open -- maybe also should verify there is no greeting
  12639. if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
  12640. {
  12641. if (qmi0.QuestIcon == 4)
  12642. PlayerTalkClass->SendQuestGiverRequestItems(quest, guid, CanRewardQuest(quest, false), true);
  12643. // Send completable on repeatable and autoCompletable quest if player don't have quest
  12644. /// @todo verify if check for !quest->IsDaily() is really correct (possibly not)
  12645. else
  12646. {
  12647. Object* object = ObjectAccessor::GetObjectByTypeMask(*this, guid, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM);
  12648. if (!object || (!object->hasQuest(questId) && !object->hasInvolvedQuest(questId)))
  12649. {
  12650. PlayerTalkClass->SendCloseGossip();
  12651. return;
  12652. }
  12653. if (quest->IsAutoAccept() && CanAddQuest(quest, true) && CanTakeQuest(quest, true))
  12654. AddQuestAndCheckCompletion(quest, object);
  12655. if (quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
  12656. PlayerTalkClass->SendQuestGiverRequestItems(quest, guid, CanCompleteRepeatableQuest(quest), true);
  12657. else
  12658. PlayerTalkClass->SendQuestGiverQuestDetails(quest, guid, true);
  12659. }
  12660. }
  12661. }
  12662. // multiple entries
  12663. else
  12664. {
  12665. QEmote qe;
  12666. qe._Delay = 0;
  12667. qe._Emote = 0;
  12668. std::string title = "";
  12669. // need pet case for some quests
  12670. Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid);
  12671. if (creature)
  12672. {
  12673. uint32 textid = GetGossipTextId(creature);
  12674. GossipText const* gossiptext = sObjectMgr->GetGossipText(textid);
  12675. if (!gossiptext)
  12676. {
  12677. qe._Delay = 0; //TEXTEMOTE_MESSAGE; //zyg: player emote
  12678. qe._Emote = 0; //TEXTEMOTE_HELLO; //zyg: NPC emote
  12679. title.clear();
  12680. }
  12681. else
  12682. {
  12683. qe = gossiptext->Options[0].Emotes[0];
  12684. if (!gossiptext->Options[0].Text_0.empty())
  12685. {
  12686. title = gossiptext->Options[0].Text_0;
  12687. int loc_idx = GetSession()->GetSessionDbLocaleIndex();
  12688. if (loc_idx >= 0)
  12689. if (NpcTextLocale const* nl = sObjectMgr->GetNpcTextLocale(textid))
  12690. ObjectMgr::GetLocaleString(nl->Text_0[0], loc_idx, title);
  12691. }
  12692. else
  12693. {
  12694. title = gossiptext->Options[0].Text_1;
  12695. int loc_idx = GetSession()->GetSessionDbLocaleIndex();
  12696. if (loc_idx >= 0)
  12697. if (NpcTextLocale const* nl = sObjectMgr->GetNpcTextLocale(textid))
  12698. ObjectMgr::GetLocaleString(nl->Text_1[0], loc_idx, title);
  12699. }
  12700. }
  12701. }
  12702. PlayerTalkClass->SendQuestGiverQuestList(qe, title, guid);
  12703. }
  12704. }
  12705. bool Player::IsActiveQuest(uint32 quest_id) const
  12706. {
  12707. return m_QuestStatus.find(quest_id) != m_QuestStatus.end();
  12708. }
  12709. Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* quest)
  12710. {
  12711. QuestRelationBounds objectQR;
  12712. uint32 nextQuestID = quest->GetNextQuestInChain();
  12713. switch (guid.GetHigh())
  12714. {
  12715. case HIGHGUID_PLAYER:
  12716. ASSERT(quest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE));
  12717. return sObjectMgr->GetQuestTemplate(nextQuestID);
  12718. case HIGHGUID_UNIT:
  12719. case HIGHGUID_PET:
  12720. case HIGHGUID_VEHICLE:
  12721. {
  12722. if (Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid))
  12723. objectQR = sObjectMgr->GetCreatureQuestRelationBounds(creature->GetEntry());
  12724. else
  12725. return NULL;
  12726. break;
  12727. }
  12728. case HIGHGUID_GAMEOBJECT:
  12729. {
  12730. //we should obtain map pointer from GetMap() in 99% of cases. Special case
  12731. //only for quests which cast teleport spells on player
  12732. Map* _map = IsInWorld() ? GetMap() : sMapMgr->FindMap(GetMapId(), GetInstanceId());
  12733. ASSERT(_map);
  12734. if (GameObject* gameObject = _map->GetGameObject(guid))
  12735. objectQR = sObjectMgr->GetGOQuestRelationBounds(gameObject->GetEntry());
  12736. else
  12737. return NULL;
  12738. break;
  12739. }
  12740. default:
  12741. return NULL;
  12742. }
  12743. // for unit and go state
  12744. for (QuestRelations::const_iterator itr = objectQR.first; itr != objectQR.second; ++itr)
  12745. {
  12746. if (itr->second == nextQuestID)
  12747. return sObjectMgr->GetQuestTemplate(nextQuestID);
  12748. }
  12749. return NULL;
  12750. }
  12751. bool Player::CanSeeStartQuest(Quest const* quest)
  12752. {
  12753. if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this) && SatisfyQuestClass(quest, false) && SatisfyQuestRace(quest, false) &&
  12754. SatisfyQuestSkill(quest, false) && SatisfyQuestExclusiveGroup(quest, false) && SatisfyQuestReputation(quest, false) &&
  12755. SatisfyQuestPreviousQuest(quest, false) && SatisfyQuestNextChain(quest, false) &&
  12756. SatisfyQuestPrevChain(quest, false) && SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) &&
  12757. SatisfyQuestMonth(quest, false) && SatisfyQuestSeasonal(quest, false))
  12758. {
  12759. return getLevel() + sWorld->getIntConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF) >= quest->GetMinLevel();
  12760. }
  12761. return false;
  12762. }
  12763. bool Player::CanTakeQuest(Quest const* quest, bool msg)
  12764. {
  12765. return !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this)
  12766. && SatisfyQuestStatus(quest, msg) && SatisfyQuestExclusiveGroup(quest, msg)
  12767. && SatisfyQuestClass(quest, msg) && SatisfyQuestRace(quest, msg) && SatisfyQuestLevel(quest, msg)
  12768. && SatisfyQuestSkill(quest, msg) && SatisfyQuestReputation(quest, msg)
  12769. && SatisfyQuestPreviousQuest(quest, msg) && SatisfyQuestTimed(quest, msg)
  12770. && SatisfyQuestNextChain(quest, msg) && SatisfyQuestPrevChain(quest, msg)
  12771. && SatisfyQuestDay(quest, msg) && SatisfyQuestWeek(quest, msg)
  12772. && SatisfyQuestMonth(quest, msg) && SatisfyQuestSeasonal(quest, msg)
  12773. && SatisfyQuestConditions(quest, msg);
  12774. }
  12775. bool Player::CanAddQuest(Quest const* quest, bool msg)
  12776. {
  12777. if (!SatisfyQuestLog(msg))
  12778. return false;
  12779. uint32 srcitem = quest->GetSrcItemId();
  12780. if (srcitem > 0)
  12781. {
  12782. uint32 count = quest->GetSrcItemCount();
  12783. ItemPosCountVec dest;
  12784. InventoryResult msg2 = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, srcitem, count);
  12785. // player already have max number (in most case 1) source item, no additional item needed and quest can be added.
  12786. if (msg2 == EQUIP_ERR_ITEM_MAX_COUNT)
  12787. return true;
  12788. else if (msg2 != EQUIP_ERR_OK)
  12789. {
  12790. SendEquipError(msg2, NULL, NULL, srcitem);
  12791. return false;
  12792. }
  12793. }
  12794. return true;
  12795. }
  12796. bool Player::CanCompleteQuest(uint32 quest_id)
  12797. {
  12798. if (quest_id)
  12799. {
  12800. Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
  12801. if (!qInfo)
  12802. return false;
  12803. if (!qInfo->IsRepeatable() && m_RewardedQuests.find(quest_id) != m_RewardedQuests.end())
  12804. return false; // not allow re-complete quest
  12805. // auto complete quest
  12806. if (qInfo->IsAutoComplete() && CanTakeQuest(qInfo, false))
  12807. return true;
  12808. QuestStatusMap::iterator itr = m_QuestStatus.find(quest_id);
  12809. if (itr == m_QuestStatus.end())
  12810. return false;
  12811. QuestStatusData &q_status = itr->second;
  12812. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  12813. {
  12814. if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER))
  12815. {
  12816. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
  12817. {
  12818. if (qInfo->RequiredItemCount[i]!= 0 && q_status.ItemCount[i] < qInfo->RequiredItemCount[i])
  12819. return false;
  12820. }
  12821. }
  12822. if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO))
  12823. {
  12824. for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
  12825. {
  12826. if (qInfo->RequiredNpcOrGo[i] == 0)
  12827. continue;
  12828. if (qInfo->RequiredNpcOrGoCount[i] != 0 && q_status.CreatureOrGOCount[i] < qInfo->RequiredNpcOrGoCount[i])
  12829. return false;
  12830. }
  12831. }
  12832. if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL))
  12833. if (qInfo->GetPlayersSlain() != 0 && q_status.PlayerCount < qInfo->GetPlayersSlain())
  12834. return false;
  12835. if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT) && !q_status.Explored)
  12836. return false;
  12837. if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED) && q_status.Timer == 0)
  12838. return false;
  12839. if (qInfo->GetRewOrReqMoney() < 0)
  12840. {
  12841. if (!HasEnoughMoney(-int64(qInfo->GetRewOrReqMoney())))
  12842. return false;
  12843. }
  12844. uint32 repFacId = qInfo->GetRepObjectiveFaction();
  12845. if (repFacId && GetReputationMgr().GetReputation(repFacId) < qInfo->GetRepObjectiveValue())
  12846. return false;
  12847. return true;
  12848. }
  12849. }
  12850. return false;
  12851. }
  12852. bool Player::CanCompleteRepeatableQuest(Quest const* quest)
  12853. {
  12854. // Solve problem that player don't have the quest and try complete it.
  12855. // if repeatable she must be able to complete event if player don't have it.
  12856. // Seem that all repeatable quest are DELIVER Flag so, no need to add more.
  12857. if (!CanTakeQuest(quest, false))
  12858. return false;
  12859. if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER))
  12860. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
  12861. if (quest->RequiredItemId[i] && quest->RequiredItemCount[i] && !HasItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i]))
  12862. return false;
  12863. if (!CanRewardQuest(quest, false))
  12864. return false;
  12865. return true;
  12866. }
  12867. bool Player::CanRewardQuest(Quest const* quest, bool msg)
  12868. {
  12869. // not auto complete quest and not completed quest (only cheating case, then ignore without message)
  12870. if (!quest->IsDFQuest() && !quest->IsAutoComplete() && GetQuestStatus(quest->GetQuestId()) != QUEST_STATUS_COMPLETE)
  12871. return false;
  12872. // daily quest can't be rewarded (25 daily quest already completed)
  12873. if (!SatisfyQuestDay(quest, true) || !SatisfyQuestWeek(quest, true) || !SatisfyQuestMonth(quest, true) || !SatisfyQuestSeasonal(quest, true))
  12874. return false;
  12875. // rewarded and not repeatable quest (only cheating case, then ignore without message)
  12876. if (GetQuestRewardStatus(quest->GetQuestId()))
  12877. return false;
  12878. // prevent receive reward with quest items in bank
  12879. if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER))
  12880. {
  12881. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
  12882. {
  12883. if (quest->RequiredItemCount[i]!= 0 &&
  12884. GetItemCount(quest->RequiredItemId[i]) < quest->RequiredItemCount[i])
  12885. {
  12886. if (msg)
  12887. SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL, quest->RequiredItemId[i]);
  12888. return false;
  12889. }
  12890. }
  12891. }
  12892. for (uint8 i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; i++)
  12893. if (quest->RequiredCurrencyId[i] && !HasCurrency(quest->RequiredCurrencyId[i], quest->RequiredCurrencyCount[i]))
  12894. return false;
  12895. // prevent receive reward with low money and GetRewOrReqMoney() < 0
  12896. if (quest->GetRewOrReqMoney() < 0 && !HasEnoughMoney(-int64(quest->GetRewOrReqMoney())))
  12897. return false;
  12898. return true;
  12899. }
  12900. void Player::AddQuestAndCheckCompletion(Quest const* quest, Object* questGiver)
  12901. {
  12902. AddQuest(quest, questGiver);
  12903. if (CanCompleteQuest(quest->GetQuestId()))
  12904. CompleteQuest(quest->GetQuestId());
  12905. if (!questGiver)
  12906. return;
  12907. switch (questGiver->GetTypeId())
  12908. {
  12909. case TYPEID_UNIT:
  12910. sScriptMgr->OnQuestAccept(this, questGiver->ToCreature(), quest);
  12911. questGiver->ToCreature()->AI()->sQuestAccept(this, quest);
  12912. break;
  12913. case TYPEID_ITEM:
  12914. case TYPEID_CONTAINER:
  12915. {
  12916. Item* item = (Item*)questGiver;
  12917. sScriptMgr->OnQuestAccept(this, item, quest);
  12918. // destroy not required for quest finish quest starting item
  12919. bool destroyItem = true;
  12920. for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
  12921. {
  12922. if (quest->RequiredItemId[i] == item->GetEntry() && item->GetTemplate()->MaxCount > 0)
  12923. {
  12924. destroyItem = false;
  12925. break;
  12926. }
  12927. }
  12928. if (destroyItem)
  12929. DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
  12930. break;
  12931. }
  12932. case TYPEID_GAMEOBJECT:
  12933. sScriptMgr->OnQuestAccept(this, questGiver->ToGameObject(), quest);
  12934. questGiver->ToGameObject()->AI()->QuestAccept(this, quest);
  12935. break;
  12936. default:
  12937. break;
  12938. }
  12939. }
  12940. bool Player::CanRewardQuest(Quest const* quest, uint32 reward, bool msg)
  12941. {
  12942. // prevent receive reward with quest items in bank or for not completed quest
  12943. if (!CanRewardQuest(quest, msg))
  12944. return false;
  12945. ItemPosCountVec dest;
  12946. if (quest->GetRewChoiceItemsCount() > 0)
  12947. {
  12948. if (quest->RewardChoiceItemId[reward])
  12949. {
  12950. InventoryResult res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, quest->RewardChoiceItemId[reward], quest->RewardChoiceItemCount[reward]);
  12951. if (res != EQUIP_ERR_OK)
  12952. {
  12953. SendEquipError(res, NULL, NULL, quest->RewardChoiceItemId[reward]);
  12954. return false;
  12955. }
  12956. }
  12957. }
  12958. if (quest->GetRewItemsCount() > 0)
  12959. {
  12960. for (uint32 i = 0; i < quest->GetRewItemsCount(); ++i)
  12961. {
  12962. if (quest->RewardItemId[i])
  12963. {
  12964. InventoryResult res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, quest->RewardItemId[i], quest->RewardItemIdCount[i]);
  12965. if (res != EQUIP_ERR_OK)
  12966. {
  12967. SendEquipError(res, NULL, NULL, quest->RewardItemId[i]);
  12968. return false;
  12969. }
  12970. }
  12971. }
  12972. }
  12973. return true;
  12974. }
  12975. void Player::AddQuest(Quest const* quest, Object* questGiver)
  12976. {
  12977. uint16 log_slot = FindQuestSlot(0);
  12978. if (log_slot >= MAX_QUEST_LOG_SIZE) // Player does not have any free slot in the quest log
  12979. return;
  12980. uint32 quest_id = quest->GetQuestId();
  12981. // if not exist then created with set uState == NEW and rewarded=false
  12982. QuestStatusData& questStatusData = m_QuestStatus[quest_id];
  12983. // check for repeatable quests status reset
  12984. questStatusData.Status = QUEST_STATUS_INCOMPLETE;
  12985. questStatusData.Explored = false;
  12986. if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER))
  12987. {
  12988. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
  12989. questStatusData.ItemCount[i] = 0;
  12990. }
  12991. if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO))
  12992. {
  12993. for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
  12994. questStatusData.CreatureOrGOCount[i] = 0;
  12995. }
  12996. if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL))
  12997. questStatusData.PlayerCount = 0;
  12998. GiveQuestSourceItem(quest);
  12999. AdjustQuestReqItemCount(quest, questStatusData);
  13000. if (quest->GetRepObjectiveFaction())
  13001. if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(quest->GetRepObjectiveFaction()))
  13002. GetReputationMgr().SetVisible(factionEntry);
  13003. if (quest->GetRepObjectiveFaction2())
  13004. if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(quest->GetRepObjectiveFaction2()))
  13005. GetReputationMgr().SetVisible(factionEntry);
  13006. uint32 qtime = 0;
  13007. if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED))
  13008. {
  13009. uint32 limittime = quest->GetLimitTime();
  13010. // shared timed quest
  13011. if (questGiver && questGiver->GetTypeId() == TYPEID_PLAYER)
  13012. limittime = questGiver->ToPlayer()->getQuestStatusMap()[quest_id].Timer / IN_MILLISECONDS;
  13013. AddTimedQuest(quest_id);
  13014. questStatusData.Timer = limittime * IN_MILLISECONDS;
  13015. qtime = static_cast<uint32>(time(NULL)) + limittime;
  13016. }
  13017. else
  13018. questStatusData.Timer = 0;
  13019. if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP))
  13020. {
  13021. pvpInfo.IsHostile = true;
  13022. UpdatePvPState();
  13023. }
  13024. SetQuestSlot(log_slot, quest_id, qtime);
  13025. m_QuestStatusSave[quest_id] = QUEST_DEFAULT_SAVE_TYPE;
  13026. StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, quest_id);
  13027. SendQuestUpdate(quest_id);
  13028. if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled
  13029. {
  13030. // prepare Quest Tracker datas
  13031. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_TRACK);
  13032. stmt->setUInt32(0, quest_id);
  13033. stmt->setUInt32(1, GetGUIDLow());
  13034. stmt->setString(2, _HASH);
  13035. stmt->setString(3, _DATE);
  13036. // add to Quest Tracker
  13037. CharacterDatabase.Execute(stmt);
  13038. }
  13039. }
  13040. void Player::CompleteQuest(uint32 quest_id)
  13041. {
  13042. if (quest_id)
  13043. {
  13044. SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE);
  13045. uint16 log_slot = FindQuestSlot(quest_id);
  13046. if (log_slot < MAX_QUEST_LOG_SIZE)
  13047. SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
  13048. if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id))
  13049. {
  13050. if (qInfo->HasFlag(QUEST_FLAGS_TRACKING))
  13051. RewardQuest(qInfo, 0, this, false);
  13052. else
  13053. SendQuestComplete(qInfo);
  13054. }
  13055. }
  13056. if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled
  13057. {
  13058. // prepare Quest Tracker datas
  13059. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_COMPLETE_TIME);
  13060. stmt->setUInt32(0, quest_id);
  13061. stmt->setUInt32(1, GetGUIDLow());
  13062. // add to Quest Tracker
  13063. CharacterDatabase.Execute(stmt);
  13064. }
  13065. }
  13066. void Player::IncompleteQuest(uint32 quest_id)
  13067. {
  13068. if (quest_id)
  13069. {
  13070. SetQuestStatus(quest_id, QUEST_STATUS_INCOMPLETE);
  13071. uint16 log_slot = FindQuestSlot(quest_id);
  13072. if (log_slot < MAX_QUEST_LOG_SIZE)
  13073. RemoveQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
  13074. }
  13075. }
  13076. void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce)
  13077. {
  13078. //this THING should be here to protect code from quest, which cast on player far teleport as a reward
  13079. //should work fine, cause far teleport will be executed in Player::Update()
  13080. SetCanDelayTeleport(true);
  13081. uint32 quest_id = quest->GetQuestId();
  13082. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
  13083. if (quest->RequiredItemId[i])
  13084. DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true);
  13085. for (uint8 i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; ++i)
  13086. if (quest->RequiredCurrencyId[i])
  13087. ModifyCurrency(quest->RequiredCurrencyId[i], -int32(quest->RequiredCurrencyCount[i]));
  13088. for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
  13089. {
  13090. if (quest->RequiredSourceItemId[i])
  13091. {
  13092. uint32 count = quest->RequiredSourceItemCount[i];
  13093. DestroyItemCount(quest->RequiredSourceItemId[i], count ? count : 9999, true);
  13094. }
  13095. }
  13096. RemoveTimedQuest(quest_id);
  13097. if (quest->GetRewChoiceItemsCount() > 0)
  13098. {
  13099. if (uint32 itemId = quest->RewardChoiceItemId[reward])
  13100. {
  13101. ItemPosCountVec dest;
  13102. if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, quest->RewardChoiceItemCount[reward]) == EQUIP_ERR_OK)
  13103. {
  13104. Item* item = StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
  13105. SendNewItem(item, quest->RewardChoiceItemCount[reward], true, false);
  13106. }
  13107. }
  13108. }
  13109. if (quest->GetRewItemsCount() > 0)
  13110. {
  13111. for (uint32 i = 0; i < quest->GetRewItemsCount(); ++i)
  13112. {
  13113. if (uint32 itemId = quest->RewardItemId[i])
  13114. {
  13115. ItemPosCountVec dest;
  13116. if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, quest->RewardItemIdCount[i]) == EQUIP_ERR_OK)
  13117. {
  13118. Item* item = StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
  13119. SendNewItem(item, quest->RewardItemIdCount[i], true, false);
  13120. }
  13121. else if (quest->IsDFQuest())
  13122. SendItemRetrievalMail(quest->RewardItemId[i], quest->RewardItemIdCount[i]);
  13123. }
  13124. }
  13125. }
  13126. for (uint8 i = 0; i < QUEST_REWARD_CURRENCY_COUNT; ++i)
  13127. if (quest->RewardCurrencyId[i])
  13128. ModifyCurrency(quest->RewardCurrencyId[i], quest->RewardCurrencyCount[i]);
  13129. if (uint32 skill = quest->GetRewardSkillId())
  13130. UpdateSkillPro(skill, 1000, quest->GetRewardSkillPoints());
  13131. RewardReputation(quest);
  13132. uint16 log_slot = FindQuestSlot(quest_id);
  13133. if (log_slot < MAX_QUEST_LOG_SIZE)
  13134. SetQuestSlot(log_slot, 0);
  13135. bool rewarded = (m_RewardedQuests.find(quest_id) != m_RewardedQuests.end());
  13136. // Not give XP in case already completed once repeatable quest
  13137. uint32 XP = rewarded && !quest->IsDFQuest() ? 0 : uint32(quest->XPValue(this) * sWorld->getRate(RATE_XP_QUEST));
  13138. // handle SPELL_AURA_MOD_XP_QUEST_PCT auras
  13139. Unit::AuraEffectList const& ModXPPctAuras = GetAuraEffectsByType(SPELL_AURA_MOD_XP_QUEST_PCT);
  13140. for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i)
  13141. AddPct(XP, (*i)->GetAmount());
  13142. int32 moneyRew = 0;
  13143. if (getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  13144. GiveXP(XP, NULL);
  13145. else
  13146. moneyRew = int32(quest->GetRewMoneyMaxLevel() * sWorld->getRate(RATE_DROP_MONEY));
  13147. if (Guild* guild = sGuildMgr->GetGuildById(GetGuildId()))
  13148. guild->GiveXP(uint32(quest->XPValue(this) * sWorld->getRate(RATE_XP_QUEST) * sWorld->getRate(RATE_XP_GUILD_MODIFIER)), this);
  13149. // Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
  13150. if (quest->GetRewOrReqMoney())
  13151. moneyRew += quest->GetRewOrReqMoney();
  13152. if (moneyRew)
  13153. {
  13154. ModifyMoney(moneyRew);
  13155. if (moneyRew > 0)
  13156. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, uint32(moneyRew));
  13157. }
  13158. // honor reward
  13159. if (uint32 honor = quest->CalculateHonorGain(getLevel()))
  13160. RewardHonor(NULL, 0, honor);
  13161. // title reward
  13162. if (quest->GetCharTitleId())
  13163. {
  13164. if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(quest->GetCharTitleId()))
  13165. SetTitle(titleEntry);
  13166. }
  13167. if (uint32 talents = quest->GetBonusTalents())
  13168. {
  13169. AddQuestRewardedTalentCount(talents);
  13170. InitTalentForLevel();
  13171. }
  13172. // Send reward mail
  13173. if (uint32 mail_template_id = quest->GetRewMailTemplateId())
  13174. {
  13175. /// @todo Poor design of mail system
  13176. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  13177. MailDraft(mail_template_id).SendMailTo(trans, this, questGiver, MAIL_CHECK_MASK_HAS_BODY, quest->GetRewMailDelaySecs());
  13178. CharacterDatabase.CommitTransaction(trans);
  13179. }
  13180. if (quest->IsDaily() || quest->IsDFQuest())
  13181. {
  13182. SetDailyQuestStatus(quest_id);
  13183. if (quest->IsDaily())
  13184. {
  13185. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, quest_id);
  13186. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY, quest_id);
  13187. }
  13188. }
  13189. else if (quest->IsWeekly())
  13190. SetWeeklyQuestStatus(quest_id);
  13191. else if (quest->IsMonthly())
  13192. SetMonthlyQuestStatus(quest_id);
  13193. else if (quest->IsSeasonal())
  13194. SetSeasonalQuestStatus(quest_id);
  13195. RemoveActiveQuest(quest_id, false);
  13196. m_RewardedQuests.insert(quest_id);
  13197. m_RewardedQuestsSave[quest_id] = QUEST_DEFAULT_SAVE_TYPE;
  13198. // StoreNewItem, mail reward, etc. save data directly to the database
  13199. // to prevent exploitable data desynchronisation we save the quest status to the database too
  13200. // (to prevent rewarding this quest another time while rewards were already given out)
  13201. SQLTransaction trans = SQLTransaction(NULL);
  13202. _SaveQuestStatus(trans);
  13203. if (announce)
  13204. SendQuestReward(quest, XP);
  13205. // cast spells after mark quest complete (some spells have quest completed state requirements in spell_area data)
  13206. if (quest->GetRewSpellCast() > 0)
  13207. {
  13208. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(quest->GetRewSpellCast());
  13209. if (questGiver->isType(TYPEMASK_UNIT) && !spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL) && !spellInfo->HasEffect(SPELL_EFFECT_CREATE_ITEM))
  13210. {
  13211. if (Creature* creature = GetMap()->GetCreature(questGiver->GetGUID()))
  13212. creature->CastSpell(this, quest->GetRewSpellCast(), true);
  13213. }
  13214. else
  13215. CastSpell(this, quest->GetRewSpellCast(), true);
  13216. }
  13217. else if (quest->GetRewSpell() > 0)
  13218. {
  13219. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(quest->GetRewSpell());
  13220. if (questGiver->isType(TYPEMASK_UNIT) && !spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL) && !spellInfo->HasEffect(SPELL_EFFECT_CREATE_ITEM))
  13221. {
  13222. if (Creature* creature = GetMap()->GetCreature(questGiver->GetGUID()))
  13223. creature->CastSpell(this, quest->GetRewSpell(), true);
  13224. }
  13225. else
  13226. CastSpell(this, quest->GetRewSpell(), true);
  13227. }
  13228. if (quest->GetZoneOrSort() > 0)
  13229. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE, quest->GetZoneOrSort());
  13230. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT);
  13231. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, quest->GetQuestId());
  13232. if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP))
  13233. {
  13234. pvpInfo.IsHostile = pvpInfo.IsInHostileArea || HasPvPForcingQuest();
  13235. UpdatePvPState();
  13236. }
  13237. SendQuestUpdate(quest_id);
  13238. //lets remove flag for delayed teleports
  13239. SetCanDelayTeleport(false);
  13240. }
  13241. void Player::FailQuest(uint32 questId)
  13242. {
  13243. // Already complete quests shouldn't turn failed.
  13244. if (GetQuestStatus(questId) == QUEST_STATUS_COMPLETE)
  13245. return;
  13246. if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
  13247. {
  13248. SetQuestStatus(questId, QUEST_STATUS_FAILED);
  13249. uint16 log_slot = FindQuestSlot(questId);
  13250. if (log_slot < MAX_QUEST_LOG_SIZE)
  13251. {
  13252. SetQuestSlotTimer(log_slot, 1);
  13253. SetQuestSlotState(log_slot, QUEST_STATE_FAIL);
  13254. }
  13255. if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED))
  13256. {
  13257. QuestStatusData& q_status = m_QuestStatus[questId];
  13258. RemoveTimedQuest(questId);
  13259. q_status.Timer = 0;
  13260. SendQuestTimerFailed(questId);
  13261. }
  13262. else
  13263. SendQuestFailed(questId);
  13264. // Destroy quest items on quest failure.
  13265. for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
  13266. if (quest->RequiredItemId[i] > 0 && quest->RequiredItemCount[i] > 0)
  13267. // Destroy items received on starting the quest.
  13268. DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true, true);
  13269. for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
  13270. if (quest->RequiredSourceItemId[i] > 0 && quest->RequiredSourceItemCount[i] > 0)
  13271. // Destroy items received during the quest.
  13272. DestroyItemCount(quest->RequiredSourceItemId[i], quest->RequiredSourceItemCount[i], true, true);
  13273. }
  13274. }
  13275. bool Player::SatisfyQuestSkill(Quest const* qInfo, bool msg) const
  13276. {
  13277. uint32 skill = qInfo->GetRequiredSkill();
  13278. // skip 0 case RequiredSkill
  13279. if (skill == 0)
  13280. return true;
  13281. // check skill value
  13282. if (GetSkillValue(skill) < qInfo->GetRequiredSkillValue())
  13283. {
  13284. if (msg)
  13285. {
  13286. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13287. TC_LOG_DEBUG("misc", "SatisfyQuestSkill: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required skill value.", qInfo->GetQuestId());
  13288. }
  13289. return false;
  13290. }
  13291. return true;
  13292. }
  13293. bool Player::SatisfyQuestLevel(Quest const* qInfo, bool msg)
  13294. {
  13295. if (getLevel() < qInfo->GetMinLevel())
  13296. {
  13297. if (msg)
  13298. {
  13299. SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_LOW_LEVEL);
  13300. TC_LOG_DEBUG("misc", "SatisfyQuestLevel: Sent INVALIDREASON_QUEST_FAILED_LOW_LEVEL (questId: %u) because player does not have required (min) level.", qInfo->GetQuestId());
  13301. }
  13302. return false;
  13303. }
  13304. else if (qInfo->GetMaxLevel() > 0 && getLevel() > qInfo->GetMaxLevel())
  13305. {
  13306. if (msg)
  13307. {
  13308. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); // There doesn't seem to be a specific response for too high player level
  13309. TC_LOG_DEBUG("misc", "SatisfyQuestLevel: Sent INVALIDREASON_QUEST_FAILED_LOW_LEVEL (questId: %u) because player does not have required (max) level.", qInfo->GetQuestId());
  13310. }
  13311. return false;
  13312. }
  13313. return true;
  13314. }
  13315. bool Player::SatisfyQuestLog(bool msg)
  13316. {
  13317. // exist free slot
  13318. if (FindQuestSlot(0) < MAX_QUEST_LOG_SIZE)
  13319. return true;
  13320. if (msg)
  13321. {
  13322. WorldPacket data(SMSG_QUESTLOG_FULL, 0);
  13323. GetSession()->SendPacket(&data);
  13324. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTLOG_FULL");
  13325. }
  13326. return false;
  13327. }
  13328. bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg)
  13329. {
  13330. // No previous quest (might be first quest in a series)
  13331. if (qInfo->prevQuests.empty())
  13332. return true;
  13333. for (Quest::PrevQuests::const_iterator iter = qInfo->prevQuests.begin(); iter != qInfo->prevQuests.end(); ++iter)
  13334. {
  13335. uint32 prevId = abs(*iter);
  13336. Quest const* qPrevInfo = sObjectMgr->GetQuestTemplate(prevId);
  13337. if (qPrevInfo)
  13338. {
  13339. // If any of the positive previous quests completed, return true
  13340. if (*iter > 0 && m_RewardedQuests.find(prevId) != m_RewardedQuests.end())
  13341. {
  13342. // skip one-from-all exclusive group
  13343. if (qPrevInfo->GetExclusiveGroup() >= 0)
  13344. return true;
  13345. // each-from-all exclusive group (< 0)
  13346. // can be start if only all quests in prev quest exclusive group completed and rewarded
  13347. ObjectMgr::ExclusiveQuestGroupsBounds range(sObjectMgr->mExclusiveQuestGroups.equal_range(qPrevInfo->GetExclusiveGroup()));
  13348. for (; range.first != range.second; ++range.first)
  13349. {
  13350. uint32 exclude_Id = range.first->second;
  13351. // skip checked quest id, only state of other quests in group is interesting
  13352. if (exclude_Id == prevId)
  13353. continue;
  13354. // alternative quest from group also must be completed and rewarded (reported)
  13355. if (m_RewardedQuests.find(exclude_Id) == m_RewardedQuests.end())
  13356. {
  13357. if (msg)
  13358. {
  13359. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13360. TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required quest (1).", qInfo->GetQuestId());
  13361. }
  13362. return false;
  13363. }
  13364. }
  13365. return true;
  13366. }
  13367. // If any of the negative previous quests active, return true
  13368. if (*iter < 0 && GetQuestStatus(prevId) != QUEST_STATUS_NONE)
  13369. {
  13370. // skip one-from-all exclusive group
  13371. if (qPrevInfo->GetExclusiveGroup() >= 0)
  13372. return true;
  13373. // each-from-all exclusive group (< 0)
  13374. // can be start if only all quests in prev quest exclusive group active
  13375. ObjectMgr::ExclusiveQuestGroupsBounds range(sObjectMgr->mExclusiveQuestGroups.equal_range(qPrevInfo->GetExclusiveGroup()));
  13376. for (; range.first != range.second; ++range.first)
  13377. {
  13378. uint32 exclude_Id = range.first->second;
  13379. // skip checked quest id, only state of other quests in group is interesting
  13380. if (exclude_Id == prevId)
  13381. continue;
  13382. // alternative quest from group also must be active
  13383. if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE)
  13384. {
  13385. if (msg)
  13386. {
  13387. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13388. TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required quest (2).", qInfo->GetQuestId());
  13389. }
  13390. return false;
  13391. }
  13392. }
  13393. return true;
  13394. }
  13395. }
  13396. }
  13397. // Has only positive prev. quests in non-rewarded state
  13398. // and negative prev. quests in non-active state
  13399. if (msg)
  13400. {
  13401. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13402. TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required quest (3).", qInfo->GetQuestId());
  13403. }
  13404. return false;
  13405. }
  13406. bool Player::SatisfyQuestClass(Quest const* qInfo, bool msg) const
  13407. {
  13408. uint32 reqClass = qInfo->GetRequiredClasses();
  13409. if (reqClass == 0)
  13410. return true;
  13411. if ((reqClass & getClassMask()) == 0)
  13412. {
  13413. if (msg)
  13414. {
  13415. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13416. TC_LOG_DEBUG("misc", "SatisfyQuestClass: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required class.", qInfo->GetQuestId());
  13417. }
  13418. return false;
  13419. }
  13420. return true;
  13421. }
  13422. bool Player::SatisfyQuestRace(Quest const* qInfo, bool msg)
  13423. {
  13424. uint32 reqraces = qInfo->GetRequiredRaces();
  13425. if (reqraces == 0)
  13426. return true;
  13427. if ((reqraces & getRaceMask()) == 0)
  13428. {
  13429. if (msg)
  13430. {
  13431. SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_WRONG_RACE);
  13432. TC_LOG_DEBUG("misc", "SatisfyQuestRace: Sent INVALIDREASON_QUEST_FAILED_WRONG_RACE (questId: %u) because player does not have required race.", qInfo->GetQuestId());
  13433. }
  13434. return false;
  13435. }
  13436. return true;
  13437. }
  13438. bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg)
  13439. {
  13440. uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); //Min required rep
  13441. if (fIdMin && GetReputationMgr().GetReputation(fIdMin) < qInfo->GetRequiredMinRepValue())
  13442. {
  13443. if (msg)
  13444. {
  13445. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13446. TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required reputation (min).", qInfo->GetQuestId());
  13447. }
  13448. return false;
  13449. }
  13450. uint32 fIdMax = qInfo->GetRequiredMaxRepFaction(); //Max required rep
  13451. if (fIdMax && GetReputationMgr().GetReputation(fIdMax) >= qInfo->GetRequiredMaxRepValue())
  13452. {
  13453. if (msg)
  13454. {
  13455. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13456. TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required reputation (max).", qInfo->GetQuestId());
  13457. }
  13458. return false;
  13459. }
  13460. // ReputationObjective2 does not seem to be an objective requirement but a requirement
  13461. // to be able to accept the quest
  13462. uint32 fIdObj = qInfo->GetRepObjectiveFaction2();
  13463. if (fIdObj && GetReputationMgr().GetReputation(fIdObj) >= qInfo->GetRepObjectiveValue2())
  13464. {
  13465. if (msg)
  13466. {
  13467. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13468. TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required reputation (ReputationObjective2).", qInfo->GetQuestId());
  13469. }
  13470. return false;
  13471. }
  13472. return true;
  13473. }
  13474. bool Player::SatisfyQuestStatus(Quest const* qInfo, bool msg)
  13475. {
  13476. if (GetQuestStatus(qInfo->GetQuestId()) != QUEST_STATUS_NONE)
  13477. {
  13478. if (msg)
  13479. {
  13480. SendCanTakeQuestResponse(INVALIDREASON_QUEST_ALREADY_ON);
  13481. TC_LOG_DEBUG("misc", "SatisfyQuestStatus: Sent INVALIDREASON_QUEST_ALREADY_ON (questId: %u) because player quest status is not NONE.", qInfo->GetQuestId());
  13482. }
  13483. return false;
  13484. }
  13485. return true;
  13486. }
  13487. bool Player::SatisfyQuestConditions(Quest const* qInfo, bool msg)
  13488. {
  13489. ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_ACCEPT, qInfo->GetQuestId());
  13490. if (!sConditionMgr->IsObjectMeetToConditions(this, conditions))
  13491. {
  13492. if (msg)
  13493. {
  13494. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13495. TC_LOG_DEBUG("misc", "SatisfyQuestConditions: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not meet conditions.", qInfo->GetQuestId());
  13496. }
  13497. TC_LOG_DEBUG("condition", "Player::SatisfyQuestConditions: conditions not met for quest %u", qInfo->GetQuestId());
  13498. return false;
  13499. }
  13500. return true;
  13501. }
  13502. bool Player::SatisfyQuestTimed(Quest const* qInfo, bool msg)
  13503. {
  13504. if (!m_timedquests.empty() && qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED))
  13505. {
  13506. if (msg)
  13507. {
  13508. SendCanTakeQuestResponse(INVALIDREASON_QUEST_ONLY_ONE_TIMED);
  13509. TC_LOG_DEBUG("misc", "SatisfyQuestTimed: Sent INVALIDREASON_QUEST_ONLY_ONE_TIMED (questId: %u) because player is already on a timed quest.", qInfo->GetQuestId());
  13510. }
  13511. return false;
  13512. }
  13513. return true;
  13514. }
  13515. bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg)
  13516. {
  13517. // non positive exclusive group, if > 0 then can be start if any other quest in exclusive group already started/completed
  13518. if (qInfo->GetExclusiveGroup() <= 0)
  13519. return true;
  13520. ObjectMgr::ExclusiveQuestGroupsBounds range(sObjectMgr->mExclusiveQuestGroups.equal_range(qInfo->GetExclusiveGroup()));
  13521. for (; range.first != range.second; ++range.first)
  13522. {
  13523. uint32 exclude_Id = range.first->second;
  13524. // skip checked quest id, only state of other quests in group is interesting
  13525. if (exclude_Id == qInfo->GetQuestId())
  13526. continue;
  13527. // not allow have daily quest if daily quest from exclusive group already recently completed
  13528. Quest const* Nquest = sObjectMgr->GetQuestTemplate(exclude_Id);
  13529. ASSERT(Nquest);
  13530. if (!SatisfyQuestDay(Nquest, false) || !SatisfyQuestWeek(Nquest, false) || !SatisfyQuestSeasonal(Nquest, false))
  13531. {
  13532. if (msg)
  13533. {
  13534. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13535. TC_LOG_DEBUG("misc", "SatisfyQuestExclusiveGroup: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player already did daily quests in exclusive group.", qInfo->GetQuestId());
  13536. }
  13537. return false;
  13538. }
  13539. // alternative quest already started or completed - but don't check rewarded states if both are repeatable
  13540. if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE || (!(qInfo->IsRepeatable() && Nquest->IsRepeatable()) && (m_RewardedQuests.find(exclude_Id) != m_RewardedQuests.end())))
  13541. {
  13542. if (msg)
  13543. {
  13544. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13545. TC_LOG_DEBUG("misc", "SatisfyQuestExclusiveGroup: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player already did quest in exclusive group.", qInfo->GetQuestId());
  13546. }
  13547. return false;
  13548. }
  13549. }
  13550. return true;
  13551. }
  13552. bool Player::SatisfyQuestNextChain(Quest const* qInfo, bool msg)
  13553. {
  13554. uint32 nextQuest = qInfo->GetNextQuestInChain();
  13555. if (!nextQuest)
  13556. return true;
  13557. // next quest in chain already started or completed
  13558. if (GetQuestStatus(nextQuest) != QUEST_STATUS_NONE) // GetQuestStatus returns QUEST_STATUS_COMPLETED for rewarded quests
  13559. {
  13560. if (msg)
  13561. {
  13562. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13563. TC_LOG_DEBUG("misc", "SatisfyQuestNextChain: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player already did or started next quest in chain.", qInfo->GetQuestId());
  13564. }
  13565. return false;
  13566. }
  13567. // check for all quests further up the chain
  13568. // only necessary if there are quest chains with more than one quest that can be skipped
  13569. //return SatisfyQuestNextChain(qInfo->GetNextQuestInChain(), msg);
  13570. return true;
  13571. }
  13572. bool Player::SatisfyQuestPrevChain(Quest const* qInfo, bool msg)
  13573. {
  13574. // No previous quest in chain
  13575. if (qInfo->prevChainQuests.empty())
  13576. return true;
  13577. for (Quest::PrevChainQuests::const_iterator iter = qInfo->prevChainQuests.begin(); iter != qInfo->prevChainQuests.end(); ++iter)
  13578. {
  13579. QuestStatusMap::const_iterator itr = m_QuestStatus.find(*iter);
  13580. // If any of the previous quests in chain active, return false
  13581. if (itr != m_QuestStatus.end() && itr->second.Status != QUEST_STATUS_NONE)
  13582. {
  13583. if (msg)
  13584. {
  13585. SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
  13586. TC_LOG_DEBUG("misc", "SatisfyQuestNextChain: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player already did or started next quest in chain.", qInfo->GetQuestId());
  13587. }
  13588. return false;
  13589. }
  13590. // check for all quests further down the chain
  13591. // only necessary if there are quest chains with more than one quest that can be skipped
  13592. //if (!SatisfyQuestPrevChain(prevId, msg))
  13593. // return false;
  13594. }
  13595. // No previous quest in chain active
  13596. return true;
  13597. }
  13598. bool Player::SatisfyQuestDay(Quest const* qInfo, bool msg)
  13599. {
  13600. if (!qInfo->IsDaily() && !qInfo->IsDFQuest())
  13601. return true;
  13602. if (qInfo->IsDFQuest())
  13603. {
  13604. if (!m_DFQuests.empty())
  13605. return false;
  13606. return true;
  13607. }
  13608. bool have_slot = false;
  13609. for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
  13610. {
  13611. uint32 id = GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx);
  13612. if (qInfo->GetQuestId() == id)
  13613. return false;
  13614. if (!id)
  13615. have_slot = true;
  13616. }
  13617. if (!have_slot)
  13618. {
  13619. if (msg)
  13620. {
  13621. SendCanTakeQuestResponse(INVALIDREASON_DAILY_QUESTS_REMAINING);
  13622. TC_LOG_DEBUG("misc", "SatisfyQuestDay: Sent INVALIDREASON_DAILY_QUESTS_REMAINING (questId: %u) because player already did all possible quests today.", qInfo->GetQuestId());
  13623. }
  13624. return false;
  13625. }
  13626. return true;
  13627. }
  13628. bool Player::SatisfyQuestWeek(Quest const* qInfo, bool /*msg*/)
  13629. {
  13630. if (!qInfo->IsWeekly() || m_weeklyquests.empty())
  13631. return true;
  13632. // if not found in cooldown list
  13633. return m_weeklyquests.find(qInfo->GetQuestId()) == m_weeklyquests.end();
  13634. }
  13635. bool Player::SatisfyQuestSeasonal(Quest const* qInfo, bool /*msg*/)
  13636. {
  13637. if (!qInfo->IsSeasonal() || m_seasonalquests.empty())
  13638. return true;
  13639. uint16 eventId = sGameEventMgr->GetEventIdForQuest(qInfo);
  13640. if (m_seasonalquests.find(eventId) == m_seasonalquests.end() || m_seasonalquests[eventId].empty())
  13641. return true;
  13642. // if not found in cooldown list
  13643. return m_seasonalquests[eventId].find(qInfo->GetQuestId()) == m_seasonalquests[eventId].end();
  13644. }
  13645. bool Player::SatisfyQuestMonth(Quest const* qInfo, bool /*msg*/)
  13646. {
  13647. if (!qInfo->IsMonthly() || m_monthlyquests.empty())
  13648. return true;
  13649. // if not found in cooldown list
  13650. return m_monthlyquests.find(qInfo->GetQuestId()) == m_monthlyquests.end();
  13651. }
  13652. bool Player::GiveQuestSourceItem(Quest const* quest)
  13653. {
  13654. uint32 srcitem = quest->GetSrcItemId();
  13655. if (srcitem > 0)
  13656. {
  13657. uint32 count = quest->GetSrcItemCount();
  13658. if (count <= 0)
  13659. count = 1;
  13660. ItemPosCountVec dest;
  13661. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, srcitem, count);
  13662. if (msg == EQUIP_ERR_OK)
  13663. {
  13664. Item* item = StoreNewItem(dest, srcitem, true);
  13665. SendNewItem(item, count, true, false);
  13666. return true;
  13667. }
  13668. // player already have max amount required item, just report success
  13669. else if (msg == EQUIP_ERR_ITEM_MAX_COUNT)
  13670. return true;
  13671. else
  13672. SendEquipError(msg, NULL, NULL, srcitem);
  13673. return false;
  13674. }
  13675. return true;
  13676. }
  13677. bool Player::TakeQuestSourceItem(uint32 questId, bool msg)
  13678. {
  13679. Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
  13680. if (quest)
  13681. {
  13682. uint32 srcItemId = quest->GetSrcItemId();
  13683. ItemTemplate const* item = sObjectMgr->GetItemTemplate(srcItemId);
  13684. if (srcItemId > 0)
  13685. {
  13686. uint32 count = quest->GetSrcItemCount();
  13687. if (count <= 0)
  13688. count = 1;
  13689. // exist two cases when destroy source quest item not possible:
  13690. // a) non un-equippable item (equipped non-empty bag, for example)
  13691. // b) when quest is started from an item and item also is needed in
  13692. // the end as RequiredItemId
  13693. InventoryResult res = CanUnequipItems(srcItemId, count);
  13694. if (res != EQUIP_ERR_OK)
  13695. {
  13696. if (msg)
  13697. SendEquipError(res, NULL, NULL, srcItemId);
  13698. return false;
  13699. }
  13700. ASSERT(item);
  13701. bool destroyItem = true;
  13702. for (uint8 n = 0; n < QUEST_ITEM_OBJECTIVES_COUNT; ++n)
  13703. if (item->StartQuest == questId && srcItemId == quest->RequiredItemId[n])
  13704. destroyItem = false;
  13705. if (destroyItem)
  13706. DestroyItemCount(srcItemId, count, true, true);
  13707. }
  13708. }
  13709. return true;
  13710. }
  13711. bool Player::GetQuestRewardStatus(uint32 quest_id) const
  13712. {
  13713. Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
  13714. if (qInfo)
  13715. {
  13716. // for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
  13717. if (!qInfo->IsRepeatable())
  13718. return m_RewardedQuests.find(quest_id) != m_RewardedQuests.end();
  13719. return false;
  13720. }
  13721. return false;
  13722. }
  13723. QuestStatus Player::GetQuestStatus(uint32 quest_id) const
  13724. {
  13725. if (quest_id)
  13726. {
  13727. QuestStatusMap::const_iterator itr = m_QuestStatus.find(quest_id);
  13728. if (itr != m_QuestStatus.end())
  13729. return itr->second.Status;
  13730. if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id))
  13731. if (!qInfo->IsRepeatable() && m_RewardedQuests.find(quest_id) != m_RewardedQuests.end())
  13732. return QUEST_STATUS_REWARDED;
  13733. }
  13734. return QUEST_STATUS_NONE;
  13735. }
  13736. bool Player::CanShareQuest(uint32 quest_id) const
  13737. {
  13738. Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
  13739. if (qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE))
  13740. {
  13741. QuestStatusMap::const_iterator itr = m_QuestStatus.find(quest_id);
  13742. if (itr != m_QuestStatus.end())
  13743. return itr->second.Status == QUEST_STATUS_INCOMPLETE;
  13744. }
  13745. return false;
  13746. }
  13747. void Player::SetQuestStatus(uint32 questId, QuestStatus status, bool update /*= true*/)
  13748. {
  13749. if (sObjectMgr->GetQuestTemplate(questId))
  13750. {
  13751. m_QuestStatus[questId].Status = status;
  13752. m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE;
  13753. }
  13754. if (update)
  13755. SendQuestUpdate(questId);
  13756. sScriptMgr->OnQuestStatusChange(this, questId, status);
  13757. }
  13758. void Player::RemoveActiveQuest(uint32 questId, bool update /*= true*/)
  13759. {
  13760. QuestStatusMap::iterator itr = m_QuestStatus.find(questId);
  13761. if (itr != m_QuestStatus.end())
  13762. {
  13763. m_QuestStatus.erase(itr);
  13764. m_QuestStatusSave[questId] = QUEST_DELETE_SAVE_TYPE;
  13765. }
  13766. if (update)
  13767. SendQuestUpdate(questId);
  13768. }
  13769. void Player::RemoveRewardedQuest(uint32 questId, bool update /*= true*/)
  13770. {
  13771. RewardedQuestSet::iterator rewItr = m_RewardedQuests.find(questId);
  13772. if (rewItr != m_RewardedQuests.end())
  13773. {
  13774. m_RewardedQuests.erase(rewItr);
  13775. m_RewardedQuestsSave[questId] = QUEST_FORCE_DELETE_SAVE_TYPE;
  13776. }
  13777. if (update)
  13778. SendQuestUpdate(questId);
  13779. }
  13780. void Player::SendQuestUpdate(uint32 questId)
  13781. {
  13782. uint32 zone = 0, area = 0;
  13783. SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForQuestMapBounds(questId);
  13784. if (saBounds.first != saBounds.second)
  13785. {
  13786. GetZoneAndAreaId(zone, area);
  13787. for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
  13788. if (itr->second->autocast && itr->second->IsFitToRequirements(this, zone, area))
  13789. if (!HasAura(itr->second->spellId))
  13790. CastSpell(this, itr->second->spellId, true);
  13791. }
  13792. saBounds = sSpellMgr->GetSpellAreaForQuestEndMapBounds(questId);
  13793. if (saBounds.first != saBounds.second)
  13794. {
  13795. if (!zone || !area)
  13796. GetZoneAndAreaId(zone, area);
  13797. for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
  13798. if (!itr->second->IsFitToRequirements(this, zone, area))
  13799. RemoveAurasDueToSpell(itr->second->spellId);
  13800. }
  13801. UpdateForQuestWorldObjects();
  13802. }
  13803. QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
  13804. {
  13805. QuestRelationBounds qr;
  13806. QuestRelationBounds qir;
  13807. switch (questgiver->GetTypeId())
  13808. {
  13809. case TYPEID_GAMEOBJECT:
  13810. {
  13811. QuestGiverStatus questStatus = QuestGiverStatus(sScriptMgr->GetDialogStatus(this, questgiver->ToGameObject()));
  13812. if (questStatus != DIALOG_STATUS_SCRIPTED_NO_STATUS)
  13813. return questStatus;
  13814. qr = sObjectMgr->GetGOQuestRelationBounds(questgiver->GetEntry());
  13815. qir = sObjectMgr->GetGOQuestInvolvedRelationBounds(questgiver->GetEntry());
  13816. break;
  13817. }
  13818. case TYPEID_UNIT:
  13819. {
  13820. QuestGiverStatus questStatus = QuestGiverStatus(sScriptMgr->GetDialogStatus(this, questgiver->ToCreature()));
  13821. if (questStatus != DIALOG_STATUS_SCRIPTED_NO_STATUS)
  13822. return questStatus;
  13823. qr = sObjectMgr->GetCreatureQuestRelationBounds(questgiver->GetEntry());
  13824. qir = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(questgiver->GetEntry());
  13825. break;
  13826. }
  13827. default:
  13828. // it's impossible, but check
  13829. TC_LOG_ERROR("entities.player.quest", "GetQuestDialogStatus called for unexpected type %u", questgiver->GetTypeId());
  13830. return DIALOG_STATUS_NONE;
  13831. }
  13832. QuestGiverStatus result = DIALOG_STATUS_NONE;
  13833. for (QuestRelations::const_iterator i = qir.first; i != qir.second; ++i)
  13834. {
  13835. QuestGiverStatus result2 = DIALOG_STATUS_NONE;
  13836. uint32 questId = i->second;
  13837. Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
  13838. if (!quest)
  13839. continue;
  13840. ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, quest->GetQuestId());
  13841. if (!sConditionMgr->IsObjectMeetToConditions(this, conditions))
  13842. continue;
  13843. QuestStatus status = GetQuestStatus(questId);
  13844. if ((status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(questId)) ||
  13845. (quest->IsAutoComplete() && CanTakeQuest(quest, false)))
  13846. {
  13847. if (quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
  13848. result2 = DIALOG_STATUS_REWARD_REP;
  13849. else
  13850. result2 = DIALOG_STATUS_REWARD;
  13851. }
  13852. else if (status == QUEST_STATUS_INCOMPLETE)
  13853. result2 = DIALOG_STATUS_INCOMPLETE;
  13854. if (result2 > result)
  13855. result = result2;
  13856. }
  13857. for (QuestRelations::const_iterator i = qr.first; i != qr.second; ++i)
  13858. {
  13859. QuestGiverStatus result2 = DIALOG_STATUS_NONE;
  13860. uint32 questId = i->second;
  13861. Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
  13862. if (!quest)
  13863. continue;
  13864. ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, quest->GetQuestId());
  13865. if (!sConditionMgr->IsObjectMeetToConditions(this, conditions))
  13866. continue;
  13867. QuestStatus status = GetQuestStatus(questId);
  13868. if (status == QUEST_STATUS_NONE)
  13869. {
  13870. if (CanSeeStartQuest(quest))
  13871. {
  13872. if (SatisfyQuestLevel(quest, false))
  13873. {
  13874. if (quest->IsAutoComplete())
  13875. result2 = DIALOG_STATUS_REWARD_REP;
  13876. else if (getLevel() <= (GetQuestLevel(quest) + sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF)))
  13877. {
  13878. if (quest->IsDaily())
  13879. result2 = DIALOG_STATUS_AVAILABLE_REP;
  13880. else
  13881. result2 = DIALOG_STATUS_AVAILABLE;
  13882. }
  13883. else
  13884. result2 = DIALOG_STATUS_LOW_LEVEL_AVAILABLE;
  13885. }
  13886. else
  13887. result2 = DIALOG_STATUS_UNAVAILABLE;
  13888. }
  13889. }
  13890. if (result2 > result)
  13891. result = result2;
  13892. }
  13893. return result;
  13894. }
  13895. // not used in Trinity, but used in scripting code
  13896. uint16 Player::GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry)
  13897. {
  13898. Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
  13899. if (!qInfo)
  13900. return 0;
  13901. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  13902. if (qInfo->RequiredNpcOrGo[j] == entry)
  13903. return m_QuestStatus[quest_id].CreatureOrGOCount[j];
  13904. return 0;
  13905. }
  13906. void Player::AdjustQuestReqItemCount(Quest const* quest, QuestStatusData& questStatusData)
  13907. {
  13908. if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER))
  13909. {
  13910. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
  13911. {
  13912. uint32 reqitemcount = quest->RequiredItemCount[i];
  13913. if (reqitemcount != 0)
  13914. {
  13915. uint32 curitemcount = GetItemCount(quest->RequiredItemId[i], true);
  13916. questStatusData.ItemCount[i] = std::min(curitemcount, reqitemcount);
  13917. m_QuestStatusSave[quest->GetQuestId()] = QUEST_DEFAULT_SAVE_TYPE;
  13918. }
  13919. }
  13920. }
  13921. }
  13922. uint16 Player::FindQuestSlot(uint32 quest_id) const
  13923. {
  13924. for (uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  13925. if (GetQuestSlotQuestId(i) == quest_id)
  13926. return i;
  13927. return MAX_QUEST_LOG_SIZE;
  13928. }
  13929. uint32 Player::GetQuestSlotQuestId(uint16 slot) const
  13930. {
  13931. return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_ID_OFFSET);
  13932. }
  13933. uint32 Player::GetQuestSlotState(uint16 slot) const
  13934. {
  13935. return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_STATE_OFFSET);
  13936. }
  13937. uint16 Player::GetQuestSlotCounter(uint16 slot, uint8 counter) const
  13938. {
  13939. return (uint16)(GetUInt64Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET) >> (counter * 16));
  13940. }
  13941. uint32 Player::GetQuestSlotTime(uint16 slot) const
  13942. {
  13943. return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_TIME_OFFSET);
  13944. }
  13945. void Player::SetQuestSlot(uint16 slot, uint32 quest_id, uint32 timer /*= 0*/)
  13946. {
  13947. SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_ID_OFFSET, quest_id);
  13948. SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_STATE_OFFSET, 0);
  13949. SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET, 0);
  13950. SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET + 1, 0);
  13951. SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_TIME_OFFSET, timer);
  13952. }
  13953. void Player::SetQuestSlotCounter(uint16 slot, uint8 counter, uint16 count)
  13954. {
  13955. uint64 val = GetUInt64Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET);
  13956. val &= ~((uint64)0xFFFF << (counter * 16));
  13957. val |= ((uint64)count << (counter * 16));
  13958. SetUInt64Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET, val);
  13959. }
  13960. void Player::SetQuestSlotState(uint16 slot, uint32 state)
  13961. {
  13962. SetFlag(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_STATE_OFFSET, state);
  13963. }
  13964. void Player::RemoveQuestSlotState(uint16 slot, uint32 state)
  13965. {
  13966. RemoveFlag(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_STATE_OFFSET, state);
  13967. }
  13968. void Player::SetQuestSlotTimer(uint16 slot, uint32 timer)
  13969. {
  13970. SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_TIME_OFFSET, timer);
  13971. }
  13972. void Player::SwapQuestSlot(uint16 slot1, uint16 slot2)
  13973. {
  13974. for (int i = 0; i < MAX_QUEST_OFFSET; ++i)
  13975. {
  13976. uint32 temp1 = GetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET * slot1 + i);
  13977. uint32 temp2 = GetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET * slot2 + i);
  13978. SetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET * slot1 + i, temp2);
  13979. SetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET * slot2 + i, temp1);
  13980. }
  13981. }
  13982. void Player::AreaExploredOrEventHappens(uint32 questId)
  13983. {
  13984. if (questId)
  13985. {
  13986. uint16 log_slot = FindQuestSlot(questId);
  13987. if (log_slot < MAX_QUEST_LOG_SIZE)
  13988. {
  13989. QuestStatusData& q_status = m_QuestStatus[questId];
  13990. if (!q_status.Explored)
  13991. {
  13992. q_status.Explored = true;
  13993. m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE;
  13994. }
  13995. }
  13996. if (CanCompleteQuest(questId))
  13997. CompleteQuest(questId);
  13998. }
  13999. }
  14000. //not used in Trinityd, function for external script library
  14001. void Player::GroupEventHappens(uint32 questId, WorldObject const* pEventObject)
  14002. {
  14003. if (Group* group = GetGroup())
  14004. {
  14005. for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  14006. {
  14007. Player* player = itr->GetSource();
  14008. // for any leave or dead (with not released body) group member at appropriate distance
  14009. if (player && player->IsAtGroupRewardDistance(pEventObject) && !player->GetCorpse())
  14010. player->AreaExploredOrEventHappens(questId);
  14011. }
  14012. }
  14013. else
  14014. AreaExploredOrEventHappens(questId);
  14015. }
  14016. void Player::ItemAddedQuestCheck(uint32 entry, uint32 count)
  14017. {
  14018. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14019. {
  14020. uint32 questid = GetQuestSlotQuestId(i);
  14021. if (questid == 0)
  14022. continue;
  14023. QuestStatusData& q_status = m_QuestStatus[questid];
  14024. if (q_status.Status != QUEST_STATUS_INCOMPLETE)
  14025. continue;
  14026. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  14027. if (!qInfo || !qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER))
  14028. continue;
  14029. for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
  14030. {
  14031. uint32 reqitem = qInfo->RequiredItemId[j];
  14032. if (reqitem == entry)
  14033. {
  14034. uint32 reqitemcount = qInfo->RequiredItemCount[j];
  14035. uint16 curitemcount = q_status.ItemCount[j];
  14036. if (curitemcount < reqitemcount)
  14037. {
  14038. q_status.ItemCount[j] = std::min<uint16>(q_status.ItemCount[j] + count, reqitemcount);
  14039. m_QuestStatusSave[questid] = QUEST_DEFAULT_SAVE_TYPE;
  14040. //SendQuestUpdateAddItem(qInfo, j, additemcount);
  14041. // FIXME: verify if there's any packet sent updating item
  14042. }
  14043. if (CanCompleteQuest(questid))
  14044. CompleteQuest(questid);
  14045. return;
  14046. }
  14047. }
  14048. }
  14049. UpdateForQuestWorldObjects();
  14050. }
  14051. void Player::ItemRemovedQuestCheck(uint32 entry, uint32 count)
  14052. {
  14053. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14054. {
  14055. uint32 questid = GetQuestSlotQuestId(i);
  14056. if (!questid)
  14057. continue;
  14058. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  14059. if (!qInfo)
  14060. continue;
  14061. if (!qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER))
  14062. continue;
  14063. for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
  14064. {
  14065. uint32 reqitem = qInfo->RequiredItemId[j];
  14066. if (reqitem == entry)
  14067. {
  14068. QuestStatusData& q_status = m_QuestStatus[questid];
  14069. uint32 reqitemcount = qInfo->RequiredItemCount[j];
  14070. uint16 curitemcount = q_status.ItemCount[j];
  14071. if (q_status.ItemCount[j] >= reqitemcount) // we may have more than what the status shows
  14072. curitemcount = GetItemCount(entry, false);
  14073. uint16 newItemCount = (count > curitemcount) ? 0 : curitemcount - count;
  14074. newItemCount = std::min<uint16>(newItemCount, reqitemcount);
  14075. if (newItemCount != q_status.ItemCount[j])
  14076. {
  14077. q_status.ItemCount[j] = newItemCount;
  14078. m_QuestStatusSave[questid] = QUEST_DEFAULT_SAVE_TYPE;
  14079. IncompleteQuest(questid);
  14080. }
  14081. return;
  14082. }
  14083. }
  14084. }
  14085. UpdateForQuestWorldObjects();
  14086. }
  14087. void Player::KilledMonster(CreatureTemplate const* cInfo, ObjectGuid guid)
  14088. {
  14089. ASSERT(cInfo);
  14090. if (cInfo->Entry)
  14091. KilledMonsterCredit(cInfo->Entry, guid);
  14092. for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
  14093. if (cInfo->KillCredit[i])
  14094. KilledMonsterCredit(cInfo->KillCredit[i], ObjectGuid::Empty);
  14095. }
  14096. void Player::KilledMonsterCredit(uint32 entry, ObjectGuid guid /*= ObjectGuid::Empty*/)
  14097. {
  14098. uint16 addkillcount = 1;
  14099. uint32 real_entry = entry;
  14100. Creature* killed = NULL;
  14101. if (guid)
  14102. {
  14103. killed = GetMap()->GetCreature(guid);
  14104. if (killed && killed->GetEntry())
  14105. real_entry = killed->GetEntry();
  14106. }
  14107. StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_CREATURE, real_entry); // MUST BE CALLED FIRST
  14108. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, real_entry, addkillcount, 0, killed);
  14109. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14110. {
  14111. uint32 questid = GetQuestSlotQuestId(i);
  14112. if (!questid)
  14113. continue;
  14114. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  14115. if (!qInfo)
  14116. continue;
  14117. // just if !ingroup || !noraidgroup || raidgroup
  14118. QuestStatusData& q_status = m_QuestStatus[questid];
  14119. if (q_status.Status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->IsAllowedInRaid(GetMap()->GetDifficulty())))
  14120. {
  14121. if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL) /*&& !qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_CAST)*/)
  14122. {
  14123. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  14124. {
  14125. // skip GO activate objective or none
  14126. if (qInfo->RequiredNpcOrGo[j] <= 0)
  14127. continue;
  14128. uint32 reqkill = qInfo->RequiredNpcOrGo[j];
  14129. if (reqkill == real_entry)
  14130. {
  14131. uint32 reqkillcount = qInfo->RequiredNpcOrGoCount[j];
  14132. uint16 curkillcount = q_status.CreatureOrGOCount[j];
  14133. if (curkillcount < reqkillcount)
  14134. {
  14135. q_status.CreatureOrGOCount[j] = curkillcount + addkillcount;
  14136. m_QuestStatusSave[questid] = QUEST_DEFAULT_SAVE_TYPE;
  14137. SendQuestUpdateAddCreatureOrGo(qInfo, guid, j, curkillcount, addkillcount);
  14138. }
  14139. if (CanCompleteQuest(questid))
  14140. CompleteQuest(questid);
  14141. // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
  14142. break;
  14143. }
  14144. }
  14145. }
  14146. }
  14147. }
  14148. }
  14149. void Player::KilledPlayerCredit()
  14150. {
  14151. uint16 addkillcount = 1;
  14152. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14153. {
  14154. uint32 questid = GetQuestSlotQuestId(i);
  14155. if (!questid)
  14156. continue;
  14157. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  14158. if (!qInfo)
  14159. continue;
  14160. // just if !ingroup || !noraidgroup || raidgroup
  14161. QuestStatusData& q_status = m_QuestStatus[questid];
  14162. if (q_status.Status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->IsAllowedInRaid(GetMap()->GetDifficulty())))
  14163. {
  14164. if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL))
  14165. {
  14166. uint32 reqkill = qInfo->GetPlayersSlain();
  14167. uint16 curkill = q_status.PlayerCount;
  14168. if (curkill < reqkill)
  14169. {
  14170. q_status.PlayerCount = curkill + addkillcount;
  14171. m_QuestStatusSave[questid] = QUEST_DEFAULT_SAVE_TYPE;
  14172. SendQuestUpdateAddPlayer(qInfo, curkill, addkillcount);
  14173. }
  14174. if (CanCompleteQuest(questid))
  14175. CompleteQuest(questid);
  14176. break;
  14177. }
  14178. }
  14179. }
  14180. }
  14181. void Player::KillCreditGO(uint32 entry, ObjectGuid guid)
  14182. {
  14183. uint16 addCastCount = 1;
  14184. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14185. {
  14186. uint32 questid = GetQuestSlotQuestId(i);
  14187. if (!questid)
  14188. continue;
  14189. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  14190. if (!qInfo)
  14191. continue;
  14192. QuestStatusData& q_status = m_QuestStatus[questid];
  14193. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  14194. {
  14195. if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_CAST) /*&& !qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL)*/)
  14196. {
  14197. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  14198. {
  14199. uint32 reqTarget = 0;
  14200. // GO activate objective
  14201. if (qInfo->RequiredNpcOrGo[j] < 0)
  14202. // checked at quest_template loading
  14203. reqTarget = - qInfo->RequiredNpcOrGo[j];
  14204. // other not this creature/GO related objectives
  14205. if (reqTarget != entry)
  14206. continue;
  14207. uint32 reqCastCount = qInfo->RequiredNpcOrGoCount[j];
  14208. uint16 curCastCount = q_status.CreatureOrGOCount[j];
  14209. if (curCastCount < reqCastCount)
  14210. {
  14211. q_status.CreatureOrGOCount[j] = curCastCount + addCastCount;
  14212. m_QuestStatusSave[questid] = QUEST_DEFAULT_SAVE_TYPE;
  14213. SendQuestUpdateAddCreatureOrGo(qInfo, guid, j, curCastCount, addCastCount);
  14214. }
  14215. if (CanCompleteQuest(questid))
  14216. CompleteQuest(questid);
  14217. // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
  14218. break;
  14219. }
  14220. }
  14221. }
  14222. }
  14223. }
  14224. void Player::TalkedToCreature(uint32 entry, ObjectGuid guid)
  14225. {
  14226. uint16 addTalkCount = 1;
  14227. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14228. {
  14229. uint32 questid = GetQuestSlotQuestId(i);
  14230. if (!questid)
  14231. continue;
  14232. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  14233. if (!qInfo)
  14234. continue;
  14235. QuestStatusData& q_status = m_QuestStatus[questid];
  14236. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  14237. {
  14238. if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO))
  14239. {
  14240. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  14241. {
  14242. // skip gameobject objectives
  14243. if (qInfo->RequiredNpcOrGo[j] < 0)
  14244. continue;
  14245. uint32 reqTarget = 0;
  14246. if (qInfo->RequiredNpcOrGo[j] > 0) // creature activate objectives
  14247. // checked at quest_template loading
  14248. reqTarget = qInfo->RequiredNpcOrGo[j];
  14249. else
  14250. continue;
  14251. if (reqTarget == entry)
  14252. {
  14253. uint32 reqTalkCount = qInfo->RequiredNpcOrGoCount[j];
  14254. uint16 curTalkCount = q_status.CreatureOrGOCount[j];
  14255. if (curTalkCount < reqTalkCount)
  14256. {
  14257. q_status.CreatureOrGOCount[j] = curTalkCount + addTalkCount;
  14258. m_QuestStatusSave[questid] = QUEST_DEFAULT_SAVE_TYPE;
  14259. SendQuestUpdateAddCreatureOrGo(qInfo, guid, j, curTalkCount, addTalkCount);
  14260. }
  14261. if (CanCompleteQuest(questid))
  14262. CompleteQuest(questid);
  14263. // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
  14264. continue;
  14265. }
  14266. }
  14267. }
  14268. }
  14269. }
  14270. }
  14271. void Player::MoneyChanged(uint32 count)
  14272. {
  14273. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14274. {
  14275. uint32 questid = GetQuestSlotQuestId(i);
  14276. if (!questid)
  14277. continue;
  14278. Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
  14279. if (qInfo && qInfo->GetRewOrReqMoney() < 0)
  14280. {
  14281. QuestStatusData& q_status = m_QuestStatus[questid];
  14282. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  14283. {
  14284. if (int32(count) >= -qInfo->GetRewOrReqMoney())
  14285. {
  14286. if (CanCompleteQuest(questid))
  14287. CompleteQuest(questid);
  14288. }
  14289. }
  14290. else if (q_status.Status == QUEST_STATUS_COMPLETE)
  14291. {
  14292. if (int32(count) < -qInfo->GetRewOrReqMoney())
  14293. IncompleteQuest(questid);
  14294. }
  14295. }
  14296. }
  14297. }
  14298. void Player::ReputationChanged(FactionEntry const* factionEntry)
  14299. {
  14300. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14301. {
  14302. if (uint32 questid = GetQuestSlotQuestId(i))
  14303. {
  14304. if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid))
  14305. {
  14306. if (qInfo->GetRepObjectiveFaction() == factionEntry->ID)
  14307. {
  14308. QuestStatusData& q_status = m_QuestStatus[questid];
  14309. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  14310. {
  14311. if (GetReputationMgr().GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue())
  14312. if (CanCompleteQuest(questid))
  14313. CompleteQuest(questid);
  14314. }
  14315. else if (q_status.Status == QUEST_STATUS_COMPLETE)
  14316. {
  14317. if (GetReputationMgr().GetReputation(factionEntry) < qInfo->GetRepObjectiveValue())
  14318. IncompleteQuest(questid);
  14319. }
  14320. }
  14321. }
  14322. }
  14323. }
  14324. }
  14325. void Player::ReputationChanged2(FactionEntry const* factionEntry)
  14326. {
  14327. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14328. {
  14329. if (uint32 questid = GetQuestSlotQuestId(i))
  14330. {
  14331. if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid))
  14332. {
  14333. if (qInfo->GetRepObjectiveFaction2() == factionEntry->ID)
  14334. {
  14335. QuestStatusData& q_status = m_QuestStatus[questid];
  14336. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  14337. {
  14338. if (GetReputationMgr().GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue2())
  14339. if (CanCompleteQuest(questid))
  14340. CompleteQuest(questid);
  14341. }
  14342. else if (q_status.Status == QUEST_STATUS_COMPLETE)
  14343. {
  14344. if (GetReputationMgr().GetReputation(factionEntry) < qInfo->GetRepObjectiveValue2())
  14345. IncompleteQuest(questid);
  14346. }
  14347. }
  14348. }
  14349. }
  14350. }
  14351. }
  14352. bool Player::HasQuestForItem(uint32 itemid) const
  14353. {
  14354. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14355. {
  14356. uint32 questid = GetQuestSlotQuestId(i);
  14357. if (questid == 0)
  14358. continue;
  14359. QuestStatusMap::const_iterator qs_itr = m_QuestStatus.find(questid);
  14360. if (qs_itr == m_QuestStatus.end())
  14361. continue;
  14362. QuestStatusData const& q_status = qs_itr->second;
  14363. if (q_status.Status == QUEST_STATUS_INCOMPLETE)
  14364. {
  14365. Quest const* qinfo = sObjectMgr->GetQuestTemplate(questid);
  14366. if (!qinfo)
  14367. continue;
  14368. // hide quest if player is in raid-group and quest is no raid quest
  14369. if (GetGroup() && GetGroup()->isRaidGroup() && !qinfo->IsAllowedInRaid(GetMap()->GetDifficulty()))
  14370. 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
  14371. continue;
  14372. // There should be no mixed ReqItem/ReqSource drop
  14373. // This part for ReqItem drop
  14374. for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
  14375. {
  14376. if (itemid == qinfo->RequiredItemId[j] && q_status.ItemCount[j] < qinfo->RequiredItemCount[j])
  14377. return true;
  14378. }
  14379. // This part - for ReqSource
  14380. for (uint8 j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
  14381. {
  14382. // examined item is a source item
  14383. if (qinfo->RequiredSourceItemId[j] == itemid)
  14384. {
  14385. ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid);
  14386. // 'unique' item
  14387. if (pProto->MaxCount && int32(GetItemCount(itemid, true)) < pProto->MaxCount)
  14388. return true;
  14389. // allows custom amount drop when not 0
  14390. if (qinfo->RequiredSourceItemCount[j])
  14391. {
  14392. if (GetItemCount(itemid, true) < qinfo->RequiredSourceItemCount[j])
  14393. return true;
  14394. } else if (GetItemCount(itemid, true) < pProto->GetMaxStackSize())
  14395. return true;
  14396. }
  14397. }
  14398. }
  14399. }
  14400. return false;
  14401. }
  14402. void Player::SendQuestComplete(Quest const* quest)
  14403. {
  14404. if (quest)
  14405. {
  14406. WorldPacket data(SMSG_QUESTUPDATE_COMPLETE, 4);
  14407. data << uint32(quest->GetQuestId());
  14408. GetSession()->SendPacket(&data);
  14409. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = %u", quest->GetQuestId());
  14410. }
  14411. }
  14412. void Player::SendQuestReward(Quest const* quest, uint32 XP)
  14413. {
  14414. uint32 questId = quest->GetQuestId();
  14415. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questId);
  14416. sGameEventMgr->HandleQuestComplete(questId);
  14417. uint32 xp;
  14418. uint32 moneyReward;
  14419. if (getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  14420. {
  14421. xp = XP;
  14422. moneyReward = quest->GetRewOrReqMoney();
  14423. }
  14424. else // At max level, increase gold reward
  14425. {
  14426. xp = 0;
  14427. moneyReward = uint32(quest->GetRewOrReqMoney() + int32(quest->GetRewMoneyMaxLevel() * sWorld->getRate(RATE_DROP_MONEY)));
  14428. }
  14429. WorldPacket data(SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4));
  14430. data << uint32(quest->GetBonusTalents()); // bonus talents (not verified for 4.x)
  14431. data << uint32(quest->GetRewardSkillPoints()); // 4.x bonus skill points
  14432. data << uint32(moneyReward);
  14433. data << uint32(xp);
  14434. data << uint32(questId);
  14435. data << uint32(quest->GetRewardSkillId()); // 4.x bonus skill id
  14436. data.WriteBit(0); // FIXME: unknown bits, common values sent
  14437. data.WriteBit(1);
  14438. data.FlushBits();
  14439. GetSession()->SendPacket(&data);
  14440. }
  14441. void Player::SendQuestFailed(uint32 questId, InventoryResult reason)
  14442. {
  14443. if (questId)
  14444. {
  14445. WorldPacket data(SMSG_QUESTGIVER_QUEST_FAILED, 4 + 4);
  14446. data << uint32(questId);
  14447. data << uint32(reason); // failed reason (valid reasons: 4, 16, 50, 17, 74, other values show default message)
  14448. GetSession()->SendPacket(&data);
  14449. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED");
  14450. }
  14451. }
  14452. void Player::SendQuestTimerFailed(uint32 quest_id)
  14453. {
  14454. if (quest_id)
  14455. {
  14456. WorldPacket data(SMSG_QUESTUPDATE_FAILEDTIMER, 4);
  14457. data << uint32(quest_id);
  14458. GetSession()->SendPacket(&data);
  14459. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
  14460. }
  14461. }
  14462. void Player::SendCanTakeQuestResponse(QuestFailedReason msg) const
  14463. {
  14464. WorldPacket data(SMSG_QUESTGIVER_QUEST_INVALID, 4);
  14465. data << uint32(msg);
  14466. GetSession()->SendPacket(&data);
  14467. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID");
  14468. }
  14469. void Player::SendQuestConfirmAccept(const Quest* quest, Player* pReceiver)
  14470. {
  14471. if (pReceiver)
  14472. {
  14473. std::string strTitle = quest->GetTitle();
  14474. int loc_idx = pReceiver->GetSession()->GetSessionDbLocaleIndex();
  14475. if (loc_idx >= 0)
  14476. if (const QuestLocale* pLocale = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
  14477. ObjectMgr::GetLocaleString(pLocale->Title, loc_idx, strTitle);
  14478. WorldPacket data(SMSG_QUEST_CONFIRM_ACCEPT, (4 + strTitle.size() + 8));
  14479. data << uint32(quest->GetQuestId());
  14480. data << strTitle;
  14481. data << uint64(GetGUID());
  14482. pReceiver->GetSession()->SendPacket(&data);
  14483. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_CONFIRM_ACCEPT");
  14484. }
  14485. }
  14486. void Player::SendPushToPartyResponse(Player* player, uint8 msg)
  14487. {
  14488. if (player)
  14489. {
  14490. WorldPacket data(MSG_QUEST_PUSH_RESULT, 8 + 1);
  14491. data << uint64(player->GetGUID());
  14492. data << uint8(msg); // valid values: 0-8
  14493. GetSession()->SendPacket(&data);
  14494. TC_LOG_DEBUG("network", "WORLD: Sent MSG_QUEST_PUSH_RESULT");
  14495. }
  14496. }
  14497. void Player::SendQuestUpdateAddCreatureOrGo(Quest const* quest, ObjectGuid guid, uint32 creatureOrGO_idx, uint16 old_count, uint16 add_count)
  14498. {
  14499. ASSERT(old_count + add_count < 65536 && "mob/GO count store in 16 bits 2^16 = 65536 (0..65536)");
  14500. int32 entry = quest->RequiredNpcOrGo[creatureOrGO_idx];
  14501. if (entry < 0)
  14502. // client expected gameobject template id in form (id|0x80000000)
  14503. entry = (-entry) | 0x80000000;
  14504. WorldPacket data(SMSG_QUESTUPDATE_ADD_KILL, (4*4+8));
  14505. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_ADD_KILL");
  14506. data << uint32(quest->GetQuestId());
  14507. data << uint32(entry);
  14508. data << uint32(old_count + add_count);
  14509. data << uint32(quest->RequiredNpcOrGoCount[ creatureOrGO_idx ]);
  14510. data << uint64(guid);
  14511. GetSession()->SendPacket(&data);
  14512. uint16 log_slot = FindQuestSlot(quest->GetQuestId());
  14513. if (log_slot < MAX_QUEST_LOG_SIZE)
  14514. SetQuestSlotCounter(log_slot, creatureOrGO_idx, GetQuestSlotCounter(log_slot, creatureOrGO_idx) + add_count);
  14515. }
  14516. void Player::SendQuestUpdateAddPlayer(Quest const* quest, uint16 old_count, uint16 add_count)
  14517. {
  14518. ASSERT(old_count + add_count < 65536 && "player count store in 16 bits");
  14519. WorldPacket data(SMSG_QUESTUPDATE_ADD_PVP_KILL, (3*4));
  14520. TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_ADD_PVP_KILL");
  14521. data << uint32(quest->GetQuestId());
  14522. data << uint32(old_count + add_count);
  14523. data << uint32(quest->GetPlayersSlain());
  14524. GetSession()->SendPacket(&data);
  14525. uint16 log_slot = FindQuestSlot(quest->GetQuestId());
  14526. if (log_slot < MAX_QUEST_LOG_SIZE)
  14527. SetQuestSlotCounter(log_slot, QUEST_PVP_KILL_SLOT, GetQuestSlotCounter(log_slot, QUEST_PVP_KILL_SLOT) + add_count);
  14528. }
  14529. bool Player::HasPvPForcingQuest() const
  14530. {
  14531. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  14532. {
  14533. uint32 questId = GetQuestSlotQuestId(i);
  14534. if (questId == 0)
  14535. continue;
  14536. Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
  14537. if (!quest)
  14538. continue;
  14539. if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP))
  14540. return true;
  14541. }
  14542. return false;
  14543. }
  14544. /*********************************************************/
  14545. /*** LOAD SYSTEM ***/
  14546. /*********************************************************/
  14547. void Player::Initialize(uint32 guid)
  14548. {
  14549. Object::_Create(guid, 0, HIGHGUID_PLAYER);
  14550. }
  14551. void Player::_LoadDeclinedNames(PreparedQueryResult result)
  14552. {
  14553. if (!result)
  14554. return;
  14555. delete m_declinedname;
  14556. m_declinedname = new DeclinedName;
  14557. for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
  14558. m_declinedname->name[i] = (*result)[i].GetString();
  14559. }
  14560. void Player::_LoadArenaTeamInfo(PreparedQueryResult result)
  14561. {
  14562. // arenateamid, played_week, played_season, personal_rating
  14563. memset((void*)&m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1], 0, sizeof(uint32) * MAX_ARENA_SLOT * ARENA_TEAM_END);
  14564. uint16 personalRatingCache[] = {0, 0, 0};
  14565. if (result)
  14566. {
  14567. do
  14568. {
  14569. Field* fields = result->Fetch();
  14570. uint32 arenaTeamId = fields[0].GetUInt32();
  14571. ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
  14572. if (!arenaTeam)
  14573. {
  14574. TC_LOG_ERROR("entities.player", "Player::_LoadArenaTeamInfo: couldn't load arenateam %u", arenaTeamId);
  14575. continue;
  14576. }
  14577. uint8 arenaSlot = arenaTeam->GetSlot();
  14578. personalRatingCache[arenaSlot] = fields[4].GetUInt16();
  14579. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_ID, arenaTeamId);
  14580. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_TYPE, arenaTeam->GetType());
  14581. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_MEMBER, (arenaTeam->GetCaptain() == GetGUID()) ? 0 : 1);
  14582. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_WEEK, uint32(fields[1].GetUInt16()));
  14583. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_SEASON, uint32(fields[2].GetUInt16()));
  14584. SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_WINS_SEASON, uint32(fields[3].GetUInt16()));
  14585. }
  14586. while (result->NextRow());
  14587. }
  14588. for (uint8 slot = 0; slot <= 2; ++slot)
  14589. {
  14590. SetArenaTeamInfoField(slot, ARENA_TEAM_PERSONAL_RATING, uint32(personalRatingCache[slot]));
  14591. }
  14592. }
  14593. void Player::_LoadEquipmentSets(PreparedQueryResult result)
  14594. {
  14595. // 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));
  14596. if (!result)
  14597. return;
  14598. uint32 count = 0;
  14599. do
  14600. {
  14601. Field* fields = result->Fetch();
  14602. EquipmentSet eqSet;
  14603. eqSet.Guid = fields[0].GetUInt64();
  14604. uint8 index = fields[1].GetUInt8();
  14605. eqSet.Name = fields[2].GetString();
  14606. eqSet.IconName = fields[3].GetString();
  14607. eqSet.IgnoreMask = fields[4].GetUInt32();
  14608. eqSet.state = EQUIPMENT_SET_UNCHANGED;
  14609. for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
  14610. eqSet.Items[i] = fields[5+i].GetUInt32();
  14611. m_EquipmentSets[index] = eqSet;
  14612. ++count;
  14613. if (count >= MAX_EQUIPMENT_SET_INDEX) // client limit
  14614. break;
  14615. }
  14616. while (result->NextRow());
  14617. }
  14618. void Player::_LoadBGData(PreparedQueryResult result)
  14619. {
  14620. if (!result)
  14621. return;
  14622. Field* fields = result->Fetch();
  14623. // Expecting only one row
  14624. // 0 1 2 3 4 5 6 7 8 9
  14625. // SELECT instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell FROM character_battleground_data WHERE guid = ?
  14626. m_bgData.bgInstanceID = fields[0].GetUInt32();
  14627. m_bgData.bgTeam = fields[1].GetUInt16();
  14628. m_bgData.joinPos = WorldLocation(fields[6].GetUInt16(), // Map
  14629. fields[2].GetFloat(), // X
  14630. fields[3].GetFloat(), // Y
  14631. fields[4].GetFloat(), // Z
  14632. fields[5].GetFloat()); // Orientation
  14633. m_bgData.taxiPath[0] = fields[7].GetUInt32();
  14634. m_bgData.taxiPath[1] = fields[8].GetUInt32();
  14635. m_bgData.mountSpell = fields[9].GetUInt32();
  14636. }
  14637. bool Player::LoadPositionFromDB(uint32& mapid, float& x, float& y, float& z, float& o, bool& in_flight, ObjectGuid guid)
  14638. {
  14639. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_POSITION);
  14640. stmt->setUInt32(0, guid.GetCounter());
  14641. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  14642. if (!result)
  14643. return false;
  14644. Field* fields = result->Fetch();
  14645. x = fields[0].GetFloat();
  14646. y = fields[1].GetFloat();
  14647. z = fields[2].GetFloat();
  14648. o = fields[3].GetFloat();
  14649. mapid = fields[4].GetUInt16();
  14650. in_flight = !fields[5].GetString().empty();
  14651. return true;
  14652. }
  14653. void Player::SetHomebind(WorldLocation const& loc, uint32 areaId)
  14654. {
  14655. loc.GetPosition(m_homebindX, m_homebindY, m_homebindZ);
  14656. m_homebindMapId = loc.GetMapId();
  14657. m_homebindAreaId = areaId;
  14658. // update sql homebind
  14659. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PLAYER_HOMEBIND);
  14660. stmt->setUInt16(0, m_homebindMapId);
  14661. stmt->setUInt16(1, m_homebindAreaId);
  14662. stmt->setFloat (2, m_homebindX);
  14663. stmt->setFloat (3, m_homebindY);
  14664. stmt->setFloat (4, m_homebindZ);
  14665. stmt->setUInt32(5, GetGUIDLow());
  14666. CharacterDatabase.Execute(stmt);
  14667. }
  14668. uint32 Player::GetUInt32ValueFromArray(Tokenizer const& data, uint16 index)
  14669. {
  14670. if (index >= data.size())
  14671. return 0;
  14672. return (uint32)atoi(data[index]);
  14673. }
  14674. float Player::GetFloatValueFromArray(Tokenizer const& data, uint16 index)
  14675. {
  14676. float result;
  14677. uint32 temp = Player::GetUInt32ValueFromArray(data, index);
  14678. memcpy(&result, &temp, sizeof(result));
  14679. return result;
  14680. }
  14681. bool Player::IsLoading() const
  14682. {
  14683. return GetSession()->PlayerLoading();
  14684. }
  14685. bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
  14686. {
  14687. //// 0 1 2 3 4 5 6 7 8 9 10 11
  14688. //QueryResult* result = CharacterDatabase.PQuery("SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, "
  14689. // 12 13 14 15 16 17 18 19 20 21 22 23 24
  14690. //"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, "
  14691. // 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
  14692. //"resettalents_time, talentTree, 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, "
  14693. // 40 41 42 43 44 45
  14694. //"totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, "
  14695. // 46 47 48 49 50 51 52 53 54 55 56
  14696. //"health, power1, power2, power3, power4, power5, instance_id, speccount, activespec, exploredZones, equipmentCache, "
  14697. // 57 58 59
  14698. //"knownTitles, actionBars, grantableLevels FROM characters WHERE guid = '%u'", guid);
  14699. PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM);
  14700. if (!result)
  14701. {
  14702. std::string name = "<unknown>";
  14703. sObjectMgr->GetPlayerNameByGUID(guid, name);
  14704. TC_LOG_ERROR("entities.player", "Player %s %s not found in table `characters`, can't load. ", name.c_str(), guid.ToString().c_str());
  14705. return false;
  14706. }
  14707. Field* fields = result->Fetch();
  14708. uint32 dbAccountId = fields[1].GetUInt32();
  14709. // check if the character's account in the db and the logged in account match.
  14710. // player should be able to load/delete character only with correct account!
  14711. if (dbAccountId != GetSession()->GetAccountId())
  14712. {
  14713. TC_LOG_ERROR("entities.player", "Player %s loading from wrong account (is: %u, should be: %u)", guid.ToString().c_str(), GetSession()->GetAccountId(), dbAccountId);
  14714. return false;
  14715. }
  14716. if (holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BANNED))
  14717. {
  14718. TC_LOG_ERROR("entities.player", "%s is banned, can't load.", guid.ToString().c_str());
  14719. return false;
  14720. }
  14721. Object::_Create(guid.GetCounter(), 0, HIGHGUID_PLAYER);
  14722. m_name = fields[2].GetString();
  14723. // check name limitations
  14724. if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS ||
  14725. (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) &&
  14726. sObjectMgr->IsReservedName(m_name)))
  14727. {
  14728. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
  14729. stmt->setUInt16(0, uint16(AT_LOGIN_RENAME));
  14730. stmt->setUInt32(1, guid.GetCounter());
  14731. CharacterDatabase.Execute(stmt);
  14732. return false;
  14733. }
  14734. // overwrite possible wrong/corrupted guid
  14735. SetGuidValue(OBJECT_FIELD_GUID, guid);
  14736. uint8 gender = fields[5].GetUInt8();
  14737. if (!IsValidGender(gender))
  14738. {
  14739. TC_LOG_ERROR("entities.player", "Player %s has wrong gender (%u), can't be loaded.", guid.ToString().c_str(), gender);
  14740. return false;
  14741. }
  14742. // overwrite some data fields
  14743. uint32 bytes0 = 0;
  14744. bytes0 |= fields[3].GetUInt8(); // race
  14745. bytes0 |= fields[4].GetUInt8() << 8; // class
  14746. bytes0 |= gender << 16; // gender
  14747. SetUInt32Value(UNIT_FIELD_BYTES_0, bytes0);
  14748. // check if race/class combination is valid
  14749. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  14750. if (!info)
  14751. {
  14752. TC_LOG_ERROR("entities.player", "Player %s has wrong race/class (%u/%u), can't be loaded.", guid.ToString().c_str(), getRace(), getClass());
  14753. return false;
  14754. }
  14755. SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8());
  14756. SetUInt32Value(PLAYER_XP, fields[7].GetUInt32());
  14757. _LoadIntoDataField(fields[55].GetCString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE);
  14758. _LoadIntoDataField(fields[57].GetCString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2);
  14759. SetObjectScale(1.0f);
  14760. SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f);
  14761. // load achievements before anything else to prevent multiple gains for the same achievement/criteria on every loading (as loading does call UpdateAchievementCriteria)
  14762. m_achievementMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS));
  14763. uint64 money = fields[8].GetUInt64();
  14764. if (money > MAX_MONEY_AMOUNT)
  14765. money = MAX_MONEY_AMOUNT;
  14766. SetMoney(money);
  14767. SetUInt32Value(PLAYER_BYTES, fields[9].GetUInt32());
  14768. SetUInt32Value(PLAYER_BYTES_2, fields[10].GetUInt32());
  14769. SetByteValue(PLAYER_BYTES_3, 0, fields[5].GetUInt8());
  14770. SetByteValue(PLAYER_BYTES_3, 1, fields[45].GetUInt8());
  14771. SetUInt32Value(PLAYER_FLAGS, fields[11].GetUInt32());
  14772. SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[44].GetUInt32());
  14773. // set which actionbars the client has active - DO NOT REMOVE EVER AGAIN (can be changed though, if it does change fieldwise)
  14774. SetByteValue(PLAYER_FIELD_BYTES, 2, fields[58].GetUInt8());
  14775. InitDisplayIds();
  14776. // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
  14777. for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
  14778. {
  14779. SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), ObjectGuid::Empty);
  14780. SetVisibleItemSlot(slot, NULL);
  14781. delete m_items[slot];
  14782. m_items[slot] = NULL;
  14783. }
  14784. TC_LOG_DEBUG("entities.player.loading", "Load Basic value of player %s is: ", m_name.c_str());
  14785. outDebugValues();
  14786. //Need to call it to initialize m_team (m_team can be calculated from race)
  14787. //Other way is to saves m_team into characters table.
  14788. setFactionForRace(getRace());
  14789. // load home bind and check in same time class/race pair, it used later for restore broken positions
  14790. if (!_LoadHomeBind(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_HOME_BIND)))
  14791. return false;
  14792. InitPrimaryProfessions(); // to max set before any spell loaded
  14793. // init saved position, and fix it later if problematic
  14794. uint32 transLowGUID = uint32(fields[31].GetUInt32());
  14795. Relocate(fields[12].GetFloat(), fields[13].GetFloat(), fields[14].GetFloat(), fields[16].GetFloat());
  14796. uint32 mapId = fields[15].GetUInt16();
  14797. uint32 instanceId = fields[52].GetUInt32();
  14798. uint32 dungeonDiff = fields[39].GetUInt8() & 0x0F;
  14799. if (dungeonDiff >= MAX_DUNGEON_DIFFICULTY)
  14800. dungeonDiff = DUNGEON_DIFFICULTY_NORMAL;
  14801. uint32 raidDiff = (fields[39].GetUInt8() >> 4) & 0x0F;
  14802. if (raidDiff >= MAX_RAID_DIFFICULTY)
  14803. raidDiff = RAID_DIFFICULTY_10MAN_NORMAL;
  14804. SetDungeonDifficulty(Difficulty(dungeonDiff)); // may be changed in _LoadGroup
  14805. SetRaidDifficulty(Difficulty(raidDiff)); // may be changed in _LoadGroup
  14806. std::string taxi_nodes = fields[38].GetString();
  14807. #define RelocateToHomebind(){ mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ); }
  14808. _LoadGroup(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GROUP));
  14809. _LoadArenaTeamInfo(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ARENA_INFO));
  14810. // check arena teams integrity
  14811. for (uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot)
  14812. {
  14813. uint32 arena_team_id = GetArenaTeamId(arena_slot);
  14814. if (!arena_team_id)
  14815. continue;
  14816. if (ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(arena_team_id))
  14817. if (at->IsMember(GetGUID()))
  14818. continue;
  14819. // arena team not exist or not member, cleanup fields
  14820. for (int j = 0; j < 6; ++j)
  14821. SetArenaTeamInfoField(arena_slot, ArenaTeamInfoType(j), 0);
  14822. }
  14823. _LoadCurrency(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CURRENCY));
  14824. SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, fields[40].GetUInt32());
  14825. SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[41].GetUInt16());
  14826. SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[42].GetUInt16());
  14827. _LoadBoundInstances(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BOUND_INSTANCES));
  14828. _LoadInstanceTimeRestrictions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES));
  14829. _LoadBGData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BG_DATA));
  14830. GetSession()->SetPlayer(this);
  14831. MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
  14832. if (!mapEntry || !IsPositionValid())
  14833. {
  14834. TC_LOG_ERROR("entities.player", "Player %s have invalid coordinates (MapId: %u X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
  14835. guid.ToString().c_str(), mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  14836. RelocateToHomebind();
  14837. }
  14838. // Player was saved in Arena or Bg
  14839. else if (mapEntry && mapEntry->IsBattlegroundOrArena())
  14840. {
  14841. Battleground* currentBg = NULL;
  14842. if (m_bgData.bgInstanceID) //saved in Battleground
  14843. currentBg = sBattlegroundMgr->GetBattleground(m_bgData.bgInstanceID, BATTLEGROUND_TYPE_NONE);
  14844. bool player_at_bg = currentBg && currentBg->IsPlayerInBattleground(GetGUID());
  14845. if (player_at_bg && currentBg->GetStatus() != STATUS_WAIT_LEAVE)
  14846. {
  14847. BattlegroundQueueTypeId bgQueueTypeId = sBattlegroundMgr->BGQueueTypeId(currentBg->GetTypeID(), currentBg->GetArenaType());
  14848. AddBattlegroundQueueId(bgQueueTypeId);
  14849. m_bgData.bgTypeID = currentBg->GetTypeID();
  14850. //join player to battleground group
  14851. currentBg->EventPlayerLoggedIn(this);
  14852. currentBg->AddOrSetPlayerToCorrectBgGroup(this, m_bgData.bgTeam);
  14853. SetInviteForBattlegroundQueueType(bgQueueTypeId, currentBg->GetInstanceID());
  14854. }
  14855. // Bg was not found - go to Entry Point
  14856. else
  14857. {
  14858. // leave bg
  14859. if (player_at_bg)
  14860. currentBg->RemovePlayerAtLeave(GetGUID(), false, true);
  14861. // Do not look for instance if bg not found
  14862. const WorldLocation& _loc = GetBattlegroundEntryPoint();
  14863. mapId = _loc.GetMapId(); instanceId = 0;
  14864. // Db field type is type int16, so it can never be MAPID_INVALID
  14865. //if (mapId == MAPID_INVALID) -- code kept for reference
  14866. if (int16(mapId) == int16(-1)) // Battleground Entry Point not found (???)
  14867. {
  14868. TC_LOG_ERROR("entities.player", "Player %s was in BG in database, but BG was not found, and entry point was invalid! Teleport to default race/class locations.",
  14869. guid.ToString().c_str());
  14870. RelocateToHomebind();
  14871. }
  14872. else
  14873. Relocate(&_loc);
  14874. // We are not in BG anymore
  14875. m_bgData.bgInstanceID = 0;
  14876. }
  14877. }
  14878. // currently we do not support transport in bg
  14879. else if (transLowGUID)
  14880. {
  14881. ObjectGuid transGUID(HIGHGUID_MO_TRANSPORT, transLowGUID);
  14882. Transport* transport = NULL;
  14883. if (GameObject* go = HashMapHolder<GameObject>::Find(transGUID))
  14884. transport = go->ToTransport();
  14885. if (transport)
  14886. {
  14887. float x = fields[27].GetFloat(), y = fields[28].GetFloat(), z = fields[29].GetFloat(), o = fields[30].GetFloat();
  14888. m_movementInfo.transport.pos.Relocate(x, y, z, o);
  14889. transport->CalculatePassengerPosition(x, y, z, &o);
  14890. if (!Trinity::IsValidMapCoord(x, y, z, o) ||
  14891. // transport size limited
  14892. std::fabs(m_movementInfo.transport.pos.GetPositionX()) > 250.0f ||
  14893. std::fabs(m_movementInfo.transport.pos.GetPositionY()) > 250.0f ||
  14894. std::fabs(m_movementInfo.transport.pos.GetPositionZ()) > 250.0f)
  14895. {
  14896. TC_LOG_ERROR("entities.player", "Player %s have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.",
  14897. guid.ToString().c_str(), x, y, z, o);
  14898. m_movementInfo.transport.Reset();
  14899. RelocateToHomebind();
  14900. }
  14901. else
  14902. {
  14903. Relocate(x, y, z, o);
  14904. mapId = transport->GetMapId();
  14905. transport->AddPassenger(this);
  14906. }
  14907. }
  14908. else
  14909. {
  14910. TC_LOG_ERROR("entities.player", "Player (%s) have problems with transport guid (%u). Teleport to bind location.",
  14911. guid.ToString().c_str(), transLowGUID);
  14912. RelocateToHomebind();
  14913. }
  14914. }
  14915. // currently we do not support taxi in instance
  14916. else if (!taxi_nodes.empty())
  14917. {
  14918. instanceId = 0;
  14919. // Not finish taxi flight path
  14920. if (m_bgData.HasTaxiPath())
  14921. {
  14922. for (int i = 0; i < 2; ++i)
  14923. m_taxi.AddTaxiDestination(m_bgData.taxiPath[i]);
  14924. }
  14925. else if (!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes, GetTeam()))
  14926. {
  14927. // problems with taxi path loading
  14928. TaxiNodesEntry const* nodeEntry = NULL;
  14929. if (uint32 node_id = m_taxi.GetTaxiSource())
  14930. nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
  14931. if (!nodeEntry) // don't know taxi start node, to homebind
  14932. {
  14933. TC_LOG_ERROR("entities.player", "Character %u have wrong data in taxi destination list, teleport to homebind.", GetGUIDLow());
  14934. RelocateToHomebind();
  14935. }
  14936. else // have start node, to it
  14937. {
  14938. TC_LOG_ERROR("entities.player", "Character %u have too short taxi destination list, teleport to original node.", GetGUIDLow());
  14939. mapId = nodeEntry->map_id;
  14940. Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z, 0.0f);
  14941. }
  14942. m_taxi.ClearTaxiDestinations();
  14943. }
  14944. if (uint32 node_id = m_taxi.GetTaxiSource())
  14945. {
  14946. // save source node as recall coord to prevent recall and fall from sky
  14947. TaxiNodesEntry const* nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
  14948. if (nodeEntry && nodeEntry->map_id == GetMapId())
  14949. {
  14950. ASSERT(nodeEntry); // checked in m_taxi.LoadTaxiDestinationsFromString
  14951. mapId = nodeEntry->map_id;
  14952. Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z, 0.0f);
  14953. }
  14954. // flight will started later
  14955. }
  14956. }
  14957. // Map could be changed before
  14958. mapEntry = sMapStore.LookupEntry(mapId);
  14959. // client without expansion support
  14960. if (mapEntry)
  14961. {
  14962. if (GetSession()->Expansion() < mapEntry->Expansion())
  14963. {
  14964. TC_LOG_DEBUG("entities.player.loading", "Player %s using client without required expansion tried login at non accessible map %u", GetName().c_str(), mapId);
  14965. RelocateToHomebind();
  14966. }
  14967. // fix crash (because of if (Map* map = _FindMap(instanceId)) in MapInstanced::CreateInstance)
  14968. if (instanceId)
  14969. if (InstanceSave* save = GetInstanceSave(mapId, mapEntry->IsRaid()))
  14970. if (save->GetInstanceId() != instanceId)
  14971. instanceId = 0;
  14972. }
  14973. // NOW player must have valid map
  14974. // load the player's map here if it's not already loaded
  14975. Map* map = sMapMgr->CreateMap(mapId, this);
  14976. AreaTriggerStruct const* areaTrigger = NULL;
  14977. bool check = false;
  14978. if (!map)
  14979. {
  14980. areaTrigger = sObjectMgr->GetGoBackTrigger(mapId);
  14981. check = true;
  14982. }
  14983. else if (map->IsDungeon()) // if map is dungeon...
  14984. {
  14985. if (!((InstanceMap*)map)->CanEnter(this)) // ... and can't enter map, then look for entry point.
  14986. {
  14987. areaTrigger = sObjectMgr->GetGoBackTrigger(mapId);
  14988. check = true;
  14989. }
  14990. else if (instanceId && !sInstanceSaveMgr->GetInstanceSave(instanceId)) // ... and instance is reseted then look for entrance.
  14991. {
  14992. areaTrigger = sObjectMgr->GetMapEntranceTrigger(mapId);
  14993. check = true;
  14994. }
  14995. }
  14996. if (check) // in case of special event when creating map...
  14997. {
  14998. if (areaTrigger) // ... if we have an areatrigger, then relocate to new map/coordinates.
  14999. {
  15000. Relocate(areaTrigger->target_X, areaTrigger->target_Y, areaTrigger->target_Z, GetOrientation());
  15001. if (mapId != areaTrigger->target_mapId)
  15002. {
  15003. mapId = areaTrigger->target_mapId;
  15004. map = sMapMgr->CreateMap(mapId, this);
  15005. }
  15006. }
  15007. else
  15008. {
  15009. TC_LOG_ERROR("entities.player", "Player %s %s Map: %u, X: %f, Y: %f, Z: %f, O: %f. Areatrigger not found.",
  15010. m_name.c_str(), guid.ToString().c_str(), mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  15011. map = NULL;
  15012. }
  15013. }
  15014. if (!map)
  15015. {
  15016. mapId = info->mapId;
  15017. Relocate(info->positionX, info->positionY, info->positionZ, 0.0f);
  15018. map = sMapMgr->CreateMap(mapId, this);
  15019. if (!map)
  15020. {
  15021. TC_LOG_ERROR("entities.player", "Player %s %s Map: %u, X: %f, Y: %f, Z: %f, O: %f. Invalid default map coordinates or instance couldn't be created.",
  15022. m_name.c_str(), guid.ToString().c_str(), mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  15023. return false;
  15024. }
  15025. }
  15026. SetMap(map);
  15027. StoreRaidMapDifficulty();
  15028. // randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE]
  15029. // this must help in case next save after mass player load after server startup
  15030. m_nextSave = urand(m_nextSave/2, m_nextSave*3/2);
  15031. SaveRecallPosition();
  15032. time_t now = time(NULL);
  15033. time_t logoutTime = time_t(fields[22].GetUInt32());
  15034. // since last logout (in seconds)
  15035. uint32 time_diff = uint32(now - logoutTime); //uint64 is excessive for a time_diff in seconds.. uint32 allows for 136~ year difference.
  15036. // set value, including drunk invisibility detection
  15037. // calculate sobering. after 15 minutes logged out, the player will be sober again
  15038. uint8 newDrunkValue = 0;
  15039. if (time_diff < uint32(GetDrunkValue()) * 9)
  15040. newDrunkValue = GetDrunkValue() - time_diff / 9;
  15041. SetDrunkValue(newDrunkValue);
  15042. m_cinematic = fields[18].GetUInt8();
  15043. m_Played_time[PLAYED_TIME_TOTAL]= fields[19].GetUInt32();
  15044. m_Played_time[PLAYED_TIME_LEVEL]= fields[20].GetUInt32();
  15045. SetTalentResetCost(fields[24].GetUInt32());
  15046. SetTalentResetTime(time_t(fields[25].GetUInt32()));
  15047. m_taxi.LoadTaxiMask(fields[17].GetString()); // must be before InitTaxiNodesForLevel
  15048. uint32 extraflags = fields[32].GetUInt16();
  15049. m_stableSlots = fields[33].GetUInt8();
  15050. if (m_stableSlots > MAX_PET_STABLES)
  15051. {
  15052. TC_LOG_ERROR("entities.player", "Player can have not more %u stable slots, but have in DB %u", MAX_PET_STABLES, uint32(m_stableSlots));
  15053. m_stableSlots = MAX_PET_STABLES;
  15054. }
  15055. m_atLoginFlags = fields[34].GetUInt16();
  15056. if (HasAtLoginFlag(AT_LOGIN_RENAME))
  15057. {
  15058. TC_LOG_ERROR("entities.player", "Player (GUID: %u) tried to login while forced to rename, can't load.'", GetGUIDLow());
  15059. return false;
  15060. }
  15061. // Honor system
  15062. // Update Honor kills data
  15063. m_lastHonorUpdateTime = logoutTime;
  15064. UpdateHonorFields();
  15065. m_deathExpireTime = time_t(fields[37].GetUInt32());
  15066. if (m_deathExpireTime > now + MAX_DEATH_COUNT * DEATH_EXPIRE_STEP)
  15067. m_deathExpireTime = now + MAX_DEATH_COUNT * DEATH_EXPIRE_STEP - 1;
  15068. // clear channel spell data (if saved at channel spell casting)
  15069. SetChannelObjectGuid(ObjectGuid::Empty);
  15070. SetUInt32Value(UNIT_CHANNEL_SPELL, 0);
  15071. // clear charm/summon related fields
  15072. SetOwnerGUID(ObjectGuid::Empty);
  15073. SetGuidValue(UNIT_FIELD_CHARMEDBY, ObjectGuid::Empty);
  15074. SetGuidValue(UNIT_FIELD_CHARM, ObjectGuid::Empty);
  15075. SetGuidValue(UNIT_FIELD_SUMMON, ObjectGuid::Empty);
  15076. SetGuidValue(PLAYER_FARSIGHT, ObjectGuid::Empty);
  15077. SetCreatorGUID(ObjectGuid::Empty);
  15078. RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVEMENT);
  15079. // reset some aura modifiers before aura apply
  15080. SetUInt32Value(PLAYER_TRACK_CREATURES, 0);
  15081. SetUInt32Value(PLAYER_TRACK_RESOURCES, 0);
  15082. // make sure the unit is considered out of combat for proper loading
  15083. ClearInCombat();
  15084. // make sure the unit is considered not in duel for proper loading
  15085. SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid::Empty);
  15086. SetUInt32Value(PLAYER_DUEL_TEAM, 0);
  15087. // reset stats before loading any modifiers
  15088. InitStatsForLevel();
  15089. InitGlyphsForLevel();
  15090. InitTaxiNodesForLevel();
  15091. InitRunes();
  15092. // rest bonus can only be calculated after InitStatsForLevel()
  15093. m_rest_bonus = fields[21].GetFloat();
  15094. if (time_diff > 0)
  15095. {
  15096. //speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour)
  15097. float bubble0 = 0.031f;
  15098. //speed collect rest bonus in offline, in logout, in tavern, city (section/in hour)
  15099. float bubble1 = 0.125f;
  15100. float bubble = fields[23].GetUInt8() > 0
  15101. ? bubble1*sWorld->getRate(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY)
  15102. : bubble0*sWorld->getRate(RATE_REST_OFFLINE_IN_WILDERNESS);
  15103. SetRestBonus(GetRestBonus()+ time_diff*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble);
  15104. }
  15105. // load skills after InitStatsForLevel because it triggering aura apply also
  15106. _LoadSkills(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SKILLS));
  15107. UpdateSkillsForLevel(); //update skills after load, to make sure they are correctly update at player load
  15108. // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
  15109. //mails are loaded only when needed ;-) - when player in game click on mailbox.
  15110. //_LoadMail();
  15111. SetSpecsCount(fields[53].GetUInt8());
  15112. SetActiveSpec(fields[54].GetUInt8());
  15113. // sanity check
  15114. if (GetSpecsCount() > MAX_TALENT_SPECS || GetActiveSpec() > MAX_TALENT_SPEC || GetSpecsCount() < MIN_TALENT_SPECS)
  15115. {
  15116. SetActiveSpec(0);
  15117. TC_LOG_ERROR("entities.player", "Player %s(GUID: %u) has SpecCount = %u and ActiveSpec = %u.", GetName().c_str(), GetGUIDLow(), GetSpecsCount(), GetActiveSpec());
  15118. }
  15119. // Only load selected specializations, learning mastery spells requires this
  15120. Tokenizer talentTrees(fields[26].GetString(), ' ', MAX_TALENT_SPECS);
  15121. for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
  15122. {
  15123. if (i >= talentTrees.size())
  15124. break;
  15125. uint32 talentTree = atol(talentTrees[i]);
  15126. if (sTalentTabStore.LookupEntry(talentTree))
  15127. SetPrimaryTalentTree(i, talentTree);
  15128. }
  15129. _LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS));
  15130. _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS));
  15131. _LoadGlyphs(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GLYPHS));
  15132. _LoadAuras(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURAS), time_diff);
  15133. _LoadGlyphAuras();
  15134. // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
  15135. if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
  15136. m_deathState = DEAD;
  15137. // after spell load, learn rewarded spell if need also
  15138. _LoadQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS));
  15139. _LoadQuestStatusRewarded(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS_REW));
  15140. _LoadDailyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS));
  15141. _LoadWeeklyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_WEEKLY_QUEST_STATUS));
  15142. _LoadSeasonalQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS));
  15143. _LoadMonthlyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS));
  15144. _LoadRandomBGStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_RANDOM_BG));
  15145. // after spell and quest load
  15146. InitTalentForLevel();
  15147. LearnDefaultSkills();
  15148. LearnCustomSpells();
  15149. // must be before inventory (some items required reputation check)
  15150. m_reputationMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION));
  15151. _LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), time_diff);
  15152. if (IsVoidStorageUnlocked())
  15153. _LoadVoidStorage(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_VOID_STORAGE));
  15154. // update items with duration and realtime
  15155. UpdateItemDuration(time_diff, true);
  15156. _LoadActions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACTIONS));
  15157. // unread mails and next delivery time, actual mails not loaded
  15158. _LoadMailInit(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE));
  15159. m_social = sSocialMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST), GetGUIDLow());
  15160. // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES
  15161. // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded
  15162. uint32 curTitle = fields[43].GetUInt32();
  15163. if (curTitle && !HasTitle(curTitle))
  15164. curTitle = 0;
  15165. SetUInt32Value(PLAYER_CHOSEN_TITLE, curTitle);
  15166. // has to be called after last Relocate() in Player::LoadFromDB
  15167. SetFallInformation(0, GetPositionZ());
  15168. _LoadSpellCooldowns(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS));
  15169. // Spell code allow apply any auras to dead character in load time in aura/spell/item loading
  15170. // Do now before stats re-calculation cleanup for ghost state unexpected auras
  15171. if (!IsAlive())
  15172. RemoveAllAurasOnDeath();
  15173. else
  15174. RemoveAllAurasRequiringDeadTarget();
  15175. //apply all stat bonuses from items and auras
  15176. SetCanModifyStats(true);
  15177. UpdateAllStats();
  15178. // restore remembered power/health values (but not more max values)
  15179. uint32 savedHealth = fields[46].GetUInt32();
  15180. SetHealth(savedHealth > GetMaxHealth() ? GetMaxHealth() : savedHealth);
  15181. uint32 loadedPowers = 0;
  15182. for (uint32 i = 0; i < MAX_POWERS; ++i)
  15183. {
  15184. if (GetPowerIndex(i) != MAX_POWERS)
  15185. {
  15186. uint32 savedPower = fields[47+loadedPowers].GetUInt32();
  15187. uint32 maxPower = GetUInt32Value(UNIT_FIELD_MAXPOWER1 + loadedPowers);
  15188. SetPower(Powers(i), (savedPower > maxPower) ? maxPower : savedPower);
  15189. if (++loadedPowers >= MAX_POWERS_PER_CLASS)
  15190. break;
  15191. }
  15192. }
  15193. for (; loadedPowers < MAX_POWERS_PER_CLASS; ++loadedPowers)
  15194. SetUInt32Value(UNIT_FIELD_POWER1 + loadedPowers, 0);
  15195. SetPower(POWER_ECLIPSE, 0);
  15196. // Verify loaded talent specializations
  15197. for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
  15198. {
  15199. if (i >= talentTrees.size())
  15200. break;
  15201. uint32 talentTree = atol(talentTrees[i]);
  15202. if (talentTree != 0 && !sTalentTabStore.LookupEntry(talentTree) && i == GetActiveSpec())
  15203. SetAtLoginFlag(AT_LOGIN_RESET_TALENTS); // invalid tree, reset talents
  15204. }
  15205. TC_LOG_DEBUG("entities.player.loading", "The value of player %s after load item and aura is: ", m_name.c_str());
  15206. outDebugValues();
  15207. // GM state
  15208. if (GetSession()->HasPermission(rbac::RBAC_PERM_RESTORE_SAVED_GM_STATE))
  15209. {
  15210. switch (sWorld->getIntConfig(CONFIG_GM_LOGIN_STATE))
  15211. {
  15212. default:
  15213. case 0: break; // disable
  15214. case 1: SetGameMaster(true); break; // enable
  15215. case 2: // save state
  15216. if (extraflags & PLAYER_EXTRA_GM_ON)
  15217. SetGameMaster(true);
  15218. break;
  15219. }
  15220. switch (sWorld->getIntConfig(CONFIG_GM_VISIBLE_STATE))
  15221. {
  15222. default:
  15223. case 0: SetGMVisible(false); break; // invisible
  15224. case 1: break; // visible
  15225. case 2: // save state
  15226. if (extraflags & PLAYER_EXTRA_GM_INVISIBLE)
  15227. SetGMVisible(false);
  15228. break;
  15229. }
  15230. switch (sWorld->getIntConfig(CONFIG_GM_CHAT))
  15231. {
  15232. default:
  15233. case 0: break; // disable
  15234. case 1: SetGMChat(true); break; // enable
  15235. case 2: // save state
  15236. if (extraflags & PLAYER_EXTRA_GM_CHAT)
  15237. SetGMChat(true);
  15238. break;
  15239. }
  15240. switch (sWorld->getIntConfig(CONFIG_GM_WHISPERING_TO))
  15241. {
  15242. default:
  15243. case 0: break; // disable
  15244. case 1: SetAcceptWhispers(true); break; // enable
  15245. case 2: // save state
  15246. if (extraflags & PLAYER_EXTRA_ACCEPT_WHISPERS)
  15247. SetAcceptWhispers(true);
  15248. break;
  15249. }
  15250. }
  15251. // RaF stuff.
  15252. m_grantableLevels = fields[59].GetUInt8();
  15253. if (GetSession()->IsARecruiter() || (GetSession()->GetRecruiterId() != 0))
  15254. SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_REFER_A_FRIEND);
  15255. if (m_grantableLevels > 0)
  15256. SetByteValue(PLAYER_FIELD_BYTES, 1, 0x01);
  15257. _LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES));
  15258. m_achievementMgr->CheckAllAchievementCriteria(this);
  15259. _LoadEquipmentSets(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS));
  15260. _LoadCUFProfiles(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CUF_PROFILES));
  15261. return true;
  15262. }
  15263. void Player::_LoadCUFProfiles(PreparedQueryResult result)
  15264. {
  15265. if (!result)
  15266. return;
  15267. do
  15268. {
  15269. // SELECT id, name, frameHeight, frameWidth, sortBy, healthText, boolOptions, unk146, unk147, unk148, unk150, unk152, unk154 FROM character_cuf_profiles WHERE guid = ?
  15270. Field* fields = result->Fetch();
  15271. uint8 id = fields[0].GetUInt8();
  15272. std::string name = fields[1].GetString();
  15273. uint16 frameHeight = fields[2].GetUInt16();
  15274. uint16 frameWidth = fields[3].GetUInt16();
  15275. uint8 sortBy = fields[4].GetUInt8();
  15276. uint8 healthText = fields[5].GetUInt8();
  15277. uint32 boolOptions = fields[6].GetUInt32();
  15278. uint8 unk146 = fields[7].GetUInt8();
  15279. uint8 unk147 = fields[8].GetUInt8();
  15280. uint8 unk148 = fields[9].GetUInt8();
  15281. uint16 unk150 = fields[10].GetUInt16();
  15282. uint16 unk152 = fields[11].GetUInt16();
  15283. uint16 unk154 = fields[12].GetUInt16();
  15284. if (id > MAX_CUF_PROFILES)
  15285. {
  15286. TC_LOG_ERROR("entities.player", "Player::_LoadCUFProfiles - Player (GUID: %u, name: %s) has an CUF profile with invalid id (id: %u), max is %i.", GetGUIDLow(), GetName().c_str(), id, MAX_CUF_PROFILES);
  15287. continue;
  15288. }
  15289. _CUFProfiles[id] = new CUFProfile(name, frameHeight, frameWidth, sortBy, healthText, boolOptions, unk146, unk147, unk148, unk150, unk152, unk154);
  15290. }
  15291. while (result->NextRow());
  15292. }
  15293. bool Player::isAllowedToLoot(const Creature* creature)
  15294. {
  15295. if (!creature->isDead() || !creature->IsDamageEnoughForLootingAndReward())
  15296. return false;
  15297. if (HasPendingBind())
  15298. return false;
  15299. const Loot* loot = &creature->loot;
  15300. if (loot->isLooted()) // nothing to loot or everything looted.
  15301. return false;
  15302. if (loot->loot_type == LOOT_SKINNING)
  15303. return creature->GetSkinner() == GetGUID();
  15304. Group* thisGroup = GetGroup();
  15305. if (!thisGroup)
  15306. return this == creature->GetLootRecipient();
  15307. else if (thisGroup != creature->GetLootRecipientGroup())
  15308. return false;
  15309. switch (thisGroup->GetLootMethod())
  15310. {
  15311. case MASTER_LOOT:
  15312. case FREE_FOR_ALL:
  15313. return true;
  15314. case ROUND_ROBIN:
  15315. // may only loot if the player is the loot roundrobin player
  15316. // or if there are free/quest/conditional item for the player
  15317. if (loot->roundRobinPlayer.IsEmpty() || loot->roundRobinPlayer == GetGUID())
  15318. return true;
  15319. return loot->hasItemFor(this);
  15320. case GROUP_LOOT:
  15321. case NEED_BEFORE_GREED:
  15322. // may only loot if the player is the loot roundrobin player
  15323. // or item over threshold (so roll(s) can be launched)
  15324. // or if there are free/quest/conditional item for the player
  15325. if (loot->roundRobinPlayer.IsEmpty() || loot->roundRobinPlayer == GetGUID())
  15326. return true;
  15327. if (loot->hasOverThresholdItem())
  15328. return true;
  15329. return loot->hasItemFor(this);
  15330. }
  15331. return false;
  15332. }
  15333. void Player::_LoadActions(PreparedQueryResult result)
  15334. {
  15335. m_actionButtons.clear();
  15336. if (result)
  15337. {
  15338. do
  15339. {
  15340. Field* fields = result->Fetch();
  15341. uint8 button = fields[0].GetUInt8();
  15342. uint32 action = fields[1].GetUInt32();
  15343. uint8 type = fields[2].GetUInt8();
  15344. if (ActionButton* ab = addActionButton(button, action, type))
  15345. ab->uState = ACTIONBUTTON_UNCHANGED;
  15346. else
  15347. {
  15348. TC_LOG_ERROR("entities.player", " ...at loading, and will deleted in DB also");
  15349. // Will deleted in DB at next save (it can create data until save but marked as deleted)
  15350. m_actionButtons[button].uState = ACTIONBUTTON_DELETED;
  15351. }
  15352. } while (result->NextRow());
  15353. }
  15354. }
  15355. void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff)
  15356. {
  15357. TC_LOG_DEBUG("entities.player.loading", "Loading auras for player %u", GetGUIDLow());
  15358. /* 0 1 2 3 4 5 6 7 8 9 10
  15359. QueryResult* result = CharacterDatabase.PQuery("SELECT caster_guid, spell, effect_mask, recalculate_mask, stackcount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2,
  15360. 11 12 13
  15361. maxduration, remaintime, remaincharges FROM character_aura WHERE guid = '%u'", GetGUIDLow());
  15362. */
  15363. if (result)
  15364. {
  15365. do
  15366. {
  15367. Field* fields = result->Fetch();
  15368. int32 damage[3];
  15369. int32 baseDamage[3];
  15370. ObjectGuid caster_guid(fields[0].GetUInt64());
  15371. uint32 spellid = fields[1].GetUInt32();
  15372. uint8 effmask = fields[2].GetUInt8();
  15373. uint8 recalculatemask = fields[3].GetUInt8();
  15374. uint8 stackcount = fields[4].GetUInt8();
  15375. damage[0] = fields[5].GetInt32();
  15376. damage[1] = fields[6].GetInt32();
  15377. damage[2] = fields[7].GetInt32();
  15378. baseDamage[0] = fields[8].GetInt32();
  15379. baseDamage[1] = fields[9].GetInt32();
  15380. baseDamage[2] = fields[10].GetInt32();
  15381. int32 maxduration = fields[11].GetInt32();
  15382. int32 remaintime = fields[12].GetInt32();
  15383. uint8 remaincharges = fields[13].GetUInt8();
  15384. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
  15385. if (!spellInfo)
  15386. {
  15387. TC_LOG_ERROR("entities.player", "Unknown aura (spellid %u), ignore.", spellid);
  15388. continue;
  15389. }
  15390. // negative effects should continue counting down after logout
  15391. if (remaintime != -1 && !spellInfo->IsPositive())
  15392. {
  15393. if (remaintime/IN_MILLISECONDS <= int32(timediff))
  15394. continue;
  15395. remaintime -= timediff*IN_MILLISECONDS;
  15396. }
  15397. // prevent wrong values of remaincharges
  15398. if (spellInfo->ProcCharges)
  15399. {
  15400. // we have no control over the order of applying auras and modifiers allow auras
  15401. // to have more charges than value in SpellInfo
  15402. if (remaincharges <= 0/* || remaincharges > spellproto->procCharges*/)
  15403. remaincharges = spellInfo->ProcCharges;
  15404. }
  15405. else
  15406. remaincharges = 0;
  15407. if (Aura* aura = Aura::TryCreate(spellInfo, effmask, this, NULL, &baseDamage[0], NULL, caster_guid))
  15408. {
  15409. if (!aura->CanBeSaved())
  15410. {
  15411. aura->Remove();
  15412. continue;
  15413. }
  15414. aura->SetLoadedState(maxduration, remaintime, remaincharges, stackcount, recalculatemask, &damage[0]);
  15415. aura->ApplyForTargets();
  15416. TC_LOG_INFO("entities.player", "Added aura spellid %u, effectmask %u", spellInfo->Id, effmask);
  15417. }
  15418. }
  15419. while (result->NextRow());
  15420. }
  15421. }
  15422. void Player::_LoadGlyphAuras()
  15423. {
  15424. for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
  15425. {
  15426. if (uint32 glyph = GetGlyph(GetActiveSpec(), i))
  15427. {
  15428. if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph))
  15429. {
  15430. if (GlyphSlotEntry const* gs = sGlyphSlotStore.LookupEntry(GetGlyphSlot(i)))
  15431. {
  15432. if (gp->TypeFlags == gs->TypeFlags)
  15433. {
  15434. CastSpell(this, gp->SpellId, true);
  15435. continue;
  15436. }
  15437. else
  15438. TC_LOG_ERROR("entities.player", "Player %s has glyph with typeflags %u in slot with typeflags %u, removing.", m_name.c_str(), gp->TypeFlags, gs->TypeFlags);
  15439. }
  15440. else
  15441. TC_LOG_ERROR("entities.player", "Player %s has not existing glyph slot entry %u on index %u", m_name.c_str(), GetGlyphSlot(i), i);
  15442. }
  15443. else
  15444. TC_LOG_ERROR("entities.player", "Player %s has not existing glyph entry %u on index %u", m_name.c_str(), glyph, i);
  15445. // On any error remove glyph
  15446. SetGlyph(i, 0);
  15447. }
  15448. }
  15449. }
  15450. void Player::LoadCorpse()
  15451. {
  15452. if (IsAlive())
  15453. sObjectAccessor->ConvertCorpseForPlayer(GetGUID());
  15454. else
  15455. {
  15456. if (Corpse* corpse = GetCorpse())
  15457. ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, corpse && !sMapStore.LookupEntry(corpse->GetMapId())->Instanceable());
  15458. else
  15459. //Prevent Dead Player login without corpse
  15460. ResurrectPlayer(0.5f);
  15461. }
  15462. }
  15463. void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
  15464. {
  15465. //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());
  15466. //NOTE: the "order by `bag`" is important because it makes sure
  15467. //the bagMap is filled before items in the bags are loaded
  15468. //NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?)
  15469. //expected to be equipped before offhand items (@todo fixme)
  15470. if (result)
  15471. {
  15472. uint32 zoneId = GetZoneId();
  15473. std::map<uint32, Bag*> bagMap; // fast guid lookup for bags
  15474. std::map<uint32, Item*> invalidBagMap; // fast guid lookup for bags
  15475. std::list<Item*> problematicItems;
  15476. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  15477. // Prevent items from being added to the queue while loading
  15478. m_itemUpdateQueueBlocked = true;
  15479. do
  15480. {
  15481. Field* fields = result->Fetch();
  15482. if (Item* item = _LoadItem(trans, zoneId, timeDiff, fields))
  15483. {
  15484. uint32 bagGuid = fields[11].GetUInt32();
  15485. uint8 slot = fields[12].GetUInt8();
  15486. uint8 err = EQUIP_ERR_OK;
  15487. // Item is not in bag
  15488. if (!bagGuid)
  15489. {
  15490. item->SetContainer(NULL);
  15491. item->SetSlot(slot);
  15492. if (IsInventoryPos(INVENTORY_SLOT_BAG_0, slot))
  15493. {
  15494. ItemPosCountVec dest;
  15495. err = CanStoreItem(INVENTORY_SLOT_BAG_0, slot, dest, item, false);
  15496. if (err == EQUIP_ERR_OK)
  15497. item = StoreItem(dest, item, true);
  15498. }
  15499. else if (IsEquipmentPos(INVENTORY_SLOT_BAG_0, slot))
  15500. {
  15501. uint16 dest;
  15502. err = CanEquipItem(slot, dest, item, false, false);
  15503. if (err == EQUIP_ERR_OK)
  15504. QuickEquipItem(dest, item);
  15505. }
  15506. else if (IsBankPos(INVENTORY_SLOT_BAG_0, slot))
  15507. {
  15508. ItemPosCountVec dest;
  15509. err = CanBankItem(INVENTORY_SLOT_BAG_0, slot, dest, item, false, false);
  15510. if (err == EQUIP_ERR_OK)
  15511. item = BankItem(dest, item, true);
  15512. }
  15513. // Remember bags that may contain items in them
  15514. if (err == EQUIP_ERR_OK)
  15515. {
  15516. if (IsBagPos(item->GetPos()))
  15517. if (Bag* pBag = item->ToBag())
  15518. bagMap[item->GetGUIDLow()] = pBag;
  15519. }
  15520. else
  15521. if (IsBagPos(item->GetPos()))
  15522. if (item->IsBag())
  15523. invalidBagMap[item->GetGUIDLow()] = item;
  15524. }
  15525. else
  15526. {
  15527. item->SetSlot(NULL_SLOT);
  15528. // Item is in the bag, find the bag
  15529. std::map<uint32, Bag*>::iterator itr = bagMap.find(bagGuid);
  15530. if (itr != bagMap.end())
  15531. {
  15532. ItemPosCountVec dest;
  15533. err = CanStoreItem(itr->second->GetSlot(), slot, dest, item);
  15534. if (err == EQUIP_ERR_OK)
  15535. item = StoreItem(dest, item, true);
  15536. }
  15537. else if (invalidBagMap.find(bagGuid) != invalidBagMap.end())
  15538. {
  15539. std::map<uint32, Item*>::iterator itr = invalidBagMap.find(bagGuid);
  15540. if (std::find(problematicItems.begin(), problematicItems.end(), itr->second) != problematicItems.end())
  15541. err = EQUIP_ERR_INTERNAL_BAG_ERROR;
  15542. }
  15543. else
  15544. {
  15545. TC_LOG_ERROR("entities.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?",
  15546. GetGUIDLow(), GetName().c_str(), item->GetGUIDLow(), item->GetEntry(), bagGuid, slot);
  15547. item->DeleteFromInventoryDB(trans);
  15548. delete item;
  15549. continue;
  15550. }
  15551. }
  15552. // Item's state may have changed after storing
  15553. if (err == EQUIP_ERR_OK)
  15554. item->SetState(ITEM_UNCHANGED, this);
  15555. else
  15556. {
  15557. TC_LOG_ERROR("entities.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.",
  15558. GetGUIDLow(), GetName().c_str(), item->GetGUIDLow(), item->GetEntry(), bagGuid, slot, err);
  15559. item->DeleteFromInventoryDB(trans);
  15560. problematicItems.push_back(item);
  15561. }
  15562. }
  15563. } while (result->NextRow());
  15564. m_itemUpdateQueueBlocked = false;
  15565. // Send problematic items by mail
  15566. while (!problematicItems.empty())
  15567. {
  15568. std::string subject = GetSession()->GetTrinityString(LANG_NOT_EQUIPPED_ITEM);
  15569. MailDraft draft(subject, "There were problems with equipping item(s).");
  15570. for (uint8 i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i)
  15571. {
  15572. draft.AddItem(problematicItems.front());
  15573. problematicItems.pop_front();
  15574. }
  15575. draft.SendMailTo(trans, this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED);
  15576. }
  15577. CharacterDatabase.CommitTransaction(trans);
  15578. }
  15579. //if (IsAlive())
  15580. _ApplyAllItemMods();
  15581. }
  15582. void Player::_LoadVoidStorage(PreparedQueryResult result)
  15583. {
  15584. if (!result)
  15585. return;
  15586. do
  15587. {
  15588. // SELECT itemid, itemEntry, slot, creatorGuid FROM character_void_storage WHERE playerGuid = ?
  15589. Field* fields = result->Fetch();
  15590. uint64 itemId = fields[0].GetUInt64();
  15591. uint32 itemEntry = fields[1].GetUInt32();
  15592. uint8 slot = fields[2].GetUInt8();
  15593. ObjectGuid creatorGuid(HIGHGUID_PLAYER, fields[3].GetUInt32());
  15594. uint32 randomProperty = fields[4].GetUInt32();
  15595. uint32 suffixFactor = fields[5].GetUInt32();
  15596. if (!itemId)
  15597. {
  15598. TC_LOG_ERROR("entities.player", "Player::_LoadVoidStorage - Player (GUID: %u, name: %s) has an item with an invalid id (item id: " UI64FMTD ", entry: %u).", GetGUIDLow(), GetName().c_str(), itemId, itemEntry);
  15599. continue;
  15600. }
  15601. if (!sObjectMgr->GetItemTemplate(itemEntry))
  15602. {
  15603. TC_LOG_ERROR("entities.player", "Player::_LoadVoidStorage - Player (GUID: %u, name: %s) has an item with an invalid entry (item id: " UI64FMTD ", entry: %u).", GetGUIDLow(), GetName().c_str(), itemId, itemEntry);
  15604. continue;
  15605. }
  15606. if (slot >= VOID_STORAGE_MAX_SLOT)
  15607. {
  15608. TC_LOG_ERROR("entities.player", "Player::_LoadVoidStorage - Player (GUID: %u, name: %s) has an item with an invalid slot (item id: " UI64FMTD ", entry: %u, slot: %u).", GetGUIDLow(), GetName().c_str(), itemId, itemEntry, slot);
  15609. continue;
  15610. }
  15611. std::string name;
  15612. if (creatorGuid && !sObjectMgr->GetPlayerNameByGUID(creatorGuid, name))
  15613. {
  15614. TC_LOG_ERROR("entities.player", "Player::_LoadVoidStorage - Player (GUID: %u, name: %s) has an item with an invalid creator guid, set to 0 (item id: " UI64FMTD ", entry: %u, creator: %s).", GetGUIDLow(), GetName().c_str(), itemId, itemEntry, creatorGuid.ToString().c_str());
  15615. creatorGuid.Clear();
  15616. }
  15617. _voidStorageItems[slot] = new VoidStorageItem(itemId, itemEntry, creatorGuid, randomProperty, suffixFactor);
  15618. }
  15619. while (result->NextRow());
  15620. }
  15621. Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields)
  15622. {
  15623. Item* item = NULL;
  15624. uint32 itemGuid = fields[13].GetUInt32();
  15625. uint32 itemEntry = fields[14].GetUInt32();
  15626. if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry))
  15627. {
  15628. bool remove = false;
  15629. item = NewItemOrBag(proto);
  15630. if (item->LoadFromDB(itemGuid, GetGUID(), fields, itemEntry))
  15631. {
  15632. PreparedStatement* stmt = NULL;
  15633. // Do not allow to have item limited to another map/zone in alive state
  15634. if (IsAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(), zoneId))
  15635. {
  15636. TC_LOG_DEBUG("entities.player.loading", "Player::_LoadInventory: player (GUID: %u, name: '%s', map: %u) has item (GUID: %u, entry: %u) limited to another map (%u). Deleting item.",
  15637. GetGUIDLow(), GetName().c_str(), GetMapId(), item->GetGUIDLow(), item->GetEntry(), zoneId);
  15638. remove = true;
  15639. }
  15640. // "Conjured items disappear if you are logged out for more than 15 minutes"
  15641. else if (timeDiff > 15 * MINUTE && proto->Flags & ITEM_PROTO_FLAG_CONJURED)
  15642. {
  15643. TC_LOG_DEBUG("entities.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.",
  15644. GetGUIDLow(), GetName().c_str(), timeDiff, item->GetGUIDLow(), item->GetEntry());
  15645. remove = true;
  15646. }
  15647. else if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE))
  15648. {
  15649. if (item->GetPlayedTime() > (2 * HOUR))
  15650. {
  15651. TC_LOG_DEBUG("entities.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.",
  15652. GetGUIDLow(), GetName().c_str(), item->GetGUIDLow(), item->GetEntry(), item->GetPlayedTime());
  15653. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
  15654. stmt->setUInt32(0, item->GetGUIDLow());
  15655. trans->Append(stmt);
  15656. item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE);
  15657. }
  15658. else
  15659. {
  15660. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_REFUNDS);
  15661. stmt->setUInt32(0, item->GetGUIDLow());
  15662. stmt->setUInt32(1, GetGUIDLow());
  15663. if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
  15664. {
  15665. item->SetRefundRecipient((*result)[0].GetUInt32());
  15666. item->SetPaidMoney((*result)[1].GetUInt32());
  15667. item->SetPaidExtendedCost((*result)[2].GetUInt16());
  15668. AddRefundReference(item->GetGUID());
  15669. }
  15670. else
  15671. {
  15672. TC_LOG_DEBUG("entities.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.",
  15673. GetGUIDLow(), GetName().c_str(), item->GetGUIDLow(), item->GetEntry());
  15674. item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE);
  15675. }
  15676. }
  15677. }
  15678. else if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
  15679. {
  15680. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_BOP_TRADE);
  15681. stmt->setUInt32(0, item->GetGUIDLow());
  15682. if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
  15683. {
  15684. std::string strGUID = (*result)[0].GetString();
  15685. Tokenizer GUIDlist(strGUID, ' ');
  15686. AllowedLooterSet looters;
  15687. for (Tokenizer::const_iterator itr = GUIDlist.begin(); itr != GUIDlist.end(); ++itr)
  15688. looters.insert(atol(*itr));
  15689. if (looters.size() > 1 && item->GetTemplate()->GetMaxStackSize() == 1 && item->IsSoulBound())
  15690. {
  15691. item->SetSoulboundTradeable(looters);
  15692. AddTradeableItem(item);
  15693. }
  15694. else
  15695. item->ClearSoulboundTradeable(this);
  15696. }
  15697. else
  15698. {
  15699. TC_LOG_DEBUG("entities.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.",
  15700. GetGUIDLow(), GetName().c_str(), item->GetGUIDLow(), item->GetEntry());
  15701. item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE);
  15702. }
  15703. }
  15704. else if (proto->HolidayId)
  15705. {
  15706. remove = true;
  15707. GameEventMgr::GameEventDataMap const& events = sGameEventMgr->GetEventMap();
  15708. GameEventMgr::ActiveEvents const& activeEventsList = sGameEventMgr->GetActiveEventList();
  15709. for (GameEventMgr::ActiveEvents::const_iterator itr = activeEventsList.begin(); itr != activeEventsList.end(); ++itr)
  15710. {
  15711. if (uint32(events[*itr].holiday_id) == proto->HolidayId)
  15712. {
  15713. remove = false;
  15714. break;
  15715. }
  15716. }
  15717. }
  15718. }
  15719. else
  15720. {
  15721. TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has broken item (GUID: %u, entry: %u) in inventory. Deleting item.",
  15722. GetGUIDLow(), GetName().c_str(), itemGuid, itemEntry);
  15723. remove = true;
  15724. }
  15725. // Remove item from inventory if necessary
  15726. if (remove)
  15727. {
  15728. Item::DeleteFromInventoryDB(trans, itemGuid);
  15729. item->FSetState(ITEM_REMOVED);
  15730. item->SaveToDB(trans); // it also deletes item object!
  15731. item = NULL;
  15732. }
  15733. }
  15734. else
  15735. {
  15736. TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has unknown item (entry: %u) in inventory. Deleting item.",
  15737. GetGUIDLow(), GetName().c_str(), itemEntry);
  15738. Item::DeleteFromInventoryDB(trans, itemGuid);
  15739. Item::DeleteFromDB(trans, itemGuid);
  15740. }
  15741. return item;
  15742. }
  15743. // load mailed item which should receive current player
  15744. void Player::_LoadMailedItems(Mail* mail)
  15745. {
  15746. // data needs to be at first place for Item::LoadFromDB
  15747. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS);
  15748. stmt->setUInt32(0, mail->messageID);
  15749. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  15750. if (!result)
  15751. return;
  15752. do
  15753. {
  15754. Field* fields = result->Fetch();
  15755. uint32 itemGuid = fields[11].GetUInt32();
  15756. uint32 itemTemplate = fields[12].GetUInt32();
  15757. mail->AddItem(itemGuid, itemTemplate);
  15758. ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemTemplate);
  15759. if (!proto)
  15760. {
  15761. TC_LOG_ERROR("entities.player", "Player %u has unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), itemGuid, itemTemplate, mail->messageID);
  15762. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM);
  15763. stmt->setUInt32(0, itemGuid);
  15764. CharacterDatabase.Execute(stmt);
  15765. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  15766. stmt->setUInt32(0, itemGuid);
  15767. CharacterDatabase.Execute(stmt);
  15768. continue;
  15769. }
  15770. Item* item = NewItemOrBag(proto);
  15771. if (!item->LoadFromDB(itemGuid, ObjectGuid(HIGHGUID_PLAYER, fields[13].GetUInt32()), fields, itemTemplate))
  15772. {
  15773. TC_LOG_ERROR("entities.player", "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, itemGuid);
  15774. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM);
  15775. stmt->setUInt32(0, itemGuid);
  15776. CharacterDatabase.Execute(stmt);
  15777. item->FSetState(ITEM_REMOVED);
  15778. SQLTransaction temp = SQLTransaction(NULL);
  15779. item->SaveToDB(temp); // it also deletes item object !
  15780. continue;
  15781. }
  15782. AddMItem(item);
  15783. }
  15784. while (result->NextRow());
  15785. }
  15786. void Player::_LoadMailInit(PreparedQueryResult resultUnread, PreparedQueryResult resultDelivery)
  15787. {
  15788. //set a count of unread mails
  15789. //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);
  15790. if (resultUnread)
  15791. unReadMails = uint8((*resultUnread)[0].GetUInt64());
  15792. // store nearest delivery time (it > 0 and if it < current then at next player update SendNewMaill will be called)
  15793. //resultMails = CharacterDatabase.PQuery("SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(playerGuid));
  15794. if (resultDelivery)
  15795. m_nextMailDelivereTime = time_t((*resultDelivery)[0].GetUInt32());
  15796. }
  15797. void Player::_LoadMail()
  15798. {
  15799. m_mail.clear();
  15800. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL);
  15801. stmt->setUInt32(0, GetGUIDLow());
  15802. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  15803. if (result)
  15804. {
  15805. do
  15806. {
  15807. Field* fields = result->Fetch();
  15808. Mail* m = new Mail;
  15809. m->messageID = fields[0].GetUInt32();
  15810. m->messageType = fields[1].GetUInt8();
  15811. m->sender = fields[2].GetUInt32();
  15812. m->receiver = fields[3].GetUInt32();
  15813. m->subject = fields[4].GetString();
  15814. m->body = fields[5].GetString();
  15815. bool has_items = fields[6].GetBool();
  15816. m->expire_time = time_t(fields[7].GetUInt32());
  15817. m->deliver_time = time_t(fields[8].GetUInt32());
  15818. m->money = fields[9].GetUInt64();
  15819. m->COD = fields[10].GetUInt64();
  15820. m->checked = fields[11].GetUInt8();
  15821. m->stationery = fields[12].GetUInt8();
  15822. m->mailTemplateId = fields[13].GetInt16();
  15823. if (m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId))
  15824. {
  15825. TC_LOG_ERROR("entities.player", "Player::_LoadMail - Mail (%u) have not existed MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId);
  15826. m->mailTemplateId = 0;
  15827. }
  15828. m->state = MAIL_STATE_UNCHANGED;
  15829. if (has_items)
  15830. _LoadMailedItems(m);
  15831. m_mail.push_back(m);
  15832. }
  15833. while (result->NextRow());
  15834. }
  15835. m_mailsLoaded = true;
  15836. }
  15837. void Player::LoadPet()
  15838. {
  15839. //fixme: the pet should still be loaded if the player is not in world
  15840. // just not added to the map
  15841. if (IsInWorld())
  15842. {
  15843. Pet* pet = new Pet(this);
  15844. if (!pet->LoadPetFromDB(this, 0, 0, true))
  15845. delete pet;
  15846. }
  15847. }
  15848. void Player::_LoadQuestStatus(PreparedQueryResult result)
  15849. {
  15850. uint16 slot = 0;
  15851. //// 0 1 2 3 4 5 6 7 8 9 10
  15852. //QueryResult* result = CharacterDatabase.PQuery("SELECT quest, status, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3,
  15853. // 11 12
  15854. // itemcount4, playercount FROM character_queststatus WHERE guid = '%u'", GetGUIDLow());
  15855. if (result)
  15856. {
  15857. do
  15858. {
  15859. Field* fields = result->Fetch();
  15860. uint32 quest_id = fields[0].GetUInt32();
  15861. // used to be new, no delete?
  15862. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  15863. if (quest)
  15864. {
  15865. // find or create
  15866. QuestStatusData& questStatusData = m_QuestStatus[quest_id];
  15867. uint8 qstatus = fields[1].GetUInt8();
  15868. if (qstatus < MAX_QUEST_STATUS)
  15869. questStatusData.Status = QuestStatus(qstatus);
  15870. else
  15871. {
  15872. questStatusData.Status = QUEST_STATUS_INCOMPLETE;
  15873. TC_LOG_ERROR("entities.player", "Player %s (GUID: %u) has invalid quest %d status (%u), replaced by QUEST_STATUS_INCOMPLETE(3).",
  15874. GetName().c_str(), GetGUIDLow(), quest_id, qstatus);
  15875. }
  15876. questStatusData.Explored = (fields[2].GetUInt8() > 0);
  15877. time_t quest_time = time_t(fields[3].GetUInt32());
  15878. if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED) && !GetQuestRewardStatus(quest_id))
  15879. {
  15880. AddTimedQuest(quest_id);
  15881. if (quest_time <= sWorld->GetGameTime())
  15882. questStatusData.Timer = 1;
  15883. else
  15884. questStatusData.Timer = uint32((quest_time - sWorld->GetGameTime()) * IN_MILLISECONDS);
  15885. }
  15886. else
  15887. quest_time = 0;
  15888. questStatusData.CreatureOrGOCount[0] = fields[4].GetUInt16();
  15889. questStatusData.CreatureOrGOCount[1] = fields[5].GetUInt16();
  15890. questStatusData.CreatureOrGOCount[2] = fields[6].GetUInt16();
  15891. questStatusData.CreatureOrGOCount[3] = fields[7].GetUInt16();
  15892. questStatusData.ItemCount[0] = fields[8].GetUInt16();
  15893. questStatusData.ItemCount[1] = fields[9].GetUInt16();
  15894. questStatusData.ItemCount[2] = fields[10].GetUInt16();
  15895. questStatusData.ItemCount[3] = fields[11].GetUInt16();
  15896. questStatusData.PlayerCount = fields[12].GetUInt16();
  15897. // add to quest log
  15898. if (slot < MAX_QUEST_LOG_SIZE && questStatusData.Status != QUEST_STATUS_NONE)
  15899. {
  15900. SetQuestSlot(slot, quest_id, uint32(quest_time)); // cast can't be helped
  15901. if (questStatusData.Status == QUEST_STATUS_COMPLETE)
  15902. SetQuestSlotState(slot, QUEST_STATE_COMPLETE);
  15903. else if (questStatusData.Status == QUEST_STATUS_FAILED)
  15904. SetQuestSlotState(slot, QUEST_STATE_FAIL);
  15905. for (uint8 idx = 0; idx < QUEST_OBJECTIVES_COUNT; ++idx)
  15906. if (questStatusData.CreatureOrGOCount[idx])
  15907. SetQuestSlotCounter(slot, idx, questStatusData.CreatureOrGOCount[idx]);
  15908. if (questStatusData.PlayerCount)
  15909. SetQuestSlotCounter(slot, QUEST_PVP_KILL_SLOT, questStatusData.PlayerCount);
  15910. ++slot;
  15911. }
  15912. TC_LOG_DEBUG("entities.player.loading", "Quest status is {%u} for quest {%u} for player (GUID: %u)", questStatusData.Status, quest_id, GetGUIDLow());
  15913. }
  15914. }
  15915. while (result->NextRow());
  15916. }
  15917. // clear quest log tail
  15918. for (uint16 i = slot; i < MAX_QUEST_LOG_SIZE; ++i)
  15919. SetQuestSlot(i, 0);
  15920. }
  15921. void Player::_LoadQuestStatusRewarded(PreparedQueryResult result)
  15922. {
  15923. // SELECT quest FROM character_queststatus_rewarded WHERE guid = ?
  15924. if (result)
  15925. {
  15926. do
  15927. {
  15928. Field* fields = result->Fetch();
  15929. uint32 quest_id = fields[0].GetUInt32();
  15930. // used to be new, no delete?
  15931. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  15932. if (quest)
  15933. {
  15934. // learn rewarded spell if unknown
  15935. LearnQuestRewardedSpells(quest);
  15936. // set rewarded title if any
  15937. if (quest->GetCharTitleId())
  15938. {
  15939. if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(quest->GetCharTitleId()))
  15940. SetTitle(titleEntry);
  15941. }
  15942. if (uint32 talents = quest->GetBonusTalents())
  15943. AddQuestRewardedTalentCount(talents);
  15944. }
  15945. m_RewardedQuests.insert(quest_id);
  15946. }
  15947. while (result->NextRow());
  15948. }
  15949. }
  15950. void Player::_LoadDailyQuestStatus(PreparedQueryResult result)
  15951. {
  15952. for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
  15953. SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx, 0);
  15954. m_DFQuests.clear();
  15955. //QueryResult* result = CharacterDatabase.PQuery("SELECT quest, time FROM character_queststatus_daily WHERE guid = '%u'", GetGUIDLow());
  15956. if (result)
  15957. {
  15958. uint32 quest_daily_idx = 0;
  15959. do
  15960. {
  15961. Field* fields = result->Fetch();
  15962. if (Quest const* qQuest = sObjectMgr->GetQuestTemplate(fields[0].GetUInt32()))
  15963. {
  15964. if (qQuest->IsDFQuest())
  15965. {
  15966. m_DFQuests.insert(qQuest->GetQuestId());
  15967. m_lastDailyQuestTime = time_t(fields[1].GetUInt32());
  15968. continue;
  15969. }
  15970. }
  15971. if (quest_daily_idx >= PLAYER_MAX_DAILY_QUESTS) // max amount with exist data in query
  15972. {
  15973. TC_LOG_ERROR("entities.player", "Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`", GetGUIDLow());
  15974. break;
  15975. }
  15976. uint32 quest_id = fields[0].GetUInt32();
  15977. // save _any_ from daily quest times (it must be after last reset anyway)
  15978. m_lastDailyQuestTime = time_t(fields[1].GetUInt32());
  15979. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  15980. if (!quest)
  15981. continue;
  15982. SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx, quest_id);
  15983. ++quest_daily_idx;
  15984. TC_LOG_DEBUG("entities.player.loading", "Daily quest (%u) cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
  15985. }
  15986. while (result->NextRow());
  15987. }
  15988. m_DailyQuestChanged = false;
  15989. }
  15990. void Player::_LoadWeeklyQuestStatus(PreparedQueryResult result)
  15991. {
  15992. m_weeklyquests.clear();
  15993. if (result)
  15994. {
  15995. do
  15996. {
  15997. Field* fields = result->Fetch();
  15998. uint32 quest_id = fields[0].GetUInt32();
  15999. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  16000. if (!quest)
  16001. continue;
  16002. m_weeklyquests.insert(quest_id);
  16003. TC_LOG_DEBUG("entities.player.loading", "Weekly quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
  16004. }
  16005. while (result->NextRow());
  16006. }
  16007. m_WeeklyQuestChanged = false;
  16008. }
  16009. void Player::_LoadSeasonalQuestStatus(PreparedQueryResult result)
  16010. {
  16011. m_seasonalquests.clear();
  16012. if (result)
  16013. {
  16014. do
  16015. {
  16016. Field* fields = result->Fetch();
  16017. uint32 quest_id = fields[0].GetUInt32();
  16018. uint32 event_id = fields[1].GetUInt32();
  16019. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  16020. if (!quest)
  16021. continue;
  16022. m_seasonalquests[event_id].insert(quest_id);
  16023. TC_LOG_DEBUG("entities.player.loading", "Seasonal quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
  16024. }
  16025. while (result->NextRow());
  16026. }
  16027. m_SeasonalQuestChanged = false;
  16028. }
  16029. void Player::_LoadMonthlyQuestStatus(PreparedQueryResult result)
  16030. {
  16031. m_monthlyquests.clear();
  16032. if (result)
  16033. {
  16034. do
  16035. {
  16036. Field* fields = result->Fetch();
  16037. uint32 quest_id = fields[0].GetUInt32();
  16038. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  16039. if (!quest)
  16040. continue;
  16041. m_monthlyquests.insert(quest_id);
  16042. TC_LOG_DEBUG("entities.player.loading", "Monthly quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
  16043. }
  16044. while (result->NextRow());
  16045. }
  16046. m_MonthlyQuestChanged = false;
  16047. }
  16048. void Player::_LoadSpells(PreparedQueryResult result)
  16049. {
  16050. //QueryResult* result = CharacterDatabase.PQuery("SELECT spell, active, disabled FROM character_spell WHERE guid = '%u'", GetGUIDLow());
  16051. if (result)
  16052. {
  16053. do
  16054. AddSpell((*result)[0].GetUInt32(), (*result)[1].GetBool(), false, false, (*result)[2].GetBool(), true);
  16055. while (result->NextRow());
  16056. }
  16057. }
  16058. void Player::_LoadGroup(PreparedQueryResult result)
  16059. {
  16060. //QueryResult* result = CharacterDatabase.PQuery("SELECT guid FROM group_member WHERE memberGuid=%u", GetGUIDLow());
  16061. if (result)
  16062. {
  16063. if (Group* group = sGroupMgr->GetGroupByDbStoreId((*result)[0].GetUInt32()))
  16064. {
  16065. if (group->IsLeader(GetGUID()))
  16066. SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GROUP_LEADER);
  16067. uint8 subgroup = group->GetMemberGroup(GetGUID());
  16068. SetGroup(group, subgroup);
  16069. if (getLevel() >= LEVELREQUIREMENT_HEROIC)
  16070. {
  16071. // the group leader may change the instance difficulty while the player is offline
  16072. SetDungeonDifficulty(group->GetDungeonDifficulty());
  16073. SetRaidDifficulty(group->GetRaidDifficulty());
  16074. }
  16075. }
  16076. }
  16077. if (!GetGroup() || !GetGroup()->IsLeader(GetGUID()))
  16078. RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GROUP_LEADER);
  16079. }
  16080. void Player::_LoadBoundInstances(PreparedQueryResult result)
  16081. {
  16082. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  16083. m_boundInstances[i].clear();
  16084. Group* group = GetGroup();
  16085. //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));
  16086. if (result)
  16087. {
  16088. do
  16089. {
  16090. Field* fields = result->Fetch();
  16091. bool perm = fields[1].GetBool();
  16092. uint32 mapId = fields[2].GetUInt16();
  16093. uint32 instanceId = fields[0].GetUInt32();
  16094. uint8 difficulty = fields[3].GetUInt8();
  16095. time_t resetTime = time_t(fields[4].GetUInt32());
  16096. // the resettime for normal instances is only saved when the InstanceSave is unloaded
  16097. // so the value read from the DB may be wrong here but only if the InstanceSave is loaded
  16098. // and in that case it is not used
  16099. bool deleteInstance = false;
  16100. MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
  16101. std::string mapname = mapEntry ? mapEntry->name : "Unknown";
  16102. if (!mapEntry || !mapEntry->IsDungeon())
  16103. {
  16104. TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to not existed or not dungeon map %d (%s)", GetName().c_str(), GetGUIDLow(), mapId, mapname.c_str());
  16105. deleteInstance = true;
  16106. }
  16107. else if (difficulty >= MAX_DIFFICULTY)
  16108. {
  16109. TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u (%s)", GetName().c_str(), GetGUIDLow(), difficulty, mapId, mapname.c_str());
  16110. deleteInstance = true;
  16111. }
  16112. else
  16113. {
  16114. MapDifficulty const* mapDiff = GetMapDifficultyData(mapId, Difficulty(difficulty));
  16115. if (!mapDiff)
  16116. {
  16117. TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u (%s)", GetName().c_str(), GetGUIDLow(), difficulty, mapId, mapname.c_str());
  16118. deleteInstance = true;
  16119. }
  16120. else if (!perm && group)
  16121. {
  16122. TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) is in group %d but has a non-permanent character bind to map %d (%s), %d, %d", GetName().c_str(), GetGUIDLow(), group->GetLowGUID(), mapId, mapname.c_str(), instanceId, difficulty);
  16123. deleteInstance = true;
  16124. }
  16125. }
  16126. if (deleteInstance)
  16127. {
  16128. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID);
  16129. stmt->setUInt32(0, GetGUIDLow());
  16130. stmt->setUInt32(1, instanceId);
  16131. CharacterDatabase.Execute(stmt);
  16132. continue;
  16133. }
  16134. // since non permanent binds are always solo bind, they can always be reset
  16135. if (InstanceSave* save = sInstanceSaveMgr->AddInstanceSave(mapId, instanceId, Difficulty(difficulty), resetTime, !perm, true))
  16136. BindToInstance(save, perm, true);
  16137. }
  16138. while (result->NextRow());
  16139. }
  16140. }
  16141. InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty)
  16142. {
  16143. // some instances only have one difficulty
  16144. MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(mapid, difficulty);
  16145. if (!mapDiff)
  16146. return NULL;
  16147. BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
  16148. if (itr != m_boundInstances[difficulty].end())
  16149. return &itr->second;
  16150. else
  16151. return NULL;
  16152. }
  16153. InstanceSave* Player::GetInstanceSave(uint32 mapid, bool raid)
  16154. {
  16155. InstancePlayerBind* pBind = GetBoundInstance(mapid, GetDifficulty(raid));
  16156. InstanceSave* pSave = pBind ? pBind->save : NULL;
  16157. if (!pBind || !pBind->perm)
  16158. if (Group* group = GetGroup())
  16159. if (InstanceGroupBind* groupBind = group->GetBoundInstance(this))
  16160. pSave = groupBind->save;
  16161. return pSave;
  16162. }
  16163. void Player::UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload)
  16164. {
  16165. BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
  16166. UnbindInstance(itr, difficulty, unload);
  16167. }
  16168. void Player::UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficulty, bool unload)
  16169. {
  16170. if (itr != m_boundInstances[difficulty].end())
  16171. {
  16172. if (!unload)
  16173. {
  16174. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID);
  16175. stmt->setUInt32(0, GetGUIDLow());
  16176. stmt->setUInt32(1, itr->second.save->GetInstanceId());
  16177. CharacterDatabase.Execute(stmt);
  16178. }
  16179. if (itr->second.perm)
  16180. GetSession()->SendCalendarRaidLockout(itr->second.save, false);
  16181. itr->second.save->RemovePlayer(this); // save can become invalid
  16182. m_boundInstances[difficulty].erase(itr++);
  16183. }
  16184. }
  16185. InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, bool load)
  16186. {
  16187. if (save)
  16188. {
  16189. InstancePlayerBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
  16190. if (!load)
  16191. {
  16192. if (bind.save)
  16193. {
  16194. // update the save when the group kills a boss
  16195. if (permanent != bind.perm || save != bind.save)
  16196. {
  16197. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE);
  16198. stmt->setUInt32(0, save->GetInstanceId());
  16199. stmt->setBool(1, permanent);
  16200. stmt->setUInt32(2, GetGUIDLow());
  16201. stmt->setUInt32(3, bind.save->GetInstanceId());
  16202. CharacterDatabase.Execute(stmt);
  16203. }
  16204. }
  16205. else
  16206. {
  16207. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_INSTANCE);
  16208. stmt->setUInt32(0, GetGUIDLow());
  16209. stmt->setUInt32(1, save->GetInstanceId());
  16210. stmt->setBool(2, permanent);
  16211. CharacterDatabase.Execute(stmt);
  16212. }
  16213. }
  16214. if (bind.save != save)
  16215. {
  16216. if (bind.save)
  16217. bind.save->RemovePlayer(this);
  16218. save->AddPlayer(this);
  16219. }
  16220. if (permanent)
  16221. save->SetCanReset(false);
  16222. bind.save = save;
  16223. bind.perm = permanent;
  16224. if (!load)
  16225. TC_LOG_DEBUG("maps", "Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", GetName().c_str(), GetGUIDLow(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
  16226. sScriptMgr->OnPlayerBindToInstance(this, save->GetDifficulty(), save->GetMapId(), permanent);
  16227. return &bind;
  16228. }
  16229. return NULL;
  16230. }
  16231. void Player::BindToInstance()
  16232. {
  16233. InstanceSave* mapSave = sInstanceSaveMgr->GetInstanceSave(_pendingBindId);
  16234. if (!mapSave) //it seems sometimes mapSave is NULL, but I did not check why
  16235. return;
  16236. WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
  16237. data << uint32(0);
  16238. GetSession()->SendPacket(&data);
  16239. BindToInstance(mapSave, true);
  16240. GetSession()->SendCalendarRaidLockout(mapSave, true);
  16241. }
  16242. void Player::SetPendingBind(uint32 instanceId, uint32 bindTimer)
  16243. {
  16244. _pendingBindId = instanceId;
  16245. _pendingBindTimer = bindTimer;
  16246. }
  16247. void Player::SendRaidInfo()
  16248. {
  16249. uint32 counter = 0;
  16250. WorldPacket data(SMSG_RAID_INSTANCE_INFO, 4);
  16251. size_t p_counter = data.wpos();
  16252. data << uint32(counter); // placeholder
  16253. time_t now = time(NULL);
  16254. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  16255. {
  16256. for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
  16257. {
  16258. if (itr->second.perm)
  16259. {
  16260. InstanceSave* save = itr->second.save;
  16261. bool isHeroic = save->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC || save->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC;
  16262. uint32 completedEncounters = 0;
  16263. if (Map* map = sMapMgr->FindMap(save->GetMapId(), save->GetInstanceId()))
  16264. if (InstanceScript* instanceScript = ((InstanceMap*)map)->GetInstanceScript())
  16265. completedEncounters = instanceScript->GetCompletedEncounterMask();
  16266. data << uint32(save->GetMapId()); // map id
  16267. data << uint32(save->GetDifficulty()); // difficulty
  16268. data << uint32(isHeroic); // heroic
  16269. data << uint64(save->GetInstanceId()); // instance id
  16270. data << uint8(1); // expired = 0
  16271. data << uint8(0); // extended = 1
  16272. data << uint32(save->GetResetTime() - now); // reset time
  16273. data << uint32(completedEncounters); // completed encounters mask
  16274. ++counter;
  16275. }
  16276. }
  16277. }
  16278. data.put<uint32>(p_counter, counter);
  16279. GetSession()->SendPacket(&data);
  16280. }
  16281. /*
  16282. - called on every successful teleportation to a map
  16283. */
  16284. void Player::SendSavedInstances()
  16285. {
  16286. bool hasBeenSaved = false;
  16287. WorldPacket data;
  16288. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  16289. {
  16290. for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
  16291. {
  16292. if (itr->second.perm) // only permanent binds are sent
  16293. {
  16294. hasBeenSaved = true;
  16295. break;
  16296. }
  16297. }
  16298. }
  16299. //Send opcode SMSG_UPDATE_INSTANCE_OWNERSHIP. true or false means, whether you have current raid/heroic instances
  16300. data.Initialize(SMSG_UPDATE_INSTANCE_OWNERSHIP, 4);
  16301. data << uint32(hasBeenSaved);
  16302. GetSession()->SendPacket(&data);
  16303. if (!hasBeenSaved)
  16304. return;
  16305. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  16306. {
  16307. for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
  16308. {
  16309. if (itr->second.perm)
  16310. {
  16311. data.Initialize(SMSG_UPDATE_LAST_INSTANCE, 4);
  16312. data << uint32(itr->second.save->GetMapId());
  16313. GetSession()->SendPacket(&data);
  16314. }
  16315. }
  16316. }
  16317. }
  16318. /// convert the player's binds to the group
  16319. void Player::ConvertInstancesToGroup(Player* player, Group* group, bool switchLeader)
  16320. {
  16321. // copy all binds to the group, when changing leader it's assumed the character
  16322. // will not have any solo binds
  16323. for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  16324. {
  16325. for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();)
  16326. {
  16327. if (!switchLeader || !group->GetBoundInstance(itr->second.save->GetDifficulty(), itr->first))
  16328. group->BindToInstance(itr->second.save, itr->second.perm, false);
  16329. // permanent binds are not removed
  16330. if (switchLeader && !itr->second.perm)
  16331. {
  16332. // increments itr in call
  16333. player->UnbindInstance(itr, Difficulty(i), false);
  16334. }
  16335. else
  16336. ++itr;
  16337. }
  16338. }
  16339. }
  16340. bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report)
  16341. {
  16342. if (!IsGameMaster() && ar)
  16343. {
  16344. uint8 LevelMin = 0;
  16345. uint8 LevelMax = 0;
  16346. MapEntry const* mapEntry = sMapStore.LookupEntry(target_map);
  16347. if (!mapEntry)
  16348. return false;
  16349. if (!sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_LEVEL))
  16350. {
  16351. if (ar->levelMin && getLevel() < ar->levelMin)
  16352. LevelMin = ar->levelMin;
  16353. if (ar->levelMax && getLevel() > ar->levelMax)
  16354. LevelMax = ar->levelMax;
  16355. }
  16356. uint32 missingItem = 0;
  16357. if (ar->item)
  16358. {
  16359. if (!HasItemCount(ar->item) &&
  16360. (!ar->item2 || !HasItemCount(ar->item2)))
  16361. missingItem = ar->item;
  16362. }
  16363. else if (ar->item2 && !HasItemCount(ar->item2))
  16364. missingItem = ar->item2;
  16365. if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, target_map, this))
  16366. {
  16367. GetSession()->SendAreaTriggerMessage("%s", GetSession()->GetTrinityString(LANG_INSTANCE_CLOSED));
  16368. return false;
  16369. }
  16370. uint32 missingQuest = 0;
  16371. if (GetTeam() == ALLIANCE && ar->quest_A && !GetQuestRewardStatus(ar->quest_A))
  16372. missingQuest = ar->quest_A;
  16373. else if (GetTeam() == HORDE && ar->quest_H && !GetQuestRewardStatus(ar->quest_H))
  16374. missingQuest = ar->quest_H;
  16375. uint32 missingAchievement = 0;
  16376. Player* leader = this;
  16377. ObjectGuid leaderGuid = GetGroup() ? GetGroup()->GetLeaderGUID() : GetGUID();
  16378. if (leaderGuid != GetGUID())
  16379. leader = ObjectAccessor::FindPlayer(leaderGuid);
  16380. if (ar->achievement)
  16381. if (!leader || !leader->HasAchieved(ar->achievement))
  16382. missingAchievement = ar->achievement;
  16383. Difficulty target_difficulty = GetDifficulty(mapEntry->IsRaid());
  16384. MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(target_map, target_difficulty);
  16385. if (LevelMin || LevelMax || missingItem || missingQuest || missingAchievement)
  16386. {
  16387. if (report)
  16388. {
  16389. if (missingQuest && !ar->questFailedText.empty())
  16390. ChatHandler(GetSession()).PSendSysMessage("%s", ar->questFailedText.c_str());
  16391. else if (mapDiff->hasErrorMessage) // if (missingAchievement) covered by this case
  16392. SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY, target_difficulty);
  16393. else if (missingItem)
  16394. GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, sObjectMgr->GetItemTemplate(missingItem)->Name1.c_str());
  16395. else if (LevelMin)
  16396. GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED), LevelMin);
  16397. }
  16398. return false;
  16399. }
  16400. }
  16401. return true;
  16402. }
  16403. bool Player::CheckInstanceLoginValid()
  16404. {
  16405. if (!FindMap())
  16406. return false;
  16407. if (!GetMap()->IsDungeon() || IsGameMaster())
  16408. return true;
  16409. if (GetMap()->IsRaid())
  16410. {
  16411. // cannot be in raid instance without a group
  16412. if (!GetGroup())
  16413. return false;
  16414. }
  16415. else
  16416. {
  16417. // cannot be in normal instance without a group and more players than 1 in instance
  16418. if (!GetGroup() && GetMap()->GetPlayersCountExceptGMs() > 1)
  16419. return false;
  16420. }
  16421. // do checks for satisfy accessreqs, instance full, encounter in progress (raid), perm bind group != perm bind player
  16422. return sMapMgr->CanPlayerEnter(GetMap()->GetId(), this, true);
  16423. }
  16424. bool Player::CheckInstanceCount(uint32 instanceId) const
  16425. {
  16426. if (_instanceResetTimes.size() < sWorld->getIntConfig(CONFIG_MAX_INSTANCES_PER_HOUR))
  16427. return true;
  16428. return _instanceResetTimes.find(instanceId) != _instanceResetTimes.end();
  16429. }
  16430. void Player::AddInstanceEnterTime(uint32 instanceId, time_t enterTime)
  16431. {
  16432. if (_instanceResetTimes.find(instanceId) == _instanceResetTimes.end())
  16433. _instanceResetTimes.insert(InstanceTimeMap::value_type(instanceId, enterTime + HOUR));
  16434. }
  16435. bool Player::_LoadHomeBind(PreparedQueryResult result)
  16436. {
  16437. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  16438. if (!info)
  16439. {
  16440. TC_LOG_ERROR("entities.player", "Player (Name %s) has incorrect race/class (%u/%u) pair. Can't be loaded.",
  16441. GetName().c_str(), uint32(getRace()), uint32(getClass()));
  16442. return false;
  16443. }
  16444. bool ok = false;
  16445. // SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?
  16446. if (result)
  16447. {
  16448. Field* fields = result->Fetch();
  16449. m_homebindMapId = fields[0].GetUInt16();
  16450. m_homebindAreaId = fields[1].GetUInt16();
  16451. m_homebindX = fields[2].GetFloat();
  16452. m_homebindY = fields[3].GetFloat();
  16453. m_homebindZ = fields[4].GetFloat();
  16454. MapEntry const* bindMapEntry = sMapStore.LookupEntry(m_homebindMapId);
  16455. // accept saved data only for valid position (and non instanceable), and accessable
  16456. if (MapManager::IsValidMapCoord(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ) &&
  16457. !bindMapEntry->Instanceable() && GetSession()->Expansion() >= bindMapEntry->Expansion())
  16458. ok = true;
  16459. else
  16460. {
  16461. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_HOMEBIND);
  16462. stmt->setUInt32(0, GetGUIDLow());
  16463. CharacterDatabase.Execute(stmt);
  16464. }
  16465. }
  16466. if (!ok)
  16467. {
  16468. m_homebindMapId = info->mapId;
  16469. m_homebindAreaId = info->areaId;
  16470. m_homebindX = info->positionX;
  16471. m_homebindY = info->positionY;
  16472. m_homebindZ = info->positionZ;
  16473. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_HOMEBIND);
  16474. stmt->setUInt32(0, GetGUIDLow());
  16475. stmt->setUInt16(1, m_homebindMapId);
  16476. stmt->setUInt16(2, m_homebindAreaId);
  16477. stmt->setFloat (3, m_homebindX);
  16478. stmt->setFloat (4, m_homebindY);
  16479. stmt->setFloat (5, m_homebindZ);
  16480. CharacterDatabase.Execute(stmt);
  16481. }
  16482. TC_LOG_DEBUG("entities.player", "Setting player home position - mapid: %u, areaid: %u, X: %f, Y: %f, Z: %f",
  16483. m_homebindMapId, m_homebindAreaId, m_homebindX, m_homebindY, m_homebindZ);
  16484. return true;
  16485. }
  16486. /*********************************************************/
  16487. /*** SAVE SYSTEM ***/
  16488. /*********************************************************/
  16489. void Player::SaveToDB(bool create /*=false*/)
  16490. {
  16491. // delay auto save at any saves (manual, in code, or autosave)
  16492. m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE);
  16493. //lets allow only players in world to be saved
  16494. if (IsBeingTeleportedFar())
  16495. {
  16496. ScheduleDelayedOperation(DELAYED_SAVE_PLAYER);
  16497. return;
  16498. }
  16499. // first save/honor gain after midnight will also update the player's honor fields
  16500. UpdateHonorFields();
  16501. TC_LOG_DEBUG("entities.unit", "The value of player %s at save: ", m_name.c_str());
  16502. outDebugValues();
  16503. if (!create)
  16504. sScriptMgr->OnPlayerSave(this);
  16505. PreparedStatement* stmt = NULL;
  16506. uint8 index = 0;
  16507. if (create)
  16508. {
  16509. //! Insert query
  16510. /// @todo: Filter out more redundant fields that can take their default value at player create
  16511. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER);
  16512. stmt->setUInt32(index++, GetGUIDLow());
  16513. stmt->setUInt32(index++, GetSession()->GetAccountId());
  16514. stmt->setString(index++, GetName());
  16515. stmt->setUInt8(index++, getRace());
  16516. stmt->setUInt8(index++, getClass());
  16517. stmt->setUInt8(index++, getGender());
  16518. stmt->setUInt8(index++, getLevel());
  16519. stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP));
  16520. stmt->setUInt64(index++, GetMoney());
  16521. stmt->setUInt32(index++, GetUInt32Value(PLAYER_BYTES));
  16522. stmt->setUInt32(index++, GetUInt32Value(PLAYER_BYTES_2));
  16523. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FLAGS));
  16524. stmt->setUInt16(index++, (uint16)GetMapId());
  16525. stmt->setUInt32(index++, (uint32)GetInstanceId());
  16526. stmt->setUInt8(index++, (uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4));
  16527. stmt->setFloat(index++, finiteAlways(GetPositionX()));
  16528. stmt->setFloat(index++, finiteAlways(GetPositionY()));
  16529. stmt->setFloat(index++, finiteAlways(GetPositionZ()));
  16530. stmt->setFloat(index++, finiteAlways(GetOrientation()));
  16531. stmt->setFloat(index++, finiteAlways(GetTransOffsetX()));
  16532. stmt->setFloat(index++, finiteAlways(GetTransOffsetY()));
  16533. stmt->setFloat(index++, finiteAlways(GetTransOffsetZ()));
  16534. stmt->setFloat(index++, finiteAlways(GetTransOffsetO()));
  16535. uint32 transLowGUID = 0;
  16536. if (GetTransport())
  16537. transLowGUID = GetTransport()->GetGUIDLow();
  16538. stmt->setUInt32(index++, transLowGUID);
  16539. std::ostringstream ss;
  16540. ss << m_taxi;
  16541. stmt->setString(index++, ss.str());
  16542. stmt->setUInt8(index++, m_cinematic);
  16543. stmt->setUInt32(index++, m_Played_time[PLAYED_TIME_TOTAL]);
  16544. stmt->setUInt32(index++, m_Played_time[PLAYED_TIME_LEVEL]);
  16545. stmt->setFloat(index++, finiteAlways(m_rest_bonus));
  16546. stmt->setUInt32(index++, uint32(time(NULL)));
  16547. stmt->setUInt8(index++, (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0));
  16548. //save, far from tavern/city
  16549. //save, but in tavern/city
  16550. stmt->setUInt32(index++, GetTalentResetCost());
  16551. stmt->setUInt32(index++, GetTalentResetTime());
  16552. ss.str("");
  16553. for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
  16554. ss << GetPrimaryTalentTree(i) << " ";
  16555. stmt->setString(index++, ss.str());
  16556. stmt->setUInt16(index++, (uint16)m_ExtraFlags);
  16557. stmt->setUInt8(index++, m_stableSlots);
  16558. stmt->setUInt16(index++, (uint16)m_atLoginFlags);
  16559. stmt->setUInt16(index++, GetZoneId());
  16560. stmt->setUInt32(index++, uint32(m_deathExpireTime));
  16561. ss.str("");
  16562. ss << m_taxi.SaveTaxiDestinationsToString();
  16563. stmt->setString(index++, ss.str());
  16564. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS));
  16565. stmt->setUInt16(index++, GetUInt16Value(PLAYER_FIELD_KILLS, 0));
  16566. stmt->setUInt16(index++, GetUInt16Value(PLAYER_FIELD_KILLS, 1));
  16567. stmt->setUInt32(index++, GetUInt32Value(PLAYER_CHOSEN_TITLE));
  16568. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX));
  16569. stmt->setUInt8(index++, GetDrunkValue());
  16570. stmt->setUInt32(index++, GetHealth());
  16571. uint32 storedPowers = 0;
  16572. for (uint32 i = 0; i < MAX_POWERS; ++i)
  16573. {
  16574. if (GetPowerIndex(i) != MAX_POWERS)
  16575. {
  16576. stmt->setUInt32(index++, GetUInt32Value(UNIT_FIELD_POWER1 + storedPowers));
  16577. if (++storedPowers >= MAX_POWERS_PER_CLASS)
  16578. break;
  16579. }
  16580. }
  16581. for (; storedPowers < MAX_POWERS_PER_CLASS; ++storedPowers)
  16582. stmt->setUInt32(index++, 0);
  16583. stmt->setUInt32(index++, GetSession()->GetLatency());
  16584. stmt->setUInt8(index++, GetSpecsCount());
  16585. stmt->setUInt8(index++, GetActiveSpec());
  16586. ss.str("");
  16587. for (uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i)
  16588. ss << GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + i) << ' ';
  16589. stmt->setString(index++, ss.str());
  16590. ss.str("");
  16591. // cache equipment...
  16592. for (uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i)
  16593. ss << GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + i) << ' ';
  16594. // ...and bags for enum opcode
  16595. for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  16596. {
  16597. if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  16598. ss << item->GetEntry();
  16599. else
  16600. ss << '0';
  16601. ss << " 0 ";
  16602. }
  16603. stmt->setString(index++, ss.str());
  16604. ss.str("");
  16605. for (uint32 i = 0; i < KNOWN_TITLES_SIZE*2; ++i)
  16606. ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << ' ';
  16607. stmt->setString(index++, ss.str());
  16608. stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, 2));
  16609. stmt->setUInt32(index++, m_grantableLevels);
  16610. }
  16611. else
  16612. {
  16613. // Update query
  16614. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER);
  16615. stmt->setString(index++, GetName());
  16616. stmt->setUInt8(index++, getRace());
  16617. stmt->setUInt8(index++, getClass());
  16618. stmt->setUInt8(index++, getGender());
  16619. stmt->setUInt8(index++, getLevel());
  16620. stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP));
  16621. stmt->setUInt64(index++, GetMoney());
  16622. stmt->setUInt32(index++, GetUInt32Value(PLAYER_BYTES));
  16623. stmt->setUInt32(index++, GetUInt32Value(PLAYER_BYTES_2));
  16624. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FLAGS));
  16625. if (!IsBeingTeleported())
  16626. {
  16627. stmt->setUInt16(index++, (uint16)GetMapId());
  16628. stmt->setUInt32(index++, (uint32)GetInstanceId());
  16629. stmt->setUInt8(index++, (uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4));
  16630. stmt->setFloat(index++, finiteAlways(GetPositionX()));
  16631. stmt->setFloat(index++, finiteAlways(GetPositionY()));
  16632. stmt->setFloat(index++, finiteAlways(GetPositionZ()));
  16633. stmt->setFloat(index++, finiteAlways(GetOrientation()));
  16634. }
  16635. else
  16636. {
  16637. stmt->setUInt16(index++, (uint16)GetTeleportDest().GetMapId());
  16638. stmt->setUInt32(index++, (uint32)0);
  16639. stmt->setUInt8(index++, (uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4));
  16640. stmt->setFloat(index++, finiteAlways(GetTeleportDest().GetPositionX()));
  16641. stmt->setFloat(index++, finiteAlways(GetTeleportDest().GetPositionY()));
  16642. stmt->setFloat(index++, finiteAlways(GetTeleportDest().GetPositionZ()));
  16643. stmt->setFloat(index++, finiteAlways(GetTeleportDest().GetOrientation()));
  16644. }
  16645. stmt->setFloat(index++, finiteAlways(GetTransOffsetX()));
  16646. stmt->setFloat(index++, finiteAlways(GetTransOffsetY()));
  16647. stmt->setFloat(index++, finiteAlways(GetTransOffsetZ()));
  16648. stmt->setFloat(index++, finiteAlways(GetTransOffsetO()));
  16649. uint32 transLowGUID = 0;
  16650. if (GetTransport())
  16651. transLowGUID = GetTransport()->GetGUIDLow();
  16652. stmt->setUInt32(index++, transLowGUID);
  16653. std::ostringstream ss;
  16654. ss << m_taxi;
  16655. stmt->setString(index++, ss.str());
  16656. stmt->setUInt8(index++, m_cinematic);
  16657. stmt->setUInt32(index++, m_Played_time[PLAYED_TIME_TOTAL]);
  16658. stmt->setUInt32(index++, m_Played_time[PLAYED_TIME_LEVEL]);
  16659. stmt->setFloat(index++, finiteAlways(m_rest_bonus));
  16660. stmt->setUInt32(index++, uint32(time(NULL)));
  16661. stmt->setUInt8(index++, (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0));
  16662. //save, far from tavern/city
  16663. //save, but in tavern/city
  16664. stmt->setUInt32(index++, GetTalentResetCost());
  16665. stmt->setUInt32(index++, GetTalentResetTime());
  16666. ss.str("");
  16667. for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
  16668. ss << GetPrimaryTalentTree(i) << " ";
  16669. stmt->setString(index++, ss.str());
  16670. stmt->setUInt16(index++, (uint16)m_ExtraFlags);
  16671. stmt->setUInt8(index++, m_stableSlots);
  16672. stmt->setUInt16(index++, (uint16)m_atLoginFlags);
  16673. stmt->setUInt16(index++, GetZoneId());
  16674. stmt->setUInt32(index++, uint32(m_deathExpireTime));
  16675. ss.str("");
  16676. ss << m_taxi.SaveTaxiDestinationsToString();
  16677. stmt->setString(index++, ss.str());
  16678. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS));
  16679. stmt->setUInt16(index++, GetUInt16Value(PLAYER_FIELD_KILLS, 0));
  16680. stmt->setUInt16(index++, GetUInt16Value(PLAYER_FIELD_KILLS, 1));
  16681. stmt->setUInt32(index++, GetUInt32Value(PLAYER_CHOSEN_TITLE));
  16682. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX));
  16683. stmt->setUInt8(index++, GetDrunkValue());
  16684. stmt->setUInt32(index++, GetHealth());
  16685. uint32 storedPowers = 0;
  16686. for (uint32 i = 0; i < MAX_POWERS; ++i)
  16687. {
  16688. if (GetPowerIndex(i) != MAX_POWERS)
  16689. {
  16690. stmt->setUInt32(index++, GetUInt32Value(UNIT_FIELD_POWER1 + storedPowers));
  16691. if (++storedPowers >= MAX_POWERS_PER_CLASS)
  16692. break;
  16693. }
  16694. }
  16695. for (; storedPowers < MAX_POWERS_PER_CLASS; ++storedPowers)
  16696. stmt->setUInt32(index++, 0);
  16697. stmt->setUInt32(index++, GetSession()->GetLatency());
  16698. stmt->setUInt8(index++, GetSpecsCount());
  16699. stmt->setUInt8(index++, GetActiveSpec());
  16700. ss.str("");
  16701. for (uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i)
  16702. ss << GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + i) << ' ';
  16703. stmt->setString(index++, ss.str());
  16704. ss.str("");
  16705. // cache equipment...
  16706. for (uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i)
  16707. ss << GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + i) << ' ';
  16708. // ...and bags for enum opcode
  16709. for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
  16710. {
  16711. if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
  16712. ss << item->GetEntry();
  16713. else
  16714. ss << '0';
  16715. ss << " 0 ";
  16716. }
  16717. stmt->setString(index++, ss.str());
  16718. ss.str("");
  16719. for (uint32 i = 0; i < KNOWN_TITLES_SIZE*2; ++i)
  16720. ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << ' ';
  16721. stmt->setString(index++, ss.str());
  16722. stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, 2));
  16723. stmt->setUInt32(index++, m_grantableLevels);
  16724. stmt->setUInt8(index++, IsInWorld() && !GetSession()->PlayerLogout() ? 1 : 0);
  16725. // Index
  16726. stmt->setUInt32(index++, GetGUIDLow());
  16727. }
  16728. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  16729. trans->Append(stmt);
  16730. if (m_mailsUpdated) //save mails only when needed
  16731. _SaveMail(trans);
  16732. _SaveBGData(trans);
  16733. _SaveInventory(trans);
  16734. _SaveVoidStorage(trans);
  16735. _SaveQuestStatus(trans);
  16736. _SaveDailyQuestStatus(trans);
  16737. _SaveWeeklyQuestStatus(trans);
  16738. _SaveSeasonalQuestStatus(trans);
  16739. _SaveMonthlyQuestStatus(trans);
  16740. _SaveTalents(trans);
  16741. _SaveSpells(trans);
  16742. _SaveSpellCooldowns(trans);
  16743. _SaveActions(trans);
  16744. _SaveAuras(trans);
  16745. _SaveSkills(trans);
  16746. m_achievementMgr->SaveToDB(trans);
  16747. m_reputationMgr->SaveToDB(trans);
  16748. _SaveEquipmentSets(trans);
  16749. GetSession()->SaveTutorialsData(trans); // changed only while character in game
  16750. _SaveGlyphs(trans);
  16751. _SaveInstanceTimeRestrictions(trans);
  16752. _SaveCurrency(trans);
  16753. _SaveCUFProfiles(trans);
  16754. // check if stats should only be saved on logout
  16755. // save stats can be out of transaction
  16756. if (m_session->isLogingOut() || !sWorld->getBoolConfig(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT))
  16757. _SaveStats(trans);
  16758. CharacterDatabase.CommitTransaction(trans);
  16759. // save pet (hunter pet level and experience and all type pets health/mana).
  16760. if (Pet* pet = GetPet())
  16761. pet->SavePetToDB(PET_SAVE_AS_CURRENT);
  16762. }
  16763. // fast save function for item/money cheating preventing - save only inventory and money state
  16764. void Player::SaveInventoryAndGoldToDB(SQLTransaction& trans)
  16765. {
  16766. _SaveInventory(trans);
  16767. _SaveCurrency(trans);
  16768. SaveGoldToDB(trans);
  16769. }
  16770. void Player::SaveGoldToDB(SQLTransaction& trans)
  16771. {
  16772. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_MONEY);
  16773. stmt->setUInt64(0, GetMoney());
  16774. stmt->setUInt32(1, GetGUIDLow());
  16775. trans->Append(stmt);
  16776. }
  16777. void Player::_SaveActions(SQLTransaction& trans)
  16778. {
  16779. PreparedStatement* stmt = NULL;
  16780. for (ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end();)
  16781. {
  16782. switch (itr->second.uState)
  16783. {
  16784. case ACTIONBUTTON_NEW:
  16785. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACTION);
  16786. stmt->setUInt32(0, GetGUIDLow());
  16787. stmt->setUInt8(1, GetActiveSpec());
  16788. stmt->setUInt8(2, itr->first);
  16789. stmt->setUInt32(3, itr->second.GetAction());
  16790. stmt->setUInt8(4, uint8(itr->second.GetType()));
  16791. trans->Append(stmt);
  16792. itr->second.uState = ACTIONBUTTON_UNCHANGED;
  16793. ++itr;
  16794. break;
  16795. case ACTIONBUTTON_CHANGED:
  16796. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_ACTION);
  16797. stmt->setUInt32(0, itr->second.GetAction());
  16798. stmt->setUInt8(1, uint8(itr->second.GetType()));
  16799. stmt->setUInt32(2, GetGUIDLow());
  16800. stmt->setUInt8(3, itr->first);
  16801. stmt->setUInt8(4, GetActiveSpec());
  16802. trans->Append(stmt);
  16803. itr->second.uState = ACTIONBUTTON_UNCHANGED;
  16804. ++itr;
  16805. break;
  16806. case ACTIONBUTTON_DELETED:
  16807. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACTION_BY_BUTTON_SPEC);
  16808. stmt->setUInt32(0, GetGUIDLow());
  16809. stmt->setUInt8(1, itr->first);
  16810. stmt->setUInt8(2, GetActiveSpec());
  16811. trans->Append(stmt);
  16812. m_actionButtons.erase(itr++);
  16813. break;
  16814. default:
  16815. ++itr;
  16816. break;
  16817. }
  16818. }
  16819. }
  16820. void Player::_SaveAuras(SQLTransaction& trans)
  16821. {
  16822. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_AURA);
  16823. stmt->setUInt32(0, GetGUIDLow());
  16824. trans->Append(stmt);
  16825. for (AuraMap::const_iterator itr = m_ownedAuras.begin(); itr != m_ownedAuras.end(); ++itr)
  16826. {
  16827. if (!itr->second->CanBeSaved())
  16828. continue;
  16829. Aura* aura = itr->second;
  16830. int32 damage[MAX_SPELL_EFFECTS];
  16831. int32 baseDamage[MAX_SPELL_EFFECTS];
  16832. uint8 effMask = 0;
  16833. uint8 recalculateMask = 0;
  16834. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  16835. {
  16836. if (AuraEffect const* effect = aura->GetEffect(i))
  16837. {
  16838. baseDamage[i] = effect->GetBaseAmount();
  16839. damage[i] = effect->GetAmount();
  16840. effMask |= 1 << i;
  16841. if (effect->CanBeRecalculated())
  16842. recalculateMask |= 1 << i;
  16843. }
  16844. else
  16845. {
  16846. baseDamage[i] = 0;
  16847. damage[i] = 0;
  16848. }
  16849. }
  16850. uint8 index = 0;
  16851. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AURA);
  16852. stmt->setUInt32(index++, GetGUIDLow());
  16853. stmt->setUInt64(index++, itr->second->GetCasterGUID().GetRawValue());
  16854. stmt->setUInt64(index++, itr->second->GetCastItemGUID().GetRawValue());
  16855. stmt->setUInt32(index++, itr->second->GetId());
  16856. stmt->setUInt8(index++, effMask);
  16857. stmt->setUInt8(index++, recalculateMask);
  16858. stmt->setUInt8(index++, itr->second->GetStackAmount());
  16859. stmt->setInt32(index++, damage[0]);
  16860. stmt->setInt32(index++, damage[1]);
  16861. stmt->setInt32(index++, damage[2]);
  16862. stmt->setInt32(index++, baseDamage[0]);
  16863. stmt->setInt32(index++, baseDamage[1]);
  16864. stmt->setInt32(index++, baseDamage[2]);
  16865. stmt->setInt32(index++, itr->second->GetMaxDuration());
  16866. stmt->setInt32(index++, itr->second->GetDuration());
  16867. stmt->setUInt8(index, itr->second->GetCharges());
  16868. trans->Append(stmt);
  16869. }
  16870. }
  16871. void Player::_SaveInventory(SQLTransaction& trans)
  16872. {
  16873. PreparedStatement* stmt = NULL;
  16874. // force items in buyback slots to new state
  16875. // and remove those that aren't already
  16876. for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; ++i)
  16877. {
  16878. Item* item = m_items[i];
  16879. if (!item || item->GetState() == ITEM_NEW)
  16880. continue;
  16881. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
  16882. stmt->setUInt32(0, item->GetGUIDLow());
  16883. trans->Append(stmt);
  16884. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  16885. stmt->setUInt32(0, item->GetGUIDLow());
  16886. trans->Append(stmt);
  16887. m_items[i]->FSetState(ITEM_NEW);
  16888. }
  16889. // Updated played time for refundable items. We don't do this in Player::Update because there's simply no need for it,
  16890. // the client auto counts down in real time after having received the initial played time on the first
  16891. // SMSG_ITEM_REFUND_INFO_RESPONSE packet.
  16892. // Item::UpdatePlayedTime is only called when needed, which is in DB saves, and item refund info requests.
  16893. GuidSet::iterator i_next;
  16894. for (GuidSet::iterator itr = m_refundableItems.begin(); itr!= m_refundableItems.end(); itr = i_next)
  16895. {
  16896. // use copy iterator because itr may be invalid after operations in this loop
  16897. i_next = itr;
  16898. ++i_next;
  16899. Item* iPtr = GetItemByGuid(*itr);
  16900. if (iPtr)
  16901. {
  16902. iPtr->UpdatePlayedTime(this);
  16903. continue;
  16904. }
  16905. else
  16906. {
  16907. TC_LOG_ERROR("entities.player", "Can't find %s but is in refundable storage for player %u ! Removing.", itr->ToString().c_str(), GetGUIDLow());
  16908. m_refundableItems.erase(itr);
  16909. }
  16910. }
  16911. // update enchantment durations
  16912. for (EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr)
  16913. itr->item->SetEnchantmentDuration(itr->slot, itr->leftduration, this);
  16914. // if no changes
  16915. if (m_itemUpdateQueue.empty())
  16916. return;
  16917. uint32 lowGuid = GetGUIDLow();
  16918. for (size_t i = 0; i < m_itemUpdateQueue.size(); ++i)
  16919. {
  16920. Item* item = m_itemUpdateQueue[i];
  16921. if (!item)
  16922. continue;
  16923. Bag* container = item->GetContainer();
  16924. uint32 bag_guid = container ? container->GetGUIDLow() : 0;
  16925. if (item->GetState() != ITEM_REMOVED)
  16926. {
  16927. Item* test = GetItemByPos(item->GetBagSlot(), item->GetSlot());
  16928. if (test == NULL)
  16929. {
  16930. uint32 bagTestGUID = 0;
  16931. if (Item* test2 = GetItemByPos(INVENTORY_SLOT_BAG_0, item->GetBagSlot()))
  16932. bagTestGUID = test2->GetGUIDLow();
  16933. TC_LOG_ERROR("entities.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().c_str(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), (int32)item->GetState());
  16934. // according to the test that was just performed nothing should be in this slot, delete
  16935. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_BAG_SLOT);
  16936. stmt->setUInt32(0, bagTestGUID);
  16937. stmt->setUInt8(1, item->GetSlot());
  16938. stmt->setUInt32(2, lowGuid);
  16939. trans->Append(stmt);
  16940. // also THIS item should be somewhere else, cheat attempt
  16941. item->FSetState(ITEM_REMOVED); // we are IN updateQueue right now, can't use SetState which modifies the queue
  16942. DeleteRefundReference(item->GetGUID());
  16943. // don't skip, let the switch delete it
  16944. //continue;
  16945. }
  16946. else if (test != item)
  16947. {
  16948. TC_LOG_ERROR("entities.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().c_str(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), test->GetGUIDLow());
  16949. // save all changes to the item...
  16950. if (item->GetState() != ITEM_NEW) // only for existing items, no dupes
  16951. item->SaveToDB(trans);
  16952. // ...but do not save position in inventory
  16953. continue;
  16954. }
  16955. }
  16956. switch (item->GetState())
  16957. {
  16958. case ITEM_NEW:
  16959. case ITEM_CHANGED:
  16960. stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_INVENTORY_ITEM);
  16961. stmt->setUInt32(0, lowGuid);
  16962. stmt->setUInt32(1, bag_guid);
  16963. stmt->setUInt8 (2, item->GetSlot());
  16964. stmt->setUInt32(3, item->GetGUIDLow());
  16965. trans->Append(stmt);
  16966. break;
  16967. case ITEM_REMOVED:
  16968. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
  16969. stmt->setUInt32(0, item->GetGUIDLow());
  16970. trans->Append(stmt);
  16971. case ITEM_UNCHANGED:
  16972. break;
  16973. }
  16974. item->SaveToDB(trans); // item have unchanged inventory record and can be save standalone
  16975. }
  16976. m_itemUpdateQueue.clear();
  16977. }
  16978. void Player::_SaveVoidStorage(SQLTransaction& trans)
  16979. {
  16980. PreparedStatement* stmt = NULL;
  16981. uint32 lowGuid = GetGUIDLow();
  16982. for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
  16983. {
  16984. if (!_voidStorageItems[i]) // unused item
  16985. {
  16986. // DELETE FROM void_storage WHERE slot = ? AND playerGuid = ?
  16987. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_VOID_STORAGE_ITEM_BY_SLOT);
  16988. stmt->setUInt8(0, i);
  16989. stmt->setUInt32(1, lowGuid);
  16990. }
  16991. else
  16992. {
  16993. // REPLACE INTO character_inventory (itemId, playerGuid, itemEntry, slot, creatorGuid) VALUES (?, ?, ?, ?, ?)
  16994. stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_VOID_STORAGE_ITEM);
  16995. stmt->setUInt64(0, _voidStorageItems[i]->ItemId);
  16996. stmt->setUInt32(1, lowGuid);
  16997. stmt->setUInt32(2, _voidStorageItems[i]->ItemEntry);
  16998. stmt->setUInt8(3, i);
  16999. stmt->setUInt32(4, _voidStorageItems[i]->CreatorGuid.GetCounter());
  17000. stmt->setUInt32(5, _voidStorageItems[i]->ItemRandomPropertyId);
  17001. stmt->setUInt32(6, _voidStorageItems[i]->ItemSuffixFactor);
  17002. }
  17003. trans->Append(stmt);
  17004. }
  17005. }
  17006. void Player::_SaveCUFProfiles(SQLTransaction& trans)
  17007. {
  17008. PreparedStatement* stmt = NULL;
  17009. uint32 lowGuid = GetGUIDLow();
  17010. for (uint8 i = 0; i < MAX_CUF_PROFILES; ++i)
  17011. {
  17012. if (!_CUFProfiles[i]) // unused profile
  17013. {
  17014. // DELETE FROM character_cuf_profiles WHERE guid = ? and id = ?
  17015. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_CUF_PROFILES);
  17016. stmt->setUInt32(0, lowGuid);
  17017. stmt->setUInt8(1, i);
  17018. }
  17019. else
  17020. {
  17021. // REPLACE INTO character_cuf_profiles (guid, id, name, frameHeight, frameWidth, sortBy, healthText, boolOptions, unk146, unk147, unk148, unk150, unk152, unk154) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  17022. stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_CUF_PROFILES);
  17023. stmt->setUInt32(0, lowGuid);
  17024. stmt->setUInt8(1, i);
  17025. stmt->setString(2, _CUFProfiles[i]->ProfileName);
  17026. stmt->setUInt16(3, _CUFProfiles[i]->FrameHeight);
  17027. stmt->setUInt16(4, _CUFProfiles[i]->FrameWidth);
  17028. stmt->setUInt8(5, _CUFProfiles[i]->SortBy);
  17029. stmt->setUInt8(6, _CUFProfiles[i]->HealthText);
  17030. stmt->setUInt32(7, _CUFProfiles[i]->BoolOptions.to_ulong()); // 27 of 32 fields used, fits in an int
  17031. stmt->setUInt8(8, _CUFProfiles[i]->Unk146);
  17032. stmt->setUInt8(9, _CUFProfiles[i]->Unk147);
  17033. stmt->setUInt8(10, _CUFProfiles[i]->Unk148);
  17034. stmt->setUInt16(11, _CUFProfiles[i]->Unk150);
  17035. stmt->setUInt16(12, _CUFProfiles[i]->Unk152);
  17036. stmt->setUInt16(13, _CUFProfiles[i]->Unk154);
  17037. }
  17038. trans->Append(stmt);
  17039. }
  17040. }
  17041. void Player::_SaveMail(SQLTransaction& trans)
  17042. {
  17043. if (!m_mailsLoaded)
  17044. return;
  17045. PreparedStatement* stmt = NULL;
  17046. for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
  17047. {
  17048. Mail* m = (*itr);
  17049. if (m->state == MAIL_STATE_CHANGED)
  17050. {
  17051. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL);
  17052. stmt->setUInt8(0, uint8(m->HasItems() ? 1 : 0));
  17053. stmt->setUInt32(1, uint32(m->expire_time));
  17054. stmt->setUInt32(2, uint32(m->deliver_time));
  17055. stmt->setUInt32(3, m->money);
  17056. stmt->setUInt32(4, m->COD);
  17057. stmt->setUInt8(5, uint8(m->checked));
  17058. stmt->setUInt32(6, m->messageID);
  17059. trans->Append(stmt);
  17060. if (!m->removedItems.empty())
  17061. {
  17062. for (std::vector<uint32>::iterator itr2 = m->removedItems.begin(); itr2 != m->removedItems.end(); ++itr2)
  17063. {
  17064. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM);
  17065. stmt->setUInt32(0, *itr2);
  17066. trans->Append(stmt);
  17067. }
  17068. m->removedItems.clear();
  17069. }
  17070. m->state = MAIL_STATE_UNCHANGED;
  17071. }
  17072. else if (m->state == MAIL_STATE_DELETED)
  17073. {
  17074. if (m->HasItems())
  17075. {
  17076. for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
  17077. {
  17078. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  17079. stmt->setUInt32(0, itr2->item_guid);
  17080. trans->Append(stmt);
  17081. }
  17082. }
  17083. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
  17084. stmt->setUInt32(0, m->messageID);
  17085. trans->Append(stmt);
  17086. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
  17087. stmt->setUInt32(0, m->messageID);
  17088. trans->Append(stmt);
  17089. }
  17090. }
  17091. //deallocate deleted mails...
  17092. for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();)
  17093. {
  17094. if ((*itr)->state == MAIL_STATE_DELETED)
  17095. {
  17096. Mail* m = *itr;
  17097. m_mail.erase(itr);
  17098. delete m;
  17099. itr = m_mail.begin();
  17100. }
  17101. else
  17102. ++itr;
  17103. }
  17104. m_mailsUpdated = false;
  17105. }
  17106. void Player::_SaveQuestStatus(SQLTransaction& trans)
  17107. {
  17108. bool isTransaction = bool(trans);
  17109. if (!isTransaction)
  17110. trans = CharacterDatabase.BeginTransaction();
  17111. QuestStatusSaveMap::iterator saveItr;
  17112. QuestStatusMap::iterator statusItr;
  17113. PreparedStatement* stmt = NULL;
  17114. bool keepAbandoned = !(sWorld->GetCleaningFlags() & CharacterDatabaseCleaner::CLEANING_FLAG_QUESTSTATUS);
  17115. for (saveItr = m_QuestStatusSave.begin(); saveItr != m_QuestStatusSave.end(); ++saveItr)
  17116. {
  17117. if (saveItr->second == QUEST_DEFAULT_SAVE_TYPE)
  17118. {
  17119. statusItr = m_QuestStatus.find(saveItr->first);
  17120. if (statusItr != m_QuestStatus.end() && (keepAbandoned || statusItr->second.Status != QUEST_STATUS_NONE))
  17121. {
  17122. uint8 index = 0;
  17123. stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_QUESTSTATUS);
  17124. stmt->setUInt32(index++, GetGUIDLow());
  17125. stmt->setUInt32(index++, statusItr->first);
  17126. stmt->setUInt8(index++, uint8(statusItr->second.Status));
  17127. stmt->setBool(index++, statusItr->second.Explored);
  17128. stmt->setUInt32(index++, uint32(statusItr->second.Timer / IN_MILLISECONDS+ sWorld->GetGameTime()));
  17129. for (uint8 i = 0; i < 4; i++)
  17130. stmt->setUInt16(index++, statusItr->second.CreatureOrGOCount[i]);
  17131. for (uint8 i = 0; i < 4; i++)
  17132. stmt->setUInt16(index++, statusItr->second.ItemCount[i]);
  17133. stmt->setUInt16(index, statusItr->second.PlayerCount);
  17134. trans->Append(stmt);
  17135. }
  17136. }
  17137. else
  17138. {
  17139. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_QUESTSTATUS_BY_QUEST);
  17140. stmt->setUInt32(0, GetGUIDLow());
  17141. stmt->setUInt32(1, saveItr->first);
  17142. trans->Append(stmt);
  17143. }
  17144. }
  17145. m_QuestStatusSave.clear();
  17146. for (saveItr = m_RewardedQuestsSave.begin(); saveItr != m_RewardedQuestsSave.end(); ++saveItr)
  17147. {
  17148. if (saveItr->second == QUEST_DEFAULT_SAVE_TYPE)
  17149. {
  17150. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_QUESTSTATUS_REWARDED);
  17151. stmt->setUInt32(0, GetGUIDLow());
  17152. stmt->setUInt32(1, saveItr->first);
  17153. trans->Append(stmt);
  17154. }
  17155. else if (saveItr->second == QUEST_FORCE_DELETE_SAVE_TYPE || !keepAbandoned)
  17156. {
  17157. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_QUESTSTATUS_REWARDED_BY_QUEST);
  17158. stmt->setUInt32(0, GetGUIDLow());
  17159. stmt->setUInt32(1, saveItr->first);
  17160. trans->Append(stmt);
  17161. }
  17162. }
  17163. m_RewardedQuestsSave.clear();
  17164. if (!isTransaction)
  17165. CharacterDatabase.CommitTransaction(trans);
  17166. }
  17167. void Player::_SaveDailyQuestStatus(SQLTransaction& trans)
  17168. {
  17169. if (!m_DailyQuestChanged)
  17170. return;
  17171. m_DailyQuestChanged = false;
  17172. // save last daily quest time for all quests: we need only mostly reset time for reset check anyway
  17173. // we don't need transactions here.
  17174. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_DAILY);
  17175. stmt->setUInt32(0, GetGUIDLow());
  17176. trans->Append(stmt);
  17177. for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
  17178. {
  17179. if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
  17180. {
  17181. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_DAILY);
  17182. stmt->setUInt32(0, GetGUIDLow());
  17183. stmt->setUInt32(1, GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx));
  17184. stmt->setUInt64(2, uint64(m_lastDailyQuestTime));
  17185. trans->Append(stmt);
  17186. }
  17187. }
  17188. if (!m_DFQuests.empty())
  17189. {
  17190. for (DFQuestsDoneList::iterator itr = m_DFQuests.begin(); itr != m_DFQuests.end(); ++itr)
  17191. {
  17192. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_DAILY);
  17193. stmt->setUInt32(0, GetGUIDLow());
  17194. stmt->setUInt32(1, (*itr));
  17195. stmt->setUInt64(2, uint64(m_lastDailyQuestTime));
  17196. trans->Append(stmt);
  17197. }
  17198. }
  17199. }
  17200. void Player::_SaveWeeklyQuestStatus(SQLTransaction& trans)
  17201. {
  17202. if (!m_WeeklyQuestChanged || m_weeklyquests.empty())
  17203. return;
  17204. // we don't need transactions here.
  17205. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_WEEKLY);
  17206. stmt->setUInt32(0, GetGUIDLow());
  17207. trans->Append(stmt);
  17208. for (QuestSet::const_iterator iter = m_weeklyquests.begin(); iter != m_weeklyquests.end(); ++iter)
  17209. {
  17210. uint32 questId = *iter;
  17211. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_WEEKLY);
  17212. stmt->setUInt32(0, GetGUIDLow());
  17213. stmt->setUInt32(1, questId);
  17214. trans->Append(stmt);
  17215. }
  17216. m_WeeklyQuestChanged = false;
  17217. }
  17218. void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans)
  17219. {
  17220. if (!m_SeasonalQuestChanged || m_seasonalquests.empty())
  17221. return;
  17222. // we don't need transactions here.
  17223. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_SEASONAL);
  17224. stmt->setUInt32(0, GetGUIDLow());
  17225. trans->Append(stmt);
  17226. for (SeasonalEventQuestMap::const_iterator iter = m_seasonalquests.begin(); iter != m_seasonalquests.end(); ++iter)
  17227. {
  17228. uint16 eventId = iter->first;
  17229. for (SeasonalQuestSet::const_iterator itr = iter->second.begin(); itr != iter->second.end(); ++itr)
  17230. {
  17231. uint32 questId = *itr;
  17232. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_SEASONAL);
  17233. stmt->setUInt32(0, GetGUIDLow());
  17234. stmt->setUInt32(1, questId);
  17235. stmt->setUInt32(2, eventId);
  17236. trans->Append(stmt);
  17237. }
  17238. }
  17239. m_SeasonalQuestChanged = false;
  17240. }
  17241. void Player::_SaveMonthlyQuestStatus(SQLTransaction& trans)
  17242. {
  17243. if (!m_MonthlyQuestChanged || m_monthlyquests.empty())
  17244. return;
  17245. // we don't need transactions here.
  17246. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_MONTHLY);
  17247. stmt->setUInt32(0, GetGUIDLow());
  17248. trans->Append(stmt);
  17249. for (QuestSet::const_iterator iter = m_monthlyquests.begin(); iter != m_monthlyquests.end(); ++iter)
  17250. {
  17251. uint32 questId = *iter;
  17252. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_MONTHLY);
  17253. stmt->setUInt32(0, GetGUIDLow());
  17254. stmt->setUInt32(1, questId);
  17255. trans->Append(stmt);
  17256. }
  17257. m_MonthlyQuestChanged = false;
  17258. }
  17259. void Player::_SaveSkills(SQLTransaction& trans)
  17260. {
  17261. PreparedStatement* stmt = NULL;
  17262. // we don't need transactions here.
  17263. for (SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end();)
  17264. {
  17265. if (itr->second.uState == SKILL_UNCHANGED)
  17266. {
  17267. ++itr;
  17268. continue;
  17269. }
  17270. if (itr->second.uState == SKILL_DELETED)
  17271. {
  17272. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SKILL_BY_SKILL);
  17273. stmt->setUInt32(0, GetGUIDLow());
  17274. stmt->setUInt32(1, itr->first);
  17275. trans->Append(stmt);
  17276. mSkillStatus.erase(itr++);
  17277. continue;
  17278. }
  17279. uint16 field = itr->second.pos / 2;
  17280. uint8 offset = itr->second.pos & 1;
  17281. uint16 value = GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset);
  17282. uint16 max = GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset);
  17283. switch (itr->second.uState)
  17284. {
  17285. case SKILL_NEW:
  17286. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_SKILLS);
  17287. stmt->setUInt32(0, GetGUIDLow());
  17288. stmt->setUInt16(1, uint16(itr->first));
  17289. stmt->setUInt16(2, value);
  17290. stmt->setUInt16(3, max);
  17291. trans->Append(stmt);
  17292. break;
  17293. case SKILL_CHANGED:
  17294. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_SKILLS);
  17295. stmt->setUInt16(0, value);
  17296. stmt->setUInt16(1, max);
  17297. stmt->setUInt32(2, GetGUIDLow());
  17298. stmt->setUInt16(3, uint16(itr->first));
  17299. trans->Append(stmt);
  17300. break;
  17301. default:
  17302. break;
  17303. }
  17304. itr->second.uState = SKILL_UNCHANGED;
  17305. ++itr;
  17306. }
  17307. }
  17308. void Player::_SaveSpells(SQLTransaction& trans)
  17309. {
  17310. PreparedStatement* stmt = NULL;
  17311. for (PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end();)
  17312. {
  17313. if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
  17314. {
  17315. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_BY_SPELL);
  17316. stmt->setUInt32(0, itr->first);
  17317. stmt->setUInt32(1, GetGUIDLow());
  17318. trans->Append(stmt);
  17319. }
  17320. // add only changed/new not dependent spells
  17321. if (!itr->second->dependent && (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED))
  17322. {
  17323. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_SPELL);
  17324. stmt->setUInt32(0, GetGUIDLow());
  17325. stmt->setUInt32(1, itr->first);
  17326. stmt->setBool(2, itr->second->active);
  17327. stmt->setBool(3, itr->second->disabled);
  17328. trans->Append(stmt);
  17329. }
  17330. if (itr->second->state == PLAYERSPELL_REMOVED)
  17331. {
  17332. delete itr->second;
  17333. m_spells.erase(itr++);
  17334. }
  17335. else
  17336. {
  17337. itr->second->state = PLAYERSPELL_UNCHANGED;
  17338. ++itr;
  17339. }
  17340. }
  17341. }
  17342. // save player stats -- only for external usage
  17343. // real stats will be recalculated on player login
  17344. void Player::_SaveStats(SQLTransaction& trans)
  17345. {
  17346. // check if stat saving is enabled and if char level is high enough
  17347. if (!sWorld->getIntConfig(CONFIG_MIN_LEVEL_STAT_SAVE) || getLevel() < sWorld->getIntConfig(CONFIG_MIN_LEVEL_STAT_SAVE))
  17348. return;
  17349. PreparedStatement* stmt = NULL;
  17350. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_STATS);
  17351. stmt->setUInt32(0, GetGUIDLow());
  17352. trans->Append(stmt);
  17353. uint8 index = 0;
  17354. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_STATS);
  17355. stmt->setUInt32(index++, GetGUIDLow());
  17356. stmt->setUInt32(index++, GetMaxHealth());
  17357. for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i)
  17358. stmt->setUInt32(index++, GetMaxPower(Powers(i)));
  17359. for (uint8 i = 0; i < MAX_STATS; ++i)
  17360. stmt->setUInt32(index++, GetStat(Stats(i)));
  17361. for (int i = 0; i < MAX_SPELL_SCHOOL; ++i)
  17362. stmt->setUInt32(index++, GetResistance(SpellSchools(i)));
  17363. stmt->setFloat(index++, GetFloatValue(PLAYER_BLOCK_PERCENTAGE));
  17364. stmt->setFloat(index++, GetFloatValue(PLAYER_DODGE_PERCENTAGE));
  17365. stmt->setFloat(index++, GetFloatValue(PLAYER_PARRY_PERCENTAGE));
  17366. stmt->setFloat(index++, GetFloatValue(PLAYER_CRIT_PERCENTAGE));
  17367. stmt->setFloat(index++, GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE));
  17368. stmt->setFloat(index++, GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1));
  17369. stmt->setUInt32(index++, GetUInt32Value(UNIT_FIELD_ATTACK_POWER));
  17370. stmt->setUInt32(index++, GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER));
  17371. stmt->setUInt32(index++, GetBaseSpellPowerBonus());
  17372. stmt->setUInt32(index++, GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_RESILIENCE_PLAYER_DAMAGE_TAKEN));
  17373. trans->Append(stmt);
  17374. }
  17375. void Player::outDebugValues() const
  17376. {
  17377. if (!sLog->ShouldLog("entities.unit", LOG_LEVEL_DEBUG))
  17378. return;
  17379. TC_LOG_DEBUG("entities.unit", "HP is: \t\t\t%u\t\tMP is: \t\t\t%u", GetMaxHealth(), GetMaxPower(POWER_MANA));
  17380. TC_LOG_DEBUG("entities.unit", "AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f", GetStat(STAT_AGILITY), GetStat(STAT_STRENGTH));
  17381. TC_LOG_DEBUG("entities.unit", "INTELLECT is: \t\t%f\t\tSPIRIT is: \t\t%f", GetStat(STAT_INTELLECT), GetStat(STAT_SPIRIT));
  17382. TC_LOG_DEBUG("entities.unit", "STAMINA is: \t\t%f", GetStat(STAT_STAMINA));
  17383. TC_LOG_DEBUG("entities.unit", "Armor is: \t\t%u\t\tBlock is: \t\t%f", GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE));
  17384. TC_LOG_DEBUG("entities.unit", "HolyRes is: \t\t%u\t\tFireRes is: \t\t%u", GetResistance(SPELL_SCHOOL_HOLY), GetResistance(SPELL_SCHOOL_FIRE));
  17385. TC_LOG_DEBUG("entities.unit", "NatureRes is: \t\t%u\t\tFrostRes is: \t\t%u", GetResistance(SPELL_SCHOOL_NATURE), GetResistance(SPELL_SCHOOL_FROST));
  17386. TC_LOG_DEBUG("entities.unit", "ShadowRes is: \t\t%u\t\tArcaneRes is: \t\t%u", GetResistance(SPELL_SCHOOL_SHADOW), GetResistance(SPELL_SCHOOL_ARCANE));
  17387. TC_LOG_DEBUG("entities.unit", "MIN_DAMAGE is: \t\t%f\tMAX_DAMAGE is: \t\t%f", GetFloatValue(UNIT_FIELD_MINDAMAGE), GetFloatValue(UNIT_FIELD_MAXDAMAGE));
  17388. TC_LOG_DEBUG("entities.unit", "MIN_OFFHAND_DAMAGE is: \t%f\tMAX_OFFHAND_DAMAGE is: \t%f", GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE), GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE));
  17389. TC_LOG_DEBUG("entities.unit", "MIN_RANGED_DAMAGE is: \t%f\tMAX_RANGED_DAMAGE is: \t%f", GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE), GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE));
  17390. TC_LOG_DEBUG("entities.unit", "ATTACK_TIME is: \t%u\t\tRANGE_ATTACK_TIME is: \t%u", GetAttackTime(BASE_ATTACK), GetAttackTime(RANGED_ATTACK));
  17391. }
  17392. /*********************************************************/
  17393. /*** FLOOD FILTER SYSTEM ***/
  17394. /*********************************************************/
  17395. void Player::UpdateSpeakTime()
  17396. {
  17397. // ignore chat spam protection for GMs in any mode
  17398. if (GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHAT_SPAM))
  17399. return;
  17400. time_t current = time (NULL);
  17401. if (m_speakTime > current)
  17402. {
  17403. uint32 max_count = sWorld->getIntConfig(CONFIG_CHATFLOOD_MESSAGE_COUNT);
  17404. if (!max_count)
  17405. return;
  17406. ++m_speakCount;
  17407. if (m_speakCount >= max_count)
  17408. {
  17409. // prevent overwrite mute time, if message send just before mutes set, for example.
  17410. time_t new_mute = current + sWorld->getIntConfig(CONFIG_CHATFLOOD_MUTE_TIME);
  17411. if (GetSession()->m_muteTime < new_mute)
  17412. GetSession()->m_muteTime = new_mute;
  17413. m_speakCount = 0;
  17414. }
  17415. }
  17416. else
  17417. m_speakCount = 1;
  17418. m_speakTime = current + sWorld->getIntConfig(CONFIG_CHATFLOOD_MESSAGE_DELAY);
  17419. }
  17420. bool Player::CanSpeak() const
  17421. {
  17422. return GetSession()->m_muteTime <= time (NULL);
  17423. }
  17424. /*********************************************************/
  17425. /*** LOW LEVEL FUNCTIONS:Notifiers ***/
  17426. /*********************************************************/
  17427. void Player::SendAttackSwingNotInRange()
  17428. {
  17429. WorldPacket data(SMSG_ATTACKSWING_NOTINRANGE, 0);
  17430. GetSession()->SendPacket(&data);
  17431. }
  17432. void Player::SavePositionInDB(uint32 mapid, float x, float y, float z, float o, uint32 zone, ObjectGuid guid)
  17433. {
  17434. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_POSITION);
  17435. stmt->setFloat(0, x);
  17436. stmt->setFloat(1, y);
  17437. stmt->setFloat(2, z);
  17438. stmt->setFloat(3, o);
  17439. stmt->setUInt16(4, uint16(mapid));
  17440. stmt->setUInt16(5, uint16(zone));
  17441. stmt->setUInt32(6, guid.GetCounter());
  17442. CharacterDatabase.Execute(stmt);
  17443. }
  17444. void Player::SetUInt32ValueInArray(Tokenizer& Tokenizer, uint16 index, uint32 value)
  17445. {
  17446. char buf[11];
  17447. snprintf(buf, 11, "%u", value);
  17448. if (index >= Tokenizer.size())
  17449. return;
  17450. Tokenizer[index] = buf;
  17451. }
  17452. void Player::Customize(ObjectGuid guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair)
  17453. {
  17454. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PLAYERBYTES2);
  17455. stmt->setUInt32(0, guid.GetCounter());
  17456. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  17457. if (!result)
  17458. return;
  17459. Field* fields = result->Fetch();
  17460. uint32 playerBytes2 = fields[0].GetUInt32();
  17461. playerBytes2 &= ~0xFF;
  17462. playerBytes2 |= facialHair;
  17463. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GENDER_PLAYERBYTES);
  17464. stmt->setUInt8(0, gender);
  17465. stmt->setUInt32(1, skin | (face << 8) | (hairStyle << 16) | (hairColor << 24));
  17466. stmt->setUInt32(2, playerBytes2);
  17467. stmt->setUInt32(3, guid.GetCounter());
  17468. CharacterDatabase.Execute(stmt);
  17469. }
  17470. void Player::SendAttackSwingDeadTarget()
  17471. {
  17472. WorldPacket data(SMSG_ATTACKSWING_DEADTARGET, 0);
  17473. GetSession()->SendPacket(&data);
  17474. }
  17475. void Player::SendAttackSwingCantAttack()
  17476. {
  17477. WorldPacket data(SMSG_ATTACKSWING_CANT_ATTACK, 0);
  17478. GetSession()->SendPacket(&data);
  17479. }
  17480. void Player::SendAttackSwingCancelAttack()
  17481. {
  17482. WorldPacket data(SMSG_CANCEL_COMBAT, 0);
  17483. GetSession()->SendPacket(&data);
  17484. }
  17485. void Player::SendAttackSwingBadFacingAttack()
  17486. {
  17487. WorldPacket data(SMSG_ATTACKSWING_BADFACING, 0);
  17488. GetSession()->SendPacket(&data);
  17489. }
  17490. void Player::SendAutoRepeatCancel(Unit* target)
  17491. {
  17492. WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, target->GetPackGUID().size());
  17493. data << target->GetPackGUID(); // may be it's target guid
  17494. GetSession()->SendPacket(&data);
  17495. }
  17496. void Player::SendExplorationExperience(uint32 Area, uint32 Experience)
  17497. {
  17498. WorldPacket data(SMSG_EXPLORATION_EXPERIENCE, 8);
  17499. data << uint32(Area);
  17500. data << uint32(Experience);
  17501. GetSession()->SendPacket(&data);
  17502. }
  17503. void Player::SendDungeonDifficulty(bool IsInGroup)
  17504. {
  17505. uint8 val = 0x00000001;
  17506. WorldPacket data(MSG_SET_DUNGEON_DIFFICULTY, 12);
  17507. data << (uint32)GetDungeonDifficulty();
  17508. data << uint32(val);
  17509. data << uint32(IsInGroup);
  17510. GetSession()->SendPacket(&data);
  17511. }
  17512. void Player::SendRaidDifficulty(bool IsInGroup, int32 forcedDifficulty)
  17513. {
  17514. uint8 val = 0x00000001;
  17515. WorldPacket data(MSG_SET_RAID_DIFFICULTY, 12);
  17516. data << uint32(forcedDifficulty == -1 ? GetRaidDifficulty() : forcedDifficulty);
  17517. data << uint32(val);
  17518. data << uint32(IsInGroup);
  17519. GetSession()->SendPacket(&data);
  17520. }
  17521. void Player::SendResetFailedNotify(uint32 /*mapid*/)
  17522. {
  17523. WorldPacket data(SMSG_RESET_FAILED_NOTIFY, 4);
  17524. GetSession()->SendPacket(&data);
  17525. }
  17526. /// Reset all solo instances and optionally send a message on success for each
  17527. void Player::ResetInstances(uint8 method, bool isRaid)
  17528. {
  17529. // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN
  17530. // we assume that when the difficulty changes, all instances that can be reset will be
  17531. Difficulty diff = GetDifficulty(isRaid);
  17532. for (BoundInstancesMap::iterator itr = m_boundInstances[diff].begin(); itr != m_boundInstances[diff].end();)
  17533. {
  17534. InstanceSave* p = itr->second.save;
  17535. const MapEntry* entry = sMapStore.LookupEntry(itr->first);
  17536. if (!entry || entry->IsRaid() != isRaid || !p->CanReset())
  17537. {
  17538. ++itr;
  17539. continue;
  17540. }
  17541. if (method == INSTANCE_RESET_ALL)
  17542. {
  17543. // the "reset all instances" method can only reset normal maps
  17544. if (entry->map_type == MAP_RAID || diff == DUNGEON_DIFFICULTY_HEROIC)
  17545. {
  17546. ++itr;
  17547. continue;
  17548. }
  17549. }
  17550. // if the map is loaded, reset it
  17551. Map* map = sMapMgr->FindMap(p->GetMapId(), p->GetInstanceId());
  17552. if (map && map->IsDungeon())
  17553. if (!((InstanceMap*)map)->Reset(method))
  17554. {
  17555. ++itr;
  17556. continue;
  17557. }
  17558. // since this is a solo instance there should not be any players inside
  17559. if (method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
  17560. SendResetInstanceSuccess(p->GetMapId());
  17561. p->DeleteFromDB();
  17562. m_boundInstances[diff].erase(itr++);
  17563. // the following should remove the instance save from the manager and delete it as well
  17564. p->RemovePlayer(this);
  17565. }
  17566. }
  17567. void Player::SendResetInstanceSuccess(uint32 MapId)
  17568. {
  17569. WorldPacket data(SMSG_INSTANCE_RESET, 4);
  17570. data << uint32(MapId);
  17571. GetSession()->SendPacket(&data);
  17572. }
  17573. void Player::SendResetInstanceFailed(uint32 reason, uint32 MapId)
  17574. {
  17575. /*reasons for instance reset failure:
  17576. // 0: There are players inside the instance.
  17577. // 1: There are players offline in your party.
  17578. // 2>: There are players in your party attempting to zone into an instance.
  17579. */
  17580. WorldPacket data(SMSG_INSTANCE_RESET_FAILED, 8);
  17581. data << uint32(reason);
  17582. data << uint32(MapId);
  17583. GetSession()->SendPacket(&data);
  17584. }
  17585. /*********************************************************/
  17586. /*** Update timers ***/
  17587. /*********************************************************/
  17588. ///checks the 15 afk reports per 5 minutes limit
  17589. void Player::UpdateAfkReport(time_t currTime)
  17590. {
  17591. if (m_bgData.bgAfkReportedTimer <= currTime)
  17592. {
  17593. m_bgData.bgAfkReportedCount = 0;
  17594. m_bgData.bgAfkReportedTimer = currTime+5*MINUTE;
  17595. }
  17596. }
  17597. void Player::UpdateContestedPvP(uint32 diff)
  17598. {
  17599. if (!m_contestedPvPTimer||IsInCombat())
  17600. return;
  17601. if (m_contestedPvPTimer <= diff)
  17602. {
  17603. ResetContestedPvP();
  17604. }
  17605. else
  17606. m_contestedPvPTimer -= diff;
  17607. }
  17608. void Player::ResetContestedPvP()
  17609. {
  17610. ClearUnitState(UNIT_STATE_ATTACK_PLAYER);
  17611. RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
  17612. m_contestedPvPTimer = 0;
  17613. }
  17614. void Player::UpdatePvPFlag(time_t currTime)
  17615. {
  17616. if (!IsPvP())
  17617. return;
  17618. if (!pvpInfo.EndTimer || currTime < (pvpInfo.EndTimer + 300) || pvpInfo.IsHostile)
  17619. return;
  17620. UpdatePvP(false);
  17621. }
  17622. void Player::UpdateDuelFlag(time_t currTime)
  17623. {
  17624. if (!duel || duel->startTimer == 0 ||currTime < duel->startTimer + 3)
  17625. return;
  17626. sScriptMgr->OnPlayerDuelStart(this, duel->opponent);
  17627. SetUInt32Value(PLAYER_DUEL_TEAM, 1);
  17628. duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 2);
  17629. duel->startTimer = 0;
  17630. duel->startTime = currTime;
  17631. duel->opponent->duel->startTimer = 0;
  17632. duel->opponent->duel->startTime = currTime;
  17633. }
  17634. Pet* Player::GetPet() const
  17635. {
  17636. if (ObjectGuid pet_guid = GetPetGUID())
  17637. {
  17638. if (!pet_guid.IsPet())
  17639. return NULL;
  17640. Pet* pet = ObjectAccessor::GetPet(*this, pet_guid);
  17641. if (!pet)
  17642. return NULL;
  17643. if (IsInWorld() && pet)
  17644. return pet;
  17645. //there may be a guardian in slot
  17646. //TC_LOG_ERROR("entities.player", "Player::GetPet: Pet %u not exist.", GUID_LOPART(pet_guid));
  17647. //const_cast<Player*>(this)->SetPetGUID(0);
  17648. }
  17649. return NULL;
  17650. }
  17651. void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
  17652. {
  17653. if (!pet)
  17654. pet = GetPet();
  17655. if (pet)
  17656. {
  17657. TC_LOG_DEBUG("entities.pet", "RemovePet %u, %u, %u", pet->GetEntry(), mode, returnreagent);
  17658. if (pet->m_removed)
  17659. return;
  17660. }
  17661. if (returnreagent && (pet || m_temporaryUnsummonedPetNumber) && !InBattleground())
  17662. {
  17663. //returning of reagents only for players, so best done here
  17664. uint32 spellId = pet ? pet->GetUInt32Value(UNIT_CREATED_BY_SPELL) : m_oldpetspell;
  17665. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  17666. if (spellInfo)
  17667. {
  17668. for (uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i)
  17669. {
  17670. if (spellInfo->Reagent[i] > 0)
  17671. {
  17672. ItemPosCountVec dest; //for succubus, voidwalker, felhunter and felguard credit soulshard when despawn reason other than death (out of range, logout)
  17673. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, spellInfo->Reagent[i], spellInfo->ReagentCount[i]);
  17674. if (msg == EQUIP_ERR_OK)
  17675. {
  17676. Item* item = StoreNewItem(dest, spellInfo->Reagent[i], true);
  17677. if (IsInWorld())
  17678. SendNewItem(item, spellInfo->ReagentCount[i], true, false);
  17679. }
  17680. }
  17681. }
  17682. }
  17683. m_temporaryUnsummonedPetNumber = 0;
  17684. }
  17685. if (!pet || pet->GetOwnerGUID() != GetGUID())
  17686. return;
  17687. pet->CombatStop();
  17688. if (returnreagent)
  17689. {
  17690. switch (pet->GetEntry())
  17691. {
  17692. //warlock pets except imp are removed(?) when logging out
  17693. case 1860:
  17694. case 1863:
  17695. case 417:
  17696. case 17252:
  17697. mode = PET_SAVE_NOT_IN_SLOT;
  17698. break;
  17699. }
  17700. }
  17701. // only if current pet in slot
  17702. pet->SavePetToDB(mode);
  17703. SetMinion(pet, false);
  17704. pet->AddObjectToRemoveList();
  17705. pet->m_removed = true;
  17706. if (pet->isControlled())
  17707. {
  17708. WorldPacket data(SMSG_PET_SPELLS, 8);
  17709. data << uint64(0);
  17710. GetSession()->SendPacket(&data);
  17711. if (GetGroup())
  17712. SetGroupUpdateFlag(GROUP_UPDATE_PET);
  17713. }
  17714. }
  17715. void Player::StopCastingCharm()
  17716. {
  17717. Unit* charm = GetCharm();
  17718. if (!charm)
  17719. return;
  17720. if (charm->GetTypeId() == TYPEID_UNIT)
  17721. {
  17722. if (charm->ToCreature()->HasUnitTypeMask(UNIT_MASK_PUPPET))
  17723. ((Puppet*)charm)->UnSummon();
  17724. else if (charm->IsVehicle())
  17725. ExitVehicle();
  17726. }
  17727. if (GetCharmGUID())
  17728. charm->RemoveCharmAuras();
  17729. if (GetCharmGUID())
  17730. {
  17731. TC_LOG_FATAL("entities.player", "Player %s (%s) is not able to uncharm unit (%s)", GetName().c_str(), GetGUID().ToString().c_str(), GetCharmGUID().ToString().c_str());
  17732. if (charm->GetCharmerGUID())
  17733. {
  17734. TC_LOG_FATAL("entities.player", "Charmed unit has charmer %s", charm->GetCharmerGUID().ToString().c_str());
  17735. ASSERT(false);
  17736. }
  17737. else
  17738. SetCharm(charm, false);
  17739. }
  17740. }
  17741. void Player::Say(std::string const& text, Language language, WorldObject const* /*= nullptr*/)
  17742. {
  17743. std::string _text(text);
  17744. sScriptMgr->OnPlayerChat(this, CHAT_MSG_SAY, language, _text);
  17745. WorldPacket data;
  17746. ChatHandler::BuildChatPacket(data, CHAT_MSG_SAY, language, this, this, _text);
  17747. SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true);
  17748. }
  17749. void Player::Yell(std::string const& text, Language language, WorldObject const* /*= nullptr*/)
  17750. {
  17751. std::string _text(text);
  17752. sScriptMgr->OnPlayerChat(this, CHAT_MSG_YELL, language, _text);
  17753. WorldPacket data;
  17754. ChatHandler::BuildChatPacket(data, CHAT_MSG_YELL, language, this, this, _text);
  17755. SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true);
  17756. }
  17757. void Player::TextEmote(std::string const& text, WorldObject const* /*= nullptr*/, bool /*= false*/)
  17758. {
  17759. std::string _text(text);
  17760. sScriptMgr->OnPlayerChat(this, CHAT_MSG_EMOTE, LANG_UNIVERSAL, _text);
  17761. WorldPacket data;
  17762. ChatHandler::BuildChatPacket(data, CHAT_MSG_EMOTE, LANG_UNIVERSAL, this, this, _text);
  17763. SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, !GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHAT));
  17764. }
  17765. void Player::WhisperAddon(std::string const& text, const std::string& prefix, Player* receiver)
  17766. {
  17767. std::string _text(text);
  17768. sScriptMgr->OnPlayerChat(this, CHAT_MSG_WHISPER, uint32(LANG_ADDON), _text, receiver);
  17769. if (!receiver->GetSession()->IsAddonRegistered(prefix))
  17770. return;
  17771. WorldPacket data;
  17772. ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, LANG_ADDON, this, this, text, 0, "", DEFAULT_LOCALE, prefix);
  17773. receiver->GetSession()->SendPacket(&data);
  17774. }
  17775. void Player::Whisper(std::string const& text, Language language, Player* target, bool /*= false*/)
  17776. {
  17777. ASSERT(target);
  17778. bool isAddonMessage = language == LANG_ADDON;
  17779. if (!isAddonMessage) // if not addon data
  17780. language = LANG_UNIVERSAL; // whispers should always be readable
  17781. std::string _text(text);
  17782. sScriptMgr->OnPlayerChat(this, CHAT_MSG_WHISPER, language, _text, target);
  17783. WorldPacket data;
  17784. ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, Language(language), this, this, _text);
  17785. target->GetSession()->SendPacket(&data);
  17786. // rest stuff shouldn't happen in case of addon message
  17787. if (isAddonMessage)
  17788. return;
  17789. ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER_INFORM, Language(language), target, target, _text);
  17790. GetSession()->SendPacket(&data);
  17791. if (!isAcceptWhispers() && !IsGameMaster() && !target->IsGameMaster())
  17792. {
  17793. SetAcceptWhispers(true);
  17794. ChatHandler(GetSession()).SendSysMessage(LANG_COMMAND_WHISPERON);
  17795. }
  17796. // announce afk or dnd message
  17797. if (target->isAFK())
  17798. ChatHandler(GetSession()).PSendSysMessage(LANG_PLAYER_AFK, target->GetName().c_str(), target->autoReplyMsg.c_str());
  17799. else if (target->isDND())
  17800. ChatHandler(GetSession()).PSendSysMessage(LANG_PLAYER_DND, target->GetName().c_str(), target->autoReplyMsg.c_str());
  17801. }
  17802. Item* Player::GetMItem(uint32 id)
  17803. {
  17804. ItemMap::const_iterator itr = mMitems.find(id);
  17805. return itr != mMitems.end() ? itr->second : NULL;
  17806. }
  17807. void Player::AddMItem(Item* it)
  17808. {
  17809. ASSERT(it);
  17810. //ASSERT deleted, because items can be added before loading
  17811. mMitems[it->GetGUIDLow()] = it;
  17812. }
  17813. bool Player::RemoveMItem(uint32 id)
  17814. {
  17815. return mMitems.erase(id) ? true : false;
  17816. }
  17817. void Player::SendOnCancelExpectedVehicleRideAura()
  17818. {
  17819. WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0);
  17820. GetSession()->SendPacket(&data);
  17821. }
  17822. void Player::PetSpellInitialize()
  17823. {
  17824. Pet* pet = GetPet();
  17825. if (!pet)
  17826. return;
  17827. TC_LOG_DEBUG("entities.pet", "Pet Spells Groups");
  17828. CharmInfo* charmInfo = pet->GetCharmInfo();
  17829. WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1);
  17830. data << uint64(pet->GetGUID());
  17831. data << uint16(pet->GetCreatureTemplate()->family); // creature family (required for pet talents)
  17832. data << uint32(pet->GetDuration());
  17833. data << uint8(pet->GetReactState());
  17834. data << uint8(charmInfo->GetCommandState());
  17835. data << uint16(0); // Flags, mostly unknown
  17836. // action bar loop
  17837. charmInfo->BuildActionBar(&data);
  17838. size_t spellsCountPos = data.wpos();
  17839. // spells count
  17840. uint8 addlist = 0;
  17841. data << uint8(addlist); // placeholder
  17842. if (pet->IsPermanentPetFor(this))
  17843. {
  17844. // spells loop
  17845. for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
  17846. {
  17847. if (itr->second.state == PETSPELL_REMOVED)
  17848. continue;
  17849. data << uint32(MAKE_UNIT_ACTION_BUTTON(itr->first, itr->second.active));
  17850. ++addlist;
  17851. }
  17852. }
  17853. data.put<uint8>(spellsCountPos, addlist);
  17854. uint8 cooldownsCount = pet->m_CreatureSpellCooldowns.size() + pet->m_CreatureCategoryCooldowns.size();
  17855. data << uint8(cooldownsCount);
  17856. time_t curTime = time(NULL);
  17857. for (CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureSpellCooldowns.begin(); itr != pet->m_CreatureSpellCooldowns.end(); ++itr)
  17858. {
  17859. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
  17860. if (!spellInfo)
  17861. {
  17862. data << uint32(0);
  17863. data << uint16(0);
  17864. data << uint32(0);
  17865. data << uint32(0);
  17866. continue;
  17867. }
  17868. time_t cooldown = (itr->second > curTime) ? (itr->second - curTime) * IN_MILLISECONDS : 0;
  17869. data << uint32(itr->first); // spell ID
  17870. CreatureSpellCooldowns::const_iterator categoryitr = pet->m_CreatureCategoryCooldowns.find(spellInfo->GetCategory());
  17871. if (categoryitr != pet->m_CreatureCategoryCooldowns.end())
  17872. {
  17873. time_t categoryCooldown = (categoryitr->second > curTime) ? (categoryitr->second - curTime) * IN_MILLISECONDS : 0;
  17874. data << uint16(spellInfo->GetCategory()); // spell category
  17875. data << uint32(cooldown); // spell cooldown
  17876. data << uint32(categoryCooldown); // category cooldown
  17877. }
  17878. else
  17879. {
  17880. data << uint16(0);
  17881. data << uint32(cooldown);
  17882. data << uint32(0);
  17883. }
  17884. }
  17885. GetSession()->SendPacket(&data);
  17886. }
  17887. void Player::PossessSpellInitialize()
  17888. {
  17889. Unit* charm = GetCharm();
  17890. if (!charm)
  17891. return;
  17892. CharmInfo* charmInfo = charm->GetCharmInfo();
  17893. if (!charmInfo)
  17894. {
  17895. TC_LOG_ERROR("entities.player", "Player::PossessSpellInitialize(): charm (%s) has no charminfo!", charm->GetGUID().ToString().c_str());
  17896. return;
  17897. }
  17898. WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1);
  17899. data << uint64(charm->GetGUID());
  17900. data << uint16(0);
  17901. data << uint32(0);
  17902. data << uint32(0);
  17903. charmInfo->BuildActionBar(&data);
  17904. data << uint8(0); // spells count
  17905. data << uint8(0); // cooldowns count
  17906. GetSession()->SendPacket(&data);
  17907. }
  17908. void Player::VehicleSpellInitialize()
  17909. {
  17910. Creature* vehicle = GetVehicleCreatureBase();
  17911. if (!vehicle)
  17912. return;
  17913. uint8 cooldownCount = vehicle->m_CreatureSpellCooldowns.size();
  17914. WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * 10 + 1 + 1 + cooldownCount * (4 + 2 + 4 + 4));
  17915. data << uint64(vehicle->GetGUID()); // Guid
  17916. data << uint16(0); // Pet Family (0 for all vehicles)
  17917. data << uint32(vehicle->IsSummon() ? vehicle->ToTempSummon()->GetTimer() : 0); // Duration
  17918. // The following three segments are read by the client as one uint32
  17919. data << uint8(vehicle->GetReactState()); // React State
  17920. data << uint8(0); // Command State
  17921. data << uint16(0x800); // DisableActions (set for all vehicles)
  17922. for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
  17923. {
  17924. uint32 spellId = vehicle->m_spells[i];
  17925. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  17926. if (!spellInfo)
  17927. {
  17928. data << uint16(0) << uint8(0) << uint8(i+8);
  17929. continue;
  17930. }
  17931. ConditionList conditions = sConditionMgr->GetConditionsForVehicleSpell(vehicle->GetEntry(), spellId);
  17932. if (!sConditionMgr->IsObjectMeetToConditions(this, vehicle, conditions))
  17933. {
  17934. TC_LOG_DEBUG("condition", "VehicleSpellInitialize: conditions not met for Vehicle entry %u spell %u", vehicle->ToCreature()->GetEntry(), spellId);
  17935. data << uint16(0) << uint8(0) << uint8(i+8);
  17936. continue;
  17937. }
  17938. if (spellInfo->IsPassive())
  17939. vehicle->CastSpell(vehicle, spellId, true);
  17940. data << uint32(MAKE_UNIT_ACTION_BUTTON(spellId, i+8));
  17941. }
  17942. for (uint32 i = CREATURE_MAX_SPELLS; i < MAX_SPELL_CONTROL_BAR; ++i)
  17943. data << uint32(0);
  17944. data << uint8(0); // Auras?
  17945. // Cooldowns
  17946. data << uint8(cooldownCount);
  17947. time_t now = sWorld->GetGameTime();
  17948. for (CreatureSpellCooldowns::const_iterator itr = vehicle->m_CreatureSpellCooldowns.begin(); itr != vehicle->m_CreatureSpellCooldowns.end(); ++itr)
  17949. {
  17950. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
  17951. if (!spellInfo)
  17952. {
  17953. data << uint32(0);
  17954. data << uint16(0);
  17955. data << uint32(0);
  17956. data << uint32(0);
  17957. continue;
  17958. }
  17959. time_t cooldown = (itr->second > now) ? (itr->second - now) * IN_MILLISECONDS : 0;
  17960. data << uint32(itr->first); // spell ID
  17961. CreatureSpellCooldowns::const_iterator categoryitr = vehicle->m_CreatureCategoryCooldowns.find(spellInfo->GetCategory());
  17962. if (categoryitr != vehicle->m_CreatureCategoryCooldowns.end())
  17963. {
  17964. time_t categoryCooldown = (categoryitr->second > now) ? (categoryitr->second - now) * IN_MILLISECONDS : 0;
  17965. data << uint16(spellInfo->GetCategory()); // spell category
  17966. data << uint32(cooldown); // spell cooldown
  17967. data << uint32(categoryCooldown); // category cooldown
  17968. }
  17969. else
  17970. {
  17971. data << uint16(0);
  17972. data << uint32(cooldown);
  17973. data << uint32(0);
  17974. }
  17975. }
  17976. GetSession()->SendPacket(&data);
  17977. }
  17978. void Player::CharmSpellInitialize()
  17979. {
  17980. Unit* charm = GetFirstControlled();
  17981. if (!charm)
  17982. return;
  17983. CharmInfo* charmInfo = charm->GetCharmInfo();
  17984. if (!charmInfo)
  17985. {
  17986. TC_LOG_ERROR("entities.player", "Player::CharmSpellInitialize(): the player's charm (%s) has no charminfo!", charm->GetGUID().ToString().c_str());
  17987. return;
  17988. }
  17989. uint8 addlist = 0;
  17990. if (charm->GetTypeId() != TYPEID_PLAYER)
  17991. {
  17992. //CreatureInfo const* cinfo = charm->ToCreature()->GetCreatureTemplate();
  17993. //if (cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK)
  17994. {
  17995. for (uint32 i = 0; i < MAX_SPELL_CHARM; ++i)
  17996. if (charmInfo->GetCharmSpell(i)->GetAction())
  17997. ++addlist;
  17998. }
  17999. }
  18000. WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+4*addlist+1);
  18001. data << uint64(charm->GetGUID());
  18002. data << uint16(0);
  18003. data << uint32(0);
  18004. if (charm->GetTypeId() != TYPEID_PLAYER)
  18005. data << uint8(charm->ToCreature()->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0);
  18006. else
  18007. data << uint32(0);
  18008. charmInfo->BuildActionBar(&data);
  18009. data << uint8(addlist);
  18010. if (addlist)
  18011. {
  18012. for (uint32 i = 0; i < MAX_SPELL_CHARM; ++i)
  18013. {
  18014. CharmSpellInfo* cspell = charmInfo->GetCharmSpell(i);
  18015. if (cspell->GetAction())
  18016. data << uint32(cspell->packedData);
  18017. }
  18018. }
  18019. data << uint8(0); // cooldowns count
  18020. GetSession()->SendPacket(&data);
  18021. }
  18022. void Player::SendRemoveControlBar()
  18023. {
  18024. WorldPacket data(SMSG_PET_SPELLS, 8);
  18025. data << uint64(0);
  18026. GetSession()->SendPacket(&data);
  18027. }
  18028. bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell)
  18029. {
  18030. if (!mod || !spellInfo)
  18031. return false;
  18032. // Mod out of charges
  18033. if (spell && mod->charges == -1 && spell->m_appliedMods.find(mod->ownerAura) == spell->m_appliedMods.end())
  18034. return false;
  18035. // +duration to infinite duration spells making them limited
  18036. if (mod->op == SPELLMOD_DURATION && spellInfo->GetDuration() == -1)
  18037. return false;
  18038. return spellInfo->IsAffectedBySpellMod(mod);
  18039. }
  18040. void Player::AddSpellMod(SpellModifier* mod, bool apply)
  18041. {
  18042. TC_LOG_DEBUG("spells", "Player::AddSpellMod %d", mod->spellId);
  18043. Opcodes opcode = Opcodes((mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER);
  18044. int i = 0;
  18045. flag96 _mask = 0;
  18046. uint32 modTypeCount = 0; // count of mods per one mod->op
  18047. WorldPacket data(opcode);
  18048. data << uint32(1); // count of different mod->op's in packet
  18049. size_t writePos = data.wpos();
  18050. data << uint32(modTypeCount);
  18051. data << uint8(mod->op);
  18052. for (int eff = 0; eff < 96; ++eff)
  18053. {
  18054. if (eff != 0 && (eff % 32) == 0)
  18055. _mask[i++] = 0;
  18056. _mask[i] = uint32(1) << (eff - (32 * i));
  18057. if (mod->mask & _mask)
  18058. {
  18059. int32 val = 0;
  18060. for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr)
  18061. if ((*itr)->type == mod->type && (*itr)->mask & _mask)
  18062. val += (*itr)->value;
  18063. val += apply ? mod->value : -(mod->value);
  18064. data << uint8(eff);
  18065. data << float(val);
  18066. ++modTypeCount;
  18067. }
  18068. }
  18069. data.put<uint32>(writePos, modTypeCount);
  18070. SendDirectMessage(&data);
  18071. if (apply)
  18072. m_spellMods[mod->op].push_back(mod);
  18073. else
  18074. {
  18075. m_spellMods[mod->op].remove(mod);
  18076. // mods bound to aura will be removed in AuraEffect::~AuraEffect
  18077. if (!mod->ownerAura)
  18078. delete mod;
  18079. }
  18080. }
  18081. // Restore spellmods in case of failed cast
  18082. void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId, Aura* aura)
  18083. {
  18084. if (!spell || spell->m_appliedMods.empty())
  18085. return;
  18086. std::list<Aura*> aurasQueue;
  18087. for (uint8 i=0; i<MAX_SPELLMOD; ++i)
  18088. {
  18089. for (SpellModList::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end(); ++itr)
  18090. {
  18091. SpellModifier* mod = *itr;
  18092. // Spellmods without aura set cannot be charged
  18093. if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges())
  18094. continue;
  18095. // Restore only specific owner aura mods
  18096. if (ownerAuraId && (ownerAuraId != mod->ownerAura->GetSpellInfo()->Id))
  18097. continue;
  18098. if (aura && mod->ownerAura != aura)
  18099. continue;
  18100. // Check if mod affected this spell
  18101. // First, check if the mod aura applied at least one spellmod to this spell
  18102. Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(mod->ownerAura);
  18103. if (iterMod == spell->m_appliedMods.end())
  18104. continue;
  18105. // Second, check if the current mod is one of those applied by the mod aura
  18106. if (!(mod->mask & spell->m_spellInfo->SpellFamilyFlags))
  18107. continue;
  18108. // remove from list - This will be done after all mods have been gone through
  18109. // to ensure we iterate over all mods of an aura before removing said aura
  18110. // from applied mods (Else, an aura with two mods on the current spell would
  18111. // only see the first of its modifier restored)
  18112. aurasQueue.push_back(mod->ownerAura);
  18113. // add mod charges back to mod
  18114. if (mod->charges == -1)
  18115. mod->charges = 1;
  18116. else
  18117. mod->charges++;
  18118. // Do not set more spellmods than available
  18119. if (mod->ownerAura->GetCharges() < mod->charges)
  18120. mod->charges = mod->ownerAura->GetCharges();
  18121. // Skip this check for now - aura charges may change due to various reason
  18122. /// @todo track these changes correctly
  18123. //ASSERT (mod->ownerAura->GetCharges() <= mod->charges);
  18124. }
  18125. }
  18126. for (std::list<Aura*>::iterator itr = aurasQueue.begin(); itr != aurasQueue.end(); ++itr)
  18127. {
  18128. Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(*itr);
  18129. if (iterMod != spell->m_appliedMods.end())
  18130. spell->m_appliedMods.erase(iterMod);
  18131. }
  18132. }
  18133. void Player::RestoreAllSpellMods(uint32 ownerAuraId, Aura* aura)
  18134. {
  18135. for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i)
  18136. if (m_currentSpells[i])
  18137. RestoreSpellMods(m_currentSpells[i], ownerAuraId, aura);
  18138. }
  18139. void Player::RemoveSpellMods(Spell* spell)
  18140. {
  18141. if (!spell)
  18142. return;
  18143. if (spell->m_appliedMods.empty())
  18144. return;
  18145. for (uint8 i=0; i<MAX_SPELLMOD; ++i)
  18146. {
  18147. for (SpellModList::const_iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();)
  18148. {
  18149. SpellModifier* mod = *itr;
  18150. ++itr;
  18151. // spellmods without aura set cannot be charged
  18152. if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges())
  18153. continue;
  18154. // check if mod affected this spell
  18155. Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(mod->ownerAura);
  18156. if (iterMod == spell->m_appliedMods.end())
  18157. continue;
  18158. // remove from list
  18159. spell->m_appliedMods.erase(iterMod);
  18160. if (mod->ownerAura->DropCharge(AURA_REMOVE_BY_EXPIRE))
  18161. itr = m_spellMods[i].begin();
  18162. }
  18163. }
  18164. }
  18165. void Player::DropModCharge(SpellModifier* mod, Spell* spell)
  18166. {
  18167. // don't handle spells with proc_event entry defined
  18168. // this is a temporary workaround, because all spellmods should be handled like that
  18169. if (sSpellMgr->GetSpellProcEvent(mod->spellId))
  18170. return;
  18171. if (spell && mod->ownerAura && mod->charges > 0)
  18172. {
  18173. if (--mod->charges == 0)
  18174. mod->charges = -1;
  18175. spell->m_appliedMods.insert(mod->ownerAura);
  18176. }
  18177. }
  18178. void Player::SetSpellModTakingSpell(Spell* spell, bool apply)
  18179. {
  18180. if (!spell || (m_spellModTakingSpell && m_spellModTakingSpell != spell))
  18181. return;
  18182. if (apply && spell->getState() == SPELL_STATE_FINISHED)
  18183. return;
  18184. m_spellModTakingSpell = apply ? spell : NULL;
  18185. }
  18186. // send Proficiency
  18187. void Player::SendProficiency(ItemClass itemClass, uint32 itemSubclassMask)
  18188. {
  18189. WorldPacket data(SMSG_SET_PROFICIENCY, 1 + 4);
  18190. data << uint8(itemClass) << uint32(itemSubclassMask);
  18191. GetSession()->SendPacket(&data);
  18192. }
  18193. void Player::RemovePetitionsAndSigns(ObjectGuid guid, uint32 type)
  18194. {
  18195. PreparedStatement* stmt;
  18196. if (type == 10)
  18197. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PETITION_SIG_BY_GUID);
  18198. else
  18199. {
  18200. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PETITION_SIG_BY_GUID_TYPE);
  18201. stmt->setUInt8(1, uint8(type));
  18202. }
  18203. stmt->setUInt32(0, guid.GetCounter());
  18204. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  18205. if (result)
  18206. {
  18207. 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.
  18208. { // and SendPetitionQueryOpcode reads data from the DB
  18209. Field* fields = result->Fetch();
  18210. ObjectGuid ownerguid = ObjectGuid(HIGHGUID_PLAYER, fields[0].GetUInt32());
  18211. ObjectGuid petitionguid = ObjectGuid(HIGHGUID_ITEM, fields[1].GetUInt32());
  18212. // send update if charter owner in game
  18213. Player* owner = ObjectAccessor::FindConnectedPlayer(ownerguid);
  18214. if (owner)
  18215. owner->GetSession()->SendPetitionQueryOpcode(petitionguid);
  18216. } while (result->NextRow());
  18217. if (type == 10)
  18218. {
  18219. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_PETITION_SIGNATURES);
  18220. stmt->setUInt32(0, guid.GetCounter());
  18221. CharacterDatabase.Execute(stmt);
  18222. }
  18223. else
  18224. {
  18225. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE);
  18226. stmt->setUInt32(0, guid.GetCounter());
  18227. stmt->setUInt8(1, uint8(type));
  18228. CharacterDatabase.Execute(stmt);
  18229. }
  18230. }
  18231. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  18232. if (type == 10)
  18233. {
  18234. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER);
  18235. stmt->setUInt32(0, guid.GetCounter());
  18236. trans->Append(stmt);
  18237. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER);
  18238. stmt->setUInt32(0, guid.GetCounter());
  18239. trans->Append(stmt);
  18240. }
  18241. else
  18242. {
  18243. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER_AND_TYPE);
  18244. stmt->setUInt32(0, guid.GetCounter());
  18245. stmt->setUInt8(1, uint8(type));
  18246. trans->Append(stmt);
  18247. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER_AND_TYPE);
  18248. stmt->setUInt32(0, guid.GetCounter());
  18249. stmt->setUInt8(1, uint8(type));
  18250. trans->Append(stmt);
  18251. }
  18252. CharacterDatabase.CommitTransaction(trans);
  18253. }
  18254. void Player::LeaveAllArenaTeams(ObjectGuid guid)
  18255. {
  18256. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYER_ARENA_TEAMS);
  18257. stmt->setUInt32(0, guid.GetCounter());
  18258. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  18259. if (!result)
  18260. return;
  18261. do
  18262. {
  18263. Field* fields = result->Fetch();
  18264. uint32 arenaTeamId = fields[0].GetUInt32();
  18265. if (arenaTeamId != 0)
  18266. {
  18267. ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
  18268. if (arenaTeam)
  18269. arenaTeam->DelMember(guid, true);
  18270. }
  18271. }
  18272. while (result->NextRow());
  18273. }
  18274. void Player::SetRestBonus(float rest_bonus_new)
  18275. {
  18276. // Prevent resting on max level
  18277. if (getLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  18278. rest_bonus_new = 0;
  18279. if (rest_bonus_new < 0)
  18280. rest_bonus_new = 0;
  18281. float rest_bonus_max = (float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)*1.5f/2;
  18282. if (rest_bonus_new > rest_bonus_max)
  18283. m_rest_bonus = rest_bonus_max;
  18284. else
  18285. m_rest_bonus = rest_bonus_new;
  18286. // update data for client
  18287. if (GetSession()->IsARecruiter() || (GetSession()->GetRecruiterId() != 0))
  18288. SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RAF_LINKED);
  18289. else if (m_rest_bonus > 10)
  18290. SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RESTED); // Set Reststate = Rested
  18291. else if (m_rest_bonus <= 1)
  18292. SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_NOT_RAF_LINKED); // Set Reststate = Normal
  18293. //RestTickUpdate
  18294. SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus));
  18295. }
  18296. bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc /*= NULL*/, uint32 spellid /*= 0*/)
  18297. {
  18298. if (nodes.size() < 2)
  18299. return false;
  18300. // not let cheating with start flight in time of logout process || while in combat || has type state: stunned || has type state: root
  18301. if (GetSession()->isLogingOut() || IsInCombat() || HasUnitState(UNIT_STATE_STUNNED) || HasUnitState(UNIT_STATE_ROOT))
  18302. {
  18303. GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERBUSY);
  18304. return false;
  18305. }
  18306. if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
  18307. return false;
  18308. // taximaster case
  18309. if (npc)
  18310. {
  18311. // not let cheating with start flight mounted
  18312. if (IsMounted())
  18313. {
  18314. GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERALREADYMOUNTED);
  18315. return false;
  18316. }
  18317. if (IsInDisallowedMountForm())
  18318. {
  18319. GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERSHAPESHIFTED);
  18320. return false;
  18321. }
  18322. // 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
  18323. if (IsNonMeleeSpellCast(false))
  18324. {
  18325. GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERBUSY);
  18326. return false;
  18327. }
  18328. }
  18329. // cast case or scripted call case
  18330. else
  18331. {
  18332. RemoveAurasByType(SPELL_AURA_MOUNTED);
  18333. if (IsInDisallowedMountForm())
  18334. RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
  18335. if (Spell* spell = GetCurrentSpell(CURRENT_GENERIC_SPELL))
  18336. if (spell->m_spellInfo->Id != spellid)
  18337. InterruptSpell(CURRENT_GENERIC_SPELL, false);
  18338. InterruptSpell(CURRENT_AUTOREPEAT_SPELL, false);
  18339. if (Spell* spell = GetCurrentSpell(CURRENT_CHANNELED_SPELL))
  18340. if (spell->m_spellInfo->Id != spellid)
  18341. InterruptSpell(CURRENT_CHANNELED_SPELL, true);
  18342. }
  18343. uint32 sourcenode = nodes[0];
  18344. // starting node too far away (cheat?)
  18345. TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(sourcenode);
  18346. if (!node)
  18347. {
  18348. GetSession()->SendActivateTaxiReply(ERR_TAXINOSUCHPATH);
  18349. return false;
  18350. }
  18351. // check node starting pos data set case if provided
  18352. if (node->x != 0.0f || node->y != 0.0f || node->z != 0.0f)
  18353. {
  18354. if (node->map_id != GetMapId() ||
  18355. (node->x - GetPositionX())*(node->x - GetPositionX())+
  18356. (node->y - GetPositionY())*(node->y - GetPositionY())+
  18357. (node->z - GetPositionZ())*(node->z - GetPositionZ()) >
  18358. (2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE))
  18359. {
  18360. GetSession()->SendActivateTaxiReply(ERR_TAXITOOFARAWAY);
  18361. return false;
  18362. }
  18363. }
  18364. // node must have pos if taxi master case (npc != NULL)
  18365. else if (npc)
  18366. {
  18367. GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR);
  18368. return false;
  18369. }
  18370. // Prepare to flight start now
  18371. // stop combat at start taxi flight if any
  18372. CombatStop();
  18373. StopCastingCharm();
  18374. StopCastingBindSight();
  18375. ExitVehicle();
  18376. // stop trade (client cancel trade at taxi map open but cheating tools can be used for reopen it)
  18377. TradeCancel(true);
  18378. // clean not finished taxi path if any
  18379. m_taxi.ClearTaxiDestinations();
  18380. // 0 element current node
  18381. m_taxi.AddTaxiDestination(sourcenode);
  18382. // fill destinations path tail
  18383. uint32 sourcepath = 0;
  18384. uint32 totalcost = 0;
  18385. uint32 prevnode = sourcenode;
  18386. uint32 lastnode = 0;
  18387. for (uint32 i = 1; i < nodes.size(); ++i)
  18388. {
  18389. uint32 path, cost;
  18390. lastnode = nodes[i];
  18391. sObjectMgr->GetTaxiPath(prevnode, lastnode, path, cost);
  18392. if (!path)
  18393. {
  18394. m_taxi.ClearTaxiDestinations();
  18395. return false;
  18396. }
  18397. totalcost += cost;
  18398. if (prevnode == sourcenode)
  18399. sourcepath = path;
  18400. m_taxi.AddTaxiDestination(lastnode);
  18401. prevnode = lastnode;
  18402. }
  18403. // get mount model (in case non taximaster (npc == NULL) allow more wide lookup)
  18404. //
  18405. // Hack-Fix for Alliance not being able to use Acherus taxi. There is
  18406. // only one mount ID for both sides. Probably not good to use 315 in case DBC nodes
  18407. // change but I couldn't find a suitable alternative. OK to use class because only DK
  18408. // can use this taxi.
  18409. uint32 mount_display_id = sObjectMgr->GetTaxiMountDisplayId(sourcenode, GetTeam(), npc == NULL || (sourcenode == 315 && getClass() == CLASS_DEATH_KNIGHT));
  18410. // in spell case allow 0 model
  18411. if ((mount_display_id == 0 && spellid == 0) || sourcepath == 0)
  18412. {
  18413. GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR);
  18414. m_taxi.ClearTaxiDestinations();
  18415. return false;
  18416. }
  18417. uint64 money = GetMoney();
  18418. if (npc)
  18419. totalcost = (uint32)ceil(totalcost*GetReputationPriceDiscount(npc));
  18420. if (money < totalcost)
  18421. {
  18422. GetSession()->SendActivateTaxiReply(ERR_TAXINOTENOUGHMONEY);
  18423. m_taxi.ClearTaxiDestinations();
  18424. return false;
  18425. }
  18426. //Checks and preparations done, DO FLIGHT
  18427. ModifyMoney(-int64(totalcost));
  18428. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost);
  18429. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN, 1);
  18430. // prevent stealth flight
  18431. //RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);
  18432. if (sWorld->getBoolConfig(CONFIG_INSTANT_TAXI))
  18433. {
  18434. TaxiNodesEntry const* lastPathNode = sTaxiNodesStore.LookupEntry(nodes[nodes.size()-1]);
  18435. ASSERT(lastPathNode);
  18436. m_taxi.ClearTaxiDestinations();
  18437. TeleportTo(lastPathNode->map_id, lastPathNode->x, lastPathNode->y, lastPathNode->z, GetOrientation());
  18438. return false;
  18439. }
  18440. else
  18441. {
  18442. GetSession()->SendActivateTaxiReply(ERR_TAXIOK);
  18443. GetSession()->SendDoFlight(mount_display_id, sourcepath);
  18444. }
  18445. return true;
  18446. }
  18447. bool Player::ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid /*= 0*/)
  18448. {
  18449. TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(taxi_path_id);
  18450. if (!entry)
  18451. return false;
  18452. std::vector<uint32> nodes;
  18453. nodes.resize(2);
  18454. nodes[0] = entry->from;
  18455. nodes[1] = entry->to;
  18456. return ActivateTaxiPathTo(nodes, NULL, spellid);
  18457. }
  18458. void Player::CleanupAfterTaxiFlight()
  18459. {
  18460. m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
  18461. Dismount();
  18462. RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
  18463. getHostileRefManager().setOnlineOfflineState(true);
  18464. }
  18465. void Player::ContinueTaxiFlight()
  18466. {
  18467. uint32 sourceNode = m_taxi.GetTaxiSource();
  18468. if (!sourceNode)
  18469. return;
  18470. TC_LOG_DEBUG("entities.unit", "WORLD: Restart character %u taxi flight", GetGUIDLow());
  18471. uint32 mountDisplayId = sObjectMgr->GetTaxiMountDisplayId(sourceNode, GetTeam(), true);
  18472. if (!mountDisplayId)
  18473. return;
  18474. uint32 path = m_taxi.GetCurrentTaxiPath();
  18475. // search appropriate start path node
  18476. uint32 startNode = 0;
  18477. TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path];
  18478. float distPrev = MAP_SIZE*MAP_SIZE;
  18479. float distNext =
  18480. (nodeList[0].x-GetPositionX())*(nodeList[0].x-GetPositionX())+
  18481. (nodeList[0].y-GetPositionY())*(nodeList[0].y-GetPositionY())+
  18482. (nodeList[0].z-GetPositionZ())*(nodeList[0].z-GetPositionZ());
  18483. for (uint32 i = 1; i < nodeList.size(); ++i)
  18484. {
  18485. TaxiPathNodeEntry const& node = nodeList[i];
  18486. TaxiPathNodeEntry const& prevNode = nodeList[i-1];
  18487. // skip nodes at another map
  18488. if (node.mapid != GetMapId())
  18489. continue;
  18490. distPrev = distNext;
  18491. distNext =
  18492. (node.x-GetPositionX())*(node.x-GetPositionX())+
  18493. (node.y-GetPositionY())*(node.y-GetPositionY())+
  18494. (node.z-GetPositionZ())*(node.z-GetPositionZ());
  18495. float distNodes =
  18496. (node.x-prevNode.x)*(node.x-prevNode.x)+
  18497. (node.y-prevNode.y)*(node.y-prevNode.y)+
  18498. (node.z-prevNode.z)*(node.z-prevNode.z);
  18499. if (distNext + distPrev < distNodes)
  18500. {
  18501. startNode = i;
  18502. break;
  18503. }
  18504. }
  18505. GetSession()->SendDoFlight(mountDisplayId, path, startNode);
  18506. }
  18507. void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
  18508. {
  18509. PacketCooldowns cooldowns;
  18510. WorldPacket data;
  18511. time_t curTime = time(NULL);
  18512. for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
  18513. {
  18514. if (itr->second->state == PLAYERSPELL_REMOVED)
  18515. continue;
  18516. uint32 unSpellId = itr->first;
  18517. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(unSpellId);
  18518. if (!spellInfo)
  18519. {
  18520. ASSERT(spellInfo);
  18521. continue;
  18522. }
  18523. // Not send cooldown for this spells
  18524. if (spellInfo->IsCooldownStartedOnEvent())
  18525. continue;
  18526. if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE)
  18527. continue;
  18528. if ((idSchoolMask & spellInfo->GetSchoolMask()) && GetSpellCooldownDelay(unSpellId) < unTimeMs)
  18529. {
  18530. cooldowns[unSpellId] = unTimeMs;
  18531. AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/IN_MILLISECONDS);
  18532. }
  18533. }
  18534. if (!cooldowns.empty())
  18535. {
  18536. BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
  18537. GetSession()->SendPacket(&data);
  18538. }
  18539. }
  18540. void Player::InitDataForForm(bool reapplyMods)
  18541. {
  18542. ShapeshiftForm form = GetShapeshiftForm();
  18543. SpellShapeshiftFormEntry const* ssEntry = sSpellShapeshiftFormStore.LookupEntry(form);
  18544. if (ssEntry && ssEntry->attackSpeed)
  18545. {
  18546. SetAttackTime(BASE_ATTACK, ssEntry->attackSpeed);
  18547. SetAttackTime(OFF_ATTACK, ssEntry->attackSpeed);
  18548. SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
  18549. }
  18550. else
  18551. SetRegularAttackTime();
  18552. switch (form)
  18553. {
  18554. case FORM_GHOUL:
  18555. case FORM_CAT:
  18556. {
  18557. if (getPowerType() != POWER_ENERGY)
  18558. setPowerType(POWER_ENERGY);
  18559. break;
  18560. }
  18561. case FORM_BEAR:
  18562. {
  18563. if (getPowerType() != POWER_RAGE)
  18564. setPowerType(POWER_RAGE);
  18565. break;
  18566. }
  18567. default: // 0, for example
  18568. {
  18569. ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(getClass());
  18570. if (cEntry && cEntry->powerType < MAX_POWERS && uint32(getPowerType()) != cEntry->powerType)
  18571. setPowerType(Powers(cEntry->powerType));
  18572. break;
  18573. }
  18574. }
  18575. // update auras at form change, ignore this at mods reapply (.reset stats/etc) when form not change.
  18576. if (!reapplyMods)
  18577. UpdateEquipSpellsAtFormChange();
  18578. UpdateAttackPowerAndDamage();
  18579. UpdateAttackPowerAndDamage(true);
  18580. }
  18581. void Player::InitDisplayIds()
  18582. {
  18583. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  18584. if (!info)
  18585. {
  18586. TC_LOG_ERROR("entities.player", "Player %u has incorrect race/class pair. Can't init display ids.", GetGUIDLow());
  18587. return;
  18588. }
  18589. uint8 gender = getGender();
  18590. switch (gender)
  18591. {
  18592. case GENDER_FEMALE:
  18593. SetDisplayId(info->displayId_f);
  18594. SetNativeDisplayId(info->displayId_f);
  18595. break;
  18596. case GENDER_MALE:
  18597. SetDisplayId(info->displayId_m);
  18598. SetNativeDisplayId(info->displayId_m);
  18599. break;
  18600. default:
  18601. TC_LOG_ERROR("entities.player", "Invalid gender %u for player", gender);
  18602. return;
  18603. }
  18604. }
  18605. 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)
  18606. {
  18607. uint32 stacks = count / pProto->BuyCount;
  18608. ItemPosCountVec vDest;
  18609. uint16 uiDest = 0;
  18610. InventoryResult msg = bStore ?
  18611. CanStoreNewItem(bag, slot, vDest, item, count) :
  18612. CanEquipNewItem(slot, uiDest, item, false);
  18613. if (msg != EQUIP_ERR_OK)
  18614. {
  18615. SendEquipError(msg, NULL, NULL, item);
  18616. return false;
  18617. }
  18618. ModifyMoney(-price);
  18619. if (crItem->ExtendedCost) // case for new honor system
  18620. {
  18621. ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
  18622. ASSERT(iece);
  18623. for (int i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
  18624. {
  18625. if (iece->RequiredItem[i])
  18626. DestroyItemCount(iece->RequiredItem[i], iece->RequiredItemCount[i] * stacks, true);
  18627. }
  18628. for (int i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
  18629. {
  18630. if (iece->RequirementFlags & (ITEM_EXT_COST_CURRENCY_REQ_IS_SEASON_EARNED_1 << i))
  18631. continue;
  18632. if (iece->RequiredCurrency[i])
  18633. ModifyCurrency(iece->RequiredCurrency[i], -int32(iece->RequiredCurrencyCount[i] * stacks), true, true);
  18634. }
  18635. }
  18636. Item* it = bStore ?
  18637. StoreNewItem(vDest, item, true) :
  18638. EquipNewItem(uiDest, item, true);
  18639. if (it)
  18640. {
  18641. uint32 new_count = pVendor->UpdateVendorItemCurrentCount(crItem, count);
  18642. WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
  18643. data << uint64(pVendor->GetGUID());
  18644. data << uint32(vendorslot + 1); // numbered from 1 at client
  18645. data << int32(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
  18646. data << uint32(count);
  18647. GetSession()->SendPacket(&data);
  18648. SendNewItem(it, count, true, false, false);
  18649. if (!bStore)
  18650. AutoUnequipOffhandIfNeed();
  18651. if (pProto->Flags & ITEM_PROTO_FLAG_REFUNDABLE && crItem->ExtendedCost && pProto->GetMaxStackSize() == 1)
  18652. {
  18653. it->SetFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE);
  18654. it->SetRefundRecipient(GetGUIDLow());
  18655. it->SetPaidMoney(price);
  18656. it->SetPaidExtendedCost(crItem->ExtendedCost);
  18657. it->SaveRefundDataToDB();
  18658. AddRefundReference(it->GetGUID());
  18659. }
  18660. }
  18661. return true;
  18662. }
  18663. bool Player::BuyCurrencyFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorSlot, uint32 currency, uint32 count)
  18664. {
  18665. // cheating attempt
  18666. if (count < 1) count = 1;
  18667. if (!IsAlive())
  18668. return false;
  18669. CurrencyTypesEntry const* proto = sCurrencyTypesStore.LookupEntry(currency);
  18670. if (!proto)
  18671. {
  18672. SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, currency, 0);
  18673. return false;
  18674. }
  18675. Creature* creature = GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR);
  18676. if (!creature)
  18677. {
  18678. TC_LOG_DEBUG("network", "WORLD: BuyCurrencyFromVendorSlot - %s not found or you can't interact with him.", vendorGuid.ToString().c_str());
  18679. SendBuyError(BUY_ERR_DISTANCE_TOO_FAR, NULL, currency, 0);
  18680. return false;
  18681. }
  18682. VendorItemData const* vItems = creature->GetVendorItems();
  18683. if (!vItems || vItems->Empty())
  18684. {
  18685. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, currency, 0);
  18686. return false;
  18687. }
  18688. if (vendorSlot >= vItems->GetItemCount())
  18689. {
  18690. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, currency, 0);
  18691. return false;
  18692. }
  18693. VendorItem const* crItem = vItems->GetItem(vendorSlot);
  18694. // store diff item (cheating)
  18695. if (!crItem || crItem->item != currency || crItem->Type != ITEM_VENDOR_TYPE_CURRENCY)
  18696. {
  18697. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, currency, 0);
  18698. return false;
  18699. }
  18700. if (count % crItem->maxcount)
  18701. {
  18702. SendEquipError(EQUIP_ERR_CANT_BUY_QUANTITY, NULL, NULL);
  18703. return false;
  18704. }
  18705. uint32 stacks = count / crItem->maxcount;
  18706. ItemExtendedCostEntry const* iece = NULL;
  18707. if (crItem->ExtendedCost)
  18708. {
  18709. iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
  18710. if (!iece)
  18711. {
  18712. TC_LOG_ERROR("entities.player", "Currency %u have wrong ExtendedCost field value %u", currency, crItem->ExtendedCost);
  18713. return false;
  18714. }
  18715. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i)
  18716. {
  18717. if (iece->RequiredItem[i] && !HasItemCount(iece->RequiredItem[i], (iece->RequiredItemCount[i] * stacks)))
  18718. {
  18719. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
  18720. return false;
  18721. }
  18722. }
  18723. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
  18724. {
  18725. if (!iece->RequiredCurrency[i])
  18726. continue;
  18727. CurrencyTypesEntry const* entry = sCurrencyTypesStore.LookupEntry(iece->RequiredCurrency[i]);
  18728. if (!entry)
  18729. {
  18730. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, currency, 0); // Find correct error
  18731. return false;
  18732. }
  18733. if (iece->RequirementFlags & (ITEM_EXT_COST_CURRENCY_REQ_IS_SEASON_EARNED_1 << i))
  18734. {
  18735. // Not implemented
  18736. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); // Find correct error
  18737. return false;
  18738. }
  18739. else if (!HasCurrency(iece->RequiredCurrency[i], (iece->RequiredCurrencyCount[i] * stacks)))
  18740. {
  18741. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); // Find correct error
  18742. return false;
  18743. }
  18744. }
  18745. // check for personal arena rating requirement
  18746. if (GetMaxPersonalArenaRatingRequirement(iece->RequiredArenaSlot) < iece->RequiredPersonalArenaRating)
  18747. {
  18748. // probably not the proper equip err
  18749. SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK, NULL, NULL);
  18750. return false;
  18751. }
  18752. if (iece->RequiredFactionId && uint32(GetReputationRank(iece->RequiredFactionId)) < iece->RequiredFactionStanding)
  18753. {
  18754. SendBuyError(BUY_ERR_REPUTATION_REQUIRE, creature, currency, 0);
  18755. return false;
  18756. }
  18757. if (iece->RequirementFlags & ITEM_EXT_COST_FLAG_REQUIRE_GUILD && !GetGuildId())
  18758. {
  18759. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); // Find correct error
  18760. return false;
  18761. }
  18762. if (iece->RequiredGuildLevel && iece->RequiredGuildLevel < GetGuildLevel())
  18763. {
  18764. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); // Find correct error
  18765. return false;
  18766. }
  18767. if (iece->RequiredAchievement && !HasAchieved(iece->RequiredAchievement))
  18768. {
  18769. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); // Find correct error
  18770. return false;
  18771. }
  18772. }
  18773. else // currencies have no price defined, can only be bought with ExtendedCost
  18774. {
  18775. SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, currency, 0);
  18776. return false;
  18777. }
  18778. ModifyCurrency(currency, count, true, true);
  18779. if (iece)
  18780. {
  18781. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i)
  18782. {
  18783. if (!iece->RequiredItem[i])
  18784. continue;
  18785. DestroyItemCount(iece->RequiredItem[i], iece->RequiredItemCount[i] * stacks, true);
  18786. }
  18787. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
  18788. {
  18789. if (!iece->RequiredCurrency[i])
  18790. continue;
  18791. if (iece->RequirementFlags & (ITEM_EXT_COST_CURRENCY_REQ_IS_SEASON_EARNED_1 << i))
  18792. continue;
  18793. ModifyCurrency(iece->RequiredCurrency[i], -int32(iece->RequiredCurrencyCount[i]) * stacks, false, true);
  18794. }
  18795. }
  18796. return true;
  18797. }
  18798. // Return true is the bought item has a max count to force refresh of window by caller
  18799. bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot)
  18800. {
  18801. // cheating attempt
  18802. if (count < 1) count = 1;
  18803. // cheating attempt
  18804. if (slot > MAX_BAG_SIZE && slot != NULL_SLOT)
  18805. return false;
  18806. if (!IsAlive())
  18807. return false;
  18808. ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(item);
  18809. if (!pProto)
  18810. {
  18811. SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, item, 0);
  18812. return false;
  18813. }
  18814. if (!(pProto->AllowableClass & getClassMask()) && pProto->Bonding == BIND_WHEN_PICKED_UP && !IsGameMaster())
  18815. {
  18816. SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, item, 0);
  18817. return false;
  18818. }
  18819. if (!IsGameMaster() && ((pProto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY && GetTeam() == ALLIANCE) || (pProto->Flags2 == ITEM_FLAGS_EXTRA_ALLIANCE_ONLY && GetTeam() == HORDE)))
  18820. return false;
  18821. Creature* creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
  18822. if (!creature)
  18823. {
  18824. TC_LOG_DEBUG("network", "WORLD: BuyItemFromVendor - %s not found or you can't interact with him.", vendorguid.ToString().c_str());
  18825. SendBuyError(BUY_ERR_DISTANCE_TOO_FAR, NULL, item, 0);
  18826. return false;
  18827. }
  18828. ConditionList conditions = sConditionMgr->GetConditionsForNpcVendorEvent(creature->GetEntry(), item);
  18829. if (!sConditionMgr->IsObjectMeetToConditions(this, creature, conditions))
  18830. {
  18831. TC_LOG_DEBUG("condition", "BuyItemFromVendor: conditions not met for creature entry %u item %u", creature->GetEntry(), item);
  18832. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
  18833. return false;
  18834. }
  18835. VendorItemData const* vItems = creature->GetVendorItems();
  18836. if (!vItems || vItems->Empty())
  18837. {
  18838. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
  18839. return false;
  18840. }
  18841. if (vendorslot >= vItems->GetItemCount())
  18842. {
  18843. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
  18844. return false;
  18845. }
  18846. VendorItem const* crItem = vItems->GetItem(vendorslot);
  18847. // store diff item (cheating)
  18848. if (!crItem || crItem->item != item)
  18849. {
  18850. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
  18851. return false;
  18852. }
  18853. // check current item amount if it limited
  18854. if (crItem->maxcount != 0)
  18855. {
  18856. if (creature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count)
  18857. {
  18858. SendBuyError(BUY_ERR_ITEM_ALREADY_SOLD, creature, item, 0);
  18859. return false;
  18860. }
  18861. }
  18862. if (pProto->RequiredReputationFaction && (uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank))
  18863. {
  18864. SendBuyError(BUY_ERR_REPUTATION_REQUIRE, creature, item, 0);
  18865. return false;
  18866. }
  18867. if (crItem->ExtendedCost)
  18868. {
  18869. // Can only buy full stacks for extended cost
  18870. if (count % pProto->BuyCount)
  18871. {
  18872. SendEquipError(EQUIP_ERR_CANT_BUY_QUANTITY, NULL, NULL);
  18873. return false;
  18874. }
  18875. uint32 stacks = count / pProto->BuyCount;
  18876. ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
  18877. if (!iece)
  18878. {
  18879. TC_LOG_ERROR("entities.player", "Item %u have wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost);
  18880. return false;
  18881. }
  18882. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i)
  18883. {
  18884. if (iece->RequiredItem[i] && !HasItemCount(iece->RequiredItem[i], iece->RequiredItemCount[i] * stacks))
  18885. {
  18886. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
  18887. return false;
  18888. }
  18889. }
  18890. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
  18891. {
  18892. if (!iece->RequiredCurrency[i])
  18893. continue;
  18894. CurrencyTypesEntry const* entry = sCurrencyTypesStore.LookupEntry(iece->RequiredCurrency[i]);
  18895. if (!entry)
  18896. {
  18897. SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
  18898. return false;
  18899. }
  18900. if (iece->RequirementFlags & (ITEM_EXT_COST_CURRENCY_REQ_IS_SEASON_EARNED_1 << i))
  18901. {
  18902. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); // Find correct error
  18903. return false;
  18904. }
  18905. else if (!HasCurrency(iece->RequiredCurrency[i], iece->RequiredCurrencyCount[i] * stacks))
  18906. {
  18907. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
  18908. return false;
  18909. }
  18910. }
  18911. // check for personal arena rating requirement
  18912. if (GetMaxPersonalArenaRatingRequirement(iece->RequiredArenaSlot) < iece->RequiredPersonalArenaRating)
  18913. {
  18914. // probably not the proper equip err
  18915. SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK, NULL, NULL);
  18916. return false;
  18917. }
  18918. if (iece->RequiredFactionId && uint32(GetReputationRank(iece->RequiredFactionId)) < iece->RequiredFactionStanding)
  18919. {
  18920. SendBuyError(BUY_ERR_REPUTATION_REQUIRE, creature, item, 0);
  18921. return false;
  18922. }
  18923. if (iece->RequirementFlags & ITEM_EXT_COST_FLAG_REQUIRE_GUILD && !GetGuildId())
  18924. {
  18925. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); // Find correct error
  18926. return false;
  18927. }
  18928. if (iece->RequiredGuildLevel && iece->RequiredGuildLevel < GetGuildLevel())
  18929. {
  18930. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); // Find correct error
  18931. return false;
  18932. }
  18933. if (iece->RequiredAchievement && !HasAchieved(iece->RequiredAchievement))
  18934. {
  18935. SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); // Find correct error
  18936. return false;
  18937. }
  18938. }
  18939. uint32 price = 0;
  18940. if (crItem->IsGoldRequired(pProto) && pProto->BuyPrice > 0) //Assume price cannot be negative (do not know why it is int32)
  18941. {
  18942. uint32 maxCount = MAX_MONEY_AMOUNT / pProto->BuyPrice;
  18943. if ((uint32)count > maxCount)
  18944. {
  18945. TC_LOG_ERROR("entities.player", "Player %s tried to buy %u item id %u, causing overflow", GetName().c_str(), (uint32)count, pProto->ItemId);
  18946. count = (uint8)maxCount;
  18947. }
  18948. price = pProto->BuyPrice * count; //it should not exceed MAX_MONEY_AMOUNT
  18949. // reputation discount
  18950. price = uint32(floor(price * GetReputationPriceDiscount(creature)));
  18951. if (int32 priceMod = GetTotalAuraModifier(SPELL_AURA_MOD_VENDOR_ITEMS_PRICES))
  18952. price -= CalculatePct(price, priceMod);
  18953. if (!HasEnoughMoney(uint64(price)))
  18954. {
  18955. SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, creature, item, 0);
  18956. return false;
  18957. }
  18958. }
  18959. if ((bag == NULL_BAG && slot == NULL_SLOT) || IsInventoryPos(bag, slot))
  18960. {
  18961. if (!_StoreOrEquipNewItem(vendorslot, item, count, bag, slot, price, pProto, creature, crItem, true))
  18962. return false;
  18963. }
  18964. else if (IsEquipmentPos(bag, slot))
  18965. {
  18966. if (count != 1)
  18967. {
  18968. SendEquipError(EQUIP_ERR_NOT_EQUIPPABLE, NULL, NULL);
  18969. return false;
  18970. }
  18971. if (!_StoreOrEquipNewItem(vendorslot, item, count, bag, slot, price, pProto, creature, crItem, false))
  18972. return false;
  18973. }
  18974. else
  18975. {
  18976. SendEquipError(EQUIP_ERR_WRONG_SLOT, NULL, NULL);
  18977. return false;
  18978. }
  18979. if (crItem->maxcount != 0) // bought
  18980. {
  18981. if (pProto->Quality > ITEM_QUALITY_EPIC || (pProto->Quality == ITEM_QUALITY_EPIC && pProto->ItemLevel >= MinNewsItemLevel[sWorld->getIntConfig(CONFIG_EXPANSION)]))
  18982. if (Guild* guild = GetGuild())
  18983. guild->AddGuildNews(GUILD_NEWS_ITEM_PURCHASED, GetGUID(), 0, item);
  18984. return true;
  18985. }
  18986. return false;
  18987. }
  18988. uint32 Player::GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot) const
  18989. {
  18990. // returns the maximal personal arena rating that can be used to purchase items requiring this condition
  18991. // the personal rating of the arena team must match the required limit as well
  18992. // so return max[in arenateams](min(personalrating[teamtype], teamrating[teamtype]))
  18993. uint32 max_personal_rating = 0;
  18994. for (uint8 i = minarenaslot; i < MAX_ARENA_SLOT; ++i)
  18995. {
  18996. if (ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamId(i)))
  18997. {
  18998. uint32 p_rating = GetArenaPersonalRating(i);
  18999. uint32 t_rating = at->GetRating();
  19000. p_rating = p_rating < t_rating ? p_rating : t_rating;
  19001. if (max_personal_rating < p_rating)
  19002. max_personal_rating = p_rating;
  19003. }
  19004. }
  19005. return max_personal_rating;
  19006. }
  19007. void Player::UpdateHomebindTime(uint32 time)
  19008. {
  19009. // GMs never get homebind timer online
  19010. if (m_InstanceValid || IsGameMaster())
  19011. {
  19012. if (m_HomebindTimer) // instance valid, but timer not reset
  19013. {
  19014. // hide reminder
  19015. WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
  19016. data << uint32(0);
  19017. data << uint32(0);
  19018. GetSession()->SendPacket(&data);
  19019. }
  19020. // instance is valid, reset homebind timer
  19021. m_HomebindTimer = 0;
  19022. }
  19023. else if (m_HomebindTimer > 0)
  19024. {
  19025. if (time >= m_HomebindTimer)
  19026. {
  19027. // teleport to nearest graveyard
  19028. RepopAtGraveyard();
  19029. }
  19030. else
  19031. m_HomebindTimer -= time;
  19032. }
  19033. else
  19034. {
  19035. // instance is invalid, start homebind timer
  19036. m_HomebindTimer = 60000;
  19037. // send message to player
  19038. WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
  19039. data << uint32(m_HomebindTimer);
  19040. data << uint32(1);
  19041. GetSession()->SendPacket(&data);
  19042. TC_LOG_DEBUG("maps", "PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName().c_str(), GetGUIDLow());
  19043. }
  19044. }
  19045. void Player::UpdatePvPState(bool onlyFFA)
  19046. {
  19047. /// @todo should we always synchronize UNIT_FIELD_BYTES_2, 1 of controller and controlled?
  19048. // no, we shouldn't, those are checked for affecting player by client
  19049. if (!pvpInfo.IsInNoPvPArea && !IsGameMaster()
  19050. && (pvpInfo.IsInFFAPvPArea || sWorld->IsFFAPvPRealm()))
  19051. {
  19052. if (!IsFFAPvP())
  19053. {
  19054. SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
  19055. for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
  19056. (*itr)->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
  19057. }
  19058. }
  19059. else if (IsFFAPvP())
  19060. {
  19061. RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
  19062. for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
  19063. (*itr)->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
  19064. }
  19065. if (onlyFFA)
  19066. return;
  19067. if (pvpInfo.IsHostile) // in hostile area
  19068. {
  19069. if (!IsPvP() || pvpInfo.EndTimer)
  19070. UpdatePvP(true, true);
  19071. }
  19072. else // in friendly area
  19073. {
  19074. if (IsPvP() && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP) && !pvpInfo.EndTimer)
  19075. pvpInfo.EndTimer = time(NULL); // start toggle-off
  19076. }
  19077. }
  19078. void Player::SetPvP(bool state)
  19079. {
  19080. Unit::SetPvP(state);
  19081. for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
  19082. (*itr)->SetPvP(state);
  19083. }
  19084. void Player::UpdatePvP(bool state, bool override)
  19085. {
  19086. if (!state || override)
  19087. {
  19088. SetPvP(state);
  19089. pvpInfo.EndTimer = 0;
  19090. }
  19091. else
  19092. {
  19093. pvpInfo.EndTimer = time(NULL);
  19094. SetPvP(state);
  19095. }
  19096. }
  19097. bool Player::HasSpellCooldown(uint32 spell_id) const
  19098. {
  19099. SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
  19100. return itr != m_spellCooldowns.end() && itr->second.end > time(NULL);
  19101. }
  19102. uint32 Player::GetSpellCooldownDelay(uint32 spell_id) const
  19103. {
  19104. SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
  19105. time_t t = time(NULL);
  19106. return uint32(itr != m_spellCooldowns.end() && itr->second.end > t ? itr->second.end - t : 0);
  19107. }
  19108. void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown)
  19109. {
  19110. // init cooldown values
  19111. uint32 cat = 0;
  19112. int32 rec = -1;
  19113. int32 catrec = -1;
  19114. // some special item spells without correct cooldown in SpellInfo
  19115. // cooldown information stored in item prototype
  19116. // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client.
  19117. if (itemId)
  19118. {
  19119. if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
  19120. {
  19121. for (uint8 idx = 0; idx < MAX_ITEM_SPELLS; ++idx)
  19122. {
  19123. if (uint32(proto->Spells[idx].SpellId) == spellInfo->Id)
  19124. {
  19125. cat = proto->Spells[idx].SpellCategory;
  19126. rec = proto->Spells[idx].SpellCooldown;
  19127. catrec = proto->Spells[idx].SpellCategoryCooldown;
  19128. break;
  19129. }
  19130. }
  19131. }
  19132. }
  19133. // if no cooldown found above then base at DBC data
  19134. if (rec < 0 && catrec < 0)
  19135. {
  19136. cat = spellInfo->GetCategory();
  19137. rec = spellInfo->RecoveryTime;
  19138. catrec = spellInfo->CategoryRecoveryTime;
  19139. }
  19140. time_t curTime = time(NULL);
  19141. time_t catrecTime;
  19142. time_t recTime;
  19143. bool needsCooldownPacket = false;
  19144. // overwrite time for selected category
  19145. if (infinityCooldown)
  19146. {
  19147. // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped)
  19148. // but not allow ignore until reset or re-login
  19149. catrecTime = catrec > 0 ? curTime+infinityCooldownDelay : 0;
  19150. recTime = rec > 0 ? curTime+infinityCooldownDelay : catrecTime;
  19151. }
  19152. else
  19153. {
  19154. // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
  19155. // prevent 0 cooldowns set by another way
  19156. if (rec <= 0 && catrec <= 0 && (cat == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75)))
  19157. rec = GetAttackTime(RANGED_ATTACK);
  19158. // Now we have cooldown data (if found any), time to apply mods
  19159. if (rec > 0)
  19160. ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell);
  19161. if (catrec > 0 && !(spellInfo->AttributesEx6 & SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS))
  19162. ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell);
  19163. if (int32 cooldownMod = GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN))
  19164. {
  19165. // Apply SPELL_AURA_MOD_COOLDOWN only to own spells
  19166. if (HasSpell(spellInfo->Id))
  19167. {
  19168. needsCooldownPacket = true;
  19169. rec += cooldownMod * IN_MILLISECONDS; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks
  19170. }
  19171. }
  19172. // Apply SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN modifiers
  19173. // Note: This aura applies its modifiers to all cooldowns of spells with set category, not to category cooldown only
  19174. if (cat)
  19175. {
  19176. if (int32 categoryModifier = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN, cat))
  19177. {
  19178. if (rec > 0)
  19179. rec += categoryModifier;
  19180. if (catrec > 0)
  19181. catrec += categoryModifier;
  19182. }
  19183. SpellCategoryEntry const* categoryEntry = sSpellCategoryStore.LookupEntry(cat);
  19184. ASSERT(categoryEntry);
  19185. if (categoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_MIDNIGHT)
  19186. {
  19187. tm date;
  19188. localtime_r(&curTime, &date);
  19189. catrec = catrec * DAY - (date.tm_hour * HOUR + date.tm_min * MINUTE + date.tm_sec) * IN_MILLISECONDS;
  19190. }
  19191. }
  19192. // replace negative cooldowns by 0
  19193. if (rec < 0)
  19194. rec = 0;
  19195. if (catrec < 0)
  19196. catrec = 0;
  19197. // no cooldown after applying spell mods
  19198. if (rec == 0 && catrec == 0)
  19199. return;
  19200. catrecTime = catrec ? curTime+catrec/IN_MILLISECONDS : 0;
  19201. recTime = rec ? curTime+rec/IN_MILLISECONDS : catrecTime;
  19202. }
  19203. // self spell cooldown
  19204. if (recTime > 0)
  19205. {
  19206. AddSpellCooldown(spellInfo->Id, itemId, recTime);
  19207. if (needsCooldownPacket)
  19208. {
  19209. WorldPacket data;
  19210. BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, rec);
  19211. SendDirectMessage(&data);
  19212. }
  19213. }
  19214. // category spells
  19215. if (cat && catrec > 0)
  19216. {
  19217. SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(cat);
  19218. if (i_scstore != sSpellsByCategoryStore.end())
  19219. {
  19220. for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
  19221. {
  19222. if (*i_scset == spellInfo->Id) // skip main spell, already handled above
  19223. continue;
  19224. AddSpellCooldown(*i_scset, itemId, catrecTime);
  19225. }
  19226. }
  19227. }
  19228. }
  19229. void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
  19230. {
  19231. SpellCooldown sc;
  19232. sc.end = end_time;
  19233. sc.itemid = itemid;
  19234. m_spellCooldowns[spellid] = sc;
  19235. }
  19236. void Player::ModifySpellCooldown(uint32 spellId, int32 cooldown)
  19237. {
  19238. SpellCooldowns::iterator itr = m_spellCooldowns.find(spellId);
  19239. if (itr == m_spellCooldowns.end())
  19240. return;
  19241. time_t now = time(NULL);
  19242. if (itr->second.end + (cooldown / IN_MILLISECONDS) > now)
  19243. itr->second.end += (cooldown / IN_MILLISECONDS);
  19244. else
  19245. m_spellCooldowns.erase(itr);
  19246. WorldPacket data(SMSG_MODIFY_COOLDOWN, 4 + 8 + 4);
  19247. data << uint32(spellId); // Spell ID
  19248. data << uint64(GetGUID()); // Player GUID
  19249. data << int32(cooldown); // Cooldown mod in milliseconds
  19250. GetSession()->SendPacket(&data);
  19251. TC_LOG_DEBUG("misc", "ModifySpellCooldown:: Player: %s (GUID: %u) Spell: %u cooldown: %u", GetName().c_str(), GetGUIDLow(), spellId, GetSpellCooldownDelay(spellId));
  19252. }
  19253. void Player::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= NULL*/, bool setCooldown /*= true*/)
  19254. {
  19255. // start cooldowns at server side, if any
  19256. if (setCooldown)
  19257. AddSpellAndCategoryCooldowns(spellInfo, itemId, spell);
  19258. // Send activate cooldown timer (possible 0) at client side
  19259. WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
  19260. data << uint32(spellInfo->Id);
  19261. data << uint64(GetGUID());
  19262. SendDirectMessage(&data);
  19263. }
  19264. void Player::UpdatePotionCooldown(Spell* spell)
  19265. {
  19266. // no potion used i combat or still in combat
  19267. if (!m_lastPotionId || IsInCombat())
  19268. return;
  19269. // Call not from spell cast, send cooldown event for item spells if no in combat
  19270. if (!spell)
  19271. {
  19272. // spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions)
  19273. if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(m_lastPotionId))
  19274. for (uint8 idx = 0; idx < MAX_ITEM_SPELLS; ++idx)
  19275. if (proto->Spells[idx].SpellId && proto->Spells[idx].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
  19276. if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[idx].SpellId))
  19277. SendCooldownEvent(spellInfo, m_lastPotionId);
  19278. }
  19279. // from spell cases (m_lastPotionId set in Spell::SendSpellCooldown)
  19280. else
  19281. SendCooldownEvent(spell->m_spellInfo, m_lastPotionId, spell);
  19282. m_lastPotionId = 0;
  19283. }
  19284. void Player::SetResurrectRequestData(Unit* caster, uint32 health, uint32 mana, uint32 appliedAura)
  19285. {
  19286. ASSERT(!IsResurrectRequested());
  19287. _resurrectionData = new ResurrectionData();
  19288. _resurrectionData->GUID = caster->GetGUID();
  19289. _resurrectionData->Location.WorldRelocate(*caster);
  19290. _resurrectionData->Health = health;
  19291. _resurrectionData->Mana = mana;
  19292. _resurrectionData->Aura = appliedAura;
  19293. }
  19294. //slot to be excluded while counting
  19295. bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot)
  19296. {
  19297. if (!enchantmentcondition)
  19298. return true;
  19299. SpellItemEnchantmentConditionEntry const* Condition = sSpellItemEnchantmentConditionStore.LookupEntry(enchantmentcondition);
  19300. if (!Condition)
  19301. return true;
  19302. uint8 curcount[4] = {0, 0, 0, 0};
  19303. //counting current equipped gem colors
  19304. for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
  19305. {
  19306. if (i == slot)
  19307. continue;
  19308. Item* pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
  19309. if (pItem2 && !pItem2->IsBroken() && pItem2->GetTemplate()->Socket[0].Color)
  19310. {
  19311. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
  19312. {
  19313. uint32 enchant_id = pItem2->GetEnchantmentId(EnchantmentSlot(enchant_slot));
  19314. if (!enchant_id)
  19315. continue;
  19316. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  19317. if (!enchantEntry)
  19318. continue;
  19319. uint32 gemid = enchantEntry->GemID;
  19320. if (!gemid)
  19321. continue;
  19322. ItemTemplate const* gemProto = sObjectMgr->GetItemTemplate(gemid);
  19323. if (!gemProto)
  19324. continue;
  19325. GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties);
  19326. if (!gemProperty)
  19327. continue;
  19328. uint8 GemColor = gemProperty->color;
  19329. for (uint8 b = 0, tmpcolormask = 1; b < 4; b++, tmpcolormask <<= 1)
  19330. {
  19331. if (tmpcolormask & GemColor)
  19332. ++curcount[b];
  19333. }
  19334. }
  19335. }
  19336. }
  19337. bool activate = true;
  19338. for (uint8 i = 0; i < 5; i++)
  19339. {
  19340. if (!Condition->Color[i])
  19341. continue;
  19342. uint32 _cur_gem = curcount[Condition->Color[i] - 1];
  19343. // if have <CompareColor> use them as count, else use <value> from Condition
  19344. uint32 _cmp_gem = Condition->CompareColor[i] ? curcount[Condition->CompareColor[i] - 1]: Condition->Value[i];
  19345. switch (Condition->Comparator[i])
  19346. {
  19347. case 2: // requires less <color> than (<value> || <comparecolor>) gems
  19348. activate &= (_cur_gem < _cmp_gem) ? true : false;
  19349. break;
  19350. case 3: // requires more <color> than (<value> || <comparecolor>) gems
  19351. activate &= (_cur_gem > _cmp_gem) ? true : false;
  19352. break;
  19353. case 5: // requires at least <color> than (<value> || <comparecolor>) gems
  19354. activate &= (_cur_gem >= _cmp_gem) ? true : false;
  19355. break;
  19356. }
  19357. }
  19358. TC_LOG_DEBUG("entities.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");
  19359. return activate;
  19360. }
  19361. void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply)
  19362. {
  19363. //cycle all equipped items
  19364. for (uint32 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
  19365. {
  19366. //enchants for the slot being socketed are handled by Player::ApplyItemMods
  19367. if (slot == exceptslot)
  19368. continue;
  19369. Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
  19370. if (!pItem || !pItem->GetTemplate()->Socket[0].Color)
  19371. continue;
  19372. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
  19373. {
  19374. uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
  19375. if (!enchant_id)
  19376. continue;
  19377. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  19378. if (!enchantEntry)
  19379. continue;
  19380. uint32 condition = enchantEntry->EnchantmentCondition;
  19381. if (condition)
  19382. {
  19383. //was enchant active with/without item?
  19384. bool wasactive = EnchantmentFitsRequirements(condition, apply ? exceptslot : -1);
  19385. //should it now be?
  19386. if (wasactive ^ EnchantmentFitsRequirements(condition, apply ? -1 : exceptslot))
  19387. {
  19388. // ignore item gem conditions
  19389. //if state changed, (dis)apply enchant
  19390. ApplyEnchantment(pItem, EnchantmentSlot(enchant_slot), !wasactive, true, true);
  19391. }
  19392. }
  19393. }
  19394. }
  19395. }
  19396. //if false -> then toggled off if was on| if true -> toggled on if was off AND meets requirements
  19397. void Player::ToggleMetaGemsActive(uint8 exceptslot, bool apply)
  19398. {
  19399. //cycle all equipped items
  19400. for (int slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
  19401. {
  19402. //enchants for the slot being socketed are handled by WorldSession::HandleSocketOpcode(WorldPacket& recvData)
  19403. if (slot == exceptslot)
  19404. continue;
  19405. Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
  19406. if (!pItem || !pItem->GetTemplate()->Socket[0].Color) //if item has no sockets or no item is equipped go to next item
  19407. continue;
  19408. //cycle all (gem)enchants
  19409. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
  19410. {
  19411. uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
  19412. if (!enchant_id) //if no enchant go to next enchant(slot)
  19413. continue;
  19414. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  19415. if (!enchantEntry)
  19416. continue;
  19417. //only metagems to be (de)activated, so only enchants with condition
  19418. uint32 condition = enchantEntry->EnchantmentCondition;
  19419. if (condition)
  19420. ApplyEnchantment(pItem, EnchantmentSlot(enchant_slot), apply);
  19421. }
  19422. }
  19423. }
  19424. void Player::SetBattlegroundEntryPoint()
  19425. {
  19426. // Taxi path store
  19427. if (!m_taxi.empty())
  19428. {
  19429. m_bgData.mountSpell = 0;
  19430. m_bgData.taxiPath[0] = m_taxi.GetTaxiSource();
  19431. m_bgData.taxiPath[1] = m_taxi.GetTaxiDestination();
  19432. // On taxi we don't need check for dungeon
  19433. m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  19434. }
  19435. else
  19436. {
  19437. m_bgData.ClearTaxiPath();
  19438. // Mount spell id storing
  19439. if (IsMounted())
  19440. {
  19441. AuraEffectList const& auras = GetAuraEffectsByType(SPELL_AURA_MOUNTED);
  19442. if (!auras.empty())
  19443. m_bgData.mountSpell = (*auras.begin())->GetId();
  19444. }
  19445. else
  19446. m_bgData.mountSpell = 0;
  19447. // If map is dungeon find linked graveyard
  19448. if (GetMap()->IsDungeon())
  19449. {
  19450. if (const WorldSafeLocsEntry* entry = sObjectMgr->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam()))
  19451. m_bgData.joinPos = WorldLocation(entry->map_id, entry->x, entry->y, entry->z, 0.0f);
  19452. else
  19453. TC_LOG_ERROR("entities.player", "SetBattlegroundEntryPoint: Dungeon map %u has no linked graveyard, setting home location as entry point.", GetMapId());
  19454. }
  19455. // If new entry point is not BG or arena set it
  19456. else if (!GetMap()->IsBattlegroundOrArena())
  19457. m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
  19458. }
  19459. if (m_bgData.joinPos.m_mapId == MAPID_INVALID) // In error cases use homebind position
  19460. m_bgData.joinPos = WorldLocation(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, 0.0f);
  19461. }
  19462. void Player::SetBGTeam(uint32 team)
  19463. {
  19464. m_bgData.bgTeam = team;
  19465. SetByteValue(PLAYER_BYTES_3, 3, uint8(team == ALLIANCE ? 1 : 0));
  19466. }
  19467. uint32 Player::GetBGTeam() const
  19468. {
  19469. return m_bgData.bgTeam ? m_bgData.bgTeam : GetTeam();
  19470. }
  19471. void Player::LeaveBattleground(bool teleportToEntryPoint)
  19472. {
  19473. if (Battleground* bg = GetBattleground())
  19474. {
  19475. bg->RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true);
  19476. // call after remove to be sure that player resurrected for correct cast
  19477. if (bg->isBattleground() && !IsGameMaster() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_CAST_DESERTER))
  19478. {
  19479. if (bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN)
  19480. {
  19481. //lets check if player was teleported from BG and schedule delayed Deserter spell cast
  19482. if (IsBeingTeleportedFar())
  19483. {
  19484. ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER);
  19485. return;
  19486. }
  19487. CastSpell(this, 26013, true); // Deserter
  19488. }
  19489. }
  19490. }
  19491. }
  19492. bool Player::CanJoinToBattleground(Battleground const* bg) const
  19493. {
  19494. // check Deserter debuff
  19495. if (HasAura(26013))
  19496. return false;
  19497. if (bg->isArena() && !GetSession()->HasPermission(rbac::RBAC_PERM_JOIN_ARENAS))
  19498. return false;
  19499. if (bg->IsRandom() && !GetSession()->HasPermission(rbac::RBAC_PERM_JOIN_RANDOM_BG))
  19500. return false;
  19501. if (!GetSession()->HasPermission(rbac::RBAC_PERM_JOIN_NORMAL_BG))
  19502. return false;
  19503. return true;
  19504. }
  19505. bool Player::CanReportAfkDueToLimit()
  19506. {
  19507. // a player can complain about 15 people per 5 minutes
  19508. if (m_bgData.bgAfkReportedCount++ >= 15)
  19509. return false;
  19510. return true;
  19511. }
  19512. ///This player has been blamed to be inactive in a battleground
  19513. void Player::ReportedAfkBy(Player* reporter)
  19514. {
  19515. Battleground* bg = GetBattleground();
  19516. // Battleground also must be in progress!
  19517. if (!bg || bg != reporter->GetBattleground() || GetTeam() != reporter->GetTeam() || bg->GetStatus() != STATUS_IN_PROGRESS)
  19518. return;
  19519. // check if player has 'Idle' or 'Inactive' debuff
  19520. if (m_bgData.bgAfkReporter.find(reporter->GetGUIDLow()) == m_bgData.bgAfkReporter.end() && !HasAura(43680) && !HasAura(43681) && reporter->CanReportAfkDueToLimit())
  19521. {
  19522. m_bgData.bgAfkReporter.insert(reporter->GetGUIDLow());
  19523. // 3 players have to complain to apply debuff
  19524. if (m_bgData.bgAfkReporter.size() >= 3)
  19525. {
  19526. // cast 'Idle' spell
  19527. CastSpell(this, 43680, true);
  19528. m_bgData.bgAfkReporter.clear();
  19529. }
  19530. }
  19531. }
  19532. WorldLocation Player::GetStartPosition() const
  19533. {
  19534. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  19535. uint32 mapId = info->mapId;
  19536. if (getClass() == CLASS_DEATH_KNIGHT && HasSpell(50977))
  19537. mapId = 0;
  19538. return WorldLocation(mapId, info->positionX, info->positionY, info->positionZ, 0);
  19539. }
  19540. bool Player::HaveAtClient(WorldObject const* u) const
  19541. {
  19542. return u == this || m_clientGUIDs.find(u->GetGUID()) != m_clientGUIDs.end();
  19543. }
  19544. bool Player::IsNeverVisible() const
  19545. {
  19546. if (Unit::IsNeverVisible())
  19547. return true;
  19548. if (GetSession()->PlayerLogout() || GetSession()->PlayerLoading())
  19549. return true;
  19550. return false;
  19551. }
  19552. bool Player::CanAlwaysSee(WorldObject const* obj) const
  19553. {
  19554. // Always can see self
  19555. if (m_mover == obj)
  19556. return true;
  19557. if (ObjectGuid guid = GetGuidValue(PLAYER_FARSIGHT))
  19558. if (obj->GetGUID() == guid)
  19559. return true;
  19560. return false;
  19561. }
  19562. bool Player::IsAlwaysDetectableFor(WorldObject const* seer) const
  19563. {
  19564. if (Unit::IsAlwaysDetectableFor(seer))
  19565. return true;
  19566. if (const Player* seerPlayer = seer->ToPlayer())
  19567. if (IsGroupVisibleFor(seerPlayer))
  19568. return !(seerPlayer->duel && seerPlayer->duel->startTime != 0 && seerPlayer->duel->opponent == this);
  19569. return false;
  19570. }
  19571. bool Player::IsVisibleGloballyFor(Player const* u) const
  19572. {
  19573. if (!u)
  19574. return false;
  19575. // Always can see self
  19576. if (u == this)
  19577. return true;
  19578. // Visible units, always are visible for all players
  19579. if (IsVisible())
  19580. return true;
  19581. // GMs are visible for higher gms (or players are visible for gms)
  19582. if (!AccountMgr::IsPlayerAccount(u->GetSession()->GetSecurity()))
  19583. return GetSession()->GetSecurity() <= u->GetSession()->GetSecurity();
  19584. // non faction visibility non-breakable for non-GMs
  19585. if (!IsVisible())
  19586. return false;
  19587. // non-gm stealth/invisibility not hide from global player lists
  19588. return true;
  19589. }
  19590. template<class T>
  19591. inline void UpdateVisibilityOf_helper(GuidSet& s64, T* target, std::set<Unit*>& /*v*/)
  19592. {
  19593. s64.insert(target->GetGUID());
  19594. }
  19595. template<>
  19596. inline void UpdateVisibilityOf_helper(GuidSet& s64, GameObject* target, std::set<Unit*>& /*v*/)
  19597. {
  19598. // @HACK: This is to prevent objects like deeprun tram from disappearing when player moves far from its spawn point while riding it
  19599. // But exclude stoppable elevators from this hack - they would be teleporting from one end to another
  19600. // if affected transports move so far horizontally that it causes them to run out of visibility range then you are out of luck
  19601. // fix visibility instead of adding hacks here
  19602. if (!target->IsDynTransport())
  19603. s64.insert(target->GetGUID());
  19604. }
  19605. template<>
  19606. inline void UpdateVisibilityOf_helper(GuidSet& s64, Creature* target, std::set<Unit*>& v)
  19607. {
  19608. s64.insert(target->GetGUID());
  19609. v.insert(target);
  19610. }
  19611. template<>
  19612. inline void UpdateVisibilityOf_helper(GuidSet& s64, Player* target, std::set<Unit*>& v)
  19613. {
  19614. s64.insert(target->GetGUID());
  19615. v.insert(target);
  19616. }
  19617. template<class T>
  19618. inline void BeforeVisibilityDestroy(T* /*t*/, Player* /*p*/) { }
  19619. template<>
  19620. inline void BeforeVisibilityDestroy<Creature>(Creature* t, Player* p)
  19621. {
  19622. if (p->GetPetGUID() == t->GetGUID() && t->ToCreature()->IsPet())
  19623. ((Pet*)t)->Remove(PET_SAVE_NOT_IN_SLOT, true);
  19624. }
  19625. void Player::UpdateVisibilityOf(WorldObject* target)
  19626. {
  19627. if (HaveAtClient(target))
  19628. {
  19629. if (!CanSeeOrDetect(target, false, true))
  19630. {
  19631. if (target->GetTypeId() == TYPEID_UNIT)
  19632. BeforeVisibilityDestroy<Creature>(target->ToCreature(), this);
  19633. target->DestroyForPlayer(this);
  19634. m_clientGUIDs.erase(target->GetGUID());
  19635. #ifdef TRINITY_DEBUG
  19636. TC_LOG_DEBUG("maps", "Object %u (Type: %u) out of range for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), GetGUIDLow(), GetDistance(target));
  19637. #endif
  19638. }
  19639. }
  19640. else
  19641. {
  19642. if (CanSeeOrDetect(target, false, true))
  19643. {
  19644. target->SendUpdateToPlayer(this);
  19645. m_clientGUIDs.insert(target->GetGUID());
  19646. #ifdef TRINITY_DEBUG
  19647. TC_LOG_DEBUG("maps", "Object %u (Type: %u) is visible now for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), GetGUIDLow(), GetDistance(target));
  19648. #endif
  19649. // target aura duration for caster show only if target exist at caster client
  19650. // send data at target visibility change (adding to client)
  19651. if (target->isType(TYPEMASK_UNIT))
  19652. SendInitialVisiblePackets((Unit*)target);
  19653. }
  19654. }
  19655. }
  19656. void Player::UpdateTriggerVisibility()
  19657. {
  19658. if (m_clientGUIDs.empty())
  19659. return;
  19660. if (!IsInWorld())
  19661. return;
  19662. UpdateData udata(GetMapId());
  19663. WorldPacket packet;
  19664. for (GuidSet::iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
  19665. {
  19666. if (itr->IsCreature())
  19667. {
  19668. Creature* creature = GetMap()->GetCreature(*itr);
  19669. // Update fields of triggers, transformed units or unselectable units (values dependent on GM state)
  19670. if (!creature || (!creature->IsTrigger() && !creature->HasAuraType(SPELL_AURA_TRANSFORM) && !creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)))
  19671. continue;
  19672. creature->SetFieldNotifyFlag(UF_FLAG_PUBLIC);
  19673. creature->BuildValuesUpdateBlockForPlayer(&udata, this);
  19674. creature->RemoveFieldNotifyFlag(UF_FLAG_PUBLIC);
  19675. }
  19676. else if (itr->IsGameObject())
  19677. {
  19678. GameObject* go = GetMap()->GetGameObject(*itr);
  19679. if (!go)
  19680. continue;
  19681. go->SetFieldNotifyFlag(UF_FLAG_PUBLIC);
  19682. go->BuildValuesUpdateBlockForPlayer(&udata, this);
  19683. go->RemoveFieldNotifyFlag(UF_FLAG_PUBLIC);
  19684. }
  19685. }
  19686. if (!udata.HasData())
  19687. return;
  19688. udata.BuildPacket(&packet);
  19689. GetSession()->SendPacket(&packet);
  19690. }
  19691. void Player::SendInitialVisiblePackets(Unit* target)
  19692. {
  19693. SendAurasForTarget(target);
  19694. if (target->IsAlive())
  19695. {
  19696. if (target->HasUnitState(UNIT_STATE_MELEE_ATTACKING) && target->GetVictim())
  19697. target->SendMeleeAttackStart(target->GetVictim());
  19698. }
  19699. }
  19700. template<class T>
  19701. void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<Unit*>& visibleNow)
  19702. {
  19703. if (HaveAtClient(target))
  19704. {
  19705. if (!CanSeeOrDetect(target, false, true))
  19706. {
  19707. BeforeVisibilityDestroy<T>(target, this);
  19708. target->BuildOutOfRangeUpdateBlock(&data);
  19709. m_clientGUIDs.erase(target->GetGUID());
  19710. #ifdef TRINITY_DEBUG
  19711. TC_LOG_DEBUG("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));
  19712. #endif
  19713. }
  19714. }
  19715. else //if (visibleNow.size() < 30 || target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->IsVehicle())
  19716. {
  19717. if (CanSeeOrDetect(target, false, true))
  19718. {
  19719. target->BuildCreateUpdateBlockForPlayer(&data, this);
  19720. UpdateVisibilityOf_helper(m_clientGUIDs, target, visibleNow);
  19721. #ifdef TRINITY_DEBUG
  19722. TC_LOG_DEBUG("maps", "Object %u (Type: %u, Entry: %u) is visible now for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), target->GetEntry(), GetGUIDLow(), GetDistance(target));
  19723. #endif
  19724. }
  19725. }
  19726. }
  19727. template void Player::UpdateVisibilityOf(Player* target, UpdateData& data, std::set<Unit*>& visibleNow);
  19728. template void Player::UpdateVisibilityOf(Creature* target, UpdateData& data, std::set<Unit*>& visibleNow);
  19729. template void Player::UpdateVisibilityOf(Corpse* target, UpdateData& data, std::set<Unit*>& visibleNow);
  19730. template void Player::UpdateVisibilityOf(GameObject* target, UpdateData& data, std::set<Unit*>& visibleNow);
  19731. template void Player::UpdateVisibilityOf(DynamicObject* target, UpdateData& data, std::set<Unit*>& visibleNow);
  19732. void Player::UpdateObjectVisibility(bool forced)
  19733. {
  19734. if (!forced)
  19735. AddToNotify(NOTIFY_VISIBILITY_CHANGED);
  19736. else
  19737. {
  19738. Unit::UpdateObjectVisibility(true);
  19739. UpdateVisibilityForPlayer();
  19740. }
  19741. }
  19742. void Player::UpdateVisibilityForPlayer()
  19743. {
  19744. // updates visibility of all objects around point of view for current player
  19745. Trinity::VisibleNotifier notifier(*this);
  19746. m_seer->VisitNearbyObject(GetSightRange(), notifier);
  19747. notifier.SendToSelf(); // send gathered data
  19748. }
  19749. void Player::InitPrimaryProfessions()
  19750. {
  19751. SetFreePrimaryProfessions(sWorld->getIntConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL));
  19752. }
  19753. bool Player::ModifyMoney(int64 amount, bool sendError /*= true*/)
  19754. {
  19755. if (!amount)
  19756. return true;
  19757. sScriptMgr->OnPlayerMoneyChanged(this, amount);
  19758. if (amount < 0)
  19759. SetMoney(GetMoney() > uint64(-amount) ? GetMoney() + amount : 0);
  19760. else
  19761. {
  19762. if (GetMoney() < MAX_MONEY_AMOUNT - static_cast<uint64>(amount))
  19763. SetMoney(GetMoney() + amount);
  19764. else
  19765. {
  19766. sScriptMgr->OnPlayerMoneyLimit(this, amount);
  19767. if (sendError)
  19768. SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL);
  19769. return false;
  19770. }
  19771. }
  19772. return true;
  19773. }
  19774. bool Player::HasEnoughMoney(int64 amount) const
  19775. {
  19776. if (amount > 0)
  19777. return (GetMoney() >= (uint64) amount);
  19778. return true;
  19779. }
  19780. void Player::SetMoney(uint64 value)
  19781. {
  19782. SetUInt64Value(PLAYER_FIELD_COINAGE, value);
  19783. MoneyChanged(value);
  19784. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED);
  19785. }
  19786. bool Player::IsQuestRewarded(uint32 quest_id) const
  19787. {
  19788. return m_RewardedQuests.find(quest_id) != m_RewardedQuests.end();
  19789. }
  19790. Unit* Player::GetSelectedUnit() const
  19791. {
  19792. if (ObjectGuid selectionGUID = GetTarget())
  19793. return ObjectAccessor::GetUnit(*this, selectionGUID);
  19794. return NULL;
  19795. }
  19796. Player* Player::GetSelectedPlayer() const
  19797. {
  19798. if (ObjectGuid selectionGUID = GetTarget())
  19799. return ObjectAccessor::GetPlayer(*this, selectionGUID);
  19800. return NULL;
  19801. }
  19802. void Player::SendComboPoints()
  19803. {
  19804. Unit* combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget);
  19805. if (combotarget)
  19806. {
  19807. WorldPacket data;
  19808. if (m_mover != this)
  19809. {
  19810. data.Initialize(SMSG_PET_UPDATE_COMBO_POINTS, m_mover->GetPackGUID().size()+combotarget->GetPackGUID().size()+1);
  19811. data << m_mover->GetPackGUID();
  19812. }
  19813. else
  19814. data.Initialize(SMSG_UPDATE_COMBO_POINTS, combotarget->GetPackGUID().size()+1);
  19815. data << combotarget->GetPackGUID();
  19816. data << uint8(m_comboPoints);
  19817. GetSession()->SendPacket(&data);
  19818. }
  19819. }
  19820. void Player::AddComboPoints(Unit* target, int8 count, Spell* spell)
  19821. {
  19822. if (!count)
  19823. return;
  19824. int8 * comboPoints = spell ? &spell->m_comboPointGain : &m_comboPoints;
  19825. // without combo points lost (duration checked in aura)
  19826. RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS);
  19827. if (target->GetGUID() == m_comboTarget)
  19828. *comboPoints += count;
  19829. else
  19830. {
  19831. if (m_comboTarget)
  19832. if (Unit* target2 = ObjectAccessor::GetUnit(*this, m_comboTarget))
  19833. target2->RemoveComboPointHolder(GetGUID());
  19834. // Spells will always add value to m_comboPoints eventualy, so it must be cleared first
  19835. if (spell)
  19836. m_comboPoints = 0;
  19837. m_comboTarget = target->GetGUID();
  19838. *comboPoints = count;
  19839. target->AddComboPointHolder(GetGUID());
  19840. }
  19841. if (*comboPoints > 5)
  19842. *comboPoints = 5;
  19843. else if (*comboPoints < 0)
  19844. *comboPoints = 0;
  19845. if (!spell)
  19846. SendComboPoints();
  19847. }
  19848. void Player::GainSpellComboPoints(int8 count)
  19849. {
  19850. if (!count)
  19851. return;
  19852. m_comboPoints += count;
  19853. if (m_comboPoints > 5) m_comboPoints = 5;
  19854. else if (m_comboPoints < 0) m_comboPoints = 0;
  19855. SendComboPoints();
  19856. }
  19857. void Player::ClearComboPoints()
  19858. {
  19859. if (!m_comboTarget)
  19860. return;
  19861. // without combopoints lost (duration checked in aura)
  19862. RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS);
  19863. m_comboPoints = 0;
  19864. SendComboPoints();
  19865. if (Unit* target = ObjectAccessor::GetUnit(*this, m_comboTarget))
  19866. target->RemoveComboPointHolder(GetGUID());
  19867. m_comboTarget.Clear();
  19868. }
  19869. void Player::SetGroup(Group* group, int8 subgroup)
  19870. {
  19871. if (group == NULL)
  19872. m_group.unlink();
  19873. else
  19874. {
  19875. // never use SetGroup without a subgroup unless you specify NULL for group
  19876. ASSERT(subgroup >= 0);
  19877. m_group.link(group, this);
  19878. m_group.setSubGroup((uint8)subgroup);
  19879. }
  19880. UpdateObjectVisibility(false);
  19881. }
  19882. void Player::SendInitialPacketsBeforeAddToMap()
  19883. {
  19884. /// Pass 'this' as argument because we're not stored in ObjectAccessor yet
  19885. GetSocial()->SendSocialList(this);
  19886. // guild bank list wtf?
  19887. // Homebind
  19888. WorldPacket data(SMSG_BINDPOINTUPDATE, 5*4);
  19889. data << m_homebindX << m_homebindY << m_homebindZ;
  19890. data << (uint32) m_homebindMapId;
  19891. data << (uint32) m_homebindAreaId;
  19892. GetSession()->SendPacket(&data);
  19893. // SMSG_SET_PROFICIENCY
  19894. // SMSG_SET_PCT_SPELL_MODIFIER
  19895. // SMSG_SET_FLAT_SPELL_MODIFIER
  19896. // SMSG_UPDATE_AURA_DURATION
  19897. SendTalentsInfoData(false);
  19898. data.Initialize(SMSG_WORLD_SERVER_INFO, 1 + 1 + 4 + 4);
  19899. data.WriteBit(0); // HasRestrictedLevel
  19900. data.WriteBit(0); // HasRestrictedMoney
  19901. data.WriteBit(0); // IneligibleForLoot
  19902. data.FlushBits();
  19903. //if (IneligibleForLoot)
  19904. // data << uint32(0); // EncounterMask
  19905. data << uint8(0); // IsOnTournamentRealm
  19906. //if (HasRestrictedMoney)
  19907. // data << uint32(100000); // RestrictedMoney (starter accounts)
  19908. //if (HasRestrictedLevel)
  19909. // data << uint32(20); // RestrictedLevel (starter accounts)
  19910. data << uint32(sWorld->GetNextWeeklyQuestsResetTime() - WEEK); // LastWeeklyReset (not instance reset)
  19911. data << uint32(GetMap()->GetDifficulty());
  19912. GetSession()->SendPacket(&data);
  19913. SendInitialSpells();
  19914. data.Initialize(SMSG_SEND_UNLEARN_SPELLS, 4);
  19915. data << uint32(0); // count, for (count) uint32;
  19916. GetSession()->SendPacket(&data);
  19917. SendInitialActionButtons();
  19918. m_reputationMgr->SendInitialReputations();
  19919. m_achievementMgr->SendAllAchievementData(this);
  19920. SendEquipmentSetList();
  19921. data.Initialize(SMSG_LOGIN_SETTIMESPEED, 4 + 4 + 4);
  19922. data.AppendPackedTime(sWorld->GetGameTime());
  19923. data << float(0.01666667f); // game speed
  19924. data << uint32(0); // added in 3.1.2
  19925. GetSession()->SendPacket(&data);
  19926. GetReputationMgr().SendForceReactions(); // SMSG_SET_FORCED_REACTIONS
  19927. // SMSG_TALENTS_INFO x 2 for pet (unspent points and talents in separate packets...)
  19928. // SMSG_PET_GUIDS
  19929. // SMSG_UPDATE_WORLD_STATE
  19930. // SMSG_POWER_UPDATE
  19931. SendCurrencies();
  19932. SetMover(this);
  19933. }
  19934. void Player::SendInitialPacketsAfterAddToMap()
  19935. {
  19936. UpdateVisibilityForPlayer();
  19937. // update zone
  19938. uint32 newzone, newarea;
  19939. GetZoneAndAreaId(newzone, newarea);
  19940. UpdateZone(newzone, newarea); // also call SendInitWorldStates();
  19941. ResetTimeSync();
  19942. SendTimeSync();
  19943. GetSession()->SendLoadCUFProfiles();
  19944. CastSpell(this, 836, true); // LOGINEFFECT
  19945. // set some aura effects that send packet to player client after add player to map
  19946. // SendMessageToSet not send it to player not it map, only for aura that not changed anything at re-apply
  19947. // same auras state lost at far teleport, send it one more time in this case also
  19948. static const AuraType auratypes[] =
  19949. {
  19950. SPELL_AURA_MOD_FEAR, SPELL_AURA_TRANSFORM, SPELL_AURA_WATER_WALK,
  19951. SPELL_AURA_FEATHER_FALL, SPELL_AURA_HOVER, SPELL_AURA_SAFE_FALL,
  19952. SPELL_AURA_FLY, SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED, SPELL_AURA_NONE
  19953. };
  19954. for (AuraType const* itr = &auratypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr)
  19955. {
  19956. Unit::AuraEffectList const& auraList = GetAuraEffectsByType(*itr);
  19957. if (!auraList.empty())
  19958. auraList.front()->HandleEffect(this, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true);
  19959. }
  19960. if (HasAuraType(SPELL_AURA_MOD_STUN))
  19961. SetRooted(true);
  19962. // manual send package (have code in HandleEffect(this, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true); that must not be re-applied.
  19963. if (HasAuraType(SPELL_AURA_MOD_ROOT))
  19964. SetRooted(true, true);
  19965. SendAurasForTarget(this);
  19966. SendEnchantmentDurations(); // must be after add to map
  19967. SendItemDurations(); // must be after add to map
  19968. // raid downscaling - send difficulty to player
  19969. if (GetMap()->IsRaid())
  19970. {
  19971. if (GetMap()->GetDifficulty() != GetRaidDifficulty())
  19972. {
  19973. StoreRaidMapDifficulty();
  19974. SendRaidDifficulty(GetGroup() != NULL, GetStoredRaidDifficulty());
  19975. }
  19976. }
  19977. else if (GetRaidDifficulty() != GetStoredRaidDifficulty())
  19978. SendRaidDifficulty(GetGroup() != NULL);
  19979. }
  19980. void Player::SendUpdateToOutOfRangeGroupMembers()
  19981. {
  19982. if (m_groupUpdateMask == GROUP_UPDATE_FLAG_NONE)
  19983. return;
  19984. if (Group* group = GetGroup())
  19985. group->UpdatePlayerOutOfRange(this);
  19986. m_groupUpdateMask = GROUP_UPDATE_FLAG_NONE;
  19987. m_auraRaidUpdateMask = 0;
  19988. if (Pet* pet = GetPet())
  19989. pet->ResetAuraUpdateMaskForRaid();
  19990. }
  19991. void Player::SendTransferAborted(uint32 mapid, TransferAbortReason reason, uint8 arg)
  19992. {
  19993. WorldPacket data(SMSG_TRANSFER_ABORTED, 4+2);
  19994. data << uint32(mapid);
  19995. data << uint8(reason); // transfer abort reason
  19996. data << uint8(arg);
  19997. GetSession()->SendPacket(&data);
  19998. }
  19999. void Player::SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time)
  20000. {
  20001. // type of warning, based on the time remaining until reset
  20002. uint32 type;
  20003. if (time > 3600)
  20004. type = RAID_INSTANCE_WELCOME;
  20005. else if (time > 900 && time <= 3600)
  20006. type = RAID_INSTANCE_WARNING_HOURS;
  20007. else if (time > 300 && time <= 900)
  20008. type = RAID_INSTANCE_WARNING_MIN;
  20009. else
  20010. type = RAID_INSTANCE_WARNING_MIN_SOON;
  20011. WorldPacket data(SMSG_RAID_INSTANCE_MESSAGE, 4+4+4+4);
  20012. data << uint32(type);
  20013. data << uint32(mapid);
  20014. data << uint32(difficulty); // difficulty
  20015. data << uint32(time);
  20016. if (type == RAID_INSTANCE_WELCOME)
  20017. {
  20018. data << uint8(0); // is locked
  20019. data << uint8(0); // is extended, ignored if prev field is 0
  20020. }
  20021. GetSession()->SendPacket(&data);
  20022. }
  20023. void Player::ApplyEquipCooldown(Item* pItem)
  20024. {
  20025. if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_PROTO_FLAG_NO_EQUIP_COOLDOWN))
  20026. return;
  20027. for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  20028. {
  20029. _Spell const& spellData = pItem->GetTemplate()->Spells[i];
  20030. // no spell
  20031. if (!spellData.SpellId)
  20032. continue;
  20033. // wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown)
  20034. if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
  20035. continue;
  20036. // Don't replace longer cooldowns by equip cooldown if we have any.
  20037. SpellCooldowns::iterator itr = m_spellCooldowns.find(spellData.SpellId);
  20038. if (itr != m_spellCooldowns.end() && itr->second.itemid == pItem->GetEntry() && itr->second.end > time(NULL) + 30)
  20039. continue;
  20040. AddSpellCooldown(spellData.SpellId, pItem->GetEntry(), time(NULL) + 30);
  20041. WorldPacket data(SMSG_ITEM_COOLDOWN, 12);
  20042. data << pItem->GetGUID();
  20043. data << uint32(spellData.SpellId);
  20044. GetSession()->SendPacket(&data);
  20045. }
  20046. }
  20047. void Player::ResetSpells(bool myClassOnly)
  20048. {
  20049. // not need after this call
  20050. if (HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
  20051. RemoveAtLoginFlag(AT_LOGIN_RESET_SPELLS, true);
  20052. // make full copy of map (spells removed and marked as deleted at another spell remove
  20053. // and we can't use original map for safe iterative with visit each spell at loop end
  20054. PlayerSpellMap smap = GetSpellMap();
  20055. uint32 family;
  20056. if (myClassOnly)
  20057. {
  20058. ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(getClass());
  20059. if (!clsEntry)
  20060. return;
  20061. family = clsEntry->spellfamily;
  20062. for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter)
  20063. {
  20064. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(iter->first);
  20065. if (!spellInfo)
  20066. continue;
  20067. // skip server-side/triggered spells
  20068. if (spellInfo->SpellLevel == 0)
  20069. continue;
  20070. // skip wrong class/race skills
  20071. if (!IsSpellFitByClassAndRace(spellInfo->Id))
  20072. continue;
  20073. // skip other spell families
  20074. if (spellInfo->SpellFamilyName != family)
  20075. continue;
  20076. // skip spells with first rank learned as talent (and all talents then also)
  20077. uint32 firstRank = spellInfo->GetFirstRankSpell()->Id;
  20078. if (GetTalentSpellCost(firstRank) > 0)
  20079. continue;
  20080. // skip broken spells
  20081. if (!SpellMgr::IsSpellValid(spellInfo, this, false))
  20082. continue;
  20083. }
  20084. }
  20085. else
  20086. for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter)
  20087. RemoveSpell(iter->first, false, false); // only iter->first can be accessed, object by iter->second can be deleted already
  20088. LearnDefaultSkills();
  20089. LearnCustomSpells();
  20090. LearnQuestRewardedSpells();
  20091. }
  20092. void Player::LearnCustomSpells()
  20093. {
  20094. if (!sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS))
  20095. return;
  20096. // learn default race/class spells
  20097. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  20098. for (PlayerCreateInfoSpells::const_iterator itr = info->customSpells.begin(); itr != info->customSpells.end(); ++itr)
  20099. {
  20100. uint32 tspell = *itr;
  20101. TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell);
  20102. if (!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add
  20103. AddSpell(tspell, true, true, true, false);
  20104. else // but send in normal spell in game learn case
  20105. LearnSpell(tspell, true);
  20106. }
  20107. }
  20108. void Player::LearnDefaultSkills()
  20109. {
  20110. // learn default race/class skills
  20111. PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
  20112. for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
  20113. {
  20114. uint32 skillId = itr->SkillId;
  20115. if (HasSkill(skillId))
  20116. continue;
  20117. LearnDefaultSkill(skillId, itr->Rank);
  20118. }
  20119. }
  20120. void Player::LearnDefaultSkill(uint32 skillId, uint16 rank)
  20121. {
  20122. SkillRaceClassInfoEntry const* rcInfo = GetSkillRaceClassInfo(skillId, getRace(), getClass());
  20123. if (!rcInfo)
  20124. return;
  20125. TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial skill, id = %u", uint32(getClass()), uint32(getRace()), skillId);
  20126. switch (GetSkillRangeType(rcInfo))
  20127. {
  20128. case SKILL_RANGE_LANGUAGE:
  20129. SetSkill(skillId, 0, 300, 300);
  20130. break;
  20131. case SKILL_RANGE_LEVEL:
  20132. {
  20133. uint16 skillValue = 1;
  20134. uint16 maxValue = GetMaxSkillValueForLevel();
  20135. if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE)
  20136. skillValue = maxValue;
  20137. else if (getClass() == CLASS_DEATH_KNIGHT)
  20138. skillValue = std::min(std::max<uint16>({ 1, uint16((getLevel() - 1) * 5) }), maxValue);
  20139. else if (skillId == SKILL_FIST_WEAPONS)
  20140. skillValue = std::max<uint16>(1, GetSkillValue(SKILL_UNARMED));
  20141. else if (skillId == SKILL_LOCKPICKING)
  20142. skillValue = std::max<uint16>(1, GetSkillValue(SKILL_LOCKPICKING));
  20143. SetSkill(skillId, 0, skillValue, maxValue);
  20144. break;
  20145. }
  20146. case SKILL_RANGE_MONO:
  20147. SetSkill(skillId, 0, 1, 1);
  20148. break;
  20149. case SKILL_RANGE_RANK:
  20150. {
  20151. if (!rank)
  20152. break;
  20153. SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcInfo->SkillTier);
  20154. uint16 maxValue = tier->MaxSkill[std::max<int32>(rank - 1, 0)];
  20155. uint16 skillValue = 1;
  20156. if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE)
  20157. skillValue = maxValue;
  20158. else if (getClass() == CLASS_DEATH_KNIGHT)
  20159. skillValue = std::min(std::max<uint16>({ uint16(1), uint16((getLevel() - 1) * 5) }), maxValue);
  20160. SetSkill(skillId, rank, skillValue, maxValue);
  20161. break;
  20162. }
  20163. default:
  20164. break;
  20165. }
  20166. }
  20167. void Player::LearnQuestRewardedSpells(Quest const* quest)
  20168. {
  20169. int32 spell_id = quest->GetRewSpellCast();
  20170. uint32 src_spell_id = quest->GetSrcSpell();
  20171. // skip quests without rewarded spell
  20172. if (!spell_id)
  20173. return;
  20174. // if RewSpellCast = -1 we remove aura do to SrcSpell from player.
  20175. if (spell_id == -1 && src_spell_id)
  20176. {
  20177. RemoveAurasDueToSpell(src_spell_id);
  20178. return;
  20179. }
  20180. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
  20181. if (!spellInfo)
  20182. return;
  20183. // check learned spells state
  20184. bool found = false;
  20185. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  20186. {
  20187. if (spellInfo->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL && !HasSpell(spellInfo->Effects[i].TriggerSpell))
  20188. {
  20189. found = true;
  20190. break;
  20191. }
  20192. }
  20193. // skip quests with not teaching spell or already known spell
  20194. if (!found)
  20195. return;
  20196. // prevent learn non first rank unknown profession and second specialization for same profession)
  20197. uint32 learned_0 = spellInfo->Effects[0].TriggerSpell;
  20198. if (sSpellMgr->GetSpellRank(learned_0) > 1 && !HasSpell(learned_0))
  20199. {
  20200. SpellInfo const* learnedInfo = sSpellMgr->GetSpellInfo(learned_0);
  20201. if (!learnedInfo)
  20202. return;
  20203. // not have first rank learned (unlearned prof?)
  20204. if (!HasSpell(learnedInfo->GetFirstRankSpell()->Id))
  20205. return;
  20206. SpellsRequiringSpellMapBounds spellsRequired = sSpellMgr->GetSpellsRequiredForSpellBounds(learned_0);
  20207. for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequired.first; itr2 != spellsRequired.second; ++itr2)
  20208. {
  20209. uint32 profSpell = itr2->second;
  20210. // specialization
  20211. if (learnedInfo->Effects[0].Effect == SPELL_EFFECT_TRADE_SKILL && learnedInfo->Effects[1].Effect == 0 && profSpell)
  20212. {
  20213. // search other specialization for same prof
  20214. for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
  20215. {
  20216. if (itr->second->state == PLAYERSPELL_REMOVED || itr->first == learned_0)
  20217. continue;
  20218. SpellInfo const* itrInfo = sSpellMgr->GetSpellInfo(itr->first);
  20219. if (!itrInfo)
  20220. return;
  20221. // compare only specializations
  20222. if (itrInfo->Effects[0].Effect != SPELL_EFFECT_TRADE_SKILL || itrInfo->Effects[1].Effect != 0)
  20223. continue;
  20224. // compare same chain spells
  20225. if (sSpellMgr->IsSpellRequiringSpell(itr->first, profSpell))
  20226. return;
  20227. }
  20228. }
  20229. }
  20230. }
  20231. CastSpell(this, spell_id, true);
  20232. }
  20233. void Player::LearnQuestRewardedSpells()
  20234. {
  20235. // learn spells received from quest completing
  20236. for (RewardedQuestSet::const_iterator itr = m_RewardedQuests.begin(); itr != m_RewardedQuests.end(); ++itr)
  20237. {
  20238. Quest const* quest = sObjectMgr->GetQuestTemplate(*itr);
  20239. if (!quest)
  20240. continue;
  20241. LearnQuestRewardedSpells(quest);
  20242. }
  20243. }
  20244. void Player::LearnSkillRewardedSpells(uint32 skillId, uint32 skillValue)
  20245. {
  20246. uint32 raceMask = getRaceMask();
  20247. uint32 classMask = getClassMask();
  20248. for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
  20249. {
  20250. SkillLineAbilityEntry const* ability = sSkillLineAbilityStore.LookupEntry(j);
  20251. if (!ability || ability->skillId != skillId)
  20252. continue;
  20253. if (!sSpellMgr->GetSpellInfo(ability->spellId))
  20254. continue;
  20255. if (ability->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE && ability->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
  20256. continue;
  20257. // Check race if set
  20258. if (ability->racemask && !(ability->racemask & raceMask))
  20259. continue;
  20260. // Check class if set
  20261. if (ability->classmask && !(ability->classmask & classMask))
  20262. continue;
  20263. // need unlearn spell
  20264. if (skillValue < ability->req_skill_value && ability->AutolearnType == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
  20265. RemoveSpell(ability->spellId);
  20266. // need learn
  20267. else if (!IsInWorld())
  20268. AddSpell(ability->spellId, true, true, true, false, false, true);
  20269. else
  20270. LearnSpell(ability->spellId, true, true);
  20271. }
  20272. }
  20273. void Player::SendAurasForTarget(Unit* target)
  20274. {
  20275. if (!target || target->GetVisibleAuras()->empty()) // speedup things
  20276. return;
  20277. /*! Blizz sends certain movement packets sometimes even before CreateObject
  20278. These movement packets are usually found in SMSG_COMPRESSED_MOVES
  20279. */
  20280. if (target->HasAuraType(SPELL_AURA_FEATHER_FALL))
  20281. target->SetFeatherFall(true, true);
  20282. if (target->HasAuraType(SPELL_AURA_WATER_WALK))
  20283. target->SetWaterWalking(true, true);
  20284. if (target->HasAuraType(SPELL_AURA_HOVER))
  20285. target->SetHover(true, true);
  20286. WorldPacket data(SMSG_AURA_UPDATE_ALL);
  20287. data << target->GetPackGUID();
  20288. Unit::VisibleAuraMap const* visibleAuras = target->GetVisibleAuras();
  20289. for (Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr)
  20290. {
  20291. AuraApplication * auraApp = itr->second;
  20292. auraApp->BuildUpdatePacket(data, false);
  20293. }
  20294. GetSession()->SendPacket(&data);
  20295. }
  20296. void Player::SetDailyQuestStatus(uint32 quest_id)
  20297. {
  20298. if (Quest const* qQuest = sObjectMgr->GetQuestTemplate(quest_id))
  20299. {
  20300. if (!qQuest->IsDFQuest())
  20301. {
  20302. for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
  20303. {
  20304. if (!GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
  20305. {
  20306. SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx, quest_id);
  20307. m_lastDailyQuestTime = time(NULL); // last daily quest time
  20308. m_DailyQuestChanged = true;
  20309. break;
  20310. }
  20311. }
  20312. } else
  20313. {
  20314. m_DFQuests.insert(quest_id);
  20315. m_lastDailyQuestTime = time(NULL);
  20316. m_DailyQuestChanged = true;
  20317. }
  20318. }
  20319. }
  20320. void Player::SetWeeklyQuestStatus(uint32 quest_id)
  20321. {
  20322. m_weeklyquests.insert(quest_id);
  20323. m_WeeklyQuestChanged = true;
  20324. }
  20325. void Player::SetSeasonalQuestStatus(uint32 quest_id)
  20326. {
  20327. Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
  20328. if (!quest)
  20329. return;
  20330. m_seasonalquests[sGameEventMgr->GetEventIdForQuest(quest)].insert(quest_id);
  20331. m_SeasonalQuestChanged = true;
  20332. }
  20333. void Player::SetMonthlyQuestStatus(uint32 quest_id)
  20334. {
  20335. m_monthlyquests.insert(quest_id);
  20336. m_MonthlyQuestChanged = true;
  20337. }
  20338. void Player::ResetDailyQuestStatus()
  20339. {
  20340. for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
  20341. SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx, 0);
  20342. m_DFQuests.clear(); // Dungeon Finder Quests.
  20343. // DB data deleted in caller
  20344. m_DailyQuestChanged = false;
  20345. m_lastDailyQuestTime = 0;
  20346. }
  20347. void Player::ResetWeeklyQuestStatus()
  20348. {
  20349. if (m_weeklyquests.empty())
  20350. return;
  20351. m_weeklyquests.clear();
  20352. // DB data deleted in caller
  20353. m_WeeklyQuestChanged = false;
  20354. }
  20355. void Player::ResetSeasonalQuestStatus(uint16 event_id)
  20356. {
  20357. if (m_seasonalquests.empty() || m_seasonalquests[event_id].empty())
  20358. return;
  20359. m_seasonalquests.erase(event_id);
  20360. // DB data deleted in caller
  20361. m_SeasonalQuestChanged = false;
  20362. }
  20363. void Player::ResetMonthlyQuestStatus()
  20364. {
  20365. if (m_monthlyquests.empty())
  20366. return;
  20367. m_monthlyquests.clear();
  20368. // DB data deleted in caller
  20369. m_MonthlyQuestChanged = false;
  20370. }
  20371. Battleground* Player::GetBattleground() const
  20372. {
  20373. if (GetBattlegroundId() == 0)
  20374. return NULL;
  20375. return sBattlegroundMgr->GetBattleground(GetBattlegroundId(), m_bgData.bgTypeID);
  20376. }
  20377. bool Player::InBattlegroundQueue() const
  20378. {
  20379. for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
  20380. if (m_bgBattlegroundQueueID[i].bgQueueTypeId != BATTLEGROUND_QUEUE_NONE)
  20381. return true;
  20382. return false;
  20383. }
  20384. BattlegroundQueueTypeId Player::GetBattlegroundQueueTypeId(uint32 index) const
  20385. {
  20386. return m_bgBattlegroundQueueID[index].bgQueueTypeId;
  20387. }
  20388. uint32 Player::GetBattlegroundQueueIndex(BattlegroundQueueTypeId bgQueueTypeId) const
  20389. {
  20390. for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
  20391. if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
  20392. return i;
  20393. return PLAYER_MAX_BATTLEGROUND_QUEUES;
  20394. }
  20395. bool Player::IsInvitedForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId) const
  20396. {
  20397. for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
  20398. if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
  20399. return m_bgBattlegroundQueueID[i].invitedToInstance != 0;
  20400. return false;
  20401. }
  20402. bool Player::InBattlegroundQueueForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId) const
  20403. {
  20404. return GetBattlegroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES;
  20405. }
  20406. void Player::SetBattlegroundId(uint32 val, BattlegroundTypeId bgTypeId)
  20407. {
  20408. m_bgData.bgInstanceID = val;
  20409. m_bgData.bgTypeID = bgTypeId;
  20410. }
  20411. uint32 Player::AddBattlegroundQueueId(BattlegroundQueueTypeId val)
  20412. {
  20413. for (uint8 i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
  20414. {
  20415. if (m_bgBattlegroundQueueID[i].bgQueueTypeId == BATTLEGROUND_QUEUE_NONE || m_bgBattlegroundQueueID[i].bgQueueTypeId == val)
  20416. {
  20417. m_bgBattlegroundQueueID[i].bgQueueTypeId = val;
  20418. m_bgBattlegroundQueueID[i].invitedToInstance = 0;
  20419. return i;
  20420. }
  20421. }
  20422. return PLAYER_MAX_BATTLEGROUND_QUEUES;
  20423. }
  20424. bool Player::HasFreeBattlegroundQueueId()
  20425. {
  20426. for (uint8 i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
  20427. if (m_bgBattlegroundQueueID[i].bgQueueTypeId == BATTLEGROUND_QUEUE_NONE)
  20428. return true;
  20429. return false;
  20430. }
  20431. void Player::RemoveBattlegroundQueueId(BattlegroundQueueTypeId val)
  20432. {
  20433. for (uint8 i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
  20434. {
  20435. if (m_bgBattlegroundQueueID[i].bgQueueTypeId == val)
  20436. {
  20437. m_bgBattlegroundQueueID[i].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE;
  20438. m_bgBattlegroundQueueID[i].invitedToInstance = 0;
  20439. return;
  20440. }
  20441. }
  20442. }
  20443. void Player::SetInviteForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId, uint32 instanceId)
  20444. {
  20445. for (uint8 i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
  20446. if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
  20447. m_bgBattlegroundQueueID[i].invitedToInstance = instanceId;
  20448. }
  20449. bool Player::IsInvitedForBattlegroundInstance(uint32 instanceId) const
  20450. {
  20451. for (uint8 i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
  20452. if (m_bgBattlegroundQueueID[i].invitedToInstance == instanceId)
  20453. return true;
  20454. return false;
  20455. }
  20456. bool Player::InArena() const
  20457. {
  20458. Battleground* bg = GetBattleground();
  20459. if (!bg || !bg->isArena())
  20460. return false;
  20461. return true;
  20462. }
  20463. bool Player::GetBGAccessByLevel(BattlegroundTypeId bgTypeId) const
  20464. {
  20465. // get a template bg instead of running one
  20466. Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
  20467. if (!bg)
  20468. return false;
  20469. // limit check leel to dbc compatible level range
  20470. uint32 level = getLevel();
  20471. if (level > DEFAULT_MAX_LEVEL)
  20472. level = DEFAULT_MAX_LEVEL;
  20473. if (level < bg->GetMinLevel() || level > bg->GetMaxLevel())
  20474. return false;
  20475. return true;
  20476. }
  20477. float Player::GetReputationPriceDiscount(Creature const* creature) const
  20478. {
  20479. FactionTemplateEntry const* vendor_faction = creature->GetFactionTemplateEntry();
  20480. if (!vendor_faction || !vendor_faction->faction)
  20481. return 1.0f;
  20482. ReputationRank rank = GetReputationRank(vendor_faction->faction);
  20483. if (rank <= REP_NEUTRAL)
  20484. return 1.0f;
  20485. return 1.0f - 0.05f* (rank - REP_NEUTRAL);
  20486. }
  20487. bool Player::IsSpellFitByClassAndRace(uint32 spell_id) const
  20488. {
  20489. uint32 racemask = getRaceMask();
  20490. uint32 classmask = getClassMask();
  20491. SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spell_id);
  20492. if (bounds.first == bounds.second)
  20493. return true;
  20494. for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
  20495. {
  20496. // skip wrong race skills
  20497. if (_spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0)
  20498. continue;
  20499. // skip wrong class skills
  20500. if (_spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0)
  20501. continue;
  20502. return true;
  20503. }
  20504. return false;
  20505. }
  20506. bool Player::HasQuestForGO(int32 GOId) const
  20507. {
  20508. for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
  20509. {
  20510. uint32 questid = GetQuestSlotQuestId(i);
  20511. if (questid == 0)
  20512. continue;
  20513. QuestStatusMap::const_iterator qs_itr = m_QuestStatus.find(questid);
  20514. if (qs_itr == m_QuestStatus.end())
  20515. continue;
  20516. QuestStatusData const& qs = qs_itr->second;
  20517. if (qs.Status == QUEST_STATUS_INCOMPLETE)
  20518. {
  20519. Quest const* qinfo = sObjectMgr->GetQuestTemplate(questid);
  20520. if (!qinfo)
  20521. continue;
  20522. if (GetGroup() && GetGroup()->isRaidGroup() && !qinfo->IsAllowedInRaid(GetMap()->GetDifficulty()))
  20523. continue;
  20524. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  20525. {
  20526. if (qinfo->RequiredNpcOrGo[j] >= 0) //skip non GO case
  20527. continue;
  20528. if ((-1)*GOId == qinfo->RequiredNpcOrGo[j] && qs.CreatureOrGOCount[j] < qinfo->RequiredNpcOrGoCount[j])
  20529. return true;
  20530. }
  20531. }
  20532. }
  20533. return false;
  20534. }
  20535. void Player::UpdateForQuestWorldObjects()
  20536. {
  20537. if (m_clientGUIDs.empty())
  20538. return;
  20539. UpdateData udata(GetMapId());
  20540. WorldPacket packet;
  20541. for (GuidSet::iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
  20542. {
  20543. if (itr->IsGameObject())
  20544. {
  20545. if (GameObject* obj = HashMapHolder<GameObject>::Find(*itr))
  20546. obj->BuildValuesUpdateBlockForPlayer(&udata, this);
  20547. }
  20548. else if (itr->IsCreatureOrVehicle())
  20549. {
  20550. Creature* obj = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr);
  20551. if (!obj)
  20552. continue;
  20553. // check if this unit requires quest specific flags
  20554. if (!obj->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK))
  20555. continue;
  20556. SpellClickInfoMapBounds clickPair = sObjectMgr->GetSpellClickInfoMapBounds(obj->GetEntry());
  20557. for (SpellClickInfoContainer::const_iterator _itr = clickPair.first; _itr != clickPair.second; ++_itr)
  20558. {
  20559. //! This code doesn't look right, but it was logically converted to condition system to do the exact
  20560. //! same thing it did before. It definitely needs to be overlooked for intended functionality.
  20561. ConditionList conds = sConditionMgr->GetConditionsForSpellClickEvent(obj->GetEntry(), _itr->second.spellId);
  20562. bool buildUpdateBlock = false;
  20563. for (ConditionList::const_iterator jtr = conds.begin(); jtr != conds.end() && !buildUpdateBlock; ++jtr)
  20564. if ((*jtr)->ConditionType == CONDITION_QUESTREWARDED || (*jtr)->ConditionType == CONDITION_QUESTTAKEN)
  20565. buildUpdateBlock = true;
  20566. if (buildUpdateBlock)
  20567. {
  20568. obj->BuildValuesUpdateBlockForPlayer(&udata, this);
  20569. break;
  20570. }
  20571. }
  20572. }
  20573. }
  20574. udata.BuildPacket(&packet);
  20575. GetSession()->SendPacket(&packet);
  20576. }
  20577. void Player::SetSummonPoint(uint32 mapid, float x, float y, float z)
  20578. {
  20579. m_summon_expire = time(NULL) + MAX_PLAYER_SUMMON_DELAY;
  20580. m_summon_mapid = mapid;
  20581. m_summon_x = x;
  20582. m_summon_y = y;
  20583. m_summon_z = z;
  20584. }
  20585. void Player::SummonIfPossible(bool agree)
  20586. {
  20587. if (!agree)
  20588. {
  20589. m_summon_expire = 0;
  20590. return;
  20591. }
  20592. // expire and auto declined
  20593. if (m_summon_expire < time(NULL))
  20594. return;
  20595. // stop taxi flight at summon
  20596. if (IsInFlight())
  20597. {
  20598. GetMotionMaster()->MovementExpired();
  20599. CleanupAfterTaxiFlight();
  20600. }
  20601. // drop flag at summon
  20602. // 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
  20603. if (Battleground* bg = GetBattleground())
  20604. bg->EventPlayerDroppedFlag(this);
  20605. m_summon_expire = 0;
  20606. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS, 1);
  20607. TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z, GetOrientation());
  20608. }
  20609. void Player::RemoveItemDurations(Item* item)
  20610. {
  20611. for (ItemDurationList::iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end(); ++itr)
  20612. {
  20613. if (*itr == item)
  20614. {
  20615. m_itemDuration.erase(itr);
  20616. break;
  20617. }
  20618. }
  20619. }
  20620. void Player::AddItemDurations(Item* item)
  20621. {
  20622. if (item->GetUInt32Value(ITEM_FIELD_DURATION))
  20623. {
  20624. m_itemDuration.push_back(item);
  20625. item->SendTimeUpdate(this);
  20626. }
  20627. }
  20628. void Player::AutoUnequipOffhandIfNeed(bool force /*= false*/)
  20629. {
  20630. Item* offItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
  20631. if (!offItem)
  20632. return;
  20633. // unequip offhand weapon if player doesn't have dual wield anymore
  20634. if (!CanDualWield() && (offItem->GetTemplate()->InventoryType == INVTYPE_WEAPONOFFHAND || offItem->GetTemplate()->InventoryType == INVTYPE_WEAPON))
  20635. force = true;
  20636. // need unequip offhand for 2h-weapon without TitanGrip (in any from hands)
  20637. if (!force && (CanTitanGrip() || (offItem->GetTemplate()->InventoryType != INVTYPE_2HWEAPON && !IsTwoHandUsed())))
  20638. return;
  20639. ItemPosCountVec off_dest;
  20640. uint8 off_msg = CanStoreItem(NULL_BAG, NULL_SLOT, off_dest, offItem, false);
  20641. if (off_msg == EQUIP_ERR_OK)
  20642. {
  20643. RemoveItem(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
  20644. StoreItem(off_dest, offItem, true);
  20645. }
  20646. else
  20647. {
  20648. MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
  20649. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  20650. offItem->DeleteFromInventoryDB(trans); // deletes item from character's inventory
  20651. offItem->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
  20652. std::string subject = GetSession()->GetTrinityString(LANG_NOT_EQUIPPED_ITEM);
  20653. 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);
  20654. CharacterDatabase.CommitTransaction(trans);
  20655. }
  20656. }
  20657. OutdoorPvP* Player::GetOutdoorPvP() const
  20658. {
  20659. return sOutdoorPvPMgr->GetOutdoorPvPToZoneId(GetZoneId());
  20660. }
  20661. bool Player::HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item const* ignoreItem) const
  20662. {
  20663. if (spellInfo->EquippedItemClass < 0)
  20664. return true;
  20665. // scan other equipped items for same requirements (mostly 2 daggers/etc)
  20666. // for optimize check 2 used cases only
  20667. switch (spellInfo->EquippedItemClass)
  20668. {
  20669. case ITEM_CLASS_WEAPON:
  20670. {
  20671. for (uint8 i = EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i)
  20672. if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i))
  20673. if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
  20674. return true;
  20675. break;
  20676. }
  20677. case ITEM_CLASS_ARMOR:
  20678. {
  20679. // tabard not have dependent spells
  20680. for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_MAINHAND; ++i)
  20681. if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i))
  20682. if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
  20683. return true;
  20684. // shields can be equipped to offhand slot
  20685. if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
  20686. if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
  20687. return true;
  20688. // ranged slot can have some armor subclasses
  20689. if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
  20690. if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
  20691. return true;
  20692. break;
  20693. }
  20694. default:
  20695. TC_LOG_ERROR("entities.player", "HasItemFitToSpellRequirements: Not handled spell requirement for item class %u", spellInfo->EquippedItemClass);
  20696. break;
  20697. }
  20698. return false;
  20699. }
  20700. bool Player::CanNoReagentCast(SpellInfo const* spellInfo) const
  20701. {
  20702. // don't take reagents for spells with SPELL_ATTR5_NO_REAGENT_WHILE_PREP
  20703. if (spellInfo->AttributesEx5 & SPELL_ATTR5_NO_REAGENT_WHILE_PREP &&
  20704. HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))
  20705. return true;
  20706. // Check no reagent use mask
  20707. flag96 noReagentMask;
  20708. noReagentMask[0] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1);
  20709. noReagentMask[1] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1+1);
  20710. noReagentMask[2] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1+2);
  20711. if (spellInfo->SpellFamilyFlags & noReagentMask)
  20712. return true;
  20713. return false;
  20714. }
  20715. void Player::RemoveItemDependentAurasAndCasts(Item* pItem)
  20716. {
  20717. for (AuraMap::iterator itr = m_ownedAuras.begin(); itr != m_ownedAuras.end();)
  20718. {
  20719. Aura* aura = itr->second;
  20720. // skip passive (passive item dependent spells work in another way) and not self applied auras
  20721. SpellInfo const* spellInfo = aura->GetSpellInfo();
  20722. if (aura->IsPassive() || aura->GetCasterGUID() != GetGUID())
  20723. {
  20724. ++itr;
  20725. continue;
  20726. }
  20727. // skip if not item dependent or have alternative item
  20728. if (HasItemFitToSpellRequirements(spellInfo, pItem))
  20729. {
  20730. ++itr;
  20731. continue;
  20732. }
  20733. // no alt item, remove aura, restart check
  20734. RemoveOwnedAura(itr);
  20735. }
  20736. // currently cast spells can be dependent from item
  20737. for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i)
  20738. if (Spell* spell = GetCurrentSpell(CurrentSpellTypes(i)))
  20739. if (spell->getState() != SPELL_STATE_DELAYED && !HasItemFitToSpellRequirements(spell->m_spellInfo, pItem))
  20740. InterruptSpell(CurrentSpellTypes(i));
  20741. }
  20742. uint32 Player::GetResurrectionSpellId()
  20743. {
  20744. // search priceless resurrection possibilities
  20745. uint32 prio = 0;
  20746. uint32 spell_id = 0;
  20747. AuraEffectList const& dummyAuras = GetAuraEffectsByType(SPELL_AURA_DUMMY);
  20748. for (AuraEffectList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
  20749. {
  20750. // Soulstone Resurrection // prio: 3 (max, non death persistent)
  20751. if (prio < 2 && (*itr)->GetSpellInfo()->SpellVisual[0] == 99 && (*itr)->GetSpellInfo()->SpellIconID == 92)
  20752. {
  20753. switch ((*itr)->GetId())
  20754. {
  20755. case 20707: spell_id = 3026; break; // rank 1
  20756. case 20762: spell_id = 20758; break; // rank 2
  20757. case 20763: spell_id = 20759; break; // rank 3
  20758. case 20764: spell_id = 20760; break; // rank 4
  20759. case 20765: spell_id = 20761; break; // rank 5
  20760. case 27239: spell_id = 27240; break; // rank 6
  20761. case 47883: spell_id = 47882; break; // rank 7
  20762. default:
  20763. TC_LOG_ERROR("entities.player", "Unhandled spell %u: S.Resurrection", (*itr)->GetId());
  20764. continue;
  20765. }
  20766. prio = 3;
  20767. }
  20768. // Twisting Nether // prio: 2 (max)
  20769. else if ((*itr)->GetId() == 23701 && roll_chance_i(10))
  20770. {
  20771. prio = 2;
  20772. spell_id = 23700;
  20773. }
  20774. }
  20775. // Reincarnation (passive spell) // prio: 1 // Glyph of Renewed Life
  20776. if (prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169) && (HasAura(58059) || HasItemCount(17030)))
  20777. spell_id = 21169;
  20778. return spell_id;
  20779. }
  20780. // Used in triggers for check "Only to targets that grant experience or honor" req
  20781. bool Player::isHonorOrXPTarget(Unit const* victim) const
  20782. {
  20783. uint8 v_level = victim->getLevel();
  20784. uint8 k_grey = Trinity::XP::GetGrayLevel(getLevel());
  20785. // Victim level less gray level
  20786. if (v_level <= k_grey)
  20787. return false;
  20788. if (Creature const* const creature = victim->ToCreature())
  20789. {
  20790. if (creature->IsTotem() ||
  20791. creature->IsPet() ||
  20792. creature->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL)
  20793. return false;
  20794. }
  20795. return true;
  20796. }
  20797. bool Player::GetsRecruitAFriendBonus(bool forXP)
  20798. {
  20799. bool recruitAFriend = false;
  20800. if (getLevel() <= sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL) || !forXP)
  20801. {
  20802. if (Group* group = this->GetGroup())
  20803. {
  20804. for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  20805. {
  20806. Player* player = itr->GetSource();
  20807. if (!player)
  20808. continue;
  20809. if (!player->IsAtRecruitAFriendDistance(this))
  20810. continue; // member (alive or dead) or his corpse at req. distance
  20811. if (forXP)
  20812. {
  20813. // level must be allowed to get RaF bonus
  20814. if (player->getLevel() > sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL))
  20815. continue;
  20816. // level difference must be small enough to get RaF bonus, UNLESS we are lower level
  20817. if (player->getLevel() < getLevel())
  20818. if (uint8(getLevel() - player->getLevel()) > sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL_DIFFERENCE))
  20819. continue;
  20820. }
  20821. bool ARecruitedB = (player->GetSession()->GetRecruiterId() == GetSession()->GetAccountId());
  20822. bool BRecruitedA = (GetSession()->GetRecruiterId() == player->GetSession()->GetAccountId());
  20823. if (ARecruitedB || BRecruitedA)
  20824. {
  20825. recruitAFriend = true;
  20826. break;
  20827. }
  20828. }
  20829. }
  20830. }
  20831. return recruitAFriend;
  20832. }
  20833. void Player::RewardPlayerAndGroupAtKill(Unit* victim, bool isBattleGround)
  20834. {
  20835. KillRewarder(this, victim, isBattleGround).Reward();
  20836. }
  20837. void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewardSource)
  20838. {
  20839. if (!pRewardSource)
  20840. return;
  20841. ObjectGuid creature_guid = (pRewardSource->GetTypeId() == TYPEID_UNIT) ? pRewardSource->GetGUID() : ObjectGuid::Empty;
  20842. // prepare data for near group iteration
  20843. if (Group* group = GetGroup())
  20844. {
  20845. for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  20846. {
  20847. Player* player = itr->GetSource();
  20848. if (!player)
  20849. continue;
  20850. if (!player->IsAtGroupRewardDistance(pRewardSource))
  20851. continue; // member (alive or dead) or his corpse at req. distance
  20852. // quest objectives updated only for alive group member or dead but with not released body
  20853. if (player->IsAlive()|| !player->GetCorpse())
  20854. player->KilledMonsterCredit(creature_id, creature_guid);
  20855. }
  20856. }
  20857. else // if (!group)
  20858. KilledMonsterCredit(creature_id, creature_guid);
  20859. }
  20860. bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const
  20861. {
  20862. if (!pRewardSource)
  20863. return false;
  20864. const WorldObject* player = GetCorpse();
  20865. if (!player || IsAlive())
  20866. player = this;
  20867. if (player->GetMapId() != pRewardSource->GetMapId() || player->GetInstanceId() != pRewardSource->GetInstanceId())
  20868. return false;
  20869. return pRewardSource->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE);
  20870. }
  20871. bool Player::IsAtRecruitAFriendDistance(WorldObject const* pOther) const
  20872. {
  20873. if (!pOther)
  20874. return false;
  20875. const WorldObject* player = GetCorpse();
  20876. if (!player || IsAlive())
  20877. player = this;
  20878. if (player->GetMapId() != pOther->GetMapId() || player->GetInstanceId() != pOther->GetInstanceId())
  20879. return false;
  20880. return pOther->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE);
  20881. }
  20882. void Player::ResurrectUsingRequestData()
  20883. {
  20884. /// Teleport before resurrecting by player, otherwise the player might get attacked from creatures near his corpse
  20885. float x, y, z, o;
  20886. _resurrectionData->Location.GetPosition(x, y, z, o);
  20887. TeleportTo(_resurrectionData->Location.GetMapId(), x, y, z, o);
  20888. if (IsBeingTeleported())
  20889. {
  20890. ScheduleDelayedOperation(DELAYED_RESURRECT_PLAYER);
  20891. return;
  20892. }
  20893. ResurrectPlayer(0.0f, false);
  20894. if (GetMaxHealth() > _resurrectionData->Health)
  20895. SetHealth(_resurrectionData->Health);
  20896. else
  20897. SetFullHealth();
  20898. if (uint32(GetMaxPower(POWER_MANA)) > _resurrectionData->Mana)
  20899. SetPower(POWER_MANA, _resurrectionData->Mana);
  20900. else
  20901. SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
  20902. SetPower(POWER_RAGE, 0);
  20903. SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
  20904. SetPower(POWER_FOCUS, GetMaxPower(POWER_FOCUS));
  20905. SetPower(POWER_ECLIPSE, 0);
  20906. if (uint32 aura = _resurrectionData->Aura)
  20907. CastSpell(this, aura, true, NULL, NULL, _resurrectionData->GUID);
  20908. SpawnCorpseBones();
  20909. }
  20910. void Player::SetClientControl(Unit* target, bool allowMove)
  20911. {
  20912. WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size()+1);
  20913. data << target->GetPackGUID();
  20914. data << uint8(allowMove ? 1 : 0);
  20915. GetSession()->SendPacket(&data);
  20916. if (this != target)
  20917. SetViewpoint(target, allowMove);
  20918. if (allowMove)
  20919. SetMover(target);
  20920. }
  20921. void Player::SetMover(Unit* target)
  20922. {
  20923. m_mover->m_movedPlayer = NULL;
  20924. m_mover = target;
  20925. m_mover->m_movedPlayer = this;
  20926. ObjectGuid guid = target->GetGUID();
  20927. WorldPacket data(SMSG_MOVE_SET_ACTIVE_MOVER, 9);
  20928. data.WriteBit(guid[5]);
  20929. data.WriteBit(guid[7]);
  20930. data.WriteBit(guid[3]);
  20931. data.WriteBit(guid[6]);
  20932. data.WriteBit(guid[0]);
  20933. data.WriteBit(guid[4]);
  20934. data.WriteBit(guid[1]);
  20935. data.WriteBit(guid[2]);
  20936. data.WriteByteSeq(guid[6]);
  20937. data.WriteByteSeq(guid[2]);
  20938. data.WriteByteSeq(guid[3]);
  20939. data.WriteByteSeq(guid[0]);
  20940. data.WriteByteSeq(guid[5]);
  20941. data.WriteByteSeq(guid[7]);
  20942. data.WriteByteSeq(guid[1]);
  20943. data.WriteByteSeq(guid[4]);
  20944. SendDirectMessage(&data);
  20945. }
  20946. void Player::UpdateZoneDependentAuras(uint32 newZone)
  20947. {
  20948. // Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update
  20949. SpellAreaForAreaMapBounds saBounds = sSpellMgr->GetSpellAreaForAreaMapBounds(newZone);
  20950. for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
  20951. if (itr->second->autocast && itr->second->IsFitToRequirements(this, newZone, 0))
  20952. if (!HasAura(itr->second->spellId))
  20953. CastSpell(this, itr->second->spellId, true);
  20954. }
  20955. void Player::UpdateAreaDependentAuras(uint32 newArea)
  20956. {
  20957. // remove auras from spells with area limitations
  20958. for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
  20959. {
  20960. // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date
  20961. if (iter->second->GetSpellInfo()->CheckLocation(GetMapId(), m_zoneUpdateId, newArea, this) != SPELL_CAST_OK)
  20962. RemoveOwnedAura(iter);
  20963. else
  20964. ++iter;
  20965. }
  20966. // some auras applied at subzone enter
  20967. SpellAreaForAreaMapBounds saBounds = sSpellMgr->GetSpellAreaForAreaMapBounds(newArea);
  20968. for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
  20969. if (itr->second->autocast && itr->second->IsFitToRequirements(this, m_zoneUpdateId, newArea))
  20970. if (!HasAura(itr->second->spellId))
  20971. CastSpell(this, itr->second->spellId, true);
  20972. if (newArea == 4273 && GetVehicleCreatureBase() && GetPositionX() > 400) // Ulduar
  20973. {
  20974. switch (GetVehicleBase()->GetEntry())
  20975. {
  20976. case 33062:
  20977. case 33109:
  20978. case 33060:
  20979. GetVehicleCreatureBase()->DespawnOrUnsummon();
  20980. break;
  20981. }
  20982. }
  20983. }
  20984. uint32 Player::GetCorpseReclaimDelay(bool pvp) const
  20985. {
  20986. if (pvp)
  20987. {
  20988. if (!sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP))
  20989. return copseReclaimDelay[0];
  20990. }
  20991. else if (!sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE))
  20992. return 0;
  20993. time_t now = time(NULL);
  20994. // 0..2 full period
  20995. // should be ceil(x)-1 but not floor(x)
  20996. uint64 count = (now < m_deathExpireTime - 1) ? (m_deathExpireTime - 1 - now) / DEATH_EXPIRE_STEP : 0;
  20997. return copseReclaimDelay[count];
  20998. }
  20999. void Player::UpdateCorpseReclaimDelay()
  21000. {
  21001. bool pvp = (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) != 0;
  21002. if ((pvp && !sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP)) ||
  21003. (!pvp && !sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE)))
  21004. return;
  21005. time_t now = time(NULL);
  21006. if (now < m_deathExpireTime)
  21007. {
  21008. // full and partly periods 1..3
  21009. uint64 count = (m_deathExpireTime - now) / DEATH_EXPIRE_STEP + 1;
  21010. if (count < MAX_DEATH_COUNT)
  21011. m_deathExpireTime = now+(count + 1) * DEATH_EXPIRE_STEP;
  21012. else
  21013. m_deathExpireTime = now + MAX_DEATH_COUNT*DEATH_EXPIRE_STEP;
  21014. }
  21015. else
  21016. m_deathExpireTime = now + DEATH_EXPIRE_STEP;
  21017. }
  21018. int32 Player::CalculateCorpseReclaimDelay(bool load)
  21019. {
  21020. Corpse* corpse = GetCorpse();
  21021. if (load && !corpse)
  21022. return -1;
  21023. bool pvp = corpse ? corpse->GetType() == CORPSE_RESURRECTABLE_PVP : (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) != 0;
  21024. uint32 delay;
  21025. if (load)
  21026. {
  21027. if (corpse->GetGhostTime() > m_deathExpireTime)
  21028. return -1;
  21029. uint64 count = 0;
  21030. if ((pvp && sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP)) ||
  21031. (!pvp && sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE)))
  21032. {
  21033. count = (m_deathExpireTime - corpse->GetGhostTime()) / DEATH_EXPIRE_STEP;
  21034. if (count >= MAX_DEATH_COUNT)
  21035. count = MAX_DEATH_COUNT - 1;
  21036. }
  21037. time_t expected_time = corpse->GetGhostTime() + copseReclaimDelay[count];
  21038. time_t now = time(NULL);
  21039. if (now >= expected_time)
  21040. return -1;
  21041. delay = expected_time - now;
  21042. }
  21043. else
  21044. delay = GetCorpseReclaimDelay(pvp);
  21045. return delay * IN_MILLISECONDS;
  21046. }
  21047. void Player::SendCorpseReclaimDelay(uint32 delay)
  21048. {
  21049. WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4);
  21050. data << uint32(delay);
  21051. GetSession()->SendPacket(&data);
  21052. }
  21053. Player* Player::GetNextRandomRaidMember(float radius)
  21054. {
  21055. Group* group = GetGroup();
  21056. if (!group)
  21057. return NULL;
  21058. std::vector<Player*> nearMembers;
  21059. nearMembers.reserve(group->GetMembersCount());
  21060. for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  21061. {
  21062. Player* Target = itr->GetSource();
  21063. // IsHostileTo check duel and controlled by enemy
  21064. if (Target && Target != this && IsWithinDistInMap(Target, radius) &&
  21065. !Target->HasInvisibilityAura() && !IsHostileTo(Target))
  21066. nearMembers.push_back(Target);
  21067. }
  21068. if (nearMembers.empty())
  21069. return NULL;
  21070. uint32 randTarget = urand(0, nearMembers.size()-1);
  21071. return nearMembers[randTarget];
  21072. }
  21073. PartyResult Player::CanUninviteFromGroup() const
  21074. {
  21075. Group const* grp = GetGroup();
  21076. if (!grp)
  21077. return ERR_NOT_IN_GROUP;
  21078. if (grp->isLFGGroup())
  21079. {
  21080. ObjectGuid gguid = grp->GetGUID();
  21081. if (!sLFGMgr->GetKicksLeft(gguid))
  21082. return ERR_PARTY_LFG_BOOT_LIMIT;
  21083. lfg::LfgState state = sLFGMgr->GetState(gguid);
  21084. if (state == lfg::LFG_STATE_BOOT)
  21085. return ERR_PARTY_LFG_BOOT_IN_PROGRESS;
  21086. if (grp->GetMembersCount() <= lfg::LFG_GROUP_KICK_VOTES_NEEDED)
  21087. return ERR_PARTY_LFG_BOOT_TOO_FEW_PLAYERS;
  21088. if (state == lfg::LFG_STATE_FINISHED_DUNGEON)
  21089. return ERR_PARTY_LFG_BOOT_DUNGEON_COMPLETE;
  21090. if (grp->isRollLootActive())
  21091. return ERR_PARTY_LFG_BOOT_LOOT_ROLLS;
  21092. /// @todo Should also be sent when anyone has recently left combat, with an aprox ~5 seconds timer.
  21093. for (GroupReference const* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
  21094. if (itr->GetSource() && itr->GetSource()->IsInCombat())
  21095. return ERR_PARTY_LFG_BOOT_IN_COMBAT;
  21096. /* Missing support for these types
  21097. return ERR_PARTY_LFG_BOOT_COOLDOWN_S;
  21098. return ERR_PARTY_LFG_BOOT_NOT_ELIGIBLE_S;
  21099. */
  21100. }
  21101. else
  21102. {
  21103. if (!grp->IsLeader(GetGUID()) && !grp->IsAssistant(GetGUID()))
  21104. return ERR_NOT_LEADER;
  21105. if (InBattleground())
  21106. return ERR_INVITE_RESTRICTED;
  21107. }
  21108. return ERR_PARTY_RESULT_OK;
  21109. }
  21110. bool Player::isUsingLfg()
  21111. {
  21112. return sLFGMgr->GetState(GetGUID()) != lfg::LFG_STATE_NONE;
  21113. }
  21114. bool Player::inRandomLfgDungeon()
  21115. {
  21116. if (sLFGMgr->selectedRandomLfgDungeon(GetGUID()))
  21117. {
  21118. Map const* map = GetMap();
  21119. return sLFGMgr->inLfgDungeonMap(GetGUID(), map->GetId(), map->GetDifficulty());
  21120. }
  21121. return false;
  21122. }
  21123. void Player::SetBattlegroundOrBattlefieldRaid(Group* group, int8 subgroup)
  21124. {
  21125. //we must move references from m_group to m_originalGroup
  21126. SetOriginalGroup(GetGroup(), GetSubGroup());
  21127. m_group.unlink();
  21128. m_group.link(group, this);
  21129. m_group.setSubGroup((uint8)subgroup);
  21130. }
  21131. void Player::RemoveFromBattlegroundOrBattlefieldRaid()
  21132. {
  21133. //remove existing reference
  21134. m_group.unlink();
  21135. if (Group* group = GetOriginalGroup())
  21136. {
  21137. m_group.link(group, this);
  21138. m_group.setSubGroup(GetOriginalSubGroup());
  21139. }
  21140. SetOriginalGroup(NULL);
  21141. }
  21142. void Player::SetOriginalGroup(Group* group, int8 subgroup)
  21143. {
  21144. if (group == NULL)
  21145. m_originalGroup.unlink();
  21146. else
  21147. {
  21148. // never use SetOriginalGroup without a subgroup unless you specify NULL for group
  21149. ASSERT(subgroup >= 0);
  21150. m_originalGroup.link(group, this);
  21151. m_originalGroup.setSubGroup((uint8)subgroup);
  21152. }
  21153. }
  21154. void Player::UpdateUnderwaterState(Map* m, float x, float y, float z)
  21155. {
  21156. LiquidData liquid_status;
  21157. ZLiquidStatus res = m->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status);
  21158. if (!res)
  21159. {
  21160. m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWARER_INDARKWATER);
  21161. if (_lastLiquid && _lastLiquid->SpellId)
  21162. RemoveAurasDueToSpell(_lastLiquid->SpellId);
  21163. _lastLiquid = NULL;
  21164. return;
  21165. }
  21166. if (uint32 liqEntry = liquid_status.entry)
  21167. {
  21168. LiquidTypeEntry const* liquid = sLiquidTypeStore.LookupEntry(liqEntry);
  21169. if (_lastLiquid && _lastLiquid->SpellId && _lastLiquid->Id != liqEntry)
  21170. RemoveAurasDueToSpell(_lastLiquid->SpellId);
  21171. if (liquid && liquid->SpellId)
  21172. {
  21173. if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER))
  21174. {
  21175. if (!HasAura(liquid->SpellId))
  21176. CastSpell(this, liquid->SpellId, true);
  21177. }
  21178. else
  21179. RemoveAurasDueToSpell(liquid->SpellId);
  21180. }
  21181. _lastLiquid = liquid;
  21182. }
  21183. else if (_lastLiquid && _lastLiquid->SpellId)
  21184. {
  21185. RemoveAurasDueToSpell(_lastLiquid->SpellId);
  21186. _lastLiquid = NULL;
  21187. }
  21188. // All liquids type - check under water position
  21189. if (liquid_status.type_flags & (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME))
  21190. {
  21191. if (res & LIQUID_MAP_UNDER_WATER)
  21192. m_MirrorTimerFlags |= UNDERWATER_INWATER;
  21193. else
  21194. m_MirrorTimerFlags &= ~UNDERWATER_INWATER;
  21195. }
  21196. // Allow travel in dark water on taxi or transport
  21197. if ((liquid_status.type_flags & MAP_LIQUID_TYPE_DARK_WATER) && !IsInFlight() && !GetTransport())
  21198. m_MirrorTimerFlags |= UNDERWARER_INDARKWATER;
  21199. else
  21200. m_MirrorTimerFlags &= ~UNDERWARER_INDARKWATER;
  21201. // in lava check, anywhere in lava level
  21202. if (liquid_status.type_flags & MAP_LIQUID_TYPE_MAGMA)
  21203. {
  21204. if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER | LIQUID_MAP_WATER_WALK))
  21205. m_MirrorTimerFlags |= UNDERWATER_INLAVA;
  21206. else
  21207. m_MirrorTimerFlags &= ~UNDERWATER_INLAVA;
  21208. }
  21209. // in slime check, anywhere in slime level
  21210. if (liquid_status.type_flags & MAP_LIQUID_TYPE_SLIME)
  21211. {
  21212. if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER | LIQUID_MAP_WATER_WALK))
  21213. m_MirrorTimerFlags |= UNDERWATER_INSLIME;
  21214. else
  21215. m_MirrorTimerFlags &= ~UNDERWATER_INSLIME;
  21216. }
  21217. }
  21218. void Player::SetCanParry(bool value)
  21219. {
  21220. if (m_canParry == value)
  21221. return;
  21222. m_canParry = value;
  21223. UpdateParryPercentage();
  21224. }
  21225. void Player::SetCanBlock(bool value)
  21226. {
  21227. if (m_canBlock == value)
  21228. return;
  21229. m_canBlock = value;
  21230. UpdateBlockPercentage();
  21231. }
  21232. bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const
  21233. {
  21234. for (ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end(); ++itr)
  21235. if (itr->pos == pos)
  21236. return true;
  21237. return false;
  21238. }
  21239. void Player::StopCastingBindSight()
  21240. {
  21241. if (WorldObject* target = GetViewpoint())
  21242. {
  21243. if (target->isType(TYPEMASK_UNIT))
  21244. {
  21245. ((Unit*)target)->RemoveAurasByType(SPELL_AURA_BIND_SIGHT, GetGUID());
  21246. ((Unit*)target)->RemoveAurasByType(SPELL_AURA_MOD_POSSESS, GetGUID());
  21247. ((Unit*)target)->RemoveAurasByType(SPELL_AURA_MOD_POSSESS_PET, GetGUID());
  21248. }
  21249. }
  21250. }
  21251. void Player::SetViewpoint(WorldObject* target, bool apply)
  21252. {
  21253. if (apply)
  21254. {
  21255. TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s create seer %u (TypeId: %u).", GetName().c_str(), target->GetEntry(), target->GetTypeId());
  21256. if (!AddGuidValue(PLAYER_FARSIGHT, target->GetGUID()))
  21257. {
  21258. TC_LOG_FATAL("entities.player", "Player::CreateViewpoint: Player %s cannot add new viewpoint!", GetName().c_str());
  21259. return;
  21260. }
  21261. // farsight dynobj or puppet may be very far away
  21262. UpdateVisibilityOf(target);
  21263. if (target->isType(TYPEMASK_UNIT) && !GetVehicle())
  21264. ((Unit*)target)->AddPlayerToVision(this);
  21265. }
  21266. else
  21267. {
  21268. TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s remove seer", GetName().c_str());
  21269. if (!RemoveGuidValue(PLAYER_FARSIGHT, target->GetGUID()))
  21270. {
  21271. TC_LOG_FATAL("entities.player", "Player::CreateViewpoint: Player %s cannot remove current viewpoint!", GetName().c_str());
  21272. return;
  21273. }
  21274. if (target->isType(TYPEMASK_UNIT) && !GetVehicle())
  21275. ((Unit*)target)->RemovePlayerFromVision(this);
  21276. //must immediately set seer back otherwise may crash
  21277. m_seer = this;
  21278. //WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0);
  21279. //GetSession()->SendPacket(&data);
  21280. }
  21281. }
  21282. WorldObject* Player::GetViewpoint() const
  21283. {
  21284. if (ObjectGuid guid = GetGuidValue(PLAYER_FARSIGHT))
  21285. return (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*this, guid, TYPEMASK_SEER);
  21286. return NULL;
  21287. }
  21288. bool Player::CanUseBattlegroundObject(GameObject* gameobject)
  21289. {
  21290. // It is possible to call this method with a null pointer, only skipping faction check.
  21291. if (gameobject)
  21292. {
  21293. FactionTemplateEntry const* playerFaction = GetFactionTemplateEntry();
  21294. FactionTemplateEntry const* faction = sFactionTemplateStore.LookupEntry(gameobject->GetUInt32Value(GAMEOBJECT_FACTION));
  21295. if (playerFaction && faction && !playerFaction->IsFriendlyTo(*faction))
  21296. return false;
  21297. }
  21298. // BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet
  21299. // Note: Mount, stealth and invisibility will be removed when used
  21300. return (!isTotalImmune() && // Damage immune
  21301. !HasAura(SPELL_RECENTLY_DROPPED_FLAG) && // Still has recently held flag debuff
  21302. IsAlive()); // Alive
  21303. }
  21304. bool Player::CanCaptureTowerPoint()
  21305. {
  21306. return (!HasStealthAura() && // not stealthed
  21307. !HasInvisibilityAura() && // not invisible
  21308. IsAlive()); // live player
  21309. }
  21310. uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair, BarberShopStyleEntry const* newSkin)
  21311. {
  21312. uint8 level = getLevel();
  21313. if (level > GT_MAX_LEVEL)
  21314. level = GT_MAX_LEVEL; // max level in this dbc
  21315. uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2);
  21316. uint8 haircolor = GetByteValue(PLAYER_BYTES, 3);
  21317. uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0);
  21318. uint8 skincolor = GetByteValue(PLAYER_BYTES, 0);
  21319. if ((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair) && (!newSkin || (newSkin->hair_id == skincolor)))
  21320. return 0;
  21321. GtBarberShopCostBaseEntry const* bsc = sGtBarberShopCostBaseStore.LookupEntry(level - 1);
  21322. if (!bsc) // shouldn't happen
  21323. return 0xFFFFFFFF;
  21324. float cost = 0;
  21325. if (hairstyle != newhairstyle)
  21326. cost += bsc->cost; // full price
  21327. if ((haircolor != newhaircolor) && (hairstyle == newhairstyle))
  21328. cost += bsc->cost * 0.5f; // +1/2 of price
  21329. if (facialhair != newfacialhair)
  21330. cost += bsc->cost * 0.75f; // +3/4 of price
  21331. if (newSkin && skincolor != newSkin->hair_id)
  21332. cost += bsc->cost * 0.75f; // +5/6 of price
  21333. return uint32(cost);
  21334. }
  21335. void Player::InitGlyphsForLevel()
  21336. {
  21337. uint32 slot = 0;
  21338. for (uint32 i = 0; i < sGlyphSlotStore.GetNumRows() && slot < MAX_GLYPH_SLOT_INDEX; ++i)
  21339. if (GlyphSlotEntry const* gs = sGlyphSlotStore.LookupEntry(i))
  21340. SetGlyphSlot(slot++, gs->Id);
  21341. uint8 level = getLevel();
  21342. uint32 slotMask = 0;
  21343. if (level >= 25)
  21344. slotMask |= 0x01 | 0x02 | 0x40;
  21345. if (level >= 50)
  21346. slotMask |= 0x04 | 0x08 | 0x80;
  21347. if (level >= 75)
  21348. slotMask |= 0x10 | 0x20 | 0x100;
  21349. SetUInt32Value(PLAYER_GLYPHS_ENABLED, slotMask);
  21350. }
  21351. void Player::SetGlyph(uint8 slot, uint32 glyph)
  21352. {
  21353. _talentMgr->SpecInfo[GetActiveSpec()].Glyphs[slot] = glyph;
  21354. SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, glyph);
  21355. }
  21356. bool Player::isTotalImmune()
  21357. {
  21358. AuraEffectList const& immune = GetAuraEffectsByType(SPELL_AURA_SCHOOL_IMMUNITY);
  21359. uint32 immuneMask = 0;
  21360. for (AuraEffectList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr)
  21361. {
  21362. immuneMask |= (*itr)->GetMiscValue();
  21363. if (immuneMask & SPELL_SCHOOL_MASK_ALL) // total immunity
  21364. return true;
  21365. }
  21366. return false;
  21367. }
  21368. bool Player::HasTitle(uint32 bitIndex) const
  21369. {
  21370. if (bitIndex > MAX_TITLE_INDEX)
  21371. return false;
  21372. uint32 fieldIndexOffset = bitIndex / 32;
  21373. uint32 flag = 1 << (bitIndex % 32);
  21374. return HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
  21375. }
  21376. void Player::SetTitle(CharTitlesEntry const* title, bool lost)
  21377. {
  21378. uint32 fieldIndexOffset = title->bit_index / 32;
  21379. uint32 flag = 1 << (title->bit_index % 32);
  21380. if (lost)
  21381. {
  21382. if (!HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag))
  21383. return;
  21384. RemoveFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
  21385. }
  21386. else
  21387. {
  21388. if (HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag))
  21389. return;
  21390. SetFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
  21391. }
  21392. WorldPacket data(SMSG_TITLE_EARNED, 4 + 4);
  21393. data << uint32(title->bit_index);
  21394. data << uint32(lost ? 0 : 1); // 1 - earned, 0 - lost
  21395. GetSession()->SendPacket(&data);
  21396. }
  21397. bool Player::isTotalImmunity()
  21398. {
  21399. AuraEffectList const& immune = GetAuraEffectsByType(SPELL_AURA_SCHOOL_IMMUNITY);
  21400. for (AuraEffectList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr)
  21401. {
  21402. if (((*itr)->GetMiscValue() & SPELL_SCHOOL_MASK_ALL) !=0) // total immunity
  21403. {
  21404. return true;
  21405. }
  21406. if (((*itr)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) !=0) // physical damage immunity
  21407. {
  21408. for (AuraEffectList::const_iterator i = immune.begin(); i != immune.end(); ++i)
  21409. {
  21410. if (((*i)->GetMiscValue() & SPELL_SCHOOL_MASK_MAGIC) !=0) // magic immunity
  21411. {
  21412. return true;
  21413. }
  21414. }
  21415. }
  21416. }
  21417. return false;
  21418. }
  21419. void Player::UpdateCharmedAI()
  21420. {
  21421. //This should only called in Player::Update
  21422. Creature* charmer = GetCharmer()->ToCreature();
  21423. //kill self if charm aura has infinite duration
  21424. if (charmer->IsInEvadeMode())
  21425. {
  21426. AuraEffectList const& auras = GetAuraEffectsByType(SPELL_AURA_MOD_CHARM);
  21427. for (AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
  21428. if ((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetBase()->IsPermanent())
  21429. {
  21430. charmer->DealDamage(this, GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
  21431. return;
  21432. }
  21433. }
  21434. if (!charmer->IsInCombat())
  21435. GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
  21436. Unit* target = GetVictim();
  21437. if (!target || !charmer->IsValidAttackTarget(target))
  21438. {
  21439. target = charmer->SelectNearestTarget();
  21440. if (!target)
  21441. return;
  21442. GetMotionMaster()->MoveChase(target);
  21443. Attack(target, true);
  21444. }
  21445. }
  21446. uint32 Player::GetRuneTypeBaseCooldown(RuneType runeType) const
  21447. {
  21448. float cooldown = RUNE_BASE_COOLDOWN;
  21449. float hastePct = 0.0f;
  21450. AuraEffectList const& regenAura = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
  21451. for (AuraEffectList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i)
  21452. if ((*i)->GetMiscValue() == POWER_RUNES && (*i)->GetMiscValueB() == runeType)
  21453. cooldown *= 1.0f - (*i)->GetAmount() / 100.0f;
  21454. // Runes cooldown are now affected by player's haste from equipment ...
  21455. hastePct = GetRatingBonusValue(CR_HASTE_MELEE);
  21456. // ... and some auras.
  21457. hastePct += GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_HASTE);
  21458. hastePct += GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_HASTE_2);
  21459. hastePct += GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_HASTE_3);
  21460. cooldown *= 1.0f - (hastePct / 100.0f);
  21461. return cooldown;
  21462. }
  21463. void Player::SetRuneCooldown(uint8 index, uint32 cooldown, bool casted /*= false*/)
  21464. {
  21465. uint32 gracePeriod = GetRuneTimer(index);
  21466. if (casted && IsInCombat())
  21467. {
  21468. if (gracePeriod < 0xFFFFFFFF && cooldown > 0)
  21469. {
  21470. uint32 lessCd = std::min(uint32(2500), gracePeriod);
  21471. cooldown = (cooldown > lessCd) ? (cooldown - lessCd) : 0;
  21472. SetLastRuneGraceTimer(index, lessCd);
  21473. }
  21474. SetRuneTimer(index, 0);
  21475. }
  21476. m_runes->runes[index].Cooldown = cooldown;
  21477. m_runes->SetRuneState(index, (cooldown == 0) ? true : false);
  21478. }
  21479. void Player::SetRuneConvertAura(uint8 index, AuraEffect const* aura)
  21480. {
  21481. m_runes->runes[index].ConvertAura = aura;
  21482. }
  21483. void Player::AddRuneByAuraEffect(uint8 index, RuneType newType, AuraEffect const* aura)
  21484. {
  21485. SetRuneConvertAura(index, aura); ConvertRune(index, newType);
  21486. }
  21487. void Player::RemoveRunesByAuraEffect(AuraEffect const* aura)
  21488. {
  21489. for (uint8 i = 0; i < MAX_RUNES; ++i)
  21490. {
  21491. if (m_runes->runes[i].ConvertAura == aura)
  21492. {
  21493. ConvertRune(i, GetBaseRune(i));
  21494. SetRuneConvertAura(i, NULL);
  21495. }
  21496. }
  21497. }
  21498. void Player::RestoreBaseRune(uint8 index)
  21499. {
  21500. AuraEffect const* aura = m_runes->runes[index].ConvertAura;
  21501. // If rune was converted by a non-pasive aura that still active we should keep it converted
  21502. if (aura && !(aura->GetSpellInfo()->Attributes & SPELL_ATTR0_PASSIVE))
  21503. return;
  21504. ConvertRune(index, GetBaseRune(index));
  21505. SetRuneConvertAura(index, NULL);
  21506. // Don't drop passive talents providing rune convertion
  21507. if (!aura || aura->GetAuraType() != SPELL_AURA_CONVERT_RUNE)
  21508. return;
  21509. for (uint8 i = 0; i < MAX_RUNES; ++i)
  21510. {
  21511. if (aura == m_runes->runes[i].ConvertAura)
  21512. return;
  21513. }
  21514. aura->GetBase()->Remove();
  21515. }
  21516. void Player::ConvertRune(uint8 index, RuneType newType)
  21517. {
  21518. SetCurrentRune(index, newType);
  21519. WorldPacket data(SMSG_CONVERT_RUNE, 2);
  21520. data << uint8(index);
  21521. data << uint8(newType);
  21522. GetSession()->SendPacket(&data);
  21523. }
  21524. void Player::ResyncRunes(uint8 count)
  21525. {
  21526. WorldPacket data(SMSG_RESYNC_RUNES, 4 + count * 2);
  21527. data << uint32(count);
  21528. for (uint32 i = 0; i < count; ++i)
  21529. {
  21530. data << uint8(GetCurrentRune(i)); // rune type
  21531. data << uint8(255 - (GetRuneCooldown(i) * 51)); // passed cooldown time (0-255)
  21532. }
  21533. GetSession()->SendPacket(&data);
  21534. }
  21535. void Player::AddRunePower(uint8 index)
  21536. {
  21537. WorldPacket data(SMSG_ADD_RUNE_POWER, 4);
  21538. data << uint32(1 << index); // mask (0x00-0x3F probably)
  21539. GetSession()->SendPacket(&data);
  21540. }
  21541. static RuneType runeSlotTypes[MAX_RUNES] =
  21542. {
  21543. /*0*/ RUNE_BLOOD,
  21544. /*1*/ RUNE_BLOOD,
  21545. /*2*/ RUNE_UNHOLY,
  21546. /*3*/ RUNE_UNHOLY,
  21547. /*4*/ RUNE_FROST,
  21548. /*5*/ RUNE_FROST
  21549. };
  21550. void Player::InitRunes()
  21551. {
  21552. if (getClass() != CLASS_DEATH_KNIGHT)
  21553. return;
  21554. m_runes = new Runes;
  21555. m_runes->runeState = 0;
  21556. m_runes->lastUsedRune = RUNE_BLOOD;
  21557. for (uint8 i = 0; i < MAX_RUNES; ++i)
  21558. {
  21559. SetBaseRune(i, runeSlotTypes[i]); // init base types
  21560. SetCurrentRune(i, runeSlotTypes[i]); // init current types
  21561. SetRuneCooldown(i, 0); // reset cooldowns
  21562. SetRuneTimer(i, 0xFFFFFFFF); // Reset rune flags
  21563. SetLastRuneGraceTimer(i, 0);
  21564. SetRuneConvertAura(i, NULL);
  21565. m_runes->SetRuneState(i);
  21566. }
  21567. for (uint8 i = 0; i < NUM_RUNE_TYPES; ++i)
  21568. SetFloatValue(PLAYER_RUNE_REGEN_1 + i, 0.1f); // set a base regen timer equal to 10 sec
  21569. }
  21570. bool Player::IsBaseRuneSlotsOnCooldown(RuneType runeType) const
  21571. {
  21572. for (uint8 i = 0; i < MAX_RUNES; ++i)
  21573. if (GetBaseRune(i) == runeType && GetRuneCooldown(i) == 0)
  21574. return false;
  21575. return true;
  21576. }
  21577. void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast)
  21578. {
  21579. Loot loot;
  21580. loot.FillLoot (loot_id, store, this, true);
  21581. uint32 max_slot = loot.GetMaxSlotInLootFor(this);
  21582. for (uint32 i = 0; i < max_slot; ++i)
  21583. {
  21584. LootItem* lootItem = loot.LootItemInSlot(i, this);
  21585. ItemPosCountVec dest;
  21586. InventoryResult msg = CanStoreNewItem(bag, slot, dest, lootItem->itemid, lootItem->count);
  21587. if (msg != EQUIP_ERR_OK && slot != NULL_SLOT)
  21588. msg = CanStoreNewItem(bag, NULL_SLOT, dest, lootItem->itemid, lootItem->count);
  21589. if (msg != EQUIP_ERR_OK && bag != NULL_BAG)
  21590. msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, lootItem->itemid, lootItem->count);
  21591. if (msg != EQUIP_ERR_OK)
  21592. {
  21593. SendEquipError(msg, NULL, NULL, lootItem->itemid);
  21594. continue;
  21595. }
  21596. Item* pItem = StoreNewItem(dest, lootItem->itemid, true, lootItem->randomPropertyId);
  21597. SendNewItem(pItem, lootItem->count, false, false, broadcast);
  21598. }
  21599. }
  21600. void Player::StoreLootItem(uint8 lootSlot, Loot* loot)
  21601. {
  21602. QuestItem* qitem = NULL;
  21603. QuestItem* ffaitem = NULL;
  21604. QuestItem* conditem = NULL;
  21605. LootItem* item = loot->LootItemInSlot(lootSlot, this, &qitem, &ffaitem, &conditem);
  21606. if (!item)
  21607. {
  21608. SendEquipError(EQUIP_ERR_LOOT_GONE, NULL, NULL);
  21609. return;
  21610. }
  21611. if (!item->AllowedForPlayer(this))
  21612. {
  21613. SendLootRelease(GetLootGUID());
  21614. return;
  21615. }
  21616. // questitems use the blocked field for other purposes
  21617. if (!qitem && item->is_blocked)
  21618. {
  21619. SendLootRelease(GetLootGUID());
  21620. return;
  21621. }
  21622. ItemPosCountVec dest;
  21623. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item->itemid, item->count);
  21624. if (msg == EQUIP_ERR_OK)
  21625. {
  21626. AllowedLooterSet looters = item->GetAllowedLooters();
  21627. Item* newitem = StoreNewItem(dest, item->itemid, true, item->randomPropertyId, looters);
  21628. if (qitem)
  21629. {
  21630. qitem->is_looted = true;
  21631. //freeforall is 1 if everyone's supposed to get the quest item.
  21632. if (item->freeforall || loot->GetPlayerQuestItems().size() == 1)
  21633. SendNotifyLootItemRemoved(lootSlot);
  21634. else
  21635. loot->NotifyQuestItemRemoved(qitem->index);
  21636. }
  21637. else
  21638. {
  21639. if (ffaitem)
  21640. {
  21641. //freeforall case, notify only one player of the removal
  21642. ffaitem->is_looted = true;
  21643. SendNotifyLootItemRemoved(lootSlot);
  21644. }
  21645. else
  21646. {
  21647. //not freeforall, notify everyone
  21648. if (conditem)
  21649. conditem->is_looted = true;
  21650. loot->NotifyItemRemoved(lootSlot);
  21651. }
  21652. }
  21653. //if only one person is supposed to loot the item, then set it to looted
  21654. if (!item->freeforall)
  21655. item->is_looted = true;
  21656. --loot->unlootedCount;
  21657. if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item->itemid))
  21658. if (proto->Quality > ITEM_QUALITY_EPIC || (proto->Quality == ITEM_QUALITY_EPIC && proto->ItemLevel >= MinNewsItemLevel[sWorld->getIntConfig(CONFIG_EXPANSION)]))
  21659. if (Guild* guild = GetGuild())
  21660. guild->AddGuildNews(GUILD_NEWS_ITEM_LOOTED, GetGUID(), 0, item->itemid);
  21661. SendNewItem(newitem, uint32(item->count), false, false, true);
  21662. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count);
  21663. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, item->itemid, item->count, loot->loot_type);
  21664. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count);
  21665. // LootItem is being removed (looted) from the container, delete it from the DB.
  21666. if (loot->containerID > 0)
  21667. loot->DeleteLootItemFromContainerItemDB(item->itemid);
  21668. }
  21669. else
  21670. SendEquipError(msg, NULL, NULL, item->itemid);
  21671. }
  21672. uint32 Player::CalculateTalentsPoints() const
  21673. {
  21674. // this dbc file has entries only up to level 100
  21675. NumTalentsAtLevelEntry const* count = sNumTalentsAtLevelStore.LookupEntry(std::min<uint32>(getLevel(), 100));
  21676. if (!count)
  21677. return 0;
  21678. float baseForLevel = count->Talents;
  21679. if (getClass() != CLASS_DEATH_KNIGHT || GetMapId() != 609)
  21680. return uint32(baseForLevel * sWorld->getRate(RATE_TALENT));
  21681. // Death Knight starting level
  21682. // hardcoded here - number of quest awarded talents is equal to number of talents any other class would have at level 55
  21683. if (getLevel() < 55)
  21684. return 0;
  21685. NumTalentsAtLevelEntry const* dkBase = sNumTalentsAtLevelStore.LookupEntry(55);
  21686. if (!dkBase)
  21687. return 0;
  21688. float talentPointsForLevel = count->Talents - dkBase->Talents;
  21689. talentPointsForLevel += float(GetQuestRewardedTalentCount());
  21690. if (talentPointsForLevel > baseForLevel)
  21691. talentPointsForLevel = baseForLevel;
  21692. return uint32(talentPointsForLevel * sWorld->getRate(RATE_TALENT));
  21693. }
  21694. bool Player::IsKnowHowFlyIn(uint32 mapid, uint32 zone) const
  21695. {
  21696. // continent checked in SpellInfo::CheckLocation at cast and area update
  21697. uint32 v_map = GetVirtualMapForMapAndZone(mapid, zone);
  21698. return v_map != 571 || HasSpell(54197); // Cold Weather Flying
  21699. }
  21700. void Player::LearnSpellHighestRank(uint32 spellid)
  21701. {
  21702. LearnSpell(spellid, false);
  21703. if (uint32 next = sSpellMgr->GetNextSpellInChain(spellid))
  21704. LearnSpellHighestRank(next);
  21705. }
  21706. void Player::_LoadSkills(PreparedQueryResult result)
  21707. {
  21708. // 0 1 2
  21709. // SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid));
  21710. uint32 count = 0;
  21711. uint8 professionCount = 0;
  21712. if (result)
  21713. {
  21714. do
  21715. {
  21716. Field* fields = result->Fetch();
  21717. uint16 skill = fields[0].GetUInt16();
  21718. uint16 value = fields[1].GetUInt16();
  21719. uint16 max = fields[2].GetUInt16();
  21720. SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skill, getRace(), getClass());
  21721. if (!rcEntry)
  21722. {
  21723. TC_LOG_ERROR("entities.player", "Character: %s (GUID: %u Race: %u Class: %u) has skill %u not allowed for his race/class combination",
  21724. GetName().c_str(), GetGUIDLow(), uint32(getRace()), uint32(getClass()), skill);
  21725. mSkillStatus.insert(SkillStatusMap::value_type(skill, SkillStatusData(0, SKILL_DELETED)));
  21726. continue;
  21727. }
  21728. // set fixed skill ranges
  21729. switch (GetSkillRangeType(rcEntry))
  21730. {
  21731. case SKILL_RANGE_LANGUAGE: // 300..300
  21732. value = max = 300;
  21733. break;
  21734. case SKILL_RANGE_MONO: // 1..1, grey monolite bar
  21735. value = max = 1;
  21736. break;
  21737. case SKILL_RANGE_LEVEL:
  21738. max = GetMaxSkillValueForLevel();
  21739. default:
  21740. break;
  21741. }
  21742. if (value == 0)
  21743. {
  21744. TC_LOG_ERROR("entities.player", "Character %u has skill %u with value 0. Will be deleted.", GetGUIDLow(), skill);
  21745. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SKILL);
  21746. stmt->setUInt32(0, GetGUIDLow());
  21747. stmt->setUInt16(1, skill);
  21748. CharacterDatabase.Execute(stmt);
  21749. continue;
  21750. }
  21751. uint16 field = count / 2;
  21752. uint8 offset = count & 1;
  21753. SetUInt16Value(PLAYER_SKILL_LINEID_0 + field, offset, skill);
  21754. uint16 step = 0;
  21755. SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(rcEntry->SkillId);
  21756. if (skillLine)
  21757. {
  21758. if (skillLine->categoryId == SKILL_CATEGORY_SECONDARY)
  21759. step = max / 75;
  21760. if (skillLine->categoryId == SKILL_CATEGORY_PROFESSION)
  21761. {
  21762. step = max / 75;
  21763. if (professionCount < 2)
  21764. SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + professionCount++, skill);
  21765. }
  21766. }
  21767. SetUInt16Value(PLAYER_SKILL_STEP_0 + field, offset, step);
  21768. SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, value);
  21769. SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, max);
  21770. SetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset, 0);
  21771. SetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset, 0);
  21772. mSkillStatus.insert(SkillStatusMap::value_type(skill, SkillStatusData(count, SKILL_UNCHANGED)));
  21773. LearnSkillRewardedSpells(skill, value);
  21774. ++count;
  21775. if (count >= PLAYER_MAX_SKILLS) // client limit
  21776. {
  21777. TC_LOG_ERROR("entities.player", "Character %u has more than %u skills.", GetGUIDLow(), PLAYER_MAX_SKILLS);
  21778. break;
  21779. }
  21780. }
  21781. while (result->NextRow());
  21782. }
  21783. for (; count < PLAYER_MAX_SKILLS; ++count)
  21784. {
  21785. uint16 field = count / 2;
  21786. uint8 offset = count & 1;
  21787. if (HasSkill(SKILL_FIST_WEAPONS))
  21788. SetSkill(SKILL_FIST_WEAPONS, 0, GetSkillValue(SKILL_UNARMED), GetMaxSkillValueForLevel());
  21789. SetUInt16Value(PLAYER_SKILL_LINEID_0 + field, offset, 0);
  21790. SetUInt16Value(PLAYER_SKILL_STEP_0 + field, offset, 0);
  21791. SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, 0);
  21792. SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, 0);
  21793. SetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset, 0);
  21794. SetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset, 0);
  21795. }
  21796. }
  21797. InventoryResult Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) const
  21798. {
  21799. ItemTemplate const* pProto = pItem->GetTemplate();
  21800. // proto based limitations
  21801. if (InventoryResult res = CanEquipUniqueItem(pProto, eslot, limit_count))
  21802. return res;
  21803. // check unique-equipped on gems
  21804. for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
  21805. {
  21806. uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
  21807. if (!enchant_id)
  21808. continue;
  21809. SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
  21810. if (!enchantEntry)
  21811. continue;
  21812. ItemTemplate const* pGem = sObjectMgr->GetItemTemplate(enchantEntry->GemID);
  21813. if (!pGem)
  21814. continue;
  21815. // include for check equip another gems with same limit category for not equipped item (and then not counted)
  21816. uint32 gem_limit_count = !pItem->IsEquipped() && pGem->ItemLimitCategory
  21817. ? pItem->GetGemCountWithLimitCategory(pGem->ItemLimitCategory) : 1;
  21818. if (InventoryResult res = CanEquipUniqueItem(pGem, eslot, gem_limit_count))
  21819. return res;
  21820. }
  21821. return EQUIP_ERR_OK;
  21822. }
  21823. InventoryResult Player::CanEquipUniqueItem(ItemTemplate const* itemProto, uint8 except_slot, uint32 limit_count) const
  21824. {
  21825. // check unique-equipped on item
  21826. if (itemProto->Flags & ITEM_PROTO_FLAG_UNIQUE_EQUIPPED)
  21827. {
  21828. // there is an equip limit on this item
  21829. if (HasItemOrGemWithIdEquipped(itemProto->ItemId, 1, except_slot))
  21830. return EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE;
  21831. }
  21832. // check unique-equipped limit
  21833. if (itemProto->ItemLimitCategory)
  21834. {
  21835. ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(itemProto->ItemLimitCategory);
  21836. if (!limitEntry)
  21837. return EQUIP_ERR_NOT_EQUIPPABLE;
  21838. // NOTE: limitEntry->mode not checked because if item have have-limit then it applied and to equip case
  21839. if (limit_count > limitEntry->maxCount)
  21840. return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS;
  21841. // there is an equip limit on this item
  21842. if (HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory, limitEntry->maxCount - limit_count + 1, except_slot))
  21843. return EQUIP_ERR_ITEM_MAX_COUNT_EQUIPPED_SOCKETED;
  21844. }
  21845. return EQUIP_ERR_OK;
  21846. }
  21847. void Player::SetFallInformation(uint32 time, float z)
  21848. {
  21849. m_lastFallTime = time;
  21850. m_lastFallZ = z;
  21851. }
  21852. void Player::HandleFall(MovementInfo const& movementInfo)
  21853. {
  21854. // calculate total z distance of the fall
  21855. float z_diff = m_lastFallZ - movementInfo.pos.GetPositionZ();
  21856. //TC_LOG_DEBUG("misc", "zDiff = %f", z_diff);
  21857. //Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored
  21858. // 14.57 can be calculated by resolving damageperc formula below to 0
  21859. if (z_diff >= 14.57f && !isDead() && !IsGameMaster() &&
  21860. !HasAuraType(SPELL_AURA_HOVER) && !HasAuraType(SPELL_AURA_FEATHER_FALL) &&
  21861. !HasAuraType(SPELL_AURA_FLY) && !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL))
  21862. {
  21863. //Safe fall, fall height reduction
  21864. int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL);
  21865. float damageperc = 0.018f*(z_diff-safe_fall)-0.2426f;
  21866. if (damageperc > 0)
  21867. {
  21868. uint32 damage = (uint32)(damageperc * GetMaxHealth()*sWorld->getRate(RATE_DAMAGE_FALL));
  21869. if (GetCommandStatus(CHEAT_GOD))
  21870. damage = 0;
  21871. float height = movementInfo.pos.m_positionZ;
  21872. UpdateGroundPositionZ(movementInfo.pos.m_positionX, movementInfo.pos.m_positionY, height);
  21873. if (damage > 0)
  21874. {
  21875. //Prevent fall damage from being more than the player maximum health
  21876. if (damage > GetMaxHealth())
  21877. damage = GetMaxHealth();
  21878. // Gust of Wind
  21879. if (HasAura(43621))
  21880. damage = GetMaxHealth()/2;
  21881. uint32 original_health = GetHealth();
  21882. uint32 final_damage = EnvironmentalDamage(DAMAGE_FALL, damage);
  21883. // recheck alive, might have died of EnvironmentalDamage, avoid cases when player die in fact like Spirit of Redemption case
  21884. if (IsAlive() && final_damage < original_health)
  21885. UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING, uint32(z_diff*100));
  21886. }
  21887. //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction
  21888. TC_LOG_DEBUG("entities.player", "FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d", movementInfo.pos.GetPositionZ(), height, GetPositionZ(), movementInfo.jump.fallTime, height, damage, safe_fall);
  21889. }
  21890. }
  21891. RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LANDING); // No fly zone - Parachute
  21892. }
  21893. void Player::ResetAchievements()
  21894. {
  21895. m_achievementMgr->Reset();
  21896. }
  21897. void Player::SendRespondInspectAchievements(Player* player) const
  21898. {
  21899. m_achievementMgr->SendAchievementInfo(player);
  21900. }
  21901. uint32 Player::GetAchievementPoints() const
  21902. {
  21903. return m_achievementMgr->GetAchievementPoints();
  21904. }
  21905. bool Player::HasAchieved(uint32 achievementId) const
  21906. {
  21907. return m_achievementMgr->HasAchieved(achievementId);
  21908. }
  21909. void Player::StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry, uint32 timeLost/* = 0*/)
  21910. {
  21911. m_achievementMgr->StartTimedAchievement(type, entry, timeLost);
  21912. }
  21913. void Player::RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry)
  21914. {
  21915. m_achievementMgr->RemoveTimedAchievement(type, entry);
  21916. }
  21917. void Player::ResetAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, bool evenIfCriteriaComplete /* = false*/)
  21918. {
  21919. m_achievementMgr->ResetAchievementCriteria(type, miscValue1, miscValue2, evenIfCriteriaComplete);
  21920. }
  21921. void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, Unit* unit /*= NULL*/)
  21922. {
  21923. m_achievementMgr->UpdateAchievementCriteria(type, miscValue1, miscValue2, miscValue3, unit, this);
  21924. Guild* guild = sGuildMgr->GetGuildById(GetGuildId());
  21925. if (!guild)
  21926. return;
  21927. // Update only individual achievement criteria here, otherwise we may get multiple updates
  21928. // from a single boss kill
  21929. if (sAchievementMgr->IsGroupCriteriaType(type))
  21930. return;
  21931. guild->UpdateAchievementCriteria(type, miscValue1, miscValue2, miscValue3, unit, this);
  21932. }
  21933. void Player::CompletedAchievement(AchievementEntry const* entry)
  21934. {
  21935. m_achievementMgr->CompletedAchievement(entry, this);
  21936. }
  21937. bool Player::LearnTalent(uint32 talentId, uint32 talentRank)
  21938. {
  21939. uint32 CurTalentPoints = GetFreeTalentPoints();
  21940. if (CurTalentPoints == 0)
  21941. return false;
  21942. if (talentRank >= MAX_TALENT_RANK)
  21943. return false;
  21944. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  21945. if (!talentInfo)
  21946. return false;
  21947. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
  21948. if (!talentTabInfo)
  21949. return false;
  21950. // prevent learn talent for different class (cheating)
  21951. if ((getClassMask() & talentTabInfo->ClassMask) == 0)
  21952. return false;
  21953. // find current max talent rank (0~5)
  21954. uint8 curtalent_maxrank = 0; // 0 = not learned any rank
  21955. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  21956. {
  21957. if (talentInfo->RankID[rank] && HasSpell(talentInfo->RankID[rank]))
  21958. {
  21959. curtalent_maxrank = (rank + 1);
  21960. break;
  21961. }
  21962. }
  21963. // we already have same or higher talent rank learned
  21964. if (curtalent_maxrank >= (talentRank + 1))
  21965. return false;
  21966. // check if we have enough talent points
  21967. if (CurTalentPoints < (talentRank - curtalent_maxrank + 1))
  21968. return false;
  21969. // Check if it requires another talent
  21970. if (talentInfo->DependsOn > 0)
  21971. {
  21972. if (TalentEntry const* depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
  21973. {
  21974. bool hasEnoughRank = false;
  21975. for (uint8 rank = talentInfo->DependsOnRank; rank < MAX_TALENT_RANK; rank++)
  21976. {
  21977. if (depTalentInfo->RankID[rank] != 0)
  21978. if (HasSpell(depTalentInfo->RankID[rank]))
  21979. hasEnoughRank = true;
  21980. }
  21981. if (!hasEnoughRank)
  21982. return false;
  21983. }
  21984. }
  21985. // Find out how many points we have in this field
  21986. uint32 spentPoints = 0;
  21987. uint32 primaryTreeTalents = 0;
  21988. uint32 tTab = talentInfo->TalentTab;
  21989. bool isMainTree = GetPrimaryTalentTree(GetActiveSpec()) == tTab || !GetPrimaryTalentTree(GetActiveSpec());
  21990. if (talentInfo->Row > 0 || !isMainTree)
  21991. {
  21992. for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++) // Loop through all talents.
  21993. {
  21994. if (TalentEntry const* tmpTalent = sTalentStore.LookupEntry(i)) // Someday, someone needs to revamp the way talents are tracked
  21995. {
  21996. for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++)
  21997. {
  21998. if (tmpTalent->RankID[rank] != 0)
  21999. {
  22000. if (HasSpell(tmpTalent->RankID[rank]))
  22001. {
  22002. if (tmpTalent->TalentTab == tTab)
  22003. spentPoints += (rank + 1);
  22004. if (tmpTalent->TalentTab == GetPrimaryTalentTree(GetActiveSpec()))
  22005. primaryTreeTalents += (rank + 1);
  22006. }
  22007. }
  22008. }
  22009. }
  22010. }
  22011. }
  22012. // not have required min points spent in talent tree
  22013. if (spentPoints < (talentInfo->Row * MAX_TALENT_RANK))
  22014. return false;
  22015. // player has not spent 31 talents in main tree before attempting to learn other tree's talents
  22016. if (!isMainTree && primaryTreeTalents < REQ_PRIMARY_TREE_TALENTS)
  22017. return false;
  22018. // spell not set in talent.dbc
  22019. uint32 spellid = talentInfo->RankID[talentRank];
  22020. if (spellid == 0)
  22021. {
  22022. TC_LOG_ERROR("entities.player", "Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
  22023. return false;
  22024. }
  22025. // already known
  22026. if (HasSpell(spellid))
  22027. return false;
  22028. // learn! (other talent ranks will unlearned at learning)
  22029. LearnSpell(spellid, false);
  22030. AddTalent(spellid, GetActiveSpec(), true);
  22031. TC_LOG_INFO("misc", "TalentID: %u Rank: %u Spell: %u Spec: %u\n", talentId, talentRank, spellid, GetActiveSpec());
  22032. // set talent tree for player
  22033. if (!GetPrimaryTalentTree(GetActiveSpec()))
  22034. {
  22035. SetPrimaryTalentTree(GetActiveSpec(), talentInfo->TalentTab);
  22036. std::vector<uint32> const* specSpells = GetTalentTreePrimarySpells(talentInfo->TalentTab);
  22037. if (specSpells)
  22038. for (size_t i = 0; i < specSpells->size(); ++i)
  22039. LearnSpell(specSpells->at(i), false);
  22040. if (CanUseMastery())
  22041. for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i)
  22042. if (SpellInfo const* masterySpell = sSpellMgr->GetSpellInfo(talentTabInfo->MasterySpellId[i]))
  22043. if (masterySpell->IsPassive() && IsNeedCastPassiveSpellAtLearn(masterySpell))
  22044. CastSpell(this, masterySpell->Id, true);
  22045. }
  22046. // update free talent points
  22047. SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
  22048. return true;
  22049. }
  22050. void Player::LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRank)
  22051. {
  22052. Pet* pet = GetPet();
  22053. if (!pet)
  22054. return;
  22055. if (petGuid != pet->GetGUID())
  22056. return;
  22057. uint32 CurTalentPoints = pet->GetFreeTalentPoints();
  22058. if (CurTalentPoints == 0)
  22059. return;
  22060. if (talentRank >= MAX_PET_TALENT_RANK)
  22061. return;
  22062. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  22063. if (!talentInfo)
  22064. return;
  22065. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
  22066. if (!talentTabInfo)
  22067. return;
  22068. CreatureTemplate const* ci = pet->GetCreatureTemplate();
  22069. if (!ci)
  22070. return;
  22071. CreatureFamilyEntry const* pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
  22072. if (!pet_family)
  22073. return;
  22074. if (pet_family->petTalentType < 0) // not hunter pet
  22075. return;
  22076. // prevent learn talent for different family (cheating)
  22077. if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
  22078. return;
  22079. // find current max talent rank (0~5)
  22080. uint8 curtalent_maxrank = 0; // 0 = not learned any rank
  22081. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  22082. {
  22083. if (talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank]))
  22084. {
  22085. curtalent_maxrank = (rank + 1);
  22086. break;
  22087. }
  22088. }
  22089. // we already have same or higher talent rank learned
  22090. if (curtalent_maxrank >= (talentRank + 1))
  22091. return;
  22092. // check if we have enough talent points
  22093. if (CurTalentPoints < (talentRank - curtalent_maxrank + 1))
  22094. return;
  22095. // Check if it requires another talent
  22096. if (talentInfo->DependsOn > 0)
  22097. {
  22098. if (TalentEntry const* depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
  22099. {
  22100. bool hasEnoughRank = false;
  22101. for (uint8 rank = talentInfo->DependsOnRank; rank < MAX_TALENT_RANK; rank++)
  22102. {
  22103. if (depTalentInfo->RankID[rank] != 0)
  22104. if (pet->HasSpell(depTalentInfo->RankID[rank]))
  22105. hasEnoughRank = true;
  22106. }
  22107. if (!hasEnoughRank)
  22108. return;
  22109. }
  22110. }
  22111. // Find out how many points we have in this field
  22112. uint32 spentPoints = 0;
  22113. uint32 tTab = talentInfo->TalentTab;
  22114. if (talentInfo->Row > 0)
  22115. {
  22116. uint32 numRows = sTalentStore.GetNumRows();
  22117. for (uint32 i = 0; i < numRows; ++i) // Loop through all talents.
  22118. {
  22119. // Someday, someone needs to revamp
  22120. const TalentEntry* tmpTalent = sTalentStore.LookupEntry(i);
  22121. if (tmpTalent) // the way talents are tracked
  22122. {
  22123. if (tmpTalent->TalentTab == tTab)
  22124. {
  22125. for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++)
  22126. {
  22127. if (tmpTalent->RankID[rank] != 0)
  22128. {
  22129. if (pet->HasSpell(tmpTalent->RankID[rank]))
  22130. {
  22131. spentPoints += (rank + 1);
  22132. }
  22133. }
  22134. }
  22135. }
  22136. }
  22137. }
  22138. }
  22139. // not have required min points spent in talent tree
  22140. if (spentPoints < (talentInfo->Row * MAX_PET_TALENT_RANK))
  22141. return;
  22142. // spell not set in talent.dbc
  22143. uint32 spellid = talentInfo->RankID[talentRank];
  22144. if (spellid == 0)
  22145. {
  22146. TC_LOG_ERROR("entities.player", "Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
  22147. return;
  22148. }
  22149. // already known
  22150. if (pet->HasSpell(spellid))
  22151. return;
  22152. // learn! (other talent ranks will unlearned at learning)
  22153. pet->learnSpell(spellid);
  22154. TC_LOG_INFO("entities.player", "PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
  22155. // update free talent points
  22156. pet->SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
  22157. }
  22158. void Player::AddKnownCurrency(uint32 itemId)
  22159. {
  22160. if (CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId))
  22161. SetFlag64(0, (1LL << (ctEntry->ID-1)));
  22162. }
  22163. void Player::UpdateFallInformationIfNeed(MovementInfo const& minfo, uint16 opcode)
  22164. {
  22165. if (m_lastFallTime >= minfo.jump.fallTime || m_lastFallZ <= minfo.pos.GetPositionZ() || opcode == MSG_MOVE_FALL_LAND)
  22166. SetFallInformation(minfo.jump.fallTime, minfo.pos.GetPositionZ());
  22167. }
  22168. void Player::UnsummonPetTemporaryIfAny()
  22169. {
  22170. Pet* pet = GetPet();
  22171. if (!pet)
  22172. return;
  22173. if (!m_temporaryUnsummonedPetNumber && pet->isControlled() && !pet->isTemporarySummoned())
  22174. {
  22175. m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
  22176. m_oldpetspell = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL);
  22177. }
  22178. RemovePet(pet, PET_SAVE_AS_CURRENT);
  22179. }
  22180. void Player::ResummonPetTemporaryUnSummonedIfAny()
  22181. {
  22182. if (!m_temporaryUnsummonedPetNumber)
  22183. return;
  22184. // not resummon in not appropriate state
  22185. if (IsPetNeedBeTemporaryUnsummoned())
  22186. return;
  22187. if (GetPetGUID())
  22188. return;
  22189. Pet* NewPet = new Pet(this);
  22190. if (!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
  22191. delete NewPet;
  22192. m_temporaryUnsummonedPetNumber = 0;
  22193. }
  22194. bool Player::IsPetNeedBeTemporaryUnsummoned() const
  22195. {
  22196. return !IsInWorld() || !IsAlive() || IsMounted() /*+in flight*/;
  22197. }
  22198. bool Player::CanSeeSpellClickOn(Creature const* c) const
  22199. {
  22200. if (!c->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK))
  22201. return false;
  22202. SpellClickInfoMapBounds clickPair = sObjectMgr->GetSpellClickInfoMapBounds(c->GetEntry());
  22203. if (clickPair.first == clickPair.second)
  22204. return true;
  22205. for (SpellClickInfoContainer::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr)
  22206. {
  22207. if (!itr->second.IsFitToRequirements(this, c))
  22208. return false;
  22209. ConditionList conds = sConditionMgr->GetConditionsForSpellClickEvent(c->GetEntry(), itr->second.spellId);
  22210. ConditionSourceInfo info = ConditionSourceInfo(const_cast<Player*>(this), const_cast<Creature*>(c));
  22211. if (sConditionMgr->IsObjectMeetToConditions(info, conds))
  22212. return true;
  22213. }
  22214. return false;
  22215. }
  22216. void Player::BuildPlayerTalentsInfoData(WorldPacket* data)
  22217. {
  22218. *data << uint32(GetFreeTalentPoints()); // unspentTalentPoints
  22219. *data << uint8(GetSpecsCount()); // talent group count (0, 1 or 2)
  22220. *data << uint8(GetActiveSpec()); // talent group index (0 or 1)
  22221. if (GetSpecsCount())
  22222. {
  22223. if (GetSpecsCount() > MAX_TALENT_SPECS)
  22224. SetSpecsCount(MAX_TALENT_SPECS);
  22225. // loop through all specs (only 1 for now)
  22226. for (uint8 specIdx = 0; specIdx < GetSpecsCount(); ++specIdx)
  22227. {
  22228. *data << uint32(GetPrimaryTalentTree(specIdx));
  22229. uint8 talentIdCount = 0;
  22230. size_t pos = data->wpos();
  22231. *data << uint8(talentIdCount); // [PH], talentIdCount
  22232. // find class talent tabs (all players have 3 talent tabs)
  22233. uint32 const* talentTabIds = GetTalentTabPages(getClass());
  22234. for (uint8 i = 0; i < MAX_TALENT_TABS; ++i)
  22235. {
  22236. uint32 talentTabId = talentTabIds[i];
  22237. for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
  22238. {
  22239. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  22240. if (!talentInfo)
  22241. continue;
  22242. // skip another tab talents
  22243. if (talentInfo->TalentTab != talentTabId)
  22244. continue;
  22245. // find max talent rank (0~4)
  22246. int8 curtalent_maxrank = -1;
  22247. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  22248. {
  22249. if (talentInfo->RankID[rank] && HasTalent(talentInfo->RankID[rank], specIdx))
  22250. {
  22251. curtalent_maxrank = rank;
  22252. break;
  22253. }
  22254. }
  22255. // not learned talent
  22256. if (curtalent_maxrank < 0)
  22257. continue;
  22258. *data << uint32(talentInfo->TalentID); // Talent.dbc
  22259. *data << uint8(curtalent_maxrank); // talentMaxRank (0-4)
  22260. ++talentIdCount;
  22261. }
  22262. }
  22263. data->put<uint8>(pos, talentIdCount); // put real count
  22264. *data << uint8(MAX_GLYPH_SLOT_INDEX); // glyphs count
  22265. for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
  22266. *data << uint16(GetGlyph(specIdx, i)); // GlyphProperties.dbc
  22267. }
  22268. }
  22269. }
  22270. void Player::BuildPetTalentsInfoData(WorldPacket* data)
  22271. {
  22272. uint32 unspentTalentPoints = 0;
  22273. size_t pointsPos = data->wpos();
  22274. *data << uint32(unspentTalentPoints); // [PH], unspentTalentPoints
  22275. uint8 talentIdCount = 0;
  22276. size_t countPos = data->wpos();
  22277. *data << uint8(talentIdCount); // [PH], talentIdCount
  22278. Pet* pet = GetPet();
  22279. if (!pet)
  22280. return;
  22281. unspentTalentPoints = pet->GetFreeTalentPoints();
  22282. data->put<uint32>(pointsPos, unspentTalentPoints); // put real points
  22283. CreatureTemplate const* ci = pet->GetCreatureTemplate();
  22284. if (!ci)
  22285. return;
  22286. CreatureFamilyEntry const* pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
  22287. if (!pet_family || pet_family->petTalentType < 0)
  22288. return;
  22289. for (uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId)
  22290. {
  22291. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentTabId);
  22292. if (!talentTabInfo)
  22293. continue;
  22294. if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
  22295. continue;
  22296. for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
  22297. {
  22298. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  22299. if (!talentInfo)
  22300. continue;
  22301. // skip another tab talents
  22302. if (talentInfo->TalentTab != talentTabId)
  22303. continue;
  22304. // find max talent rank (0~4)
  22305. int8 curtalent_maxrank = -1;
  22306. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  22307. {
  22308. if (talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank]))
  22309. {
  22310. curtalent_maxrank = rank;
  22311. break;
  22312. }
  22313. }
  22314. // not learned talent
  22315. if (curtalent_maxrank < 0)
  22316. continue;
  22317. *data << uint32(talentInfo->TalentID); // Talent.dbc
  22318. *data << uint8(curtalent_maxrank); // talentMaxRank (0-4)
  22319. ++talentIdCount;
  22320. }
  22321. data->put<uint8>(countPos, talentIdCount); // put real count
  22322. break;
  22323. }
  22324. }
  22325. void Player::SendTalentsInfoData(bool pet)
  22326. {
  22327. WorldPacket data(SMSG_TALENTS_INFO, 50);
  22328. data << uint8(pet ? 1 : 0);
  22329. if (pet)
  22330. BuildPetTalentsInfoData(&data);
  22331. else
  22332. BuildPlayerTalentsInfoData(&data);
  22333. GetSession()->SendPacket(&data);
  22334. }
  22335. void Player::BuildEnchantmentsInfoData(WorldPacket* data)
  22336. {
  22337. uint32 slotUsedMask = 0;
  22338. size_t slotUsedMaskPos = data->wpos();
  22339. *data << uint32(slotUsedMask); // slotUsedMask < 0x80000
  22340. for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
  22341. {
  22342. Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
  22343. if (!item)
  22344. continue;
  22345. slotUsedMask |= (1 << i);
  22346. *data << uint32(item->GetEntry()); // item entry
  22347. uint16 enchantmentMask = 0;
  22348. size_t enchantmentMaskPos = data->wpos();
  22349. *data << uint16(enchantmentMask); // enchantmentMask < 0x1000
  22350. for (uint32 j = 0; j < MAX_ENCHANTMENT_SLOT; ++j)
  22351. {
  22352. uint32 enchId = item->GetEnchantmentId(EnchantmentSlot(j));
  22353. if (!enchId)
  22354. continue;
  22355. enchantmentMask |= (1 << j);
  22356. *data << uint16(enchId); // enchantmentId?
  22357. }
  22358. data->put<uint16>(enchantmentMaskPos, enchantmentMask);
  22359. *data << uint16(0); // unknown
  22360. *data << item->GetGuidValue(ITEM_FIELD_CREATOR).WriteAsPacked(); // item creator
  22361. *data << uint32(0); // seed?
  22362. }
  22363. data->put<uint32>(slotUsedMaskPos, slotUsedMask);
  22364. }
  22365. void Player::SendEquipmentSetList()
  22366. {
  22367. uint32 count = 0;
  22368. WorldPacket data(SMSG_EQUIPMENT_SET_LIST, 4);
  22369. size_t count_pos = data.wpos();
  22370. data << uint32(count); // count placeholder
  22371. for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
  22372. {
  22373. if (itr->second.state == EQUIPMENT_SET_DELETED)
  22374. continue;
  22375. data.appendPackGUID(itr->second.Guid);
  22376. data << uint32(itr->first);
  22377. data << itr->second.Name;
  22378. data << itr->second.IconName;
  22379. for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
  22380. {
  22381. // ignored slots stored in IgnoreMask, client wants "1" as raw GUID, so no HIGHGUID_ITEM
  22382. if (itr->second.IgnoreMask & (1 << i))
  22383. data.appendPackGUID(uint64(1));
  22384. else
  22385. data << ObjectGuid(HIGHGUID_ITEM, 0, itr->second.Items[i]).WriteAsPacked();
  22386. }
  22387. ++count; // client have limit but it checked at loading and set
  22388. }
  22389. data.put<uint32>(count_pos, count);
  22390. GetSession()->SendPacket(&data);
  22391. }
  22392. void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset)
  22393. {
  22394. if (eqset.Guid != 0)
  22395. {
  22396. bool found = false;
  22397. for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
  22398. {
  22399. if ((itr->second.Guid == eqset.Guid) && (itr->first == index))
  22400. {
  22401. found = true;
  22402. break;
  22403. }
  22404. }
  22405. if (!found) // something wrong...
  22406. {
  22407. TC_LOG_ERROR("entities.player", "Player %s tried to save equipment set " UI64FMTD " (index %u), but that equipment set not found!", GetName().c_str(), eqset.Guid, index);
  22408. return;
  22409. }
  22410. }
  22411. EquipmentSet& eqslot = m_EquipmentSets[index];
  22412. EquipmentSetUpdateState old_state = eqslot.state;
  22413. eqslot = eqset;
  22414. if (eqset.Guid == 0)
  22415. {
  22416. eqslot.Guid = sObjectMgr->GenerateEquipmentSetGuid();
  22417. WorldPacket data(SMSG_EQUIPMENT_SET_SAVED, 4 + 1);
  22418. data << uint32(index);
  22419. data.appendPackGUID(eqslot.Guid);
  22420. GetSession()->SendPacket(&data);
  22421. }
  22422. eqslot.state = old_state == EQUIPMENT_SET_NEW ? EQUIPMENT_SET_NEW : EQUIPMENT_SET_CHANGED;
  22423. }
  22424. void Player::_SaveEquipmentSets(SQLTransaction& trans)
  22425. {
  22426. for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end();)
  22427. {
  22428. uint32 index = itr->first;
  22429. EquipmentSet& eqset = itr->second;
  22430. PreparedStatement* stmt = NULL;
  22431. uint8 j = 0;
  22432. switch (eqset.state)
  22433. {
  22434. case EQUIPMENT_SET_UNCHANGED:
  22435. ++itr;
  22436. break; // nothing do
  22437. case EQUIPMENT_SET_CHANGED:
  22438. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_EQUIP_SET);
  22439. stmt->setString(j++, eqset.Name.c_str());
  22440. stmt->setString(j++, eqset.IconName.c_str());
  22441. stmt->setUInt32(j++, eqset.IgnoreMask);
  22442. for (uint8 i=0; i<EQUIPMENT_SLOT_END; ++i)
  22443. stmt->setUInt32(j++, eqset.Items[i]);
  22444. stmt->setUInt32(j++, GetGUIDLow());
  22445. stmt->setUInt64(j++, eqset.Guid);
  22446. stmt->setUInt32(j, index);
  22447. trans->Append(stmt);
  22448. eqset.state = EQUIPMENT_SET_UNCHANGED;
  22449. ++itr;
  22450. break;
  22451. case EQUIPMENT_SET_NEW:
  22452. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_EQUIP_SET);
  22453. stmt->setUInt32(j++, GetGUIDLow());
  22454. stmt->setUInt64(j++, eqset.Guid);
  22455. stmt->setUInt32(j++, index);
  22456. stmt->setString(j++, eqset.Name.c_str());
  22457. stmt->setString(j++, eqset.IconName.c_str());
  22458. stmt->setUInt32(j++, eqset.IgnoreMask);
  22459. for (uint8 i=0; i<EQUIPMENT_SLOT_END; ++i)
  22460. stmt->setUInt32(j++, eqset.Items[i]);
  22461. trans->Append(stmt);
  22462. eqset.state = EQUIPMENT_SET_UNCHANGED;
  22463. ++itr;
  22464. break;
  22465. case EQUIPMENT_SET_DELETED:
  22466. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EQUIP_SET);
  22467. stmt->setUInt64(0, eqset.Guid);
  22468. trans->Append(stmt);
  22469. m_EquipmentSets.erase(itr++);
  22470. break;
  22471. }
  22472. }
  22473. }
  22474. void Player::_SaveBGData(SQLTransaction& trans)
  22475. {
  22476. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_BGDATA);
  22477. stmt->setUInt32(0, GetGUIDLow());
  22478. trans->Append(stmt);
  22479. /* guid, bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */
  22480. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_BGDATA);
  22481. stmt->setUInt32(0, GetGUIDLow());
  22482. stmt->setUInt32(1, m_bgData.bgInstanceID);
  22483. stmt->setUInt16(2, m_bgData.bgTeam);
  22484. stmt->setFloat (3, m_bgData.joinPos.GetPositionX());
  22485. stmt->setFloat (4, m_bgData.joinPos.GetPositionY());
  22486. stmt->setFloat (5, m_bgData.joinPos.GetPositionZ());
  22487. stmt->setFloat (6, m_bgData.joinPos.GetOrientation());
  22488. stmt->setUInt16(7, m_bgData.joinPos.GetMapId());
  22489. stmt->setUInt16(8, m_bgData.taxiPath[0]);
  22490. stmt->setUInt16(9, m_bgData.taxiPath[1]);
  22491. stmt->setUInt16(10, m_bgData.mountSpell);
  22492. trans->Append(stmt);
  22493. }
  22494. void Player::DeleteEquipmentSet(uint64 setGuid)
  22495. {
  22496. for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
  22497. {
  22498. if (itr->second.Guid == setGuid)
  22499. {
  22500. if (itr->second.state == EQUIPMENT_SET_NEW)
  22501. m_EquipmentSets.erase(itr);
  22502. else
  22503. itr->second.state = EQUIPMENT_SET_DELETED;
  22504. break;
  22505. }
  22506. }
  22507. }
  22508. void Player::RemoveAtLoginFlag(AtLoginFlags flags, bool persist /*= false*/)
  22509. {
  22510. m_atLoginFlags &= ~flags;
  22511. if (persist)
  22512. {
  22513. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_REM_AT_LOGIN_FLAG);
  22514. stmt->setUInt16(0, uint16(flags));
  22515. stmt->setUInt32(1, GetGUIDLow());
  22516. CharacterDatabase.Execute(stmt);
  22517. }
  22518. }
  22519. void Player::SendClearCooldown(uint32 spell_id, Unit* target)
  22520. {
  22521. WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8);
  22522. data << uint32(spell_id);
  22523. data << uint64(target->GetGUID());
  22524. SendDirectMessage(&data);
  22525. }
  22526. void Player::SendClearAllCooldowns(Unit* target)
  22527. {
  22528. uint32 spellCount = m_spellCooldowns.size();
  22529. ObjectGuid guid = target ? target->GetGUID() : ObjectGuid::Empty;
  22530. WorldPacket data(SMSG_CLEAR_COOLDOWNS, 4+8);
  22531. data.WriteBit(guid[1]);
  22532. data.WriteBit(guid[3]);
  22533. data.WriteBit(guid[6]);
  22534. data.WriteBits(spellCount, 24); // Spell Count
  22535. data.WriteBit(guid[7]);
  22536. data.WriteBit(guid[5]);
  22537. data.WriteBit(guid[2]);
  22538. data.WriteBit(guid[4]);
  22539. data.WriteBit(guid[0]);
  22540. data.FlushBits();
  22541. data.WriteByteSeq(guid[7]);
  22542. data.WriteByteSeq(guid[2]);
  22543. data.WriteByteSeq(guid[4]);
  22544. data.WriteByteSeq(guid[5]);
  22545. data.WriteByteSeq(guid[1]);
  22546. data.WriteByteSeq(guid[3]);
  22547. for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); ++itr)
  22548. data << uint32(itr->first); // Spell ID
  22549. data.WriteByteSeq(guid[0]);
  22550. data.WriteByteSeq(guid[6]);
  22551. SendDirectMessage(&data);
  22552. }
  22553. void Player::ResetMap()
  22554. {
  22555. // this may be called during Map::Update
  22556. // after decrement+unlink, ++m_mapRefIter will continue correctly
  22557. // when the first element of the list is being removed
  22558. // nocheck_prev will return the padding element of the RefManager
  22559. // instead of NULL in the case of prev
  22560. GetMap()->UpdateIteratorBack(this);
  22561. Unit::ResetMap();
  22562. GetMapRef().unlink();
  22563. }
  22564. void Player::SetMap(Map* map)
  22565. {
  22566. Unit::SetMap(map);
  22567. m_mapRef.link(map, this);
  22568. }
  22569. void Player::_LoadGlyphs(PreparedQueryResult result)
  22570. {
  22571. // SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6, glyph7, glyph8, glyph9 FROM character_glyphs WHERE guid = '%u'
  22572. if (!result)
  22573. return;
  22574. do
  22575. {
  22576. Field* fields = result->Fetch();
  22577. uint8 spec = fields[0].GetUInt8();
  22578. if (spec >= GetSpecsCount())
  22579. continue;
  22580. for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
  22581. _talentMgr->SpecInfo[spec].Glyphs[i] = fields[i + 1].GetUInt16();
  22582. }
  22583. while (result->NextRow());
  22584. }
  22585. void Player::_SaveGlyphs(SQLTransaction& trans)
  22586. {
  22587. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GLYPHS);
  22588. stmt->setUInt32(0, GetGUIDLow());
  22589. trans->Append(stmt);
  22590. for (uint8 spec = 0; spec < GetSpecsCount(); ++spec)
  22591. {
  22592. uint8 index = 0;
  22593. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_GLYPHS);
  22594. stmt->setUInt32(index++, GetGUIDLow());
  22595. stmt->setUInt8(index++, spec);
  22596. for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
  22597. stmt->setUInt16(index++, uint16(GetGlyph(spec, i)));
  22598. trans->Append(stmt);
  22599. }
  22600. }
  22601. void Player::_LoadTalents(PreparedQueryResult result)
  22602. {
  22603. // SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid = '%u'", GUID_LOPART(m_guid));
  22604. if (result)
  22605. {
  22606. do
  22607. AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt8(), false);
  22608. while (result->NextRow());
  22609. }
  22610. }
  22611. void Player::_SaveTalents(SQLTransaction& trans)
  22612. {
  22613. PreparedStatement* stmt = NULL;
  22614. for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
  22615. {
  22616. for (PlayerTalentMap::iterator itr = GetTalentMap(i)->begin(); itr != GetTalentMap(i)->end();)
  22617. {
  22618. if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
  22619. {
  22620. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC);
  22621. stmt->setUInt32(0, GetGUIDLow());
  22622. stmt->setUInt32(1, itr->first);
  22623. stmt->setUInt8(2, itr->second->spec);
  22624. trans->Append(stmt);
  22625. }
  22626. if (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED)
  22627. {
  22628. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_TALENT);
  22629. stmt->setUInt32(0, GetGUIDLow());
  22630. stmt->setUInt32(1, itr->first);
  22631. stmt->setUInt8(2, itr->second->spec);
  22632. trans->Append(stmt);
  22633. }
  22634. if (itr->second->state == PLAYERSPELL_REMOVED)
  22635. {
  22636. delete itr->second;
  22637. GetTalentMap(i)->erase(itr++);
  22638. }
  22639. else
  22640. {
  22641. itr->second->state = PLAYERSPELL_UNCHANGED;
  22642. ++itr;
  22643. }
  22644. }
  22645. }
  22646. }
  22647. void Player::UpdateSpecCount(uint8 count)
  22648. {
  22649. uint32 curCount = GetSpecsCount();
  22650. if (curCount == count)
  22651. return;
  22652. if (GetActiveSpec() >= count)
  22653. ActivateSpec(0);
  22654. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  22655. PreparedStatement* stmt = NULL;
  22656. // Copy spec data
  22657. if (count > curCount)
  22658. {
  22659. _SaveActions(trans); // make sure the button list is cleaned up
  22660. for (ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end(); ++itr)
  22661. {
  22662. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACTION);
  22663. stmt->setUInt32(0, GetGUIDLow());
  22664. stmt->setUInt8(1, 1);
  22665. stmt->setUInt8(2, itr->first);
  22666. stmt->setUInt32(3, itr->second.GetAction());
  22667. stmt->setUInt8(4, uint8(itr->second.GetType()));
  22668. trans->Append(stmt);
  22669. }
  22670. }
  22671. // Delete spec data for removed spec.
  22672. else if (count < curCount)
  22673. {
  22674. _SaveActions(trans);
  22675. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC);
  22676. stmt->setUInt8(0, GetActiveSpec());
  22677. stmt->setUInt32(1, GetGUIDLow());
  22678. trans->Append(stmt);
  22679. }
  22680. CharacterDatabase.CommitTransaction(trans);
  22681. SetSpecsCount(count);
  22682. SendTalentsInfoData(false);
  22683. }
  22684. void Player::ActivateSpec(uint8 spec)
  22685. {
  22686. if (GetActiveSpec() == spec)
  22687. return;
  22688. if (spec > GetSpecsCount())
  22689. return;
  22690. if (IsNonMeleeSpellCast(false))
  22691. InterruptNonMeleeSpells(false);
  22692. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  22693. _SaveActions(trans);
  22694. CharacterDatabase.CommitTransaction(trans);
  22695. // TO-DO: We need more research to know what happens with warlock's reagent
  22696. if (Pet* pet = GetPet())
  22697. RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
  22698. ClearComboPointHolders();
  22699. ClearAllReactives();
  22700. UnsummonAllTotems();
  22701. ExitVehicle();
  22702. RemoveAllControlled();
  22703. /*RemoveAllAurasOnDeath();
  22704. if (GetPet())
  22705. GetPet()->RemoveAllAurasOnDeath();*/
  22706. //RemoveAllAuras(GetGUID(), NULL, false, true); // removes too many auras
  22707. //ExitVehicle(); // should be impossible to switch specs from inside a vehicle..
  22708. // Let client clear his current Actions
  22709. SendActionButtons(2);
  22710. // m_actionButtons.clear() is called in the next _LoadActionButtons
  22711. for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
  22712. {
  22713. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  22714. if (!talentInfo)
  22715. continue;
  22716. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
  22717. if (!talentTabInfo)
  22718. continue;
  22719. // unlearn only talents for character class
  22720. // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
  22721. // to prevent unexpected lost normal learned spell skip another class talents
  22722. if ((getClassMask() & talentTabInfo->ClassMask) == 0)
  22723. continue;
  22724. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  22725. {
  22726. // skip non-existant talent ranks
  22727. if (talentInfo->RankID[rank] == 0)
  22728. continue;
  22729. RemoveSpell(talentInfo->RankID[rank], true); // removes the talent, and all dependant, learned, and chained spells..
  22730. if (const SpellInfo* _spellEntry = sSpellMgr->GetSpellInfo(talentInfo->RankID[rank]))
  22731. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) // search through the SpellInfo for valid trigger spells
  22732. if (_spellEntry->Effects[i].TriggerSpell > 0 && _spellEntry->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL)
  22733. RemoveSpell(_spellEntry->Effects[i].TriggerSpell, true); // and remove any spells that the talent teaches
  22734. // if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
  22735. //PlayerTalentMap::iterator plrTalent = m_talents[m_activeSpec]->find(talentInfo->RankID[rank]);
  22736. //if (plrTalent != m_talents[m_activeSpec]->end())
  22737. // plrTalent->second->state = PLAYERSPELL_REMOVED;
  22738. }
  22739. }
  22740. // Remove spec specific spells
  22741. for (uint32 i = 0; i < MAX_TALENT_TABS; ++i)
  22742. {
  22743. uint32 const* talentTabs = GetTalentTabPages(getClass());
  22744. std::vector<uint32> const* specSpells = GetTalentTreePrimarySpells(talentTabs[i]);
  22745. if (specSpells)
  22746. for (size_t i = 0; i < specSpells->size(); ++i)
  22747. RemoveSpell(specSpells->at(i), true);
  22748. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentTabs[i]);
  22749. for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i)
  22750. if (uint32 mastery = talentTabInfo->MasterySpellId[i])
  22751. RemoveSpell(mastery, true);
  22752. }
  22753. // set glyphs
  22754. for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
  22755. // remove secondary glyph
  22756. if (uint32 oldglyph = GetGlyph(GetActiveSpec(), slot))
  22757. if (GlyphPropertiesEntry const* old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph))
  22758. RemoveAurasDueToSpell(old_gp->SpellId);
  22759. SetActiveSpec(spec);
  22760. uint32 spentTalents = 0;
  22761. for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
  22762. {
  22763. TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
  22764. if (!talentInfo)
  22765. continue;
  22766. TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
  22767. if (!talentTabInfo)
  22768. continue;
  22769. // learn only talents for character class
  22770. if ((getClassMask() & talentTabInfo->ClassMask) == 0)
  22771. continue;
  22772. // learn highest talent rank that exists in newly activated spec
  22773. for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
  22774. {
  22775. // skip non-existant talent ranks
  22776. if (talentInfo->RankID[rank] == 0)
  22777. continue;
  22778. // if the talent can be found in the newly activated PlayerTalentMap
  22779. if (HasTalent(talentInfo->RankID[rank], GetActiveSpec()))
  22780. {
  22781. LearnSpell(talentInfo->RankID[rank], false); // add the talent to the PlayerSpellMap
  22782. spentTalents += (rank + 1); // increment the spentTalents count
  22783. }
  22784. }
  22785. }
  22786. std::vector<uint32> const* specSpells = GetTalentTreePrimarySpells(GetPrimaryTalentTree(GetActiveSpec()));
  22787. if (specSpells)
  22788. for (size_t i = 0; i < specSpells->size(); ++i)
  22789. LearnSpell(specSpells->at(i), false);
  22790. if (CanUseMastery())
  22791. if (TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(GetPrimaryTalentTree(GetActiveSpec())))
  22792. for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i)
  22793. if (uint32 mastery = talentTabInfo->MasterySpellId[i])
  22794. LearnSpell(mastery, false);
  22795. // set glyphs
  22796. for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
  22797. {
  22798. uint32 glyph = GetGlyph(GetActiveSpec(), slot);
  22799. // apply primary glyph
  22800. if (glyph)
  22801. if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph))
  22802. CastSpell(this, gp->SpellId, true);
  22803. SetGlyph(slot, glyph);
  22804. }
  22805. SetUsedTalentCount(spentTalents);
  22806. InitTalentForLevel();
  22807. {
  22808. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC);
  22809. stmt->setUInt32(0, GetGUIDLow());
  22810. stmt->setUInt8(1, GetActiveSpec());
  22811. if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
  22812. _LoadActions(result);
  22813. }
  22814. SendActionButtons(1);
  22815. Powers pw = getPowerType();
  22816. if (pw != POWER_MANA)
  22817. SetPower(POWER_MANA, 0); // Mana must be 0 even if it isn't the active power type.
  22818. SetPower(pw, 0);
  22819. if (!sTalentTabStore.LookupEntry(GetPrimaryTalentTree(GetActiveSpec())))
  22820. ResetTalents(true);
  22821. }
  22822. void Player::ResetTimeSync()
  22823. {
  22824. m_timeSyncTimer = 0;
  22825. m_timeSyncClient = 0;
  22826. m_timeSyncServer = getMSTime();
  22827. }
  22828. void Player::SendTimeSync()
  22829. {
  22830. m_timeSyncQueue.push(m_movementCounter++);
  22831. WorldPacket data(SMSG_TIME_SYNC_REQ, 4);
  22832. data << uint32(m_timeSyncQueue.back());
  22833. GetSession()->SendPacket(&data);
  22834. // Schedule next sync in 10 sec
  22835. m_timeSyncTimer = 10000;
  22836. m_timeSyncServer = getMSTime();
  22837. if (m_timeSyncQueue.size() > 3)
  22838. TC_LOG_ERROR("network", "Not received CMSG_TIME_SYNC_RESP for over 30 seconds from player %u (%s), possible cheater", GetGUIDLow(), GetName().c_str());
  22839. }
  22840. void Player::SetReputation(uint32 factionentry, uint32 value)
  22841. {
  22842. GetReputationMgr().SetReputation(sFactionStore.LookupEntry(factionentry), value);
  22843. }
  22844. uint32 Player::GetReputation(uint32 factionentry) const
  22845. {
  22846. return GetReputationMgr().GetReputation(sFactionStore.LookupEntry(factionentry));
  22847. }
  22848. std::string Player::GetGuildName()
  22849. {
  22850. return GetGuildId() ? sGuildMgr->GetGuildById(GetGuildId())->GetName() : "";
  22851. }
  22852. void Player::SendDuelCountdown(uint32 counter)
  22853. {
  22854. WorldPacket data(SMSG_DUEL_COUNTDOWN, 4);
  22855. data << uint32(counter); // seconds
  22856. GetSession()->SendPacket(&data);
  22857. }
  22858. void Player::AddRefundReference(ObjectGuid it)
  22859. {
  22860. m_refundableItems.insert(it);
  22861. }
  22862. void Player::DeleteRefundReference(ObjectGuid it)
  22863. {
  22864. GuidSet::iterator itr = m_refundableItems.find(it);
  22865. if (itr != m_refundableItems.end())
  22866. m_refundableItems.erase(itr);
  22867. }
  22868. void Player::SendRefundInfo(Item* item)
  22869. {
  22870. // This function call unsets ITEM_FLAGS_REFUNDABLE if played time is over 2 hours.
  22871. item->UpdatePlayedTime(this);
  22872. if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE))
  22873. {
  22874. TC_LOG_DEBUG("entities.player.items", "Item refund: item not refundable!");
  22875. return;
  22876. }
  22877. if (GetGUIDLow() != item->GetRefundRecipient()) // Formerly refundable item got traded
  22878. {
  22879. TC_LOG_DEBUG("entities.player.items", "Item refund: item was traded!");
  22880. item->SetNotRefundable(this);
  22881. return;
  22882. }
  22883. ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(item->GetPaidExtendedCost());
  22884. if (!iece)
  22885. {
  22886. TC_LOG_DEBUG("entities.player.items", "Item refund: cannot find extendedcost data.");
  22887. return;
  22888. }
  22889. ObjectGuid guid = item->GetGUID();
  22890. WorldPacket data(SMSG_ITEM_REFUND_INFO_RESPONSE, 8+4+4+4+4*4+4*4+4+4);
  22891. data.WriteBit(guid[3]);
  22892. data.WriteBit(guid[5]);
  22893. data.WriteBit(guid[7]);
  22894. data.WriteBit(guid[6]);
  22895. data.WriteBit(guid[2]);
  22896. data.WriteBit(guid[4]);
  22897. data.WriteBit(guid[0]);
  22898. data.WriteBit(guid[1]);
  22899. data.FlushBits();
  22900. data.WriteByteSeq(guid[7]);
  22901. data << uint32(GetTotalPlayedTime() - item->GetPlayedTime());
  22902. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i) // item cost data
  22903. {
  22904. data << uint32(iece->RequiredItemCount[i]);
  22905. data << uint32(iece->RequiredItem[i]);
  22906. }
  22907. data.WriteByteSeq(guid[6]);
  22908. data.WriteByteSeq(guid[4]);
  22909. data.WriteByteSeq(guid[3]);
  22910. data.WriteByteSeq(guid[2]);
  22911. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i) // currency cost data
  22912. {
  22913. if (iece->RequirementFlags & (ITEM_EXT_COST_CURRENCY_REQ_IS_SEASON_EARNED_1 << i))
  22914. {
  22915. data << uint32(0);
  22916. data << uint32(0);
  22917. continue;
  22918. }
  22919. CurrencyTypesEntry const* currencyType = sCurrencyTypesStore.LookupEntry(iece->RequiredCurrency[i]);
  22920. uint32 precision = (currencyType && currencyType->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
  22921. data << uint32(iece->RequiredCurrencyCount[i] / precision);
  22922. data << uint32(iece->RequiredCurrency[i]);
  22923. }
  22924. data.WriteByteSeq(guid[1]);
  22925. data.WriteByteSeq(guid[5]);
  22926. data << uint32(0);
  22927. data.WriteByteSeq(guid[0]);
  22928. data << uint32(item->GetPaidMoney()); // money cost
  22929. GetSession()->SendPacket(&data);
  22930. }
  22931. bool Player::AddItem(uint32 itemId, uint32 count)
  22932. {
  22933. uint32 noSpaceForCount = 0;
  22934. ItemPosCountVec dest;
  22935. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount);
  22936. if (msg != EQUIP_ERR_OK)
  22937. count -= noSpaceForCount;
  22938. if (count == 0 || dest.empty())
  22939. {
  22940. /// @todo Send to mailbox if no space
  22941. ChatHandler(GetSession()).PSendSysMessage("You don't have any space in your bags.");
  22942. return false;
  22943. }
  22944. Item* item = StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
  22945. if (item)
  22946. SendNewItem(item, count, true, false);
  22947. else
  22948. return false;
  22949. return true;
  22950. }
  22951. void Player::SendItemRefundResult(Item* item, ItemExtendedCostEntry const* iece, uint8 error)
  22952. {
  22953. ObjectGuid guid = item->GetGUID();
  22954. WorldPacket data(SMSG_ITEM_REFUND_RESULT, 1 + 1 + 8 + 4*8 + 4 + 4*8 + 1);
  22955. data.WriteBit(guid[4]);
  22956. data.WriteBit(guid[5]);
  22957. data.WriteBit(guid[1]);
  22958. data.WriteBit(guid[6]);
  22959. data.WriteBit(guid[7]);
  22960. data.WriteBit(guid[0]);
  22961. data.WriteBit(guid[3]);
  22962. data.WriteBit(guid[2]);
  22963. data.WriteBit(!error);
  22964. data.WriteBit(item->GetPaidMoney() > 0);
  22965. data.FlushBits();
  22966. if (!error)
  22967. {
  22968. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
  22969. {
  22970. if (iece->RequirementFlags & (ITEM_EXT_COST_CURRENCY_REQ_IS_SEASON_EARNED_1 << i))
  22971. {
  22972. data << uint32(0);
  22973. data << uint32(0);
  22974. continue;
  22975. }
  22976. CurrencyTypesEntry const* currencyType = sCurrencyTypesStore.LookupEntry(iece->RequiredCurrency[i]);
  22977. uint32 precision = (currencyType && currencyType->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
  22978. data << uint32(iece->RequiredCurrencyCount[i] / precision);
  22979. data << uint32(iece->RequiredCurrency[i]);
  22980. }
  22981. data << uint32(item->GetPaidMoney()); // money cost
  22982. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i) // item cost data
  22983. {
  22984. data << uint32(iece->RequiredItemCount[i]);
  22985. data << uint32(iece->RequiredItem[i]);
  22986. }
  22987. }
  22988. data.WriteByteSeq(guid[0]);
  22989. data.WriteByteSeq(guid[3]);
  22990. data.WriteByteSeq(guid[1]);
  22991. data.WriteByteSeq(guid[6]);
  22992. data.WriteByteSeq(guid[4]);
  22993. data.WriteByteSeq(guid[2]);
  22994. data.WriteByteSeq(guid[7]);
  22995. data.WriteByteSeq(guid[5]);
  22996. data << uint8(error); // error code
  22997. GetSession()->SendPacket(&data);
  22998. }
  22999. void Player::RefundItem(Item* item)
  23000. {
  23001. if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE))
  23002. {
  23003. TC_LOG_DEBUG("entities.player.items", "Item refund: item not refundable!");
  23004. return;
  23005. }
  23006. if (item->IsRefundExpired()) // item refund has expired
  23007. {
  23008. item->SetNotRefundable(this);
  23009. SendItemRefundResult(item, NULL, 10);
  23010. return;
  23011. }
  23012. if (GetGUIDLow() != item->GetRefundRecipient()) // Formerly refundable item got traded
  23013. {
  23014. TC_LOG_DEBUG("entities.player.items", "Item refund: item was traded!");
  23015. item->SetNotRefundable(this);
  23016. return;
  23017. }
  23018. ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(item->GetPaidExtendedCost());
  23019. if (!iece)
  23020. {
  23021. TC_LOG_DEBUG("entities.player.items", "Item refund: cannot find extendedcost data.");
  23022. return;
  23023. }
  23024. bool store_error = false;
  23025. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i)
  23026. {
  23027. uint32 count = iece->RequiredItemCount[i];
  23028. uint32 itemid = iece->RequiredItem[i];
  23029. if (count && itemid)
  23030. {
  23031. ItemPosCountVec dest;
  23032. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemid, count);
  23033. if (msg != EQUIP_ERR_OK)
  23034. {
  23035. store_error = true;
  23036. break;
  23037. }
  23038. }
  23039. }
  23040. if (store_error)
  23041. {
  23042. SendItemRefundResult(item, iece, 10);
  23043. return;
  23044. }
  23045. SendItemRefundResult(item, iece, 0);
  23046. uint32 moneyRefund = item->GetPaidMoney(); // item-> will be invalidated in DestroyItem
  23047. // Save all relevant data to DB to prevent desynchronisation exploits
  23048. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  23049. // Delete any references to the refund data
  23050. item->SetNotRefundable(this, true, &trans);
  23051. // Destroy item
  23052. DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
  23053. // Grant back extendedcost items
  23054. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i)
  23055. {
  23056. uint32 count = iece->RequiredItemCount[i];
  23057. uint32 itemid = iece->RequiredItem[i];
  23058. if (count && itemid)
  23059. {
  23060. ItemPosCountVec dest;
  23061. InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemid, count);
  23062. ASSERT(msg == EQUIP_ERR_OK); /// Already checked before
  23063. Item* it = StoreNewItem(dest, itemid, true);
  23064. SendNewItem(it, count, true, false, true);
  23065. }
  23066. }
  23067. // Grant back currencies
  23068. for (uint8 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
  23069. {
  23070. if (iece->RequirementFlags & (ITEM_EXT_COST_CURRENCY_REQ_IS_SEASON_EARNED_1 << i))
  23071. continue;
  23072. uint32 count = iece->RequiredCurrencyCount[i];
  23073. uint32 currencyid = iece->RequiredCurrency[i];
  23074. if (count && currencyid)
  23075. ModifyCurrency(currencyid, count);
  23076. }
  23077. // Grant back money
  23078. if (moneyRefund)
  23079. ModifyMoney(moneyRefund); // Saved in SaveInventoryAndGoldToDB
  23080. SaveInventoryAndGoldToDB(trans);
  23081. CharacterDatabase.CommitTransaction(trans);
  23082. }
  23083. void Player::SendItemRetrievalMail(uint32 itemEntry, uint32 count)
  23084. {
  23085. MailSender sender(MAIL_CREATURE, 34337 /* The Postmaster */);
  23086. MailDraft draft("Recovered Item", "We recovered a lost item in the twisting nether and noted that it was yours.$B$BPlease find said object enclosed."); // This is the text used in Cataclysm, it probably wasn't changed.
  23087. SQLTransaction trans = CharacterDatabase.BeginTransaction();
  23088. if (Item* item = Item::CreateItem(itemEntry, count, 0))
  23089. {
  23090. item->SaveToDB(trans);
  23091. draft.AddItem(item);
  23092. }
  23093. draft.SendMailTo(trans, MailReceiver(this, GetGUIDLow()), sender);
  23094. CharacterDatabase.CommitTransaction(trans);
  23095. }
  23096. void Player::SetRandomWinner(bool isWinner)
  23097. {
  23098. m_IsBGRandomWinner = isWinner;
  23099. if (m_IsBGRandomWinner)
  23100. {
  23101. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BATTLEGROUND_RANDOM);
  23102. stmt->setUInt32(0, GetGUIDLow());
  23103. CharacterDatabase.Execute(stmt);
  23104. }
  23105. }
  23106. void Player::_LoadRandomBGStatus(PreparedQueryResult result)
  23107. {
  23108. //QueryResult result = CharacterDatabase.PQuery("SELECT guid FROM character_battleground_random WHERE guid = '%u'", GetGUIDLow());
  23109. if (result)
  23110. m_IsBGRandomWinner = true;
  23111. }
  23112. float Player::GetAverageItemLevel()
  23113. {
  23114. float sum = 0;
  23115. uint32 count = 0;
  23116. for (int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
  23117. {
  23118. // don't check tabard, ranged, offhand or shirt
  23119. if (i == EQUIPMENT_SLOT_TABARD || i == EQUIPMENT_SLOT_RANGED || i == EQUIPMENT_SLOT_OFFHAND || i == EQUIPMENT_SLOT_BODY)
  23120. continue;
  23121. if (m_items[i] && m_items[i]->GetTemplate())
  23122. sum += m_items[i]->GetTemplate()->GetItemLevelIncludingQuality();
  23123. ++count;
  23124. }
  23125. return ((float)sum) / count;
  23126. }
  23127. void Player::_LoadInstanceTimeRestrictions(PreparedQueryResult result)
  23128. {
  23129. if (!result)
  23130. return;
  23131. do
  23132. {
  23133. Field* fields = result->Fetch();
  23134. _instanceResetTimes.insert(InstanceTimeMap::value_type(fields[0].GetUInt32(), fields[1].GetUInt64()));
  23135. } while (result->NextRow());
  23136. }
  23137. void Player::_SaveInstanceTimeRestrictions(SQLTransaction& trans)
  23138. {
  23139. if (_instanceResetTimes.empty())
  23140. return;
  23141. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES);
  23142. stmt->setUInt32(0, GetSession()->GetAccountId());
  23143. trans->Append(stmt);
  23144. for (InstanceTimeMap::const_iterator itr = _instanceResetTimes.begin(); itr != _instanceResetTimes.end(); ++itr)
  23145. {
  23146. stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES);
  23147. stmt->setUInt32(0, GetSession()->GetAccountId());
  23148. stmt->setUInt32(1, itr->first);
  23149. stmt->setUInt64(2, itr->second);
  23150. trans->Append(stmt);
  23151. }
  23152. }
  23153. bool Player::IsInWhisperWhiteList(ObjectGuid guid)
  23154. {
  23155. for (GuidList::const_iterator itr = WhisperList.begin(); itr != WhisperList.end(); ++itr)
  23156. if (*itr == guid)
  23157. return true;
  23158. return false;
  23159. }
  23160. uint8 Player::GetNextVoidStorageFreeSlot() const
  23161. {
  23162. for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
  23163. if (!_voidStorageItems[i]) // unused item
  23164. return i;
  23165. return VOID_STORAGE_MAX_SLOT;
  23166. }
  23167. uint8 Player::GetNumOfVoidStorageFreeSlots() const
  23168. {
  23169. uint8 count = 0;
  23170. for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
  23171. if (!_voidStorageItems[i])
  23172. count++;
  23173. return count;
  23174. }
  23175. uint8 Player::AddVoidStorageItem(const VoidStorageItem& item)
  23176. {
  23177. int8 slot = GetNextVoidStorageFreeSlot();
  23178. if (slot >= VOID_STORAGE_MAX_SLOT)
  23179. {
  23180. GetSession()->SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_FULL);
  23181. return 255;
  23182. }
  23183. _voidStorageItems[slot] = new VoidStorageItem(item.ItemId, item.ItemEntry,
  23184. item.CreatorGuid, item.ItemRandomPropertyId, item.ItemSuffixFactor);
  23185. return slot;
  23186. }
  23187. void Player::AddVoidStorageItemAtSlot(uint8 slot, const VoidStorageItem& item)
  23188. {
  23189. if (slot >= VOID_STORAGE_MAX_SLOT)
  23190. {
  23191. GetSession()->SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_FULL);
  23192. return;
  23193. }
  23194. if (_voidStorageItems[slot])
  23195. {
  23196. TC_LOG_ERROR("misc", "Player::AddVoidStorageItemAtSlot - Player (GUID: %u, name: %s) tried to add an item to an used slot (item id: " UI64FMTD ", entry: %u, slot: %u).", GetGUIDLow(), GetName().c_str(), _voidStorageItems[slot]->ItemId, _voidStorageItems[slot]->ItemEntry, slot);
  23197. GetSession()->SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_INTERNAL_ERROR_1);
  23198. return;
  23199. }
  23200. _voidStorageItems[slot] = new VoidStorageItem(item.ItemId, item.ItemId,
  23201. item.CreatorGuid, item.ItemRandomPropertyId, item.ItemSuffixFactor);
  23202. }
  23203. void Player::DeleteVoidStorageItem(uint8 slot)
  23204. {
  23205. if (slot >= VOID_STORAGE_MAX_SLOT)
  23206. {
  23207. GetSession()->SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_INTERNAL_ERROR_1);
  23208. return;
  23209. }
  23210. delete _voidStorageItems[slot];
  23211. _voidStorageItems[slot] = NULL;
  23212. }
  23213. bool Player::SwapVoidStorageItem(uint8 oldSlot, uint8 newSlot)
  23214. {
  23215. if (oldSlot >= VOID_STORAGE_MAX_SLOT || newSlot >= VOID_STORAGE_MAX_SLOT || oldSlot == newSlot)
  23216. return false;
  23217. std::swap(_voidStorageItems[newSlot], _voidStorageItems[oldSlot]);
  23218. return true;
  23219. }
  23220. VoidStorageItem* Player::GetVoidStorageItem(uint8 slot) const
  23221. {
  23222. if (slot >= VOID_STORAGE_MAX_SLOT)
  23223. {
  23224. GetSession()->SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_INTERNAL_ERROR_1);
  23225. return NULL;
  23226. }
  23227. return _voidStorageItems[slot];
  23228. }
  23229. VoidStorageItem* Player::GetVoidStorageItem(uint64 id, uint8& slot) const
  23230. {
  23231. for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
  23232. {
  23233. if (_voidStorageItems[i] && _voidStorageItems[i]->ItemId == id)
  23234. {
  23235. slot = i;
  23236. return _voidStorageItems[i];
  23237. }
  23238. }
  23239. return NULL;
  23240. }
  23241. void Player::SendMovementSetCanTransitionBetweenSwimAndFly(bool apply)
  23242. {
  23243. Movement::PacketSender(this, NULL_OPCODE, apply ?
  23244. SMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY :
  23245. SMSG_MOVE_UNSET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY).Send();
  23246. }
  23247. void Player::SendMovementSetCollisionHeight(float height)
  23248. {
  23249. static MovementStatusElements const heightElement = MSEExtraFloat;
  23250. Movement::ExtraMovementStatusElement extra(&heightElement);
  23251. extra.Data.floatData = height;
  23252. Movement::PacketSender(this, NULL_OPCODE, SMSG_MOVE_SET_COLLISION_HEIGHT, SMSG_MOVE_UPDATE_COLLISION_HEIGHT, &extra).Send();
  23253. }
  23254. float Player::GetCollisionHeight(bool mounted) const
  23255. {
  23256. if (mounted)
  23257. {
  23258. CreatureDisplayInfoEntry const* mountDisplayInfo = sCreatureDisplayInfoStore.LookupEntry(GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID));
  23259. if (!mountDisplayInfo)
  23260. return GetCollisionHeight(false);
  23261. CreatureModelDataEntry const* mountModelData = sCreatureModelDataStore.LookupEntry(mountDisplayInfo->ModelId);
  23262. if (!mountModelData)
  23263. return GetCollisionHeight(false);
  23264. CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(GetNativeDisplayId());
  23265. ASSERT(displayInfo);
  23266. CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(displayInfo->ModelId);
  23267. ASSERT(modelData);
  23268. float scaleMod = GetObjectScale(); // 99% sure about this
  23269. return scaleMod * mountModelData->MountHeight + modelData->CollisionHeight * 0.5f;
  23270. }
  23271. else
  23272. {
  23273. //! Dismounting case - use basic default model data
  23274. CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(GetNativeDisplayId());
  23275. ASSERT(displayInfo);
  23276. CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(displayInfo->ModelId);
  23277. ASSERT(modelData);
  23278. return modelData->CollisionHeight;
  23279. }
  23280. }
  23281. std::string Player::GetMapAreaAndZoneString()
  23282. {
  23283. uint32 areaId = GetAreaId();
  23284. std::string areaName = "Unknown";
  23285. std::string zoneName = "Unknown";
  23286. if (AreaTableEntry const* area = GetAreaEntryByAreaID(areaId))
  23287. {
  23288. int locale = GetSession()->GetSessionDbcLocale();
  23289. areaName = area->area_name[locale];
  23290. if (AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone))
  23291. zoneName = zone->area_name[locale];
  23292. }
  23293. std::ostringstream str;
  23294. str << "Map: " << GetMapId() << " (" << (FindMap() ? FindMap()->GetMapName() : "Unknown") << ") Area: " << areaId << " (" << areaName.c_str() << ") Zone: " << zoneName.c_str();
  23295. return str.str();
  23296. }
  23297. std::string Player::GetCoordsMapAreaAndZoneString()
  23298. {
  23299. std::ostringstream str;
  23300. str << Position::ToString() << " " << GetMapAreaAndZoneString();
  23301. return str.str();
  23302. }
  23303. Guild* Player::GetGuild()
  23304. {
  23305. uint32 guildId = GetGuildId();
  23306. return guildId ? sGuildMgr->GetGuildById(guildId) : NULL;
  23307. }
  23308. Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 duration)
  23309. {
  23310. Pet* pet = new Pet(this, petType);
  23311. if (petType == SUMMON_PET && pet->LoadPetFromDB(this, entry))
  23312. {
  23313. // Remove Demonic Sacrifice auras (known pet)
  23314. Unit::AuraEffectList const& auraClassScripts = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
  23315. for (Unit::AuraEffectList::const_iterator itr = auraClassScripts.begin(); itr != auraClassScripts.end();)
  23316. {
  23317. if ((*itr)->GetMiscValue() == 2228)
  23318. {
  23319. RemoveAurasDueToSpell((*itr)->GetId());
  23320. itr = auraClassScripts.begin();
  23321. }
  23322. else
  23323. ++itr;
  23324. }
  23325. if (duration > 0)
  23326. pet->SetDuration(duration);
  23327. return NULL;
  23328. }
  23329. // petentry == 0 for hunter "call pet" (current pet summoned if any)
  23330. if (!entry)
  23331. {
  23332. delete pet;
  23333. return NULL;
  23334. }
  23335. pet->Relocate(x, y, z, ang);
  23336. if (!pet->IsPositionValid())
  23337. {
  23338. TC_LOG_ERROR("misc", "Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)", pet->GetGUIDLow(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY());
  23339. delete pet;
  23340. return NULL;
  23341. }
  23342. Map* map = GetMap();
  23343. uint32 pet_number = sObjectMgr->GeneratePetNumber();
  23344. if (!pet->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_PET), map, GetPhaseMask(), entry, pet_number))
  23345. {
  23346. TC_LOG_ERROR("misc", "no such creature entry %u", entry);
  23347. delete pet;
  23348. return NULL;
  23349. }
  23350. for (auto itr : GetPhases())
  23351. pet->SetInPhase(itr, false, true);
  23352. pet->SetCreatorGUID(GetGUID());
  23353. pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, getFaction());
  23354. pet->setPowerType(POWER_MANA);
  23355. pet->SetUInt32Value(UNIT_NPC_FLAGS, 0);
  23356. pet->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
  23357. pet->InitStatsForLevel(getLevel());
  23358. SetMinion(pet, true);
  23359. switch (petType)
  23360. {
  23361. case SUMMON_PET:
  23362. // this enables pet details window (Shift+P)
  23363. pet->GetCharmInfo()->SetPetNumber(pet_number, true);
  23364. pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 2048);
  23365. pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
  23366. pet->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
  23367. pet->SetFullHealth();
  23368. pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA));
  23369. pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL))); // cast can't be helped in this case
  23370. break;
  23371. default:
  23372. break;
  23373. }
  23374. map->AddToMap(pet->ToCreature());
  23375. switch (petType)
  23376. {
  23377. case SUMMON_PET:
  23378. pet->InitPetCreateSpells();
  23379. pet->InitTalentForLevel();
  23380. pet->SavePetToDB(PET_SAVE_AS_CURRENT);
  23381. PetSpellInitialize();
  23382. break;
  23383. default:
  23384. break;
  23385. }
  23386. if (petType == SUMMON_PET)
  23387. {
  23388. // Remove Demonic Sacrifice auras (known pet)
  23389. Unit::AuraEffectList const& auraClassScripts = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
  23390. for (Unit::AuraEffectList::const_iterator itr = auraClassScripts.begin(); itr != auraClassScripts.end();)
  23391. {
  23392. if ((*itr)->GetMiscValue() == 2228)
  23393. {
  23394. RemoveAurasDueToSpell((*itr)->GetId());
  23395. itr = auraClassScripts.begin();
  23396. }
  23397. else
  23398. ++itr;
  23399. }
  23400. }
  23401. if (duration > 0)
  23402. pet->SetDuration(duration);
  23403. //ObjectAccessor::UpdateObjectVisibility(pet);
  23404. return pet;
  23405. }
  23406. bool Player::CanUseMastery() const
  23407. {
  23408. return HasSpell(MasterySpells[getClass()]);
  23409. }
  23410. void Player::ReadMovementInfo(WorldPacket& data, MovementInfo* mi, Movement::ExtraMovementStatusElement* extras /*= NULL*/)
  23411. {
  23412. MovementStatusElements const* sequence = GetMovementStatusElementsSequence(data.GetOpcode());
  23413. if (!sequence)
  23414. {
  23415. TC_LOG_ERROR("network", "Player::ReadMovementInfo: No movement sequence found for opcode %s", GetOpcodeNameForLogging(data.GetOpcode()).c_str());
  23416. return;
  23417. }
  23418. bool hasMovementFlags = false;
  23419. bool hasMovementFlags2 = false;
  23420. bool hasTimestamp = false;
  23421. bool hasOrientation = false;
  23422. bool hasTransportData = false;
  23423. bool hasTransportTime2 = false;
  23424. bool hasTransportTime3 = false;
  23425. bool hasPitch = false;
  23426. bool hasFallData = false;
  23427. bool hasFallDirection = false;
  23428. bool hasSplineElevation = false;
  23429. ObjectGuid guid;
  23430. ObjectGuid tguid;
  23431. for (; *sequence != MSEEnd; ++sequence)
  23432. {
  23433. MovementStatusElements const& element = *sequence;
  23434. switch (element)
  23435. {
  23436. case MSEHasGuidByte0:
  23437. case MSEHasGuidByte1:
  23438. case MSEHasGuidByte2:
  23439. case MSEHasGuidByte3:
  23440. case MSEHasGuidByte4:
  23441. case MSEHasGuidByte5:
  23442. case MSEHasGuidByte6:
  23443. case MSEHasGuidByte7:
  23444. guid[element - MSEHasGuidByte0] = data.ReadBit();
  23445. break;
  23446. case MSEHasTransportGuidByte0:
  23447. case MSEHasTransportGuidByte1:
  23448. case MSEHasTransportGuidByte2:
  23449. case MSEHasTransportGuidByte3:
  23450. case MSEHasTransportGuidByte4:
  23451. case MSEHasTransportGuidByte5:
  23452. case MSEHasTransportGuidByte6:
  23453. case MSEHasTransportGuidByte7:
  23454. if (hasTransportData)
  23455. tguid[element - MSEHasTransportGuidByte0] = data.ReadBit();
  23456. break;
  23457. case MSEGuidByte0:
  23458. case MSEGuidByte1:
  23459. case MSEGuidByte2:
  23460. case MSEGuidByte3:
  23461. case MSEGuidByte4:
  23462. case MSEGuidByte5:
  23463. case MSEGuidByte6:
  23464. case MSEGuidByte7:
  23465. data.ReadByteSeq(guid[element - MSEGuidByte0]);
  23466. break;
  23467. case MSETransportGuidByte0:
  23468. case MSETransportGuidByte1:
  23469. case MSETransportGuidByte2:
  23470. case MSETransportGuidByte3:
  23471. case MSETransportGuidByte4:
  23472. case MSETransportGuidByte5:
  23473. case MSETransportGuidByte6:
  23474. case MSETransportGuidByte7:
  23475. if (hasTransportData)
  23476. data.ReadByteSeq(tguid[element - MSETransportGuidByte0]);
  23477. break;
  23478. case MSEHasMovementFlags:
  23479. hasMovementFlags = !data.ReadBit();
  23480. break;
  23481. case MSEHasMovementFlags2:
  23482. hasMovementFlags2 = !data.ReadBit();
  23483. break;
  23484. case MSEHasTimestamp:
  23485. hasTimestamp = !data.ReadBit();
  23486. break;
  23487. case MSEHasOrientation:
  23488. hasOrientation = !data.ReadBit();
  23489. break;
  23490. case MSEHasTransportData:
  23491. hasTransportData = data.ReadBit();
  23492. break;
  23493. case MSEHasTransportTime2:
  23494. if (hasTransportData)
  23495. hasTransportTime2 = data.ReadBit();
  23496. break;
  23497. case MSEHasTransportTime3:
  23498. if (hasTransportData)
  23499. hasTransportTime3 = data.ReadBit();
  23500. break;
  23501. case MSEHasPitch:
  23502. hasPitch = !data.ReadBit();
  23503. break;
  23504. case MSEHasFallData:
  23505. hasFallData = data.ReadBit();
  23506. break;
  23507. case MSEHasFallDirection:
  23508. if (hasFallData)
  23509. hasFallDirection = data.ReadBit();
  23510. break;
  23511. case MSEHasSplineElevation:
  23512. hasSplineElevation = !data.ReadBit();
  23513. break;
  23514. case MSEHasSpline:
  23515. data.ReadBit();
  23516. break;
  23517. case MSEMovementFlags:
  23518. if (hasMovementFlags)
  23519. mi->flags = data.ReadBits(30);
  23520. break;
  23521. case MSEMovementFlags2:
  23522. if (hasMovementFlags2)
  23523. mi->flags2 = data.ReadBits(12);
  23524. break;
  23525. case MSETimestamp:
  23526. if (hasTimestamp)
  23527. data >> mi->time;
  23528. break;
  23529. case MSEPositionX:
  23530. data >> mi->pos.m_positionX;
  23531. break;
  23532. case MSEPositionY:
  23533. data >> mi->pos.m_positionY;
  23534. break;
  23535. case MSEPositionZ:
  23536. data >> mi->pos.m_positionZ;
  23537. break;
  23538. case MSEOrientation:
  23539. if (hasOrientation)
  23540. mi->pos.SetOrientation(data.read<float>());
  23541. break;
  23542. case MSETransportPositionX:
  23543. if (hasTransportData)
  23544. data >> mi->transport.pos.m_positionX;
  23545. break;
  23546. case MSETransportPositionY:
  23547. if (hasTransportData)
  23548. data >> mi->transport.pos.m_positionY;
  23549. break;
  23550. case MSETransportPositionZ:
  23551. if (hasTransportData)
  23552. data >> mi->transport.pos.m_positionZ;
  23553. break;
  23554. case MSETransportOrientation:
  23555. if (hasTransportData)
  23556. mi->transport.pos.SetOrientation(data.read<float>());
  23557. break;
  23558. case MSETransportSeat:
  23559. if (hasTransportData)
  23560. data >> mi->transport.seat;
  23561. break;
  23562. case MSETransportTime:
  23563. if (hasTransportData)
  23564. data >> mi->transport.time;
  23565. break;
  23566. case MSETransportTime2:
  23567. if (hasTransportData && hasTransportTime2)
  23568. data >> mi->transport.time2;
  23569. break;
  23570. case MSETransportTime3:
  23571. if (hasTransportData && hasTransportTime3)
  23572. data >> mi->transport.time3;
  23573. break;
  23574. case MSEPitch:
  23575. if (hasPitch)
  23576. mi->pitch = G3D::wrap(data.read<float>(), float(-M_PI), float(M_PI));
  23577. break;
  23578. case MSEFallTime:
  23579. if (hasFallData)
  23580. data >> mi->jump.fallTime;
  23581. break;
  23582. case MSEFallVerticalSpeed:
  23583. if (hasFallData)
  23584. data >> mi->jump.zspeed;
  23585. break;
  23586. case MSEFallCosAngle:
  23587. if (hasFallData && hasFallDirection)
  23588. data >> mi->jump.cosAngle;
  23589. break;
  23590. case MSEFallSinAngle:
  23591. if (hasFallData && hasFallDirection)
  23592. data >> mi->jump.sinAngle;
  23593. break;
  23594. case MSEFallHorizontalSpeed:
  23595. if (hasFallData && hasFallDirection)
  23596. data >> mi->jump.xyspeed;
  23597. break;
  23598. case MSESplineElevation:
  23599. if (hasSplineElevation)
  23600. data >> mi->splineElevation;
  23601. break;
  23602. case MSECounter:
  23603. data.read_skip<uint32>(); /// @TODO: Maybe compare it with m_movementCounter to verify that packets are sent & received in order?
  23604. break;
  23605. case MSEZeroBit:
  23606. case MSEOneBit:
  23607. data.ReadBit();
  23608. break;
  23609. case MSEExtraElement:
  23610. extras->ReadNextElement(data);
  23611. break;
  23612. default:
  23613. ASSERT(Movement::PrintInvalidSequenceElement(element, __FUNCTION__));
  23614. break;
  23615. }
  23616. }
  23617. mi->guid = guid;
  23618. mi->transport.guid = tguid;
  23619. //! Anti-cheat checks. Please keep them in seperate if () blocks to maintain a clear overview.
  23620. //! Might be subject to latency, so just remove improper flags.
  23621. #ifdef TRINITY_DEBUG
  23622. #define REMOVE_VIOLATING_FLAGS(check, maskToRemove) \
  23623. { \
  23624. if (check) \
  23625. { \
  23626. TC_LOG_DEBUG("entities.unit", "Player::ReadMovementInfo: Violation of MovementFlags found (%s). " \
  23627. "MovementFlags: %u, MovementFlags2: %u for player GUID: %u. Mask %u will be removed.", \
  23628. STRINGIZE(check), mi->GetMovementFlags(), mi->GetExtraMovementFlags(), GetGUIDLow(), maskToRemove); \
  23629. mi->RemoveMovementFlag((maskToRemove)); \
  23630. } \
  23631. }
  23632. #else
  23633. #define REMOVE_VIOLATING_FLAGS(check, maskToRemove) \
  23634. if (check) \
  23635. mi->RemoveMovementFlag((maskToRemove));
  23636. #endif
  23637. /*! This must be a packet spoofing attempt. MOVEMENTFLAG_ROOT sent from the client is not valid
  23638. in conjunction with any of the moving movement flags such as MOVEMENTFLAG_FORWARD.
  23639. It will freeze clients that receive this player's movement info.
  23640. */
  23641. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT),
  23642. MOVEMENTFLAG_ROOT);
  23643. //! Cannot hover without SPELL_AURA_HOVER
  23644. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_HOVER) && !HasAuraType(SPELL_AURA_HOVER),
  23645. MOVEMENTFLAG_HOVER);
  23646. //! Cannot ascend and descend at the same time
  23647. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ASCENDING) && mi->HasMovementFlag(MOVEMENTFLAG_DESCENDING),
  23648. MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING);
  23649. //! Cannot move left and right at the same time
  23650. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_LEFT) && mi->HasMovementFlag(MOVEMENTFLAG_RIGHT),
  23651. MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT);
  23652. //! Cannot strafe left and right at the same time
  23653. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_STRAFE_LEFT) && mi->HasMovementFlag(MOVEMENTFLAG_STRAFE_RIGHT),
  23654. MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT);
  23655. //! Cannot pitch up and down at the same time
  23656. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_PITCH_UP) && mi->HasMovementFlag(MOVEMENTFLAG_PITCH_DOWN),
  23657. MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN);
  23658. //! Cannot move forwards and backwards at the same time
  23659. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FORWARD) && mi->HasMovementFlag(MOVEMENTFLAG_BACKWARD),
  23660. MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD);
  23661. //! Cannot walk on water without SPELL_AURA_WATER_WALK
  23662. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_WATERWALKING) && !HasAuraType(SPELL_AURA_WATER_WALK),
  23663. MOVEMENTFLAG_WATERWALKING);
  23664. //! Cannot feather fall without SPELL_AURA_FEATHER_FALL
  23665. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FALLING_SLOW) && !HasAuraType(SPELL_AURA_FEATHER_FALL),
  23666. MOVEMENTFLAG_FALLING_SLOW);
  23667. /*! Cannot fly if no fly auras present. Exception is being a GM.
  23668. Note that we check for account level instead of Player::IsGameMaster() because in some
  23669. situations it may be feasable to use .gm fly on as a GM without having .gm on,
  23670. e.g. aerial combat.
  23671. */
  23672. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY) && ToPlayer()->GetSession()->GetSecurity() == SEC_PLAYER &&
  23673. !ToPlayer()->m_mover->HasAuraType(SPELL_AURA_FLY) &&
  23674. !ToPlayer()->m_mover->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED),
  23675. MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY);
  23676. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_CAN_FLY) && mi->HasMovementFlag(MOVEMENTFLAG_FALLING),
  23677. MOVEMENTFLAG_FALLING);
  23678. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FALLING) && (!hasFallData || !hasFallDirection), MOVEMENTFLAG_FALLING);
  23679. REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_SPLINE_ELEVATION) &&
  23680. (!hasSplineElevation || G3D::fuzzyEq(mi->splineElevation, 0.0f)), MOVEMENTFLAG_SPLINE_ELEVATION);
  23681. // Client first checks if spline elevation != 0, then verifies flag presence
  23682. if (hasSplineElevation)
  23683. mi->AddMovementFlag(MOVEMENTFLAG_SPLINE_ELEVATION);
  23684. #undef REMOVE_VIOLATING_FLAGS
  23685. }
  23686. void Player::UpdatePhasing()
  23687. {
  23688. if (!IsInWorld())
  23689. return;
  23690. std::set<uint32> phaseIds;
  23691. std::set<uint32> terrainswaps;
  23692. std::set<uint32> worldAreaSwaps;
  23693. for (auto phase : GetPhases())
  23694. {
  23695. PhaseInfo const* info = sObjectMgr->GetPhaseInfo(phase);
  23696. if (!info)
  23697. continue;
  23698. terrainswaps.insert(info->terrainSwapMap);
  23699. worldAreaSwaps.insert(info->worldMapAreaSwap);
  23700. }
  23701. GetSession()->SendSetPhaseShift(GetPhases(), terrainswaps, worldAreaSwaps);
  23702. }
  23703. void Player::SendSupercededSpell(uint32 oldSpell, uint32 newSpell)
  23704. {
  23705. WorldPacket data(SMSG_SUPERCEDED_SPELL, 8);
  23706. data << uint32(newSpell) << uint32(oldSpell);
  23707. GetSession()->SendPacket(&data);
  23708. }