PageRenderTime 62ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 3ms

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

https://gitlab.com/IlluminatiCore/IlluminatiCore
C++ | 13435 lines | 10334 code | 2050 blank | 1051 comment | 3369 complexity | 52a59747d8ad70a97d9fdd1dc2962043 MD5 | raw file
Possible License(s): GPL-2.0, BSD-2-Clause
  1. /*
  2. * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
  3. * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the
  7. * Free Software Foundation; either version 2 of the License, or (at your
  8. * option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but WITHOUT
  11. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13. * more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "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 && mai