OregonCore  revision 71a6c59-git
Your Favourite TBC server
Player.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "Common.h"
19 #include "Language.h"
20 #include "Database/DatabaseEnv.h"
21 #include "Log.h"
22 #include "Opcodes.h"
23 #include "ObjectMgr.h"
24 #include "SpellMgr.h"
25 #include "World.h"
26 #include "WorldPacket.h"
27 #include "WorldSession.h"
28 #include "UpdateMask.h"
29 #include "Player.h"
30 #include "SkillDiscovery.h"
31 #include "QuestDef.h"
32 #include "GossipDef.h"
33 #include "UpdateData.h"
34 #include "Channel.h"
35 #include "ChannelMgr.h"
36 #include "MapManager.h"
37 #include "InstanceSaveMgr.h"
38 #include "GridNotifiers.h"
39 #include "GridNotifiersImpl.h"
40 #include "CellImpl.h"
41 #include "ObjectAccessor.h"
42 #include "CreatureAI.h"
43 #include "Formulas.h"
44 #include "Group.h"
45 #include "Guild.h"
46 #include "Pet.h"
47 #include "SpellAuras.h"
48 #include "Utilities/Util.h"
49 #include "Transports.h"
50 #include "Weather.h"
51 #include "Battleground.h"
52 #include "BattlegroundAV.h"
53 #include "BattlegroundMgr.h"
54 #include "OutdoorPvP.h"
55 #include "OutdoorPvPMgr.h"
56 #include "ArenaTeam.h"
57 #include "Chat.h"
58 #include "Database/DatabaseImpl.h"
59 #include "Spell.h"
60 #include "SocialMgr.h"
61 #include "Mail.h"
62 #include "GameEventMgr.h"
63 #include "GameObjectAI.h"
64 #include "DisableMgr.h"
65 #include "ConditionMgr.h"
66 #include "ScriptMgr.h"
67 #include "PoolMgr.h"
68 
69 #include <cmath>
70 
71 #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)
72 
73 #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
74 #define PLAYER_SKILL_VALUE_INDEX(x) (PLAYER_SKILL_INDEX(x)+1)
75 #define PLAYER_SKILL_BONUS_INDEX(x) (PLAYER_SKILL_INDEX(x)+2)
76 
77 #define SKILL_VALUE(x) PAIR32_LOPART(x)
78 #define SKILL_MAX(x) PAIR32_HIPART(x)
79 #define MAKE_SKILL_VALUE(v, m) MAKE_PAIR32(v, m)
80 
81 #define SKILL_TEMP_BONUS(x) int16(PAIR32_LOPART(x))
82 #define SKILL_PERM_BONUS(x) int16(PAIR32_HIPART(x))
83 #define MAKE_SKILL_BONUS(t, p) MAKE_PAIR32(t, p)
84 
86 {
87  CHARACTER_FLAG_NONE = 0x00000000,
88  CHARACTER_FLAG_UNK1 = 0x00000001,
89  CHARACTER_FLAG_UNK2 = 0x00000002,
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,
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,
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 
122 // corpse reclaim times
123 #define DEATH_EXPIRE_STEP (5*MINUTE)
124 #define MAX_DEATH_COUNT 3
125 
126 static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
127 
128 //== PlayerTaxi ================================================
129 
131 {
132  // Taxi nodes
133  memset(m_taximask, 0, sizeof(m_taximask));
134 }
135 
137 {
138  // capital and taxi hub masks
139  switch (race)
140  {
141  case RACE_HUMAN: SetTaximaskNode(2); break; // Human
142  case RACE_ORC: SetTaximaskNode(23); break; // Orc
143  case RACE_DWARF: SetTaximaskNode(6); break; // Dwarf
144  case RACE_NIGHTELF: SetTaximaskNode(26);
145  SetTaximaskNode(27); break; // Night Elf
146  case RACE_UNDEAD_PLAYER: SetTaximaskNode(11); break;// Undead
147  case RACE_TAUREN: SetTaximaskNode(22); break; // Tauren
148  case RACE_GNOME: SetTaximaskNode(6); break; // Gnome
149  case RACE_TROLL: SetTaximaskNode(23); break; // Troll
150  case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf
151  case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei
152  }
153 
154  // new continent starting masks (It will be accessible only at new map)
155  switch (Player::TeamForRace(race))
156  {
157  case ALLIANCE: SetTaximaskNode(100); break;
158  case HORDE: SetTaximaskNode(99); break;
159  }
160  // level dependent taxi hubs
161  if (level >= 68)
162  SetTaximaskNode(213); //Shattered Sun Staging Area
163 }
164 
165 void PlayerTaxi::LoadTaxiMask(const char* data)
166 {
167  Tokens tokens = StrSplit(data, " ");
168 
169  int index;
170  Tokens::iterator iter;
171  for (iter = tokens.begin(), index = 0;
172  (index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index)
173  {
174  // load and set bits only for existed taxi nodes
175  m_taximask[index] = sTaxiNodesMask[index] & uint32(atol((*iter).c_str()));
176  }
177 }
178 
180 {
181  if (all)
182  {
183  for (uint8 i = 0; i < TaxiMaskSize; i++)
184  data << uint32(sTaxiNodesMask[i]); // all existed nodes
185  }
186  else
187  {
188  for (uint8 i = 0; i < TaxiMaskSize; i++)
189  data << uint32(m_taximask[i]); // known nodes
190  }
191 }
192 
193 bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values)
194 {
196 
197  Tokens tokens = StrSplit(values, " ");
198 
199  for (Tokens::iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
200  {
201  uint32 node = uint32(atol(iter->c_str()));
202  AddTaxiDestination(node);
203  }
204 
205  if (m_TaxiDestinations.empty())
206  return true;
207 
208  // Check integrity
209  if (m_TaxiDestinations.size() < 2)
210  return false;
211 
212  for (size_t i = 1; i < m_TaxiDestinations.size(); ++i)
213  {
214  uint32 cost;
215  uint32 path;
216  sObjectMgr.GetTaxiPath(m_TaxiDestinations[i - 1], m_TaxiDestinations[i], path, cost);
217  if (!path)
218  return false;
219  }
220 
221  return true;
222 }
223 
225 {
226  if (m_TaxiDestinations.empty())
227  return "";
228 
229  std::ostringstream ss;
230 
231  for (size_t i = 0; i < m_TaxiDestinations.size(); ++i)
232  ss << m_TaxiDestinations[i] << " ";
233 
234  return ss.str();
235 }
236 
238 {
239  if (m_TaxiDestinations.size() < 2)
240  return 0;
241 
242  uint32 path;
243  uint32 cost;
244 
245  sObjectMgr.GetTaxiPath(m_TaxiDestinations[0], m_TaxiDestinations[1], path, cost);
246 
247  return path;
248 }
249 
250 //== Player ====================================================
251 
253 
254 Player::Player(WorldSession* session) : Unit(true), m_reputationMgr(this)
255 {
256  m_transport = 0;
257 
258  m_speakTime = 0;
259  m_speakCount = 0;
260 
263 
265 
266  m_session = session;
267 
268  m_ExtraFlags = 0;
269 
270  // players always accept
271  if (GetSession()->GetSecurity() == SEC_PLAYER)
272  SetAcceptWhispers(true);
273 
274  m_curSelection = 0;
275  m_lootGuid = 0;
276 
277  m_comboTarget = 0;
278  m_comboPoints = 0;
279 
280  m_usedTalentCount = 0;
281 
282  m_regenTimer = 0;
284 
285  m_zoneUpdateId = 0;
286  m_zoneUpdateTimer = 0;
287 
288  m_areaUpdateId = 0;
289  m_team = 0;
290 
292 
294 
296 
297  memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT);
298 
299  m_social = NULL;
300 
301  // group is initialized in the reference constructor
302  SetGroupInvite(NULL);
303  m_groupUpdateMask = 0;
304  m_auraUpdateMask = 0;
305  _passOnGroupLoot = false;
306 
307  duel = NULL;
308 
309  m_GuildIdInvited = 0;
311 
313 
314  mSemaphoreTeleport_Near = false;
315  mSemaphoreTeleport_Far = false;
316 
318  m_bCanDelayTeleport = false;
319  m_bHasDelayedTeleport = false;
320  m_bHasBeenAliveAtDelayedTeleport = true; // overwrite always at setup teleport data, so not used infact
321  m_teleport_options = 0;
322 
323  pTrader = 0;
324  ClearTrade();
325 
326  m_cinematic = 0;
327 
330 
331  for (int aX = 0 ; aX < 8 ; aX++)
332  m_Tutorials[ aX ] = 0x00;
333  m_TutorialsChanged = false;
334 
335  m_DailyQuestChanged = false;
337 
338  for (uint8 i = 0; i < MAX_TIMERS; i++)
340 
341  _lastLiquid = NULL;
344 
345  m_GrantableLevels = 0;
346 
347  m_isInWater = false;
348  m_wasOutdoors = false;
349  m_drunkTimer = 0;
350  m_drunk = 0;
351  m_deathTimer = 0;
352  m_deathExpireTime = 0;
353 
354  m_swingErrorMsg = 0;
355 
356  for (uint8 j = 0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j)
357  {
360  }
361 
362  m_logintime = time(NULL);
365  m_ArmorProficiency = 0;
366  m_canParry = false;
367  m_canBlock = false;
368  m_canDualWield = false;
369  m_ammoDPS = 0.0f;
370 
372  //cache for UNIT_CREATED_BY_SPELL to allow
373  //returning reagents for temporarily removed pets
374  //when dying/logging out
375  m_oldpetspell = 0;
376  m_lastpetnumber = 0;
377 
379 
380  // Rest system variables
381  _restTime = 0;
382  inn_triggerId = 0;
383  m_rest_bonus = 0;
384  _restFlagMask = 0;
385 
386  // Mail system variables
387  m_mailsLoaded = false;
388  m_mailsUpdated = false;
389  unReadMails = 0;
391 
392  m_resetTalentsCost = 0;
393  m_resetTalentsTime = 0;
394  m_itemUpdateQueueBlocked = false;
395 
396  for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
397  m_forced_speed_changes[i] = 0;
398 
399  m_stableSlots = 0;
400 
402 
403  m_HomebindTimer = 0;
404  m_InstanceValid = true;
406 
407  for (int i = 0; i < BASEMOD_END; ++i)
408  {
409  m_auraBaseMod[i][FLAT_MOD] = 0.0f;
410  m_auraBaseMod[i][PCT_MOD] = 1.0f;
411  }
412 
413  // Honor System
414  m_lastHonorUpdateTime = time(NULL);
415 
416  // Player summoning
417  m_summon_expire = 0;
418  m_summon_mapid = 0;
419  m_summon_x = 0.0f;
420  m_summon_y = 0.0f;
421  m_summon_z = 0.0f;
422 
423  m_mover = this;
424 
425  m_seer = this;
426 
428 
429  m_declinedname = NULL;
430 
431  m_isActive = true;
432 
434 
435  _cinematicMgr = new CinematicMgr(this);
436 
437  m_ControlledByPlayer = true;
438 
439  m_globalCooldowns.clear();
440 }
441 
443 {
445 
446  // it must be unloaded already in PlayerLogout and accessed only for loggined player
447  //m_social = NULL;
448 
449  // Note: buy back item already deleted from DB when player was saved
450  for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; ++i)
451  delete m_items[i];
452 
453  CleanupChannels();
454 
455  //all mailed items should be deleted, also all mail should be deallocated
456  for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
457  delete *itr;
458 
459  for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
460  delete iter->second; //if item is duplicated... then server may crash ... but that item should be deallocated
461 
462  delete PlayerTalkClass;
463 
464  if (m_transport)
465  {
467  m_transport = NULL;
468  }
469 
470  for (size_t x = 0; x < ItemSetEff.size(); x++)
471  delete ItemSetEff[x];
472 
473  delete m_declinedname;
474  delete _cinematicMgr;
475 
476  // clean up player-instance binds, may unload some instance saves
477  for (uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
478  for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
479  itr->second.save->RemovePlayer(this);
480 }
481 
483 {
484  if (m_uint32Values) // only for fully created Object
485  {
486  TradeCancel(false);
488 
489  // Send update to group
490  if (Group* group = GetGroup())
491  group->SendUpdate();
492  }
493 
495 }
496 
497 bool Player::Create(uint32 guidlow, const std::string& name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 /*outfitId*/)
498 {
499  //FIXME: outfitId not used in player creating
501  // should check that skin, face, hair* are valid via DBC per race/class
502  // also do it in Player::BuildEnumData, Player::LoadFromDB
503 
504  Object::_Create(guidlow, 0, HIGHGUID_PLAYER);
505 
506  m_name = name;
507 
508  PlayerInfo const* info = sObjectMgr.GetPlayerInfo(race, class_);
509  if (!info)
510  {
511  sLog.outError("Player has incorrect race/class pair. Not loaded.");
512  return false;
513  }
514 
515  for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; i++)
516  m_items[i] = NULL;
517 
518  Relocate(info->positionX, info->positionY, info->positionZ, info->orientation);
519 
520  ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(class_);
521  if (!cEntry)
522  {
523  sLog.outError("Class %u not found in DBC (Wrong DBC files?)", class_);
524  return false;
525  }
526 
527  SetMap(MapManager::Instance().CreateMap(info->mapId, this, 0));
528 
529  uint8 powertype = cEntry->powerType;
530 
531  uint32 unitfield;
532 
533  switch (powertype)
534  {
535  case POWER_ENERGY:
536  case POWER_MANA:
537  unitfield = 0x00000000;
538  break;
539  case POWER_RAGE:
540  unitfield = 0x00110000;
541  break;
542  default:
543  sLog.outError("Invalid default powertype %u for player (class %u)", powertype, class_);
544  return false;
545  }
546 
549 
550  switch (gender)
551  {
552  case GENDER_FEMALE:
553  SetDisplayId(info->displayId_f);
555  break;
556  case GENDER_MALE:
557  SetDisplayId(info->displayId_m);
559  break;
560  default:
561  sLog.outError("Invalid gender %u for player", gender);
562  return false;
563  break;
564  }
565 
566  setFactionForRace(race);
567 
568  uint32 RaceClassGender = (race) | (class_ << 8) | (gender << 16);
569 
570  SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24)));
574  SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client
575 
576  // -1 is default value
578 
579  SetUInt32Value(PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24)));
580  SetUInt32Value(PLAYER_BYTES_2, (facialHair | (0x00 << 8) | (0x00 << 16) | (0x02 << 24)));
581  SetByteValue(PLAYER_BYTES_3, 0, gender);
582 
586 
587  for (int i = 0; i < KNOWN_TITLES_SIZE; ++i)
588  SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES + i, 0); // 0=disabled
590 
595 
596  // set starting level
597  uint32 start_level = sWorld.getConfig(CONFIG_START_PLAYER_LEVEL);
598 
599  if (GetSession()->GetSecurity() >= SEC_MODERATOR)
600  {
601  uint32 gm_level = sWorld.getConfig(CONFIG_START_GM_LEVEL);
602  if (gm_level > start_level)
603  start_level = gm_level;
604  }
605 
606  SetUInt32Value(UNIT_FIELD_LEVEL, start_level);
610 
611  // start with every map explored
612  if (sWorld.getConfig(CONFIG_START_ALL_EXPLORED))
613  {
614  for (uint8 i = 0; i < 64; i++)
615  SetFlag(PLAYER_EXPLORED_ZONES_1 + i, 0xFFFFFFFF);
616  }
617 
618  // Played time
619  m_Last_tick = time(NULL);
622 
623  // base stats and related field values
627  InitPrimaryProfessions(); // to max set before any spell added
628 
629  // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
630  UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
632  if (getPowerType() == POWER_MANA)
633  {
634  UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intellect)
636  }
637 
638  // original spells
639  LearnDefaultSpells(true);
640 
641  // original action bar
642  std::list<uint16>::const_iterator action_itr[4];
643  for (int i = 0; i < 4; i++)
644  action_itr[i] = info->action[i].begin();
645 
646  for (; action_itr[0] != info->action[0].end() && action_itr[1] != info->action[1].end();)
647  {
648  uint16 taction[4];
649  for (int i = 0; i < 4 ; i++)
650  taction[i] = (*action_itr[i]);
651 
652  addActionButton((uint8)taction[0], taction[1], (uint8)taction[2], (uint8)taction[3]);
653 
654  for (int i = 0; i < 4 ; i++)
655  ++action_itr[i];
656  }
657 
658  // original items
659  CharStartOutfitEntry const* oEntry = NULL;
660  for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
661  {
662  if (CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i))
663  {
664  if (entry->RaceClassGender == RaceClassGender)
665  {
666  oEntry = entry;
667  break;
668  }
669  }
670  }
671 
672  if (oEntry)
673  {
674  for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
675  {
676  if (oEntry->ItemId[j] <= 0)
677  continue;
678 
679  uint32 item_id = oEntry->ItemId[j];
680 
681  ItemTemplate const* iProto = sObjectMgr.GetItemTemplate(item_id);
682  if (!iProto)
683  {
684  sLog.outErrorDb("Initial item id %u (race %u class %u) from CharStartOutfit.dbc not listed in item_template, ignoring.", item_id, getRace(), getClass());
685  continue;
686  }
687 
688  uint32 count = iProto->Stackable; // max stack by default (mostly 1)
689  if (iProto->Class == ITEM_CLASS_CONSUMABLE && iProto->SubClass == ITEM_SUBCLASS_FOOD)
690  {
691  switch (iProto->Spells[0].SpellCategory)
692  {
693  case SPELL_CATEGORY_FOOD: // food
694  if (iProto->Stackable > 4)
695  count = 4;
696  break;
697  case SPELL_CATEGORY_DRINK: // drink
698  if (iProto->Stackable > 2)
699  count = 2;
700  break;
701  }
702  }
703 
704  StoreNewItemInBestSlots(item_id, count);
705  }
706  }
707 
708  for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr != info->item.end(); ++item_id_itr)
709  StoreNewItemInBestSlots(item_id_itr->item_id, item_id_itr->item_amount);
710 
711  // bags and main-hand weapon must equipped at this moment
712  // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon)
713  // or ammo not equipped in special bag
715  {
716  if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
717  {
718  uint16 eDest;
719  // equip offhand weapon/shield if it attempt equipped before main-hand weapon
720  uint8 msg = CanEquipItem(NULL_SLOT, eDest, pItem, false);
721  if (msg == EQUIP_ERR_OK)
722  {
724  EquipItem(eDest, pItem, true);
725  }
726  // move other items to more appropriate slots (ammo not equipped in special bag)
727  else
728  {
729  ItemPosCountVec sDest;
730  msg = CanStoreItem(NULL_BAG, NULL_SLOT, sDest, pItem, false);
731  if (msg == EQUIP_ERR_OK)
732  {
734  pItem = StoreItem(sDest, pItem, true);
735  }
736 
737  // if this is ammo then use it
738  uint8 msg = CanUseAmmo(pItem->GetProto()->ItemId);
739  if (msg == EQUIP_ERR_OK)
740  SetAmmo(pItem->GetProto()->ItemId);
741  }
742  }
743  }
744  // all item positions resolved
745 
746  return true;
747 }
748 
749 bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount)
750 {
751  DEBUG_LOG("STORAGE: Creating initial item, itemId = %u, count = %u", titem_id, titem_amount);
752 
753  // attempt equip by one
754  while (titem_amount > 0)
755  {
756  uint16 eDest;
757  uint8 msg = CanEquipNewItem(NULL_SLOT, eDest, titem_id, false);
758  if (msg != EQUIP_ERR_OK)
759  break;
760 
761  EquipNewItem(eDest, titem_id, true);
763  --titem_amount;
764  }
765 
766  if (titem_amount == 0)
767  return true; // equipped
768 
769  // attempt store
770  ItemPosCountVec sDest;
771  // store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
772  uint8 msg = CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount);
773  if (msg == EQUIP_ERR_OK)
774  {
775  StoreNewItem(sDest, titem_id, true, Item::GenerateItemRandomPropertyId(titem_id));
776  return true; // stored
777  }
778 
779  // item can't be added
780  sLog.outError("STORAGE: Can't equip or store initial item %u for race %u class %u , error msg = %u", titem_id, getRace(), getClass(), msg);
781  return false;
782 }
783 
784 void Player::SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, int32 Regen)
785 {
786  if (int(MaxValue) == DISABLED_MIRROR_TIMER)
787  {
788  if (int(CurrentValue) != DISABLED_MIRROR_TIMER)
789  StopMirrorTimer(Type);
790  return;
791  }
793  data << (uint32)Type;
794  data << CurrentValue;
795  data << MaxValue;
796  data << Regen;
797  data << (uint8)0;
798  data << (uint32)0; // spell id
799  GetSession()->SendPacket(&data);
800 }
801 
803 {
806  data << (uint32)Type;
807  GetSession()->SendPacket(&data);
808 }
809 
811 {
812  if (!IsAlive() || IsGameMaster())
813  return;
814 
815  // Absorb, resist some environmental damage type
816  uint32 absorb = 0;
817  uint32 resist = 0;
818  if (type == DAMAGE_LAVA)
819  CalcAbsorbResist(this, SPELL_SCHOOL_MASK_FIRE, DIRECT_DAMAGE, damage, &absorb, &resist, nullptr);
820  else if (type == DAMAGE_SLIME)
821  CalcAbsorbResist(this, SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, damage, &absorb, &resist, nullptr);
822 
823  damage -= absorb + resist;
824 
826  data << uint64(GetGUID());
827  data << uint8(type != DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL);
828  data << uint32(damage);
829  data << uint32(absorb);
830  data << uint32(resist);
831  SendMessageToSet(&data, true);
832 
833  DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
834 
835  if (type == DAMAGE_FALL && !IsAlive()) // DealDamage not apply item durability loss at self damage
836  {
837  DEBUG_LOG("We are fall to death, loosing 10 percents durability");
838  DurabilityLossAll(0.10f, false);
839  // durability lost message
841  GetSession()->SendPacket(&data2);
842  }
843 }
844 
846 {
847  switch (timer)
848  {
849  case FATIGUE_TIMER:
850  return MINUTE * IN_MILLISECONDS;
851  case BREATH_TIMER:
852  {
853  if (!IsAlive() || HasAuraType(SPELL_AURA_WATER_BREATHING) || GetSession()->GetSecurity() >= sWorld.getConfig(CONFIG_DISABLE_BREATHING))
854  return DISABLED_MIRROR_TIMER;
855  int32 UnderWaterTime = MINUTE * IN_MILLISECONDS;
856  AuraList const& mModWaterBreathing = GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING);
857  for (AuraList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i)
858  UnderWaterTime = uint32(UnderWaterTime * (100.0f + (*i)->GetModifierValue()) / 100.0f);
859  return UnderWaterTime;
860  }
861  case FIRE_TIMER:
862  {
863  if (!IsAlive())
864  return DISABLED_MIRROR_TIMER;
865  return IN_MILLISECONDS;
866  }
867  default:
868  return 0;
869  }
870 }
871 
873 {
874  // Desync flags for update on next HandleDrowning
875  if (m_MirrorTimerFlags)
877 }
878 
880 {
881  if (!m_MirrorTimerFlags)
882  return;
883 
884  // In water
886  {
887  // Breath timer not activated - activate it
889  {
892  }
893  else // If activated - do tick
894  {
895  m_MirrorTimer[BREATH_TIMER] -= time_diff;
896  // Timer limit - need deal damage
897  if (m_MirrorTimer[BREATH_TIMER] < 0)
898  {
900  // Calculate and deal damage
901  // @todo Check this formula
902  uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel() - 1);
904  }
905  else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INWATER)) // Update time in client if need
907  }
908  }
909  else if (m_MirrorTimer[BREATH_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
910  {
911  int32 UnderWaterTime = getMaxTimer(BREATH_TIMER);
912  // Need breath regen
913  m_MirrorTimer[BREATH_TIMER] += 10 * time_diff;
914  if (m_MirrorTimer[BREATH_TIMER] >= UnderWaterTime || !IsAlive())
916  else if (m_MirrorTimerFlagsLast & UNDERWATER_INWATER)
918  }
919 
920  // In dark water
922  {
923  // Fatigue timer not activated - activate it
925  {
928  }
929  else
930  {
931  m_MirrorTimer[FATIGUE_TIMER] -= time_diff;
932  // Timer limit - need deal damage or teleport ghost to graveyard
933  if (m_MirrorTimer[FATIGUE_TIMER] < 0)
934  {
936  if (IsAlive()) // Calculate and deal damage
937  {
938  uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel() - 1);
940  }
941  else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) // Teleport ghost to graveyard
943  }
944  else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER))
946  }
947  }
948  else if (m_MirrorTimer[FATIGUE_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
949  {
950  int32 DarkWaterTime = getMaxTimer(FATIGUE_TIMER);
951  m_MirrorTimer[FATIGUE_TIMER] += 10 * time_diff;
952  if (m_MirrorTimer[FATIGUE_TIMER] >= DarkWaterTime || !IsAlive())
954  else if (m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER)
956  }
957 
958  if (m_MirrorTimerFlags & (UNDERWATER_INLAVA /*| UNDERWATER_INSLIME*/) && !(_lastLiquid && _lastLiquid->SpellId))
959  {
960  // Breath timer not activated - activate it
963  else
964  {
965  m_MirrorTimer[FIRE_TIMER] -= time_diff;
966  if (m_MirrorTimer[FIRE_TIMER] < 0)
967  {
969  // Calculate and deal damage
971  uint32 damage = urand(600, 700);
974  else
976  }
977  }
978  }
979  else
981 
982  // Recheck timers flag
984  for (uint8 i = 0; i < MAX_TIMERS; ++i)
986  {
988  break;
989  }
991 }
992 
993 // The player sobers by 256 every 10 seconds
995 {
996  m_drunkTimer = 0;
997 
998  uint32 drunk = (m_drunk <= 256) ? 0 : (m_drunk - 256);
999  SetDrunkValue(drunk);
1000 }
1001 
1003 {
1004  if (value >= 23000)
1005  return DRUNKEN_SMASHED;
1006  if (value >= 12800)
1007  return DRUNKEN_DRUNK;
1008  if (value & 0xFFFE)
1009  return DRUNKEN_TIPSY;
1010  return DRUNKEN_SOBER;
1011 }
1012 
1013 void Player::SetDrunkValue(uint16 newDrunkenValue, uint32 itemId)
1014 {
1015  uint32 oldDrunkenState = Player::GetDrunkenstateByValue(m_drunk);
1016 
1017  if (!newDrunkenValue)
1019  else
1021 
1022  m_invisibilityDetect.AddValue(INVISIBILITY_DRUNK, int32(newDrunkenValue - m_drunk) / 256);
1023 
1024  m_drunk = newDrunkenValue;
1025  SetUInt32Value(PLAYER_BYTES_3, (GetUInt32Value(PLAYER_BYTES_3) & 0xFFFF0001) | (m_drunk & 0xFFFE));
1026 
1027  uint32 newDrunkenState = Player::GetDrunkenstateByValue(m_drunk);
1028 
1030 
1031  if (newDrunkenState == oldDrunkenState)
1032  return;
1033 
1035  data << uint64(GetGUID());
1036  data << uint32(newDrunkenState);
1037  data << uint32(itemId);
1038 
1039  SendMessageToSet(&data, true);
1040 }
1041 
1043 {
1044  if (!IsInWorld())
1045  return;
1046 
1047  // undelivered mail
1048  if (m_nextMailDelivereTime && m_nextMailDelivereTime <= time(NULL))
1049  {
1050  SendNewMail();
1051  ++unReadMails;
1052 
1053  // It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated)
1055  }
1056 
1057  for (std::map<uint32, uint32>::iterator itr = m_globalCooldowns.begin(); itr != m_globalCooldowns.end(); ++itr)
1058  {
1059  if (itr->second)
1060  {
1061  if (itr->second > p_time)
1062  itr->second -= p_time;
1063  else
1064  itr->second = 0;
1065  }
1066  }
1067 
1068  // Update cinematic location, if 500ms have passed and we're doing a cinematic now.
1069  _cinematicMgr->m_cinematicDiff += p_time;
1071  {
1074  }
1075 
1076  //used to implement delayed far teleports
1077  SetCanDelayTeleport(true);
1078  Unit::Update(p_time);
1079  SetCanDelayTeleport(false);
1080 
1081  time_t now = time(NULL);
1082 
1083  UpdatePvPFlag(now);
1084 
1085  UpdateContestedPvP(p_time);
1086 
1087  UpdateDuelFlag(now);
1088 
1089  CheckDuelDistance(now);
1090 
1091  UpdateAfkReport(now);
1092 
1093  if (isCharmed())
1094  if (Unit* charmer = GetCharmer())
1095  if (charmer->GetTypeId() == TYPEID_UNIT && charmer->IsAlive())
1096  UpdateCharmedAI();
1097 
1098  // Update items that have just a limited lifetime
1099  if (now > m_Last_tick)
1101 
1102  if (!m_timedquests.empty())
1103  {
1104  std::set<uint32>::iterator iter = m_timedquests.begin();
1105  while (iter != m_timedquests.end())
1106  {
1107  QuestStatusData& q_status = mQuestStatus[*iter];
1108  if (q_status.m_timer <= p_time)
1109  {
1110  uint32 quest_id = *iter;
1111  ++iter; // current iter will be removed in FailQuest
1112  FailQuest(quest_id);
1113  }
1114  else
1115  {
1116  q_status.m_timer -= p_time;
1117  if (q_status.uState != QUEST_NEW)
1118  q_status.uState = QUEST_CHANGED;
1119  ++iter;
1120  }
1121  }
1122  }
1123 
1126 
1128  {
1129  if (roll_chance_i(3) && _restTime > 0) // freeze update
1130  {
1131  time_t currTime = time(NULL);
1132  time_t timeDiff = currTime - _restTime;
1133  if (timeDiff >= 10) // freeze update
1134  {
1135  _restTime = currTime;
1136 
1137  float bubble = 0.125f * sWorld.getRate(RATE_REST_INGAME);
1138  float extraPerSec = ((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 72000.0f) * bubble;
1139 
1140  // speed collect rest bonus (section/in hour)
1141  float currRestBonus = GetRestBonus();
1142  SetRestBonus(currRestBonus + timeDiff * extraPerSec);
1143  }
1144  }
1145  }
1146 
1147  if (m_regenTimer > 0)
1148  {
1149  if (p_time >= m_regenTimer)
1150  m_regenTimer = 0;
1151  else
1152  m_regenTimer -= p_time;
1153  }
1154 
1155  if (m_weaponChangeTimer > 0)
1156  {
1157  if (p_time >= m_weaponChangeTimer)
1158  m_weaponChangeTimer = 0;
1159  else
1160  m_weaponChangeTimer -= p_time;
1161  }
1162 
1163  if (m_zoneUpdateTimer > 0)
1164  {
1165  if (p_time >= m_zoneUpdateTimer)
1166  {
1167  // On zone update tick check if we are still in an inn if we are supposed to be in one
1169  {
1170  AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(GetInnTriggerId());
1171  if (!atEntry || !IsInAreaTriggerRadius(atEntry))
1173  }
1174 
1175  uint32 newzone = GetZoneId();
1176  if (m_zoneUpdateId != newzone)
1177  UpdateZone(newzone); // also update area
1178  else
1179  {
1180  // use area updates as well
1181  // needed for free far all arenas for example
1182  uint32 newarea = GetAreaId();
1183  if (m_areaUpdateId != newarea)
1184  UpdateArea(newarea);
1185 
1187  }
1188  }
1189  else
1190  m_zoneUpdateTimer -= p_time;
1191  }
1192 
1193  if (m_timeSyncTimer > 0)
1194  {
1195  if (p_time >= m_timeSyncTimer)
1196  SendTimeSync();
1197  else
1198  m_timeSyncTimer -= p_time;
1199  }
1200 
1201  if (IsAlive())
1202  {
1203  if (IsInCombat())
1204  if (m_CombatTimer.GetInterval() != 0 && !m_CombatTimer.Passed())
1205  m_CombatTimer.Update(p_time);
1206 
1207  RegenerateAll();
1208  }
1209 
1210  if (m_deathState == JUST_DIED)
1211  KillPlayer();
1212 
1213  if (m_nextSave > 0)
1214  {
1215  if (p_time >= m_nextSave)
1216  {
1217  // m_nextSave reset in SaveToDB call
1218  SaveToDB();
1219  sLog.outDetail("Player '%s' (GUID: %u) saved", GetName(), GetGUIDLow());
1220  }
1221  else
1222  m_nextSave -= p_time;
1223  }
1224 
1225  //Handle Water/drowning
1226  HandleDrowning(p_time);
1227 
1228  // Played time
1229  if (now > m_Last_tick)
1230  {
1231  uint32 elapsed = uint32(now - m_Last_tick);
1232  m_Played_time[PLAYED_TIME_TOTAL] += elapsed; // Total played time
1233  m_Played_time[PLAYED_TIME_LEVEL] += elapsed; // Level played time
1234  m_Last_tick = now;
1235  }
1236 
1237  if (GetDrunkValue())
1238  {
1239  m_drunkTimer += p_time;
1240  if (m_drunkTimer > 9 * IN_MILLISECONDS)
1241  HandleSobering();
1242  }
1243 
1244  // not auto-free ghost from body in instances
1245  if (m_deathTimer > 0 && !GetBaseMap()->Instanceable())
1246  {
1247  if (p_time >= m_deathTimer)
1248  {
1249  m_deathTimer = 0;
1250  BuildPlayerRepop();
1251  RepopAtGraveyard();
1252  }
1253  else
1254  m_deathTimer -= p_time;
1255  }
1256 
1257  UpdateEnchantTime(p_time);
1258  UpdateHomebindTime(p_time);
1259 
1260  // group update
1262 
1263  Pet* pet = GetPet();
1264  if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityRange()) && !pet->isPossessed())
1265  RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
1266 
1267  //we should execute delayed teleports only for alive(!) players
1268  //because we don't want player's ghost teleported from graveyard
1269  if (IsHasDelayedTeleport() && IsAlive())
1271 }
1272 
1274 {
1275  uint32 ressSpellId = 0;
1276 
1277  bool cur = IsAlive();
1278 
1279  if (s == JUST_DIED)
1280  {
1281  if (!cur)
1282  {
1283  sLog.outError("setDeathState: attempt to kill a dead player %s(%d)", GetName(), GetGUIDLow());
1284  return;
1285  }
1286 
1287  // drunken state is cleared on death
1288  SetDrunkValue(0);
1289  // lost combo points at any target (targeted combo points clear in Unit::setDeathState)
1290  ClearComboPoints();
1291 
1293 
1294  // remove form before other mods to prevent incorrect stats calculation
1296 
1297  //FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
1298  RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
1299 
1300  // save value before aura remove in Unit::setDeathState
1301  ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL);
1302 
1303  // passive spell
1304  if (!ressSpellId)
1305  ressSpellId = GetResurrectionSpellId();
1306 
1307  if (m_zoneScript)
1308  m_zoneScript->OnPlayerDeath(this);
1309  }
1310 
1312 
1313  // restore resurrection spell id for player after aura remove
1314  if (s == JUST_DIED && cur && ressSpellId)
1315  SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId);
1316 
1317  if (IsAlive() && !cur)
1318  {
1319  //clear aura case after resurrection by another way (spells will be applied before next death)
1321 
1322  // restore default warrior stance
1323  if (getClass() == CLASS_WARRIOR)
1325 
1327  }
1328 }
1329 
1331 {
1332  Field* fields = result->Fetch();
1333  uint32 guid = fields[0].GetUInt32();
1334  uint8 pRace = fields[2].GetUInt8();
1335  uint8 pClass = fields[3].GetUInt8();
1336 
1337  PlayerInfo const* info = sObjectMgr.GetPlayerInfo(pRace, pClass);
1338  if (!info)
1339  {
1340  sLog.outError("Player %u has incorrect race/class pair. Don't build enum.", guid);
1341  return false;
1342  }
1343 
1344  *p_data << uint64(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
1345  *p_data << fields[1].GetString(); // name
1346  *p_data << uint8(pRace); // race
1347  *p_data << uint8(pClass); // class
1348  *p_data << uint8(fields[4].GetUInt8()); // gender
1349 
1350  uint32 playerBytes = fields[5].GetUInt32();
1351  *p_data << uint8(playerBytes); // skin
1352  *p_data << uint8(playerBytes >> 8); // face
1353  *p_data << uint8(playerBytes >> 16); // hair style
1354  *p_data << uint8(playerBytes >> 24); // hair color
1355 
1356  uint32 playerBytes2 = fields[6].GetUInt32();
1357  *p_data << uint8(playerBytes2 & 0xFF); // facial hair
1358 
1359  *p_data << uint8(fields[7].GetUInt8()); // level
1360  *p_data << uint32(fields[8].GetUInt32()); // zone
1361  *p_data << uint32(fields[9].GetUInt32()); // map
1362 
1363  *p_data << fields[10].GetFloat(); // x
1364  *p_data << fields[11].GetFloat(); // y
1365  *p_data << fields[12].GetFloat(); // z
1366 
1367  *p_data << uint32(fields[13].GetUInt32()); // guild id
1368 
1369  uint32 char_flags = 0;
1370  uint32 playerFlags = fields[14].GetUInt32();
1371  uint32 atLoginFlags = fields[15].GetUInt32();
1372  if (playerFlags & PLAYER_FLAGS_HIDE_HELM)
1373  char_flags |= CHARACTER_FLAG_HIDE_HELM;
1374  if (playerFlags & PLAYER_FLAGS_HIDE_CLOAK)
1375  char_flags |= CHARACTER_FLAG_HIDE_CLOAK;
1376  if (playerFlags & PLAYER_FLAGS_GHOST)
1377  char_flags |= CHARACTER_FLAG_GHOST;
1378  if (atLoginFlags & AT_LOGIN_RENAME)
1379  char_flags |= CHARACTER_FLAG_RENAME;
1380  if (sWorld.getConfig(CONFIG_DECLINED_NAMES_USED))
1381  {
1382  if (!fields[20].GetCppString().empty())
1383  char_flags |= CHARACTER_FLAG_DECLINED;
1384  }
1385  else
1386  char_flags |= CHARACTER_FLAG_DECLINED;
1387 
1388  *p_data << uint32(char_flags); // character flags
1389 
1390  *p_data << uint8(1); // unknown
1391 
1392  // Pets info
1393  {
1394  uint32 petDisplayId = 0;
1395  uint32 petLevel = 0;
1397 
1398  // show pet at selection character in character list only for non-ghost character
1399  if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER))
1400  {
1401  uint32 entry = fields[16].GetUInt32();
1402  CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(entry);
1403  if (cInfo)
1404  {
1405  petDisplayId = fields[17].GetUInt32();
1406  petLevel = fields[18].GetUInt32();
1407  petFamily = cInfo->family;
1408  }
1409  }
1410 
1411  *p_data << uint32(petDisplayId);
1412  *p_data << uint32(petLevel);
1413  *p_data << uint32(petFamily);
1414  }
1415 
1416  Tokens data = StrSplit(fields[19].GetCppString(), " ");
1417 
1418  for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
1419  {
1420  uint32 visualbase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET);
1421  uint32 item_id = GetUInt32ValueFromArray(data, visualbase);
1422  const ItemTemplate* proto = sObjectMgr.GetItemTemplate(item_id);
1423  if (!proto)
1424  {
1425  *p_data << uint32(0);
1426  *p_data << uint8(0);
1427  *p_data << uint32(0);
1428  continue;
1429  }
1430 
1431  SpellItemEnchantmentEntry const* enchant = NULL;
1432 
1433  for (uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot)
1434  {
1435  uint32 enchantId = GetUInt32ValueFromArray(data, visualbase + 1 + enchantSlot);
1436  if (!enchantId)
1437  continue;
1438 
1439  if ((enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId)))
1440  break;
1441  }
1442 
1443  *p_data << uint32(proto->DisplayInfoID);
1444  *p_data << uint8(proto->InventoryType);
1445  *p_data << uint32(enchant ? enchant->aura_id : 0);
1446  }
1447  *p_data << uint32(0); // first bag display id
1448  *p_data << uint8(0); // first bag inventory type
1449  *p_data << uint32(0); // enchant?
1450 
1451  return true;
1452 }
1453 
1455 {
1457 
1458  if (GetGroup())
1460 
1461  // afk player not allowed in battleground
1462  if (isAFK() && InBattleground() && !InArena())
1464 }
1465 
1467 {
1469 
1470  if (GetGroup())
1472 }
1473 
1475 {
1476  uint8 tag = CHAT_TAG_NONE;
1477 
1478  if (isGMChat())
1479  tag |= CHAT_TAG_GM;
1480  if (isDND())
1481  tag |= CHAT_TAG_DND;
1482  if (isAFK())
1483  tag |= CHAT_TAG_AFK;
1484 
1485  return tag;
1486 }
1487 
1488 bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options)
1489 {
1490  if (!MapManager::IsValidMapCoord(mapid, x, y, z, orientation))
1491  {
1492  sLog.outError("TeleportTo: invalid map %d or absent instance template.", mapid);
1493  return false;
1494  }
1495 
1496  if ((GetSession()->GetSecurity() < SEC_GAMEMASTER) && sDisableMgr.IsDisabledFor(DISABLE_TYPE_MAP, mapid, this))
1497  {
1498  sLog.outError("Player %s tried to enter a forbidden map", GetName());
1500  return false;
1501  }
1502 
1503  // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
1504  Pet* pet = GetPet();
1505 
1506  MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
1507 
1508  // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)...
1509  // don't let gm level > 1 either
1510  if (!InBattleground() && mEntry->IsBattlegroundOrArena())
1511  return false;
1512 
1513  // 449 - Champions' Hall (Alliance) // 450 - Hall of Legends (Horde)
1514  if (mapid == 449 && GetTeam() == HORDE)
1515  {
1517  return false;
1518  }
1519 
1520  if (mapid == 450 && GetTeam() == ALLIANCE)
1521  {
1523  return false;
1524  }
1525 
1526  // client without expansion support
1527  if (GetSession()->Expansion() < mEntry->Expansion())
1528  {
1529  sLog.outDebug("Player %s using client without required expansion tried teleport to non accessible map %u", GetName(), mapid);
1530 
1531  if (GetTransport())
1532  RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :)
1533 
1535 
1536  return false; // normal client can't teleport to this map...
1537  }
1538  else
1539  sLog.outDebug("Player %s is being teleported to map %u", GetName(), mapid);
1540 
1541  // reset movement flags at teleport, because player will continue move with these flags after teleport
1543  DisableSpline();
1544 
1545  if (m_transport)
1546  {
1547  if (options & TELE_TO_NOT_LEAVE_TRANSPORT)
1549  else
1550  {
1552  m_transport = NULL;
1554  }
1555  }
1556 
1557  // The player was ported to another map and loses the duel immediately.
1558  // We have to perform this check before the teleport, otherwise the
1559  // ObjectAccessor won't find the flag.
1560  if (duel && GetMapId() != mapid && GetMap()->GetGameObject(GetUInt64Value(PLAYER_DUEL_ARBITER)))
1562 
1563  if (GetMapId() == mapid && !m_transport)
1564  {
1565  //lets reset far teleport flag if it wasn't reset during chained teleports
1566  SetSemaphoreTeleportFar(false);
1567  //setup delayed teleport flag
1568  //if teleport spell is casted in Unit::Update() func
1569  //then we need to delay it until update process will be finished
1571  {
1573  //lets save teleport destination for player
1574  m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
1575  m_teleport_options = options;
1576  return true;
1577  }
1578 
1579  if (!(options & TELE_TO_NOT_UNSUMMON_PET))
1580  {
1581  //same map, only remove pet if out of range for new position
1582  if (pet && !pet->IsWithinDist3d(x, y, z, GetMap()->GetVisibilityRange()))
1583  {
1584  if (pet->isControlled() && !pet->isTemporarySummoned())
1586  else
1588 
1590  }
1591  }
1592 
1593  if (!(options & TELE_TO_NOT_LEAVE_COMBAT))
1594  CombatStop();
1595 
1596  // this will be used instead of the current location in SaveToDB
1597  m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
1598  SetFallInformation(0, z);
1599 
1600  // code for finish transfer called in WorldSession::HandleMovementOpcodes()
1601  // at client packet MSG_MOVE_TELEPORT_ACK
1603  // near teleport, triggering send MSG_MOVE_TELEPORT_ACK from client at landing
1604  if (!GetSession()->PlayerLogout())
1605  {
1606  WorldPacket data;
1607  BuildTeleportAckMsg(&data, x, y, z, orientation);
1608  GetSession()->SendPacket(&data);
1609  }
1610  }
1611  else
1612  {
1613  // far teleport to another map
1614  Map* oldmap = IsInWorld() ? GetMap() : NULL;
1615  // check if we can enter before stopping combat / removing pet / totems / interrupting spells
1616 
1617  // Check enter rights before map getting to avoid creating instance copy for player
1618  // this check not dependent from map instance copy and same for all instance copies of selected map
1619  if (MapManager::Instance().PlayerCannotEnter(mapid, this))
1620  return false;
1621 
1622  // If the map is not created, assume it is possible to enter it.
1623  // It will be created in the WorldPortAck.
1624  //Map* map = MapManager::Instance().FindMap(mapid);
1625  //if (!map || map->CannotEnter(this))
1626  {
1627  //lets reset near teleport flag if it wasn't reset during chained teleports
1628  SetSemaphoreTeleportNear(false);
1629  //setup delayed teleport flag
1630  //if teleport spell is casted in Unit::Update() func
1631  //then we need to delay it until update process will be finished
1633  {
1635  //lets save teleport destination for player
1636  m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
1637  m_teleport_options = options;
1638  return true;
1639  }
1640 
1641  SetSelection(0);
1642 
1643  CombatStop();
1644 
1646 
1647  // Remove fear and charm effects
1649  RemoveCharmAuras();
1650 
1651  // remove player from battleground on far teleport (when changing maps)
1652  if (Battleground const* bg = GetBattleground())
1653  {
1654  // Note: at battleground join battleground id set before teleport
1655  // and we already will found "current" battleground
1656  // just need check that this is targeted map or leave
1657  if (bg->GetMapId() != mapid)
1658  LeaveBattleground(false); // don't teleport to entry point
1659  }
1660 
1661  // remove pet on map change
1662  if (pet)
1663  {
1664  //leaving map -> delete pet right away (doing this later will cause problems)
1665  if (pet->isControlled() && !pet->isTemporarySummoned() && pet->GetCharmInfo())
1667  else
1669 
1671  }
1672 
1673  // remove all dyn objects
1675 
1676  // stop spellcasting
1677  // not attempt interrupt teleportation spell at caster teleport
1678  if (!(options & TELE_TO_SPELL))
1679  if (IsNonMeleeSpellCast(true))
1681 
1682  //remove auras before removing from map...
1684 
1685  if (!GetSession()->PlayerLogout())
1686  {
1687  // send transfer packets
1688  WorldPacket data(SMSG_TRANSFER_PENDING, (4 + 4 + 4));
1689  data << uint32(mapid);
1690  if (m_transport)
1691  {
1692  data << uint32(m_transport->GetEntry());
1693  data << uint32(GetMapId());
1694  }
1695  GetSession()->SendPacket(&data);
1696 
1697  data.Initialize(SMSG_NEW_WORLD, (20));
1698  if (m_transport)
1699  {
1700  data << uint32(mapid);
1701  data << float(m_movementInfo.GetTransportPos()->GetPositionX());
1702  data << float(m_movementInfo.GetTransportPos()->GetPositionY());
1703  data << float(m_movementInfo.GetTransportPos()->GetPositionZ());
1704  data << float(m_movementInfo.GetTransportPos()->GetOrientation());
1705  }
1706  else
1707  {
1708  data << uint32(mapid);
1709  data << float(x);
1710  data << float(y);
1711  data << float(z);
1712  data << float(orientation);
1713  }
1714  GetSession()->SendPacket(&data);
1716  }
1717 
1718  // remove from old map now
1719  if (oldmap)
1720  oldmap->RemovePlayerFromMap(this, false);
1721 
1722  // new final coordinates
1723  float final_x = x;
1724  float final_y = y;
1725  float final_z = z;
1726  float final_o = orientation;
1727 
1728  if (m_transport)
1729  {
1734  }
1735 
1736  m_teleport_dest = WorldLocation(mapid, final_x, final_y, final_z, final_o);
1737  SetFallInformation(0, final_z);
1738  // if the player is saved before worldport ack (at logout for example)
1739  // this will be used instead of the current location in SaveToDB
1740 
1741  // move packet sent by client always after far teleport
1742  // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
1744  }
1745  //else
1746  //return false;
1747  }
1748  return true;
1749 }
1750 
1751 bool Player::TeleportTo(WorldLocation const &loc, uint32 options /*= 0*/)
1752 {
1753  return TeleportTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation(), options);
1754 }
1755 
1757 {
1758  PlayerInfo const* info = sObjectMgr.GetPlayerInfo(getRace(), getClass());
1759  uint32 mapId = info->mapId;
1760  return WorldLocation(mapId, info->positionX, info->positionY, info->positionZ, 0);
1761 }
1762 
1764 {
1766  {
1770  }
1771  return TeleportTo(m_bgData.joinPos);
1772 }
1773 
1775 {
1776  if (m_DelayedOperations == 0)
1777  return;
1778 
1780  {
1781  ResurrectPlayer(0.0f, false);
1782 
1785  else
1787 
1790  else
1792 
1793  SetPower(POWER_RAGE, 0);
1795 
1796  SpawnCorpseBones();
1797  }
1798 
1800  SaveToDB();
1801 
1803  CastSpell(this, 26013, true); // Deserter
1804 
1806  {
1807  if (m_bgData.mountSpell)
1808  {
1811  CastSpell(this, m_bgData.mountSpell, true);
1812  m_bgData.mountSpell = 0;
1813  }
1814  }
1815 
1817  {
1818  if (m_bgData.HasTaxiPath())
1819  {
1823 
1825  }
1826  }
1827 
1829  {
1830  if (Group* g = GetGroup())
1831  g->SendUpdate();
1832  }
1833 
1834  //we have executed ALL delayed ops, so clear the flag
1835  m_DelayedOperations = 0;
1836 }
1837 
1839 {
1843  Unit::AddToWorld();
1844 
1845  for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
1846  if (m_items[i])
1847  m_items[i]->AddToWorld();
1848 }
1849 
1851 {
1852  // cleanup
1853  if (IsInWorld())
1854  {
1856  StopCastingCharm();
1859  ClearComboPoints();
1861  uint64 lootGuid = GetLootGUID();
1862  if (!lootGuid)
1863  m_session->DoLootRelease(lootGuid);
1864  sOutdoorPvPMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId);
1865  }
1866 
1867  // remove duel before calling Unit::RemoveFromWorld
1868  // otherwise there will be an existing duel flag pointer but no entry in m_gameObj
1870 
1871  for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
1872  {
1873  if (m_items[i])
1874  m_items[i]->RemoveFromWorld();
1875  }
1876 
1877  // Do not add/remove the player from the object storage
1878  // It will crash when updating the ObjectAccessor
1879  // The player should only be removed when logging out
1881 
1882  for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
1883  iter->second->RemoveFromWorld();
1884 
1885  if (m_uint32Values)
1886  {
1887  if (WorldObject* viewpoint = GetViewpoint())
1888  {
1889  sLog.outError("Crash alert! Player %s has viewpoint %u %u when removed from world", GetName(), viewpoint->GetEntry(), viewpoint->GetTypeId());
1890  SetViewpoint(viewpoint, false);
1891  }
1892  }
1893 }
1894 
1896 {
1897  if (!IsInWorld() || !IsAlive())
1898  return;
1899 
1900  Pet* pet = GetPet();
1901  if (!pet)
1902  return;
1903 
1906 
1908 }
1909 
1911 {
1912  if (!IsInWorld() || !IsAlive())
1913  return;
1914 
1916  {
1917  Pet* NewPet = new Pet(this);
1918  if (!NewPet->LoadPetFromDB(this, 0, GetTemporaryUnsummonedPetNumber(), true))
1919  delete NewPet;
1921  }
1922  else
1923  {
1924  if (Guardian* pPet = GetGuardianPet())
1925  if (pPet->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED) && !pPet->HasUnitState(UNIT_STATE_STUNNED))
1926  pPet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
1927  }
1928 }
1929 
1930 void Player::RewardRage(uint32 damage, uint32 weaponSpeedHitFactor, bool attacker)
1931 {
1932  float addRage;
1933 
1934  float rageconversion = float((0.0091107836 * getLevel() * getLevel()) + 3.225598133 * getLevel()) + 4.2652911f;
1935 
1936  if (attacker)
1937  {
1938  addRage = (damage / rageconversion * 7.5f + weaponSpeedHitFactor) / 2.0f;
1939 
1940  // talent who gave more rage on attack
1941  addRage *= 1.0f + GetTotalAuraModifier(SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT) / 100.0f;
1942  }
1943  else
1944  {
1945  addRage = damage / rageconversion * 2.5f;
1946 
1947  // Berserker Rage effect
1948  if (HasAura(18499, 0))
1949  addRage *= 2.0f;
1950  }
1951 
1952  addRage *= sWorld.getRate(RATE_POWER_RAGE_INCOME);
1953 
1954  ModifyPower(POWER_RAGE, uint32(addRage * 10));
1955 }
1956 
1958 {
1959  if (m_regenTimer != 0)
1960  return;
1961 
1962  // Not in combat or they have regeneration
1965  {
1967  RegenerateHealth();
1970  }
1971 
1973 
1975 
1976  if (IsInCombat())
1977  if (getHostileRefManager().isEmpty())
1979  ClearInCombat();
1980 
1981  m_regenTimer = 2000;
1982 }
1983 
1985 {
1986  uint32 curValue = GetPower(power);
1987  uint32 maxValue = GetMaxPower(power);
1988 
1989  float addvalue = 0.0f;
1990 
1991  switch (power)
1992  {
1993  case POWER_MANA:
1994  {
1995  bool recentCast = IsUnderLastManaUseEffect();
1996  float ManaIncreaseRate = sWorld.getRate(RATE_POWER_MANA);
1997 
1998  if (recentCast) // Oregon Updates Mana in intervals of 2s, which is correct
1999  addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT) * ManaIncreaseRate * 2.00f;
2000 
2001  else
2002  addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN) * ManaIncreaseRate * 2.00f;
2003  }
2004  break;
2005  case POWER_RAGE: // Regenerate rage
2006  {
2007  float RageDecreaseRate = sWorld.getRate(RATE_POWER_RAGE_LOSS);
2008  addvalue = 30 * RageDecreaseRate; // 3 rage by tick
2009  }
2010  break;
2011  case POWER_ENERGY: // Regenerate energy (rogue)
2012  {
2013  float EnergyIncreaseRate = sWorld.getRate(RATE_POWER_ENERGY);
2014  addvalue = 20 * EnergyIncreaseRate;
2015  break;
2016  }
2017  case POWER_FOCUS:
2018  case POWER_HAPPINESS:
2019  case POWER_HEALTH:
2020  break;
2021  }
2022 
2023  // Mana regen calculated in Player::UpdateManaRegen()
2024  // Exist only for POWER_MANA, POWER_ENERGY, POWER_FOCUS auras
2025  if (power != POWER_MANA)
2026  {
2027  AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
2028  for (AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
2029  if ((*i)->GetModifier()->m_miscvalue == int32(power))
2030  addvalue *= ((*i)->GetModifierValue() + 100) / 100.0f;
2031  }
2032 
2033  if (power != POWER_RAGE)
2034  {
2035  curValue += uint32(addvalue);
2036  if (curValue > maxValue)
2037  curValue = maxValue;
2038  }
2039  else
2040  {
2041  if (curValue <= uint32(addvalue))
2042  curValue = 0;
2043  else
2044  curValue -= uint32(addvalue);
2045  }
2046  SetPower(power, curValue);
2047 }
2048 
2050 {
2051  uint32 curValue = GetHealth();
2052  uint32 maxValue = GetMaxHealth();
2053 
2054  if (curValue >= maxValue)
2055  return;
2056 
2057  float HealthIncreaseRate = sWorld.getRate(RATE_HEALTH);
2058 
2059  if (getLevel() < 15)
2060  HealthIncreaseRate = sWorld.getRate(RATE_HEALTH) * (2.066f - (getLevel() * 0.066f));
2061 
2062  float addValue = 0.0f;
2063 
2064  // polymorphed case
2065  if (IsPolymorphed())
2066  addValue = float(GetMaxHealth()) / 3.0f;
2067  // normal regen case (maybe partly in combat case)
2069  {
2070  addValue = OCTRegenHPPerSpirit() * HealthIncreaseRate;
2071  if (!IsInCombat())
2072  {
2073  AuraList const& mModHealthRegenPct = GetAurasByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
2074  for (AuraList::const_iterator i = mModHealthRegenPct.begin(); i != mModHealthRegenPct.end(); ++i)
2075  addValue *= (100.0f + (*i)->GetModifierValue()) / 100.0f;
2076  }
2079 
2080  if (!IsStandState())
2081  addValue *= 1.5f;
2082  }
2083 
2084  // always regeneration bonus (including combat)
2085  addValue += HealthIncreaseRate * 2.0f * (GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT) / 5.0f);
2086 
2087  if (addValue < 0.0f)
2088  addValue = 0.0f;
2089 
2090  ModifyHealth(int32(addValue));
2091 }
2092 
2094 {
2096  switch (getPowerType())
2097  {
2098  case POWER_MANA:
2100  break;
2101  case POWER_RAGE:
2102  SetPower(POWER_RAGE, 0);
2103  break;
2104  case POWER_ENERGY:
2106  break;
2107  default:
2108  break;
2109  }
2110 }
2111 
2113 {
2114  switch (questGiver->GetTypeId())
2115  {
2116  case TYPEID_UNIT:
2117  return GetNPCIfCanInteractWith(questGiver->GetGUID(), UNIT_NPC_FLAG_QUESTGIVER) != NULL;
2118  case TYPEID_GAMEOBJECT:
2119  return GetGameObjectIfCanInteractWith(questGiver->GetGUID(), GAMEOBJECT_TYPE_QUESTGIVER) != NULL;
2120  case TYPEID_PLAYER:
2121  return IsAlive() && questGiver->ToPlayer()->IsAlive();
2122  case TYPEID_ITEM:
2123  return IsAlive();
2124  default:
2125  break;
2126  }
2127  return false;
2128 }
2129 
2131 {
2132  // unit checks
2133  if (!guid)
2134  return nullptr;
2135 
2136  if (!IsInWorld())
2137  return nullptr;
2138 
2139  if (IsInFlight())
2140  return nullptr;
2141 
2142  // exist
2143  Creature* creature = GetMap()->GetCreature(guid);
2144  if (!creature)
2145  return nullptr;
2146 
2147  // if a dead unit should be able to talk - the creature must be alive and have special flags
2149  return nullptr;
2150 
2151  if (IsAlive() && creature->isInvisibleForAlive())
2152  return nullptr;
2153 
2154  // appropriate npc type
2155  if (npcflagmask && !creature->HasFlag(UNIT_NPC_FLAGS, npcflagmask))
2156  return nullptr;
2157 
2158  // not allow interaction under control
2159  if (creature->GetCharmerGUID())
2160  return nullptr;
2161 
2162  // not enemy
2163  if (creature->IsHostileTo(this))
2164  return nullptr;
2165 
2166  // not too far, taken from CGGameUI::SetInteractTarget
2167  if (!creature->IsWithinDistInMap(this, creature->GetCombatReach() + 4.0f))
2168  return nullptr;
2169 
2170  return creature;
2171 }
2172 
2174 {
2175  if (!guid)
2176  return nullptr;
2177 
2178  if (!IsInWorld())
2179  return nullptr;
2180 
2181  if (IsInFlight())
2182  return nullptr;
2183 
2184  // exist
2185  GameObject* go = GetMap()->GetGameObject(guid);
2186  if (!go)
2187  return nullptr;
2188 
2189  if (!go->IsWithinDistInMap(this, go->GetInteractionDistance()))
2190  return nullptr;
2191 
2192  return go;
2193 }
2194 
2196 {
2198 }
2199 
2201 {
2202  if (m_isInWater == apply)
2203  return;
2204 
2205  //define player in water by opcodes
2206  //move player's guid into HateOfflineList of those mobs
2207  //which can't swim and move guid back into ThreatList when
2208  //on surface.
2210  m_isInWater = apply;
2211 
2212  // remove auras that need water/land
2214 
2216 }
2217 
2219 {
2220  if (!trigger || GetMapId() != trigger->mapid)
2221  return false;
2222 
2223  if (trigger->radius > 0.f)
2224  {
2225  // if we have radius check it
2226  float dist = GetDistance(trigger->x, trigger->y, trigger->z);
2227  if (dist > trigger->radius)
2228  return false;
2229  }
2230  else
2231  {
2232  Position center = {trigger->x, trigger->y, trigger->z, trigger->box_orientation};
2233  if (!IsWithinBox(center, trigger->box_x / 2.f, trigger->box_y / 2.f, trigger->box_z / 2.f))
2234  return false;
2235  }
2236 
2237  return true;
2238 }
2239 
2241 {
2242  if (on)
2243  {
2245  SetFaction(35);
2247 
2248  if (Pet* pet = GetPet())
2249  {
2250  pet->SetFaction(35);
2251  pet->getHostileRefManager().setOnlineOfflineState(false);
2252  }
2253 
2254  SetFFAPvP(false);
2256 
2259 
2260  SetPhaseMask(uint32(PHASEMASK_ANYWHERE), false); // see and visible in all phases
2262  }
2263  else
2264  {
2265  // restore phase
2266  uint32 newPhase = 0;
2267  AuraList const& phases = GetAurasByType(SPELL_AURA_PHASE);
2268  if (!phases.empty())
2269  for (AuraList::const_iterator itr = phases.begin(); itr != phases.end(); ++itr)
2270  newPhase |= (*itr)->GetMiscValue();
2271 
2272  if (!newPhase)
2273  newPhase = PHASEMASK_NORMAL;
2274 
2275  SetPhaseMask(newPhase, false);
2276 
2280 
2281  if (Pet* pet = GetPet())
2282  {
2283  pet->SetFaction(GetFaction());
2284  pet->getHostileRefManager().setOnlineOfflineState(true);
2285  }
2286 
2287  // restore FFA PvP Server state
2288  if (sWorld.IsFFAPvPRealm())
2289  SetFFAPvP(true);
2290 
2291  // restore FFA PvP area state, remove not allowed for GM mounts
2293 
2296  }
2297 
2299 }
2300 
2302 {
2303  if (on)
2304  {
2305  m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; //remove flag
2306 
2308  }
2309  else
2310  {
2312 
2313  SetAcceptWhispers(false);
2314  SetGameMaster(true);
2315 
2317  }
2318 }
2319 
2320 bool Player::IsGroupVisibleFor(Player const* p) const
2321 {
2322  switch (sWorld.getConfig(CONFIG_GROUP_VISIBILITY))
2323  {
2324  default: return IsInSameGroupWith(p);
2325  case 1: return IsInSameRaidWith(p);
2326  case 2: return GetTeam() == p->GetTeam();
2327  }
2328 }
2329 
2330 bool Player::IsInSameGroupWith(Player const* p) const
2331 {
2332  return p == this || (GetGroup() != NULL &&
2333  GetGroup() == p->GetGroup() &&
2334  GetGroup()->SameSubGroup(this, p));
2335 }
2336 
2337 // If the player is invited, remove him. If the group if then only 1 person, disband the group.
2338 // todo Shouldn't we also check if there is no other invitees before disbanding the group?
2340 {
2341  Group* group = GetGroupInvite();
2342  if (!group)
2343  return;
2344 
2345  group->RemoveInvite(this);
2346 
2347  if (group->GetMembersCount() <= 1) // group has just 1 member => disband
2348  {
2349  if (group->IsCreated())
2350  {
2351  group->Disband(true);
2352  sObjectMgr.RemoveGroup(group);
2353  }
2354  else
2355  group->RemoveAllInvites();
2356 
2357  delete group;
2358  }
2359 }
2360 
2361 void Player::RemoveFromGroup(Group* group, uint64 guid, RemoveMethod method /* = GROUP_REMOVEMETHOD_DEFAULT*/, uint64 kicker /* = 0 */, const char* reason /* = NULL */)
2362 {
2363  if (group)
2364  {
2365  if (group->RemoveMember(guid, method, kicker, reason) <= 1)
2366  {
2367  // group->Disband(); already disbanded in RemoveMember
2368  sObjectMgr.RemoveGroup(group);
2369  delete group;
2370  // removemember sets the player's group pointer to NULL
2371  }
2372  }
2373 }
2374 
2375 void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 RestXP, bool RafBonus)
2376 {
2377  WorldPacket data(SMSG_LOG_XPGAIN, 21);
2378  data << uint64(victim ? victim->GetGUID() : 0); // guid
2379  data << uint32(GivenXP + RestXP); // given experience
2380  data << uint8(victim ? 0 : 1); // 00-kill_xp type, 01-non_kill_xp type
2381  if (victim)
2382  {
2383  data << uint32(GivenXP); // experience without rested bonus
2384  data << float(1); // 1 - none 0 - 100% group bonus output
2385  }
2386  data << uint8(RafBonus); // whether is or not RAF bonus included
2387  GetSession()->SendPacket(&data);
2388 }
2389 
2390 void Player::GiveXP(uint32 xp, Unit* victim, bool disableRafBonus)
2391 {
2392  if (xp < 1)
2393  return;
2394 
2395  if (!IsAlive())
2396  return;
2397 
2398  uint32 level = getLevel();
2399 
2400  // XP to money conversion processed in Player::RewardQuest
2401  if (level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2402  return;
2403 
2404  // handle SPELL_AURA_MOD_XP_PCT auras
2405  Unit::AuraList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_XP_PCT);
2406  for (Unit::AuraList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i)
2407  xp = uint32(xp * (1.0f + (*i)->GetModifierValue() / 100.0f));
2408 
2409  Unit::AuraList const& DummyAuras = GetAurasByType(SPELL_AURA_DUMMY);
2410  for (Unit::AuraList::const_iterator i = DummyAuras.begin(); i != DummyAuras.end(); ++i)
2411  if ((*i)->GetId() == 32098 || (*i)->GetId() == 32096)
2412  {
2413  uint32 area_id = GetAreaId();
2414  if (area_id == 3483 || area_id == 3535 || area_id == 3562 || area_id == 3713)
2415  xp = uint32(xp * (1.0f + 5.0f / 100.0f));
2416  }
2417 
2418  uint32 rested_bonus_xp = 0;
2419  float RafMultiplier = GetReferFriendXPMultiplier() - 1;
2420 
2421  if (!disableRafBonus && RafMultiplier)
2422  // Refer-A-Friend Bonus XP (rest bonus doesnt count)
2423  rested_bonus_xp = xp * RafMultiplier;
2424  else if (victim)
2425  // XP resting bonus for kill
2426  rested_bonus_xp = GetXPRestBonus(xp);
2427 
2428  SendLogXPGain(xp, victim, rested_bonus_xp, (bool)m_rafLink);
2429 
2430  uint32 curXP = GetUInt32Value(PLAYER_XP);
2432  uint32 newXP = curXP + xp + rested_bonus_xp;
2433 
2434  while (newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2435  {
2436  newXP -= nextLvlXP;
2437 
2438  if (level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2439  GiveLevel(level + 1);
2440 
2441  level = getLevel();
2442  nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
2443  }
2444 
2445  SetUInt32Value(PLAYER_XP, newXP);
2446 }
2447 
2448 // Update player to next level
2449 // Current player experience not update (must be update by caller)
2450 void Player::GiveLevel(uint32 level, bool ignoreRAF)
2451 {
2452  if (level == getLevel())
2453  return;
2454 
2455  sScriptMgr.OnPlayerLevelChanged(this, getLevel(), level);
2456 
2457  int32 diff = level - getLevel();
2458 
2459  PlayerLevelInfo info;
2460  sObjectMgr.GetPlayerLevelInfo(getRace(), getClass(), level, &info);
2461 
2462  PlayerClassLevelInfo classInfo;
2463  sObjectMgr.GetPlayerClassLevelInfo(getClass(), level, &classInfo);
2464 
2465  // send levelup info to client
2466  WorldPacket data(SMSG_LEVELUP_INFO, (4 + 4 + MAX_POWERS * 4 + MAX_STATS * 4));
2467  data << uint32(level);
2468  data << uint32(int32(classInfo.basehealth) - int32(GetCreateHealth()));
2469  // for (int i = 0; i < MAX_POWERS; ++i) // Powers loop (0-6)
2470  data << uint32(int32(classInfo.basemana) - int32(GetCreateMana()));
2471  data << uint32(0);
2472  data << uint32(0);
2473  data << uint32(0);
2474  data << uint32(0);
2475  // end for
2476  for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) // Stats loop (0-4)
2477  data << uint32(int32(info.stats[i]) - GetCreateStat(Stats(i)));
2478 
2479  GetSession()->SendPacket(&data);
2480 
2482 
2483  //update level, max level of skills
2484  m_Played_time[PLAYED_TIME_LEVEL] = 0; // Level Played Time reset
2485 
2486  SetLevel(level);
2487 
2489 
2490  // save base values (bonuses already included in stored stats
2491  for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
2492  SetCreateStat(Stats(i), info.stats[i]);
2493 
2494  SetCreateHealth(classInfo.basehealth);
2495  SetCreateMana(classInfo.basemana);
2496 
2499 
2500  UpdateAllStats();
2501 
2502  if (sWorld.getConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up
2504 
2505  // set current level health and mana/energy to maximum after applying all mods.
2506  if (IsAlive())
2512  SetPower(POWER_FOCUS, 0);
2514 
2515  // give level to summoned pet
2516  Pet* pet = GetPet();
2517  if (pet && pet->getPetType() == SUMMON_PET)
2518  pet->GivePetLevel(level);
2519 
2520  if (!ignoreRAF && sObjectMgr.GetRAFLinkStatus(this) == RAF_LINK_REFERRED)
2521  while (diff-- > 0)
2523 }
2524 
2526 {
2527  uint32 level = getLevel();
2528  // talents base at level diff (talents = level - 9 but some can be used already)
2529  if (level < 10)
2530  {
2531  // Remove all talent points
2532  if (m_usedTalentCount > 0) // Free any used talents
2533  {
2534  ResetTalents(true);
2536  }
2537  }
2538  else
2539  {
2540  uint32 talentPointsForLevel = uint32((level - 9) * sWorld.getRate(RATE_TALENT));
2541  // if used more that have then reset
2542  if (m_usedTalentCount > talentPointsForLevel)
2543  {
2545  ResetTalents(true);
2546  else
2548  }
2549  // else update amount of free points
2550  else
2551  SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount);
2552  }
2553 }
2554 
2555 void Player::InitStatsForLevel(bool reapplyMods)
2556 {
2557  if (reapplyMods) //reapply stats values only on .reset stats (level) command
2559 
2560  PlayerClassLevelInfo classInfo;
2561  sObjectMgr.GetPlayerClassLevelInfo(getClass(), getLevel(), &classInfo);
2562 
2563  PlayerLevelInfo info;
2564  sObjectMgr.GetPlayerLevelInfo(getRace(), getClass(), getLevel(), &info);
2565 
2568 
2569  // reset before any aura state sources (health set/aura apply)
2571 
2573 
2574  // set default cast time multiplier
2576 
2577  // reset size before reapply auras
2578  SetObjectScale(1.0f);
2579 
2580  // save base values (bonuses already included in stored stats
2581  for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
2582  SetCreateStat(Stats(i), info.stats[i]);
2583 
2584  for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
2585  SetStat(Stats(i), info.stats[i]);
2586 
2587  SetCreateHealth(classInfo.basehealth);
2588 
2589  //set create powers
2590  SetCreateMana(classInfo.basemana);
2591 
2593 
2594  InitStatBuffMods();
2595 
2596  //reset rating fields values
2598  SetUInt32Value(index, 0);
2599 
2601  for (uint8 i = 0; i < 7; ++i)
2602  {
2606  }
2607 
2608  //reset attack power, damage and attack speed fields
2610  SetFloatValue(UNIT_FIELD_BASEATTACKTIME + 1, 2000.0f); // offhand attack time
2612 
2619 
2626 
2627  // Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
2631 
2632  // Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
2633  for (uint8 i = 0; i < 7; ++i)
2635 
2639 
2640  // Dodge percentage
2642 
2643  // set armor (resistance 0) to original value (create_agility*2)
2644  SetArmor(int32(m_createStats[STAT_AGILITY] * 2));
2645  SetResistanceBuffMods(SpellSchools(0), true, 0.0f);
2646  SetResistanceBuffMods(SpellSchools(0), false, 0.0f);
2647  // set other resistance to original value (0)
2648  for (uint8 i = 1; i < MAX_SPELL_SCHOOL; ++i)
2649  {
2650  SetResistance(SpellSchools(i), 0);
2651  SetResistanceBuffMods(SpellSchools(i), true, 0.0f);
2652  SetResistanceBuffMods(SpellSchools(i), false, 0.0f);
2653  }
2654 
2657  for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
2658  {
2661  }
2662  // Init data for form but skip reapply item mods for form
2663  InitDataForForm(reapplyMods);
2664 
2665  // save new stats
2666  for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
2668 
2669  SetMaxHealth(classInfo.basehealth); // stamina bonus will applied later
2670 
2671  // cleanup mounted state (it will set correctly at aura loading if player saved at mount.
2673 
2674  // cleanup unit flags (will be re-applied if need at aura load).
2682 
2683  // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example.
2685 
2686  RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes
2687 
2688  // restore if need some important flags
2689  SetUInt32Value(PLAYER_FIELD_BYTES2, 0); // flags empty by default
2690 
2691  if (reapplyMods) // reapply stats values only on .reset stats (level) command
2693 
2694  // set current level health and mana/energy to maximum after applying all mods.
2700  SetPower(POWER_FOCUS, 0);
2702 }
2703 
2705 {
2706  uint16 spellCount = 0;
2707 
2708  WorldPacket data(SMSG_INITIAL_SPELLS, (1+2+4*m_spells.size()+2+m_spellCooldowns.size()*(2+2+2+4+4)));
2709  data << uint8(0);
2710 
2711  size_t countPos = data.wpos();
2712  data << uint16(spellCount); // spell count placeholder
2713 
2714  for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
2715  {
2716  if (itr->second.state == PLAYERSPELL_REMOVED)
2717  continue;
2718 
2719  if (!itr->second.active || itr->second.disabled)
2720  continue;
2721 
2722  data << uint16(itr->first);
2723  data << uint16(0); // it's not slot id
2724 
2725  spellCount +=1;
2726  }
2727 
2728  data.put<uint16>(countPos, spellCount); // write real count value
2729 
2730  uint16 spellCooldowns = m_spellCooldowns.size();
2731  data << uint16(spellCooldowns);
2732  for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); ++itr)
2733  {
2734  SpellEntry const* sEntry = sSpellStore.LookupEntry(itr->first);
2735  if (!sEntry)
2736  continue;
2737 
2738  data << uint16(itr->first);
2739 
2740  time_t cooldown = 0;
2741  time_t curTime = time(NULL);
2742  if (itr->second.end > curTime)
2743  cooldown = (itr->second.end - curTime) * IN_MILLISECONDS;
2744 
2745  data << uint16(itr->second.itemid); // cast item id
2746  data << uint16(sEntry->Category); // spell category
2747  if (sEntry->Category) // may be wrong, but anyway better than nothing...
2748  {
2749  data << uint32(0); // cooldown
2750  data << uint32(cooldown); // category cooldown
2751  }
2752  else
2753  {
2754  data << uint32(cooldown); // cooldown
2755  data << uint32(0); // category cooldown
2756  }
2757  }
2758 
2759  GetSession()->SendPacket(&data);
2760 
2761  sLog.outDetail("CHARACTER: Sent Initial Spells");
2762 }
2763 
2765 {
2766  for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
2767  {
2768  if ((*itr)->messageID == id)
2769  {
2770  //do not delete item, because Player::removeMail() is called when returning mail to sender.
2771  m_mail.erase(itr);
2772  return;
2773  }
2774  }
2775 }
2776 
2777 void Player::SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError, uint32 item_guid, uint32 item_count)
2778 {
2779  WorldPacket data(SMSG_SEND_MAIL_RESULT, (4+4+4+(mailError == MAIL_ERR_EQUIP_ERROR?4:(mailAction == MAIL_ITEM_TAKEN?4+4:0))));
2780  data << (uint32) mailId;
2781  data << (uint32) mailAction;
2782  data << (uint32) mailError;
2783  if (mailError == MAIL_ERR_EQUIP_ERROR)
2784  data << (uint32) equipError;
2785  else if (mailAction == MAIL_ITEM_TAKEN)
2786  {
2787  data << (uint32) item_guid; // item guid low?
2788  data << (uint32) item_count; // item count?
2789  }
2790  GetSession()->SendPacket(&data);
2791 }
2792 
2794 {
2795  // deliver undelivered mail
2797  data << (uint32) 0;
2798  GetSession()->SendPacket(&data);
2799 }
2800 
2802 {
2803  // calculate next delivery time (min. from non-delivered mails
2804  // and recalculate unReadMail
2805  time_t cTime = time(NULL);
2807  unReadMails = 0;
2808  for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
2809  {
2810  if ((*itr)->deliver_time > cTime)
2811  {
2812  if (!m_nextMailDelivereTime || m_nextMailDelivereTime > (*itr)->deliver_time)
2813  m_nextMailDelivereTime = (*itr)->deliver_time;
2814  }
2815  else if (((*itr)->checked & MAIL_CHECK_MASK_READ) == 0)
2816  ++unReadMails;
2817  }
2818 }
2819 
2820 void Player::AddNewMailDeliverTime(time_t deliver_time)
2821 {
2822  if (deliver_time <= time(NULL)) // ready now
2823  {
2824  ++unReadMails;
2825  SendNewMail();
2826  }
2827  else // not ready and no have ready mails
2828  {
2829  if (!m_nextMailDelivereTime || m_nextMailDelivereTime > deliver_time)
2830  m_nextMailDelivereTime = deliver_time;
2831  }
2832 }
2833 
2834 bool Player::AddSpell(uint32 spell_id, bool active, bool learning, bool loading, bool disabled)
2835 {
2836  SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id);
2837  if (!spellInfo)
2838  {
2839  // do character spell book cleanup (all characters)
2840  if (loading && !learning) // spell load case
2841  {
2842  sLog.outError("Player::addSpell: Invalid SpellStore spell #%u request, deleting for all characters in character_spell.", spell_id);
2843  CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id);
2844  }
2845  else
2846  sLog.outError("Player::addSpell: Invalid SpellStore spell #%u request.", spell_id);
2847 
2848  return false;
2849  }
2850 
2851  if (!SpellMgr::IsSpellValid(spellInfo, this, false))
2852  {
2853  // do character spell book cleanup (all characters)
2854  if (loading && !learning) // spell load case
2855  {
2856  sLog.outError("Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in character_spell.", spell_id);
2857  CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id);
2858  }
2859  else
2860  sLog.outError("Player::addSpell: Broken spell #%u learning not allowed.", spell_id);
2861 
2862  return false;
2863  }
2864 
2866 
2867  bool disabled_case = false;
2868  bool superceded_old = false;
2869 
2870  PlayerSpellMap::iterator itr = m_spells.find(spell_id);
2871  if (itr != m_spells.end())
2872  {
2873  // update active state for known spell
2874  if (itr->second.active != active && itr->second.state != PLAYERSPELL_REMOVED && !itr->second.disabled)
2875  {
2876  itr->second.active = active;
2877 
2878  // loading && !learning == explicitly load from DB and then exist in it already and set correctly
2879  if (loading && !learning)
2880  itr->second.state = PLAYERSPELL_UNCHANGED;
2881  else if (itr->second.state != PLAYERSPELL_NEW)
2882  itr->second.state = PLAYERSPELL_CHANGED;
2883 
2884  if (!active)
2885  {
2887  data << uint16(spell_id);
2888  GetSession()->SendPacket(&data);
2889  }
2890  return active; // learn (show in spell book if active now)
2891  }
2892 
2893  if (itr->second.disabled != disabled && itr->second.state != PLAYERSPELL_REMOVED)
2894  {
2895  if (itr->second.state != PLAYERSPELL_NEW)
2896  itr->second.state = PLAYERSPELL_CHANGED;
2897  itr->second.disabled = disabled;
2898 
2899  if (disabled)
2900  return false;
2901 
2902  disabled_case = true;
2903  }
2904  else switch (itr->second.state)
2905  {
2906  case PLAYERSPELL_UNCHANGED: // known saved spell
2907  return false;
2908  case PLAYERSPELL_REMOVED: // re-learning removed not saved spell
2909  {
2910  m_spells.erase(itr);
2911  state = PLAYERSPELL_CHANGED;
2912  break; // need re-add
2913  }
2914  default: // known not saved yet spell (new or modified)
2915  {
2916  // can be in case spell loading but learned at some previous spell loading
2917  if (loading && !learning)
2918  itr->second.state = PLAYERSPELL_UNCHANGED;
2919 
2920  return false;
2921  }
2922  }
2923  }
2924 
2925  if (!disabled_case) // skip new spell adding if spell already known (disabled spells case)
2926  {
2927  // talent: unlearn all other talent ranks (high and low)
2928  if (TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id))
2929  {
2930  if (TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentPos->talent_id))
2931  {
2932  for (int i = 0; i < 5; ++i)
2933  {
2934  // skip learning spell and no rank spell case
2935  uint32 rankSpellId = talentInfo->RankID[i];
2936  if (!rankSpellId || rankSpellId == spell_id)
2937  continue;
2938 
2939  // skip unknown ranks
2940  if (!HasSpell(rankSpellId))
2941  continue;
2942 
2943  RemoveSpell(rankSpellId);
2944  }
2945  }
2946  }
2947  // non talent spell: learn low ranks (recursive call)
2948  else if (uint32 prev_spell = sSpellMgr.GetPrevSpellInChain(spell_id))
2949  {
2950  if (loading) // at spells loading, no output, but allow save
2951  AddSpell(prev_spell, active, true, loading, disabled);
2952  else // at normal learning
2953  LearnSpell(prev_spell);
2954  }
2955 
2956  PlayerSpell newspell;
2957  newspell.active = active;
2958  newspell.state = state;
2959  newspell.disabled = disabled;
2960 
2961  // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
2962  if (newspell.active && !newspell.disabled && !SpellMgr::canStackSpellRanks(spellInfo) && sSpellMgr.GetSpellRank(spellInfo->Id) != 0)
2963  {
2964  for (PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
2965  {
2966  if (itr->second.state == PLAYERSPELL_REMOVED) continue;
2967  SpellEntry const* i_spellInfo = sSpellStore.LookupEntry(itr->first);
2968  if (!i_spellInfo) continue;
2969 
2970  if (sSpellMgr.IsRankSpellDueToSpell(spellInfo, itr->first))
2971  {
2972  if (itr->second.active)
2973  {
2974  if (sSpellMgr.IsHighRankOfSpell(spell_id, itr->first))
2975  {
2976  if (!loading) // not send spell (re-/over-)learn packets at loading
2977  {
2979  data << uint16(itr->first);
2980  data << uint16(spell_id);
2981  GetSession()->SendPacket(&data);
2982  }
2983 
2984  // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
2985  itr->second.active = false;
2986  itr->second.state = PLAYERSPELL_CHANGED;
2987  superceded_old = true; // new spell replace old in action bars and spell book.
2988  }
2989  else // rank of known spell is higher than this one being added
2990  {
2991  if (!loading) // not send spell (re-/over-)learn packets at loading
2992  {
2994  data << uint16(spell_id);
2995  data << uint16(itr->first);
2996  GetSession()->SendPacket(&data);
2997  }
2998 
2999  // mark new spell as disable (not learned yet for client and will not learned)
3000  newspell.active = false;
3001  if (newspell.state != PLAYERSPELL_NEW)
3002  newspell.state = PLAYERSPELL_CHANGED;
3003  }
3004  }
3005  }
3006  }
3007  }
3008 
3009  m_spells[spell_id] = newspell;
3010 
3011  // return false if spell disabled
3012  if (newspell.disabled)
3013  return false;
3014  }
3015 
3016  uint32 talentCost = GetTalentSpellCost(spell_id);
3017 
3018  // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
3019  // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
3020  if (talentCost > 0 && IsSpellHaveEffect(spellInfo, SPELL_EFFECT_LEARN_SPELL))
3021  {
3022  // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
3023  CastSpell(this, spell_id, true);
3024  }
3025  // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
3026  else if (IsPassiveSpell(spell_id))
3027  {
3028  // if spell doesn't require a stance or the player is in the required stance
3029  if ((!spellInfo->Stances &&
3030  spell_id != 5420 && spell_id != 5419 && spell_id != 7376 &&
3031  spell_id != 7381 && spell_id != 21156 && spell_id != 21009 &&
3032  spell_id != 21178 && spell_id != 33948 && spell_id != 40121) ||
3033  (m_form != 0 && (spellInfo->Stances & (1 << (m_form - 1)))) ||
3034  (spell_id == 5420 && m_form == FORM_TREE) ||
3035  (spell_id == 5419 && m_form == FORM_TRAVEL) ||
3036  (spell_id == 7376 && m_form == FORM_DEFENSIVESTANCE) ||
3037  (spell_id == 7381 && m_form == FORM_BERSERKERSTANCE) ||
3038  (spell_id == 21156 && m_form == FORM_BATTLESTANCE) ||
3039  (spell_id == 21178 && (m_form == FORM_BEAR || m_form == FORM_DIREBEAR)) ||
3040  (spell_id == 33948 && m_form == FORM_FLIGHT) ||
3041  (spell_id == 40121 && m_form == FORM_FLIGHT_EPIC))
3042  //Check CasterAuraStates
3043  if (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState)))
3044  CastSpell(this, spell_id, true);
3045  }
3046  else if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_SKILL_STEP))
3047  {
3048  CastSpell(this, spell_id, true);
3049  return false;
3050  }
3051 
3052  // update used talent points count
3053  m_usedTalentCount += talentCost;
3054 
3055  // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
3056  if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
3057  {
3058  if (sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id))
3059  SetFreePrimaryProfessions(freeProfs - 1);
3060  }
3061 
3062  // add dependent skills
3063  uint16 maxskill = GetMaxSkillValueForLevel();
3064 
3065  SpellLearnSkillNode const* spellLearnSkill = sSpellMgr.GetSpellLearnSkill(spell_id);
3066 
3067  if (spellLearnSkill)
3068  {
3069  uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
3070  uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
3071 
3072  if (skill_value < spellLearnSkill->value)
3073  skill_value = spellLearnSkill->value;
3074 
3075  uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue;
3076 
3077  if (skill_max_value < new_skill_max_value)
3078  skill_max_value = new_skill_max_value;
3079 
3080  SetSkill(spellLearnSkill->skill, skill_value, skill_max_value);
3081  }
3082  else
3083  {
3084  // not ranked skills
3085  SkillLineAbilityMap::const_iterator lower = sSpellMgr.GetBeginSkillLineAbilityMap(spell_id);
3086  SkillLineAbilityMap::const_iterator upper = sSpellMgr.GetEndSkillLineAbilityMap(spell_id);
3087 
3088  for (SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
3089  {
3090  SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
3091  if (!pSkill)
3092  continue;
3093 
3094  if (HasSkill(pSkill->id))
3095  continue;
3096 
3097  if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
3098  // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
3099  (pSkill->id == SKILL_POISONS && _spell_idx->second->max_value == 0) ||
3100  // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
3101  (pSkill->id == SKILL_LOCKPICKING && _spell_idx->second->max_value == 0))
3102  {
3103  switch (GetSkillRangeType(pSkill, _spell_idx->second->racemask != 0))
3104  {
3105  case SKILL_RANGE_LANGUAGE:
3106  SetSkill(pSkill->id, 300, 300);
3107  break;
3108  case SKILL_RANGE_LEVEL:
3109  SetSkill(pSkill->id, 1, GetMaxSkillValueForLevel());
3110  break;
3111  case SKILL_RANGE_MONO:
3112  SetSkill(pSkill->id, 1, 1);
3113  break;
3114  default:
3115  break;
3116  }
3117  }
3118  }
3119  }
3120 
3121  // learn dependent spells
3122  SpellLearnSpellMap::const_iterator spell_begin = sSpellMgr.GetBeginSpellLearnSpell(spell_id);
3123  SpellLearnSpellMap::const_iterator spell_end = sSpellMgr.GetEndSpellLearnSpell(spell_id);
3124 
3125  for (SpellLearnSpellMap::const_iterator itr = spell_begin; itr != spell_end; ++itr)
3126  {
3127  if (!itr->second.autoLearned)
3128  {
3129  if (loading) // at spells loading, no output, but allow save
3130  AddSpell(itr->second.spell, true, true, loading);
3131  else // at normal learning
3132  LearnSpell(itr->second.spell);
3133  }
3134  }
3135 
3136  // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell
3137  return active && !disabled && !superceded_old;
3138 }
3139 
3141 {
3142  PlayerSpellMap::iterator itr = m_spells.find(spell_id);
3143 
3144  bool disabled = (itr != m_spells.end()) ? itr->second.disabled : false;
3145  bool active = disabled ? itr->second.active : true;
3146 
3147  bool learning = AddSpell(spell_id, active);
3148 
3149  // prevent duplicated entries in spell book
3150  if (learning && IsInWorld())
3151  {
3153  data << uint32(spell_id);
3154  GetSession()->SendPacket(&data);
3155  }
3156 
3157  // learn all disabled higher ranks (recursive)
3158  if (disabled)
3159  {
3160  if (uint32 nextSpell = sSpellMgr.GetNextSpellInChain(spell_id))
3161  {
3162  PlayerSpellMap::iterator iter = m_spells.find(nextSpell);
3163  if (iter != m_spells.end() && iter->second.disabled)
3164  LearnSpell(nextSpell);
3165  }
3166  }
3167 }
3168 
3169 void Player::RemoveSpell(uint32 spell_id, bool disabled)
3170 {
3171  PlayerSpellMap::iterator itr = m_spells.find(spell_id);
3172  if (itr == m_spells.end())
3173  return;
3174 
3175  if (itr->second.state == PLAYERSPELL_REMOVED || (disabled && itr->second.disabled))
3176  return;
3177 
3178  // remove / guard first so upcoming
3179  // recursive calls cannot corrupt m_spells
3180  if (disabled)
3181  {
3182  itr->second.disabled = disabled;
3183  if (itr->second.state != PLAYERSPELL_NEW)
3184  itr->second.state = PLAYERSPELL_CHANGED;
3185  }
3186  else
3187  {
3188  if (itr->second.state == PLAYERSPELL_NEW)
3189  m_spells.erase(itr);
3190  else
3191  itr->second.state = PLAYERSPELL_REMOVED;
3192  }
3193 
3194  // unlearn non talent higher ranks (recursive)
3195  SpellChainNode const* node = sSpellMgr.GetSpellChainNode(spell_id);
3196  if (node)
3197  {
3198  if (HasSpell(node->next) && !GetTalentSpellPos(node->next))
3199  RemoveSpell(node->next, disabled);
3200  }
3201  //unlearn spells dependent from recently removed spells
3202  SpellsRequiringSpellMap const& reqMap = sSpellMgr.GetSpellsRequiringSpell();
3203  SpellsRequiringSpellMap::const_iterator itr2 = reqMap.find(spell_id);
3204  for (uint32 i = reqMap.count(spell_id); i > 0; i--, ++itr2)
3205  RemoveSpell(itr2->second, disabled);
3206 
3207  // removing
3209  data << uint16(spell_id);
3210  GetSession()->SendPacket(&data);
3211 
3212  RemoveAurasDueToSpell(spell_id);
3213 
3214  // remove pet auras
3215  if (PetAura const* petSpell = sSpellMgr.GetPetAura(spell_id))
3216  RemovePetAura(petSpell);
3217 
3218  // free talent points
3219  uint32 talentCosts = GetTalentSpellCost(spell_id);
3220  if (talentCosts > 0)
3221  {
3222  if (talentCosts < m_usedTalentCount)
3223  m_usedTalentCount -= talentCosts;
3224  else
3225  m_usedTalentCount = 0;
3226  }
3227 
3228  // update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
3229  if (sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id))
3230  {
3231  uint32 freeProfs = GetFreePrimaryProfessionPoints() + 1;
3232  if (freeProfs <= sWorld.getConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL))
3233  SetFreePrimaryProfessions(freeProfs);
3234  }
3235 
3236  // remove dependent skill
3237  SpellLearnSkillNode const* spellLearnSkill = sSpellMgr.GetSpellLearnSkill(spell_id);
3238  if (spellLearnSkill)
3239  {
3240  uint32 prev_spell = sSpellMgr.GetPrevSpellInChain(spell_id);
3241  if (!prev_spell) // first rank, remove skill
3242  SetSkill(spellLearnSkill->skill, 0, 0);
3243  else
3244  {
3245  // search prev. skill setting by spell ranks chain
3246  SpellLearnSkillNode const* prevSkill = sSpellMgr.GetSpellLearnSkill(prev_spell);
3247  while (!prevSkill && prev_spell)
3248  {
3249  prev_spell = sSpellMgr.GetPrevSpellInChain(prev_spell);
3250  prevSkill = sSpellMgr.GetSpellLearnSkill(sSpellMgr.GetFirstSpellInChain(prev_spell));
3251  }
3252 
3253  if (!prevSkill) // not found prev skill setting, remove skill
3254  SetSkill(spellLearnSkill->skill, 0, 0);
3255  else // set to prev. skill setting values
3256  {
3257  uint32 skill_value = GetPureSkillValue(prevSkill->skill);
3258  uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill);
3259 
3260  if (skill_value > prevSkill->value)
3261  skill_value = prevSkill->value;
3262 
3263  uint32 new_skill_max_value = prevSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : prevSkill->maxvalue;
3264 
3265  if (skill_max_value > new_skill_max_value)
3266  skill_max_value = new_skill_max_value;
3267 
3268  SetSkill(prevSkill->skill, skill_value, skill_max_value);
3269  }
3270  }
3271 
3272  }
3273  else
3274  {
3275  // not ranked skills
3276  SkillLineAbilityMap::const_iterator lower = sSpellMgr.GetBeginSkillLineAbilityMap(spell_id);
3277  SkillLineAbilityMap::const_iterator upper = sSpellMgr.GetEndSkillLineAbilityMap(spell_id);
3278 
3279  for (SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
3280  {
3281  SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
3282  if (!pSkill)
3283  continue;
3284 
3285  if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
3286  // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
3287  (pSkill->id == SKILL_POISONS && _spell_idx->second->max_value == 0) ||
3288  // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
3289  (pSkill->id == SKILL_LOCKPICKING && _spell_idx->second->max_value == 0))
3290  {
3291  // not reset skills for professions and racial abilities
3293  (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask != 0))
3294  continue;
3295 
3296  SetSkill(pSkill->id, 0, 0);
3297  }
3298  }
3299  }
3300 
3301  // remove dependent spells
3302  SpellLearnSpellMap::const_iterator spell_begin = sSpellMgr.GetBeginSpellLearnSpell(spell_id);
3303  SpellLearnSpellMap::const_iterator spell_end = sSpellMgr.GetEndSpellLearnSpell(spell_id);
3304 
3305  for (SpellLearnSpellMap::const_iterator itr2 = spell_begin; itr2 != spell_end; ++itr2)
3306  RemoveSpell(itr2->second.spell, disabled);
3307 }
3308 
3309 void Player::RemoveSpellCooldown(uint32 spell_id, bool update /* = false */)
3310 {
3311  m_spellCooldowns.erase(spell_id);
3312 
3313  if (update)
3314  SendClearCooldown(spell_id, this);
3315 }
3316 
3318 {
3319  // remove cooldowns on spells that has < 15 min CD
3320  SpellCooldowns::iterator itr, next;
3321  // iterate spell cooldowns
3322  for (itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); itr = next)
3323  {
3324  next = itr;
3325  ++next;
3326  SpellEntry const* entry = sSpellStore.LookupEntry(itr->first);
3327  // check if spellentry is present and if the cooldown is less than 15 mins
3328  if (entry &&
3329  entry->RecoveryTime <= 15 * MINUTE * IN_MILLISECONDS &&
3330  entry->CategoryRecoveryTime <= 15 * MINUTE * IN_MILLISECONDS)
3331  {
3332  // notify player
3333  WorldPacket data(SMSG_CLEAR_COOLDOWN, (4 + 8));
3334  data << uint32(itr->first);
3335  data << GetGUID();
3336  GetSession()->SendPacket(&data);
3337  // remove cooldown
3338  m_spellCooldowns.erase(itr);
3339  }
3340  }
3341 }
3342 
3344 {
3345  if (!m_spellCooldowns.empty())
3346  {
3347  for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); ++itr)
3348  SendClearCooldown(itr->first, this);
3349 
3350  m_spellCooldowns.clear();
3351  }
3352 }
3353 
3360 {
3361  SpellChainNode const* node = sSpellMgr.GetSpellChainNode(spellid);
3362  if (!node)
3363  return spellid;
3364  uint32 i = node->last;
3365 
3366  while (!HasSpell(i))
3367  {
3368  i = node->prev;
3369  node = sSpellMgr.GetSpellChainNode(i);
3370  if (!node)
3371  break;
3372  }
3373 
3374  return i;
3375 }
3376 
3378 {
3379  m_spellCooldowns.clear();
3380 
3381  //QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'",GetGUIDLow());
3382 
3383  if (result)
3384  {
3385  time_t curTime = time(NULL);
3386 
3387  do
3388  {
3389  Field* fields = result->Fetch();
3390 
3391  uint32 spell_id = fields[0].GetUInt32();
3392  uint32 item_id = fields[1].GetUInt32();
3393  time_t db_time = (time_t)fields[2].GetUInt64();
3394 
3395  if (!sSpellStore.LookupEntry(spell_id))
3396  {
3397  sLog.outError("Player %u has unknown spell %u in character_spell_cooldown, skipping.", GetGUIDLow(), spell_id);
3398  continue;
3399  }
3400 
3401  // skip outdated cooldown
3402  if (db_time <= curTime)
3403  continue;
3404 
3405  AddSpellCooldown(spell_id, item_id, db_time);
3406 
3407  sLog.outDebug("Player (GUID: %u) spell %u, item %u cooldown loaded (%u secs).", GetGUIDLow(), spell_id, item_id, uint32(db_time - curTime));
3408  }
3409  while (result->NextRow());
3410  }
3411 }
3412 
3414 {
3415  CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow());
3416 
3417  time_t curTime = time(NULL);
3418 
3419  // remove outdated and save active
3420  for (SpellCooldowns::iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end();)
3421  {
3422  if (itr->second.end <= curTime)
3423  m_spellCooldowns.erase(itr++);
3424  else
3425  {
3426  CharacterDatabase.PExecute("INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES ('%u', '%u', '%u', '" UI64FMTD "')", GetGUIDLow(), itr->first, itr->second.itemid, uint64(itr->second.end));
3427  ++itr;
3428  }
3429  }
3430 }
3431 
3433 {
3434  // The first time reset costs 1 gold
3435  if (m_resetTalentsCost < 1 * GOLD)
3436  return 1 * GOLD;
3437  // then 5 gold
3438  else if (m_resetTalentsCost < 5 * GOLD)
3439  return 5 * GOLD;
3440  // After that it increases in increments of 5 gold
3441  else if (m_resetTalentsCost < 10 * GOLD)
3442  return 10 * GOLD;
3443  else
3444  {
3445  uint32 months = (sWorld.GetGameTime() - m_resetTalentsTime) / MONTH;
3446  if (months > 0)
3447  {
3448  // This cost will be reduced by a rate of 5 gold per month
3449  int32 new_cost = int32(m_resetTalentsCost) - 5 * GOLD * months;
3450  // to a minimum of 10 gold.
3451  return (new_cost < 10 * GOLD ? 10 * GOLD : new_cost);
3452  }
3453  else
3454  {
3455  // After that it increases in increments of 5 gold
3456  int32 new_cost = m_resetTalentsCost + 5 * GOLD;
3457  // until it hits a cap of 50 gold.
3458  if (new_cost > 50 * GOLD)
3459  new_cost = 50 * GOLD;
3460  return new_cost;
3461  }
3462  }
3463 }
3464 
3465 bool Player::ResetTalents(bool no_cost)
3466 {
3467  sScriptMgr.OnPlayerTalentsReset(this, no_cost);
3468 
3469  // not need after this call
3471  {
3473  CharacterDatabase.PExecute("UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'", uint32(AT_LOGIN_RESET_TALENTS), GetGUIDLow());
3474  }
3475 
3476  uint32 level = getLevel();
3477  uint32 talentPointsForLevel = level < 10 ? 0 : uint32((level - 9) * sWorld.getRate(RATE_TALENT));
3478 
3479  if (m_usedTalentCount == 0)
3480  {
3481  SetFreeTalentPoints(talentPointsForLevel);
3482  return false;
3483  }
3484 
3485  uint32 cost = 0;
3486 
3487  if (!no_cost && !sWorld.getConfig(CONFIG_NO_RESET_TALENT_COST))
3488  {
3489  cost = ResetTalentsCost();
3490 
3491  if (GetMoney() < cost)
3492  {
3494  return false;
3495  }
3496  }
3497 
3498  RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
3499 
3500  for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
3501  {
3502  TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
3503 
3504  if (!talentInfo)
3505  continue;
3506 
3507  TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
3508 
3509  if (!talentTabInfo)
3510  continue;
3511 
3512  // unlearn only talents for character class
3513  // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
3514  // to prevent unexpected lost normal learned spell skip another class talents
3515  if ((getClassMask() & talentTabInfo->ClassMask) == 0)
3516  continue;
3517 
3518  for (int j = 0; j < 5; ++j)
3519  {
3520  for (PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();)
3521  {
3522  if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.disabled)
3523  {
3524  ++itr;
3525  continue;
3526  }
3527 
3528  // remove learned spells (all ranks)
3529  uint32 itrFirstId = sSpellMgr.GetFirstSpellInChain(itr->first);
3530 
3531  // unlearn if first rank is talent or learned by talent
3532  if (itrFirstId == talentInfo->RankID[j] || sSpellMgr.IsSpellLearnToSpell(talentInfo->RankID[j], itrFirstId))
3533  {
3534  RemoveSpell(itr->first, !IsPassiveSpell(itr->first));
3535  itr = GetSpellMap().begin();
3536  continue;
3537  }
3538  else
3539  ++itr;
3540  }
3541  }
3542  }
3543 
3544  SetFreeTalentPoints(talentPointsForLevel);
3545 
3546  if (!no_cost)
3547  {
3548  ModifyMoney(-(int32)cost);
3549 
3550  m_resetTalentsCost = cost;
3551  m_resetTalentsTime = time(NULL);
3552  }
3553 
3554  return true;
3555 }
3556 
3558 {
3559  for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
3560  if ((*itr)->messageID == id)
3561  return (*itr);
3562 
3563  return NULL;
3564 }
3565 
3566 void Player::_SetCreateBits(UpdateMask* updateMask, Player* target) const
3567 {
3568  if (target == this)
3569  Object::_SetCreateBits(updateMask, target);
3570  else
3571  {
3572  for (uint16 index = 0; index < m_valuesCount; index++)
3573  {
3574  if (GetUInt32Value(index) != 0 && updateVisualBits.GetBit(index))
3575  updateMask->SetBit(index);
3576  }
3577  }
3578 }
3579 
3580 void Player::_SetUpdateBits(UpdateMask* updateMask, Player* target) const
3581 {
3582  if (target == this)
3583  Object::_SetUpdateBits(updateMask, target);
3584  else
3585  {
3586  Object::_SetUpdateBits(updateMask, target);
3587  *updateMask &= updateVisualBits;
3588  }
3589 }
3590 
3592 {
3594 
3595  // @todo really implement OWNER_ONLY and GROUP_ONLY. Flags can be found in UpdateFields.h
3596 
3600 
3603 
3606 
3609 
3612 
3615 
3622 
3629 
3635  for (uint16 i = UNIT_FIELD_AURA; i < UNIT_FIELD_AURASTATE; ++i)
3637  updateVisualBits.SetBit(UNIT_FIELD_AURASTATE);
3652 
3663 
3664  // PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)...
3665  for (uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i += 4)
3667 
3668  //Players visible items are not inventory stuff
3669  //431) = 884 (0x374) = main weapon
3670  for (uint16 i = 0; i < EQUIPMENT_SLOT_END; i++)
3671  {
3672  // item creator
3674  updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i * MAX_VISIBLE_ITEM_OFFSET) + 1);
3675 
3677 
3678  // item entry
3679  updateVisualBits.SetBit(visual_base + 0);
3680 
3681  // item enchantment IDs
3682  for (uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j)
3683  updateVisualBits.SetBit(visual_base + 1 + j);
3684 
3685  // random properties
3686  updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 0 + (i * MAX_VISIBLE_ITEM_OFFSET));
3687  updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (i * MAX_VISIBLE_ITEM_OFFSET));
3688  }
3689 
3691 }
3692 
3694 {
3695  if (target == this)
3696  {
3697  for (int i = 0; i < EQUIPMENT_SLOT_END; i++)
3698  {
3699  if (m_items[i] == NULL)
3700  continue;
3701 
3702  m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
3703  }
3704 
3705  for (int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
3706  {
3707  if (m_items[i] == NULL)
3708  continue;
3709 
3710  m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
3711  }
3712  for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
3713  {
3714  if (m_items[i] == NULL)
3715  continue;
3716 
3717  m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
3718  }
3719  }
3720 
3722 }
3723 
3724 void Player::DestroyForPlayer(Player* target, bool /*onDeath*/) const
3725 {
3726  Unit::DestroyForPlayer(target);
3727 
3728  for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
3729  {
3730  if (m_items[i] == NULL)
3731  continue;
3732 
3733  m_items[i]->DestroyForPlayer(target);
3734  }
3735 
3736  if (target == this)
3737  {
3739  {
3740  if (m_items[i] == NULL)
3741  continue;
3742 
3743  m_items[i]->DestroyForPlayer(target);
3744  }
3745  for (uint8 i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
3746  {
3747  if (m_items[i] == NULL)
3748  continue;
3749 
3750  m_items[i]->DestroyForPlayer(target);
3751  }
3752  }
3753 }
3754 
3755 bool Player::HasSpell(uint32 spell) const
3756 {
3757  PlayerSpellMap::const_iterator itr = m_spells.find(spell);
3758  return (itr != m_spells.end() && itr->second.state != PLAYERSPELL_REMOVED &&
3759  !itr->second.disabled);
3760 }
3761 
3763 {
3764  PlayerSpellMap::const_iterator itr = m_spells.find(spell);
3765  return (itr != m_spells.end() && itr->second.state != PLAYERSPELL_REMOVED &&
3766  itr->second.active && !itr->second.disabled);
3767 }
3768 
3770 {
3771  if (!trainer_spell)
3772  return TRAINER_SPELL_RED;
3773 
3774  if (!trainer_spell->spell)
3775  return TRAINER_SPELL_RED;
3776 
3777  // known spell
3778  if (HasSpell(trainer_spell->spell))
3779  return TRAINER_SPELL_GRAY;
3780 
3781  // check race/class requirement
3782  if (!IsSpellFitByClassAndRace(trainer_spell->spell))
3783  return TRAINER_SPELL_RED;
3784 
3785  // check level requirement
3786  if (getLevel() < trainer_spell->reqlevel)
3787  return TRAINER_SPELL_RED;
3788 
3789  if (SpellChainNode const* spell_chain = sSpellMgr.GetSpellChainNode(trainer_spell->spell))
3790  {
3791  // check prev.rank requirement
3792  if (spell_chain->prev && !HasSpell(spell_chain->prev))
3793  return TRAINER_SPELL_RED;
3794  }
3795 
3796  if (uint32 spell_req = sSpellMgr.GetSpellRequired(trainer_spell->spell))
3797  {
3798  // check additional spell requirement
3799  if (!HasSpell(spell_req))
3800  return TRAINER_SPELL_RED;
3801  }
3802 
3803  // check skill requirement
3804  if (trainer_spell->reqskill && GetBaseSkillValue(trainer_spell->reqskill) < trainer_spell->reqskillvalue)
3805  return TRAINER_SPELL_RED;
3806 
3807  // exist, already checked at loading
3808  SpellEntry const* spell = sSpellStore.LookupEntry(trainer_spell->spell);
3809 
3810  // secondary prof. or not prof. spell
3811  uint32 skill = spell->EffectMiscValue[1];
3812 
3813  if (spell->Effect[1] != SPELL_EFFECT_SKILL || !IsPrimaryProfessionSkill(skill))
3814  return TRAINER_SPELL_GREEN;
3815 
3816  // check primary prof. limit
3817  if (sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell->Id) && GetFreePrimaryProfessionPoints() == 0)
3818  return TRAINER_SPELL_RED;
3819 
3820  return TRAINER_SPELL_GREEN;
3821 }
3822 
3835 void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars, bool deleteFinally)
3836 {
3837  // Avoid realm-update for non-existing account
3838  if (accountId == 0)
3839  updateRealmChars = false;
3840 
3841  uint32 charDelete_method = sWorld.getConfig(CONFIG_CHARDELETE_METHOD);
3842  uint32 charDelete_minLvl = sWorld.getConfig(CONFIG_CHARDELETE_MIN_LEVEL);
3843 
3844  // if we want to finally delete the character or the character does not meet the level requirement
3845  // we set it to mode CHAR_DELETE_REMOVE
3846  if (deleteFinally || Player::GetLevelFromDB(playerguid) < charDelete_minLvl)
3847  charDelete_method = CHAR_DELETE_REMOVE;
3848 
3849  uint32 guid = GUID_LOPART(playerguid);
3850 
3851  // convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry)
3852  // bones will be deleted by corpse/bones deleting thread shortly
3854 
3855  // remove from guild
3856  if (uint32 guildId = GetGuildIdFromDB(playerguid))
3857  if (Guild* guild = sObjectMgr.GetGuildById(guildId))
3858  guild->DelMember(guid, false);
3859 
3860  // remove from arena teams
3861  uint32 at_id = GetArenaTeamIdFromDB(playerguid, ARENA_TEAM_2v2);
3862  if (at_id != 0)
3863  {
3864  ArenaTeam* at = sObjectMgr.GetArenaTeamById(at_id);
3865  if (at)
3866  at->DelMember(playerguid);
3867  }
3868  at_id = GetArenaTeamIdFromDB(playerguid, ARENA_TEAM_3v3);
3869  if (at_id != 0)
3870  {
3871  ArenaTeam* at = sObjectMgr.GetArenaTeamById(at_id);
3872  if (at)
3873  at->DelMember(playerguid);
3874  }
3875  at_id = GetArenaTeamIdFromDB(playerguid, ARENA_TEAM_5v5);
3876  if (at_id != 0)
3877  {
3878  ArenaTeam* at = sObjectMgr.GetArenaTeamById(at_id);
3879  if (at)
3880  at->DelMember(playerguid);
3881  }
3882 
3883  // the player was uninvited already on logout so just remove from group
3884  QueryResult_AutoPtr resultGroup = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", guid);
3885  if (resultGroup)
3886  {
3887  uint64 leaderGuid = MAKE_NEW_GUID((*resultGroup)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
3888  Group* group = sObjectMgr.GetGroupByLeader(leaderGuid);
3889  if (group)
3890  RemoveFromGroup(group, playerguid);
3891  }
3892 
3893  // remove signs from petitions (also remove petitions if owner);
3894  RemovePetitionsAndSigns(playerguid, 10);
3895 
3896  switch (charDelete_method)
3897  {
3898  // completely remove from the database
3899  case CHAR_DELETE_REMOVE:
3900  {
3901  // return back all mails with COD and Item 0 1 2 3 4 5 6 7
3902  QueryResult_AutoPtr resultMail = CharacterDatabase.PQuery("SELECT id,messageType,mailTemplateId,sender,subject,itemTextId,money,has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0", guid);
3903  if (resultMail)
3904  {
3905  do
3906  {
3907  Field* fields = resultMail->Fetch();
3908 
3909  uint32 mail_id = fields[0].GetUInt32();
3910  uint16 mailType = fields[1].GetUInt16();
3911  uint16 mailTemplateId = fields[2].GetUInt16();
3912  uint32 sender = fields[3].GetUInt32();
3913  std::string subject = fields[4].GetCppString();
3914  uint32 itemTextId = fields[5].GetUInt32();
3915  uint32 money = fields[6].GetUInt32();
3916  bool has_items = fields[7].GetBool();
3917 
3918  // we can return mail now
3919  // so firstly delete the old one
3920  CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id);
3921 
3922  // mail not from player
3923  if (mailType != MAIL_NORMAL)
3924  {
3925  if (has_items)
3926  CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
3927  continue;
3928  }
3929 
3930  MailDraft draft(subject, itemTextId);
3931  if (mailTemplateId)
3932  draft = MailDraft(mailTemplateId, false); // items already included
3933 
3934  if (has_items)
3935  {
3936  // data needs to be at first place for Item::LoadFromDB
3937  QueryResult_AutoPtr resultItems = CharacterDatabase.PQuery("SELECT itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, itemTextId, item_guid, item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE mail_id='%u'", mail_id);
3938  if (resultItems)
3939  {
3940  do
3941  {
3942  Field* fields2 = resultItems->Fetch();
3943 
3944  uint32 item_guidlow = fields2[11].GetUInt32();
3945  uint32 item_template = fields2[12].GetUInt32();
3946 
3947  ItemTemplate const* itemProto = ObjectMgr::GetItemTemplate(item_template);
3948  if (!itemProto)
3949  {
3950  CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guidlow);
3951  continue;
3952  }
3953 
3954  Item* pItem = NewItemOrBag(itemProto);
3955  if (!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER), fields2))
3956  {
3957  pItem->FSetState(ITEM_REMOVED);
3958  pItem->SaveToDB(); // it also deletes item object !
3959  continue;
3960  }
3961 
3962  draft.AddItem(pItem);
3963  }
3964  while (resultItems->NextRow());
3965  }
3966  }
3967 
3968  CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
3969 
3970  uint32 pl_account = sObjectMgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
3971 
3972  draft.AddMoney(money).SendReturnToSender(pl_account, guid, sender);
3973  }
3974  while (resultMail->NextRow());
3975  }
3976 
3977  // NOW we can finally clear other DB data related to character
3979  if (QueryResult_AutoPtr resultPets = CharacterDatabase.PQuery("SELECT id FROM character_pet WHERE owner = '%u'", guid))
3980  {
3981  do
3982  {
3983  uint32 petguidlow = (*resultPets)[0].GetUInt32();
3984  Pet::DeleteFromDB(petguidlow);
3985  }
3986  while (resultPets->NextRow());
3987  }
3988 
3989  // Delete character from social list of online chars
3990  if (QueryResult_AutoPtr resultFriends = CharacterDatabase.PQuery("SELECT DISTINCT guid FROM character_social WHERE friend = '%u'", guid))
3991  {
3992  do
3993  {
3994  if (Player* pFriend = ObjectAccessor::FindPlayer(MAKE_NEW_GUID((*resultFriends)[0].GetUInt32(), 0, HIGHGUID_PLAYER)))
3995  {
3996  if (pFriend->IsInWorld())
3997  {
3998  pFriend->GetSocial()->RemoveFromSocialList(guid, false);
3999  sSocialMgr.SendFriendStatus(pFriend, FRIEND_REMOVED, guid, false);
4000  }
4001  }
4002  }
4003  while (resultFriends->NextRow());
4004  }
4005 
4006  CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'", guid);
4007  CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", guid);
4008  CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u'", guid);
4009  CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'", guid);
4010  CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE guid = '%u'", guid);
4011  CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'", guid);
4012  CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u'", guid);
4013  CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u'", guid);
4014  CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE guid = '%u'", guid);
4015  CharacterDatabase.PExecute("DELETE FROM character_queststatus WHERE guid = '%u'", guid);
4016  CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'", guid);
4017  CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u'", guid);
4018  CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u'", guid);
4019  CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u'", guid);
4020  CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", guid);
4021  CharacterDatabase.PExecute("DELETE FROM gm_tickets WHERE playerGuid = '%u'", guid);
4022  CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'", guid);
4023  CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'", guid, guid);
4024  CharacterDatabase.PExecute("DELETE FROM mail WHERE receiver = '%u'", guid);
4025  CharacterDatabase.PExecute("DELETE FROM mail_items WHERE receiver = '%u'", guid);
4026  CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u'", guid);
4027  CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'", guid);
4028  CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE PlayerGuid1 = '%u' OR PlayerGuid2 = '%u'", guid, guid);
4029  CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE PlayerGuid = '%u'", guid);
4030 
4032  break;
4033  }
4034  // The character gets unlinked from the account, the name gets freed up and appears as deleted ingame
4035  case CHAR_DELETE_UNLINK:
4036  CharacterDatabase.PExecute("UPDATE characters SET deleteInfos_Name=name, deleteInfos_Account=account, deleteDate='" UI64FMTD "', name='', account=0 WHERE guid=%u", uint64(time(NULL)), guid);
4037  break;
4038  default:
4039  sLog.outError("Player::DeleteFromDB: Unsupported delete method: %u.", charDelete_method);
4040  }
4041 
4042  if (updateRealmChars)
4043  sWorld.UpdateRealmCharCount(accountId);
4044 }
4045 
4052 {
4053  uint32 keepDays = sWorld.getConfig(CONFIG_CHARDELETE_KEEP_DAYS);
4054  if (!keepDays)
4055  return;
4056 
4057  Player::DeleteOldCharacters(keepDays);
4058 }
4059 
4068 {
4069  sLog.outString("Player::DeleteOldChars: Removing characters older than %u day(s)", keepDays);
4070 
4071  QueryResult_AutoPtr resultChars = CharacterDatabase.PQuery("SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < " UI64FMTD "", uint64(time(NULL) - time_t(keepDays * DAY)));
4072  if (resultChars)
4073  {
4074  sLog.outString("Player::DeleteOldChars: " UI64FMTD " character(s) to remove", resultChars->GetRowCount());
4075  do
4076  {
4077  Field* charFields = resultChars->Fetch();
4078  Player::DeleteFromDB(charFields[0].GetUInt64(), charFields[1].GetUInt32(), true, true);
4079  }
4080  while (resultChars->NextRow());
4081  }
4082 
4083 }
4084 
4085 /* Preconditions:
4086  - a resurrectable corpse must not be loaded for the player (only bones)
4087  - the player must be in world
4088 */
4090 {
4091  if (getRace() == RACE_NIGHTELF)
4092  CastSpell(this, 20584, true); // auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form)
4093  CastSpell(this, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
4094 
4095  // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK
4096  // there must be SMSG.STOP_MIRROR_TIMER
4097  // there we must send 888 opcode
4098 
4099  // the player cannot have a corpse already, only bones which are not returned by GetCorpse
4100  if (GetCorpse())
4101  {
4102  sLog.outError("BuildPlayerRepop: player %s(%d) already has a corpse", GetName(), GetGUIDLow());
4103  return;
4104  }
4105 
4106  // create a corpse and place it at the player's location
4107  CreateCorpse();
4108  Corpse* corpse = GetCorpse();
4109  if (!corpse)
4110  {
4111  sLog.outError("Error creating corpse for Player %s [%u]", GetName(), GetGUIDLow());
4112  return;
4113  }
4114  GetMap()->AddToMap(corpse);
4115 
4116  // convert player body to ghost
4117  SetHealth(1);
4118 
4119  SetWaterWalking(true);
4120  if (!GetSession()->isLogingOut())
4121  SetRooted(false);
4122 
4123  // BG - remove insignia related
4125 
4126  int32 corpseReclaimDelay = CalculateCorpseReclaimDelay();
4127 
4128  if (corpseReclaimDelay >= 0)
4129  SendCorpseReclaimDelay(corpseReclaimDelay);
4130 
4131  // to prevent cheating
4132  corpse->ResetGhostTime();
4133 
4134  StopMirrorTimers(); //disable timers(bars)
4135 
4136  SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (float)1.0); //see radius of death player?
4137 
4138  // set and clear other
4140 }
4141 
4142 void Player::SendDelayResponse(const uint32 /*ml_seconds*/)
4143 {
4144  //FIXME: is this delay time arg really need? 50msec by default in code
4146  data << (uint32)time(NULL);
4147  data << (uint32)0;
4148  GetSession()->SendPacket(&data);
4149 }
4150 
4151 void Player::ResurrectPlayer(float restore_percent, bool applySickness)
4152 {
4153  WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // remove spirit healer position
4154  data << uint32(-1);
4155  data << float(0);
4156  data << float(0);
4157  data << float(0);
4158  GetSession()->SendPacket(&data);
4159 
4160  // speed change, land walk
4161 
4162  // remove death flag + set aura
4163  SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00);
4164 
4165  // This must be called always even on Players with race != RACE_NIGHTELF in case of faction change
4166  RemoveAurasDueToSpell(20584); // RACE_NIGHTELF speed bonuses
4167  RemoveAurasDueToSpell(8326); // SPELL_AURA_GHOST
4168 
4170 
4171  SetWaterWalking(false);
4172  SetRooted(false);
4173 
4174  m_deathTimer = 0;
4175 
4176  // set health/powers (0- will be set in caller)
4177  if (restore_percent > 0.0f)
4178  {
4179  SetHealth(uint32(GetMaxHealth()*restore_percent));
4180  SetPower(POWER_MANA, uint32(GetMaxPower(POWER_MANA)*restore_percent));
4181  SetPower(POWER_RAGE, 0);
4182  SetPower(POWER_ENERGY, uint32(GetMaxPower(POWER_ENERGY)*restore_percent));
4183  }
4184 
4185  // update visibility
4187 
4188  // some items limited to specific map
4190 
4191  if (!applySickness)
4192  return;
4193 
4194  //Characters from level 1-10 are not affected by resurrection sickness.
4195  //Characters from level 11-19 will suffer from one minute of sickness
4196  //for each level they are above 10.
4197  //Characters level 20 and up suffer from ten minutes of sickness.
4198  int32 startLevel = sWorld.getConfig(CONFIG_DEATH_SICKNESS_LEVEL);
4199 
4200  if (int32(getLevel()) >= startLevel)
4201  {
4202  // set resurrection sickness
4204 
4205  // not full duration
4206  if (int32(getLevel()) < startLevel + 9)
4207  {
4208  int32 delta = (int32(getLevel()) - startLevel + 1) * MINUTE;
4209 
4210  for (int i = 0; i < MAX_SPELL_EFFECTS; ++i)
4211  {
4213  {
4214  Aur->SetAuraDuration(delta * IN_MILLISECONDS);
4215  Aur->UpdateAuraDuration();
4216  }
4217  }
4218  }
4219  }
4220 }
4221 
4223 {
4224  if (IsFlying() && !GetTransport())
4226 
4227  SetRooted(true);
4228 
4229  StopMirrorTimers(); //disable timers(bars)
4230 
4232  //SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_IN_PVP);
4233 
4236 
4237  // 6 minutes until repop at graveyard
4239 
4240  UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill
4241  int32 corpseReclaimDelay = CalculateCorpseReclaimDelay();
4242 
4243  if (corpseReclaimDelay >= 0)
4244  SendCorpseReclaimDelay(corpseReclaimDelay);
4245 
4246  // don't create corpse at this moment, player might be falling
4247 
4248  // update visibility
4250 }
4251 
4253 {
4254  // prevent existence 2 corpse for player
4255  SpawnCorpseBones();
4256 
4257  uint32 _uf, _pb, _pb2, _cfb1, _cfb2;
4258 
4260  SetPvPDeath(false);
4261 
4262  if (!corpse->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_CORPSE), this, GetMapId(), GetPositionX(),
4264  {
4265  delete corpse;
4266  return;
4267  }
4268 
4272 
4273  uint8 race = (uint8)(_uf);
4274  uint8 skin = (uint8)(_pb);
4275  uint8 face = (uint8)(_pb >> 8);
4276  uint8 hairstyle = (uint8)(_pb >> 16);
4277  uint8 haircolor = (uint8)(_pb >> 24);
4278  uint8 facialhair = (uint8)(_pb2);
4279 
4280  _cfb1 = ((0x00) | (race << 8) | (getGender() << 16) | (skin << 24));
4281  _cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));
4282 
4283  corpse->SetUInt32Value(CORPSE_FIELD_BYTES_1, _cfb1);
4284  corpse->SetUInt32Value(CORPSE_FIELD_BYTES_2, _cfb2);
4285 
4286  uint32 flags = CORPSE_FLAG_UNK2;
4288  flags |= CORPSE_FLAG_HIDE_HELM;
4290  flags |= CORPSE_FLAG_HIDE_CLOAK;
4291  if (InBattleground() && !InArena())
4292  flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia
4293  corpse->SetUInt32Value(CORPSE_FIELD_FLAGS, flags);
4294 
4296 
4298 
4299  uint32 iDisplayID;
4300  uint32 iIventoryType;
4301  uint32 _cfi;
4302  for (uint8 i = 0; i < EQUIPMENT_SLOT_END; i++)
4303  {
4304  if (m_items[i])
4305  {
4306  iDisplayID = m_items[i]->GetProto()->DisplayInfoID;
4307  iIventoryType = m_items[i]->GetProto()->InventoryType;
4308 
4309  _cfi = iDisplayID | (iIventoryType << 24);
4310  corpse->SetUInt32Value(CORPSE_FIELD_ITEM + i, _cfi);
4311  }
4312  }
4313 
4314  // we do not need to save corpses for BG/arenas
4315  if (!GetMap()->IsBattlegroundOrArena())
4316  corpse->SaveToDB();
4317 
4318  // register for player, but not show
4320 }
4321 
4323 {
4324  if (ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID()) && !GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player
4325  SaveToDB(); // prevent loading as ghost without corpse
4326 }
4327 
4329 {
4331 }
4332 
4333 void Player::DurabilityLossAll(double percent, bool inventory)
4334 {
4335  for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
4336  if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
4337  DurabilityLoss(pItem, percent);
4338 
4339  if (inventory)
4340  {
4341  // bags not have durability
4342  // for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
4343 
4345  if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
4346  DurabilityLoss(pItem, percent);
4347 
4348  // keys not have durability
4349  //for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
4350 
4352  if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
4353  for (uint32 j = 0; j < pBag->GetBagSize(); j++)
4354  if (Item* pItem = GetItemByPos(i, j))
4355  DurabilityLoss(pItem, percent);
4356  }
4357 }
4358 
4359 void Player::DurabilityLoss(Item* item, double percent)
4360 {
4361  if (!item)
4362  return;
4363 
4364  uint32 pMaxDurability = item ->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
4365 
4366  if (!pMaxDurability)
4367  return;
4368 
4369  uint32 pDurabilityLoss = uint32(pMaxDurability * percent);
4370 
4371  if (pDurabilityLoss < 1)
4372  pDurabilityLoss = 1;
4373 
4374  DurabilityPointsLoss(item, pDurabilityLoss);
4375 }
4376 
4377 void Player::DurabilityPointsLossAll(int32 points, bool inventory)
4378 {
4379  for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
4380  if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
4381  DurabilityPointsLoss(pItem, points);
4382 
4383  if (inventory)
4384  {
4385  // bags not have durability
4386  // for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
4387 
4389  if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
4390  DurabilityPointsLoss(pItem, points);
4391 
4392  // keys not have durability
4393  //for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
4394 
4396  if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
4397  for (uint32 j = 0; j < pBag->GetBagSize(); j++)
4398  if (Item* pItem = GetItemByPos(i, j))
4399  DurabilityPointsLoss(pItem, points);
4400  }
4401 }
4402 
4404 {
4405  int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
4406  int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
4407  int32 pNewDurability = pOldDurability - points;
4408 
4409  if (pNewDurability < 0)
4410  pNewDurability = 0;
4411  else if (pNewDurability > pMaxDurability)
4412  pNewDurability = pMaxDurability;
4413 
4414  if (pOldDurability != pNewDurability)
4415  {
4416  // modify item stats _before_ Durability set to 0 to pass _ApplyItemMods internal check
4417  if (pNewDurability == 0 && pOldDurability > 0 && item->IsEquipped())
4418  _ApplyItemMods(item, item->GetSlot(), false);
4419 
4420  item->SetUInt32Value(ITEM_FIELD_DURABILITY, pNewDurability);
4421 
4422  // modify item stats _after_ restore durability to pass _ApplyItemMods internal check
4423  if (pNewDurability > 0 && pOldDurability == 0 && item->IsEquipped())
4424  _ApplyItemMods(item, item->GetSlot(), true);
4425 
4426  item->SetState(ITEM_CHANGED, this);
4427  }
4428 }
4429 
4431 {
4432  if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
4433  DurabilityPointsLoss(pItem, 1);
4434 }
4435 
4436 uint32 Player::DurabilityRepairAll(bool cost, float discountMod, bool guildBank)
4437 {
4438  uint32 TotalCost = 0;
4439  // equipped, backpack, bags itself
4441  TotalCost += DurabilityRepair(((INVENTORY_SLOT_BAG_0 << 8) | i), cost, discountMod, guildBank);
4442 
4443  // bank, buyback and keys not repaired
4444 
4445  // items in inventory bags
4447  for (uint8 i = 0; i < MAX_BAG_SIZE; i++)
4448  TotalCost += DurabilityRepair(((j << 8) | i), cost, discountMod, guildBank);
4449  return TotalCost;
4450 }
4451 
4452 uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank)
4453 {
4454  Item* item = GetItemByPos(pos);
4455 
4456  uint32 TotalCost = 0;
4457  if (!item)
4458  return TotalCost;
4459 
4460  uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
4461  if (!maxDurability)
4462  return TotalCost;
4463 
4464  uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
4465 
4466  if (cost)
4467  {
4468  uint32 LostDurability = maxDurability - curDurability;
4469  if (LostDurability > 0)
4470  {
4471  ItemTemplate const* ditemProto = item->GetProto();
4472 
4473  DurabilityCostsEntry const* dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
4474  if (!dcost)
4475  {
4476  sLog.outError("RepairDurability: Wrong item lvl %u", ditemProto->ItemLevel);
4477  return TotalCost;
4478  }
4479 
4480  uint32 dQualitymodEntryId = (ditemProto->Quality + 1) * 2;
4481  DurabilityQualityEntry const* dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId);
4482  if (!dQualitymodEntry)
4483  {
4484  sLog.outError("RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntryId);
4485  return TotalCost;
4486  }
4487 
4488  uint32 dmultiplier = dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class, ditemProto->SubClass)];
4489  uint32 costs = uint32(LostDurability * dmultiplier * double(dQualitymodEntry->quality_mod));
4490 
4491  costs = uint32(costs * discountMod);
4492 
4493  if (costs == 0) //fix for ITEM_QUALITY_ARTIFACT
4494  costs = 1;
4495 
4496  if (guildBank)
4497  {
4498  if (GetGuildId() == 0)
4499  {
4500  DEBUG_LOG("You are not member of a guild");
4501  return TotalCost;
4502  }
4503 
4504  Guild* pGuild = sObjectMgr.GetGuildById(GetGuildId());
4505  if (!pGuild)
4506  return TotalCost;
4507 
4509  {
4510  DEBUG_LOG("You do not have rights to withdraw for repairs");
4511  return TotalCost;
4512  }
4513 
4514  if (pGuild->GetMemberMoneyWithdrawRem(GetGUIDLow()) < costs)
4515  {
4516  DEBUG_LOG("You do not have enough money withdraw amount remaining");
4517  return TotalCost;
4518  }
4519 
4520  if (pGuild->GetGuildBankMoney() < costs)
4521  {
4522  DEBUG_LOG("There is not enough money in bank");
4523  return TotalCost;
4524  }
4525 
4526  pGuild->MemberMoneyWithdraw(costs, GetGUIDLow());
4527  TotalCost = costs;
4528  }
4529  else if (GetMoney() < costs)
4530  {
4531  DEBUG_LOG("You do not have enough money");
4532  return TotalCost;
4533  }
4534  else
4535  ModifyMoney(-int32(costs));
4536  }
4537  }
4538 
4539  item->SetUInt32Value(ITEM_FIELD_DURABILITY, maxDurability);
4540  item->SetState(ITEM_CHANGED, this);
4541 
4542  // reapply mods for total broken and repaired item if equipped
4543  if (IsEquipmentPos(pos) && !curDurability)
4544  _ApplyItemMods(item, pos & 255, true);
4545  return TotalCost;
4546 }
4547 
4549 {
4550  // note: this can be called also when the player is alive
4551  // for example from WorldSession::HandleMovementOpcodes
4552 
4554 
4555  // Such zones are considered unreachable as a ghost and the player must be automatically revived
4556  if ((!IsAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY) || GetTransport() || GetPositionZ() < -500.0f)
4557  {
4558  ResurrectPlayer(0.5f);
4559  SpawnCorpseBones();
4560  }
4561 
4562  WorldSafeLocsEntry const* ClosestGrave = NULL;
4563 
4564  // Special handle for battleground maps
4565  if (Battleground* bg = GetBattleground())
4566  ClosestGrave = bg->GetClosestGraveYard(this);
4567  else
4568  ClosestGrave = sObjectMgr.GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam());
4569 
4570  // stop countdown until repop
4571  m_deathTimer = 0;
4572 
4573  // if no grave found, stay at the current location
4574  // and don't show spirit healer location
4575  if (ClosestGrave)
4576  {
4577  TeleportTo(ClosestGrave->map_id, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, GetOrientation());
4578  if (isDead()) // not send if alive, because it used in TeleportTo()
4579  {
4580  WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // show spirit healer position on minimap
4581  data << ClosestGrave->map_id;
4582  data << ClosestGrave->x;
4583  data << ClosestGrave->y;
4584  data << ClosestGrave->z;
4585  SendDirectMessage(&data);
4586  }
4587  }
4588  else if (GetPositionZ() < -500.0f)
4590 }
4591 
4593 {
4594  m_channels.push_back(c);
4595 }
4596 
4598 {
4599  m_channels.remove(c);
4600 }
4601 
4603 {
4604  while (!m_channels.empty())
4605  {
4606  Channel* ch = *m_channels.begin();
4607  m_channels.erase(m_channels.begin()); // remove from player's channel list
4608  ch->Leave(GetGUID(), false); // not send to client, not remove from player's channel list
4609  if (ChannelMgr* cMgr = channelMgr(GetTeam()))
4610  cMgr->LeftChannel(ch->GetName()); // deleted channel if empty
4611 
4612  }
4613  DEBUG_LOG("Player %s: channels cleaned up!", GetName());
4614 }
4615 
4617 {
4618  if (m_channels.empty())
4619  return;
4620 
4621  AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone);
4622  if (!current_zone)
4623  return;
4624 
4625  ChannelMgr* cMgr = channelMgr(GetTeam());
4626  if (!cMgr)
4627  return;
4628 
4629  std::string current_zone_name = current_zone->area_name[GetSession()->GetSessionDbcLocale()];
4630 
4631  for (JoinedChannelsList::iterator i = m_channels.begin(), next; i != m_channels.end(); i = next)
4632  {
4633  next = i;
4634  ++next;
4635 
4636  // skip non built-in channels
4637  if (!(*i)->IsConstant())
4638  continue;
4639 
4640  ChatChannelsEntry const* ch = GetChannelEntryFor((*i)->GetChannelId());
4641  if (!ch)
4642  continue;
4643 
4644  if ((ch->flags & 4) == 4) // global channel without zone name in pattern
4645  continue;
4646 
4647  // new channel
4648  char new_channel_name_buf[100];
4649  snprintf(new_channel_name_buf, 100, ch->pattern[m_session->GetSessionDbcLocale()], current_zone_name.c_str());
4650  Channel* new_channel = cMgr->GetJoinChannel(new_channel_name_buf, ch->ChannelID);
4651 
4652  if ((*i) != new_channel)
4653  {
4654  new_channel->Join(GetGUID(), ""); // will output Changed Channel: N. Name
4655 
4656  // leave old channel
4657  (*i)->Leave(GetGUID(), false); // not send leave channel, it already replaced at client
4658  std::string name = (*i)->GetName(); // store name, (*i)erase in LeftChannel
4659  LeftChannel(*i); // remove from player's channel list
4660  cMgr->LeftChannel(name); // delete if empty
4661  }
4662  }
4663  DEBUG_LOG("Player: channels cleaned up!");
4664 }
4665 
4667 {
4668  for (JoinedChannelsList::iterator i = m_channels.begin(); i != m_channels.end(); ++i)
4669  {
4670  if ((*i)->IsLFG())
4671  {
4672  (*i)->Leave(GetGUID());
4673  break;
4674  }
4675  }
4676 }
4677 
4679 {
4681  UpdateDefenseBonusesMod(); // update dependent from defense skill part
4682 }
4683 
4684 void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply)
4685 {
4686  if (modGroup >= BASEMOD_END || modType >= MOD_END)
4687  {
4688  sLog.outError("ERROR in HandleBaseModValue(): Invalid BaseModGroup or invalid BaseModType!");
4689  return;
4690  }
4691 
4692  if (modType == FLAT_MOD)
4693  m_auraBaseMod[modGroup][modType] += apply ? amount : -amount;
4694  else // PCT_MOD
4695  ApplyPercentModFloatVar(m_auraBaseMod[modGroup][modType], amount, apply);
4696 
4697  if (!CanModifyStats())
4698  return;
4699 
4700  switch (modGroup)
4701  {
4706  default: break;
4707  }
4708 }
4709 
4710 float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const
4711 {
4712  if (modGroup >= BASEMOD_END || modType >= MOD_END)
4713  {
4714  sLog.outError("GetBaseModType: Invalid BaseModGroup or invalid BaseModType!");
4715  return 0.0f;
4716  }
4717 
4718  if (modType == PCT_MOD && m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
4719  return 0.0f;
4720 
4721  return m_auraBaseMod[modGroup][modType];
4722 }
4723 
4725 {
4726  if (modGroup >= BASEMOD_END)
4727  {
4728  sLog.outError("wrong BaseModGroup in GetTotalBaseModValue()!");
4729  return 0.0f;
4730  }
4731 
4732  if (m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
4733  return 0.0f;
4734 
4735  return m_auraBaseMod[modGroup][FLAT_MOD] * m_auraBaseMod[modGroup][PCT_MOD];
4736 }
4737 
4739 {
4741 
4742  value = (value < 0) ? 0 : value;
4743 
4744  return uint32(value);
4745 }
4746 
4748 {
4749  uint32 level = getLevel();
4750  uint32 pclass = getClass();
4751 
4752  if (level > GT_MAX_LEVEL)
4753  level = GT_MAX_LEVEL;
4754 
4755  GtChanceToMeleeCritBaseEntry const* critBase = sGtChanceToMeleeCritBaseStore.LookupEntry(pclass - 1);
4756  GtChanceToMeleeCritEntry const* critRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1);
4757  if (critBase == NULL || critRatio == NULL)
4758  return 0.0f;
4759 
4760  float crit = critBase->base + GetStat(STAT_AGILITY) * critRatio->ratio;
4761  return crit * 100.0f;
4762 }
4763 
4765 {
4766  // Table for base dodge values
4767  float dodge_base[MAX_CLASSES] =
4768  {
4769  0.0075f, // Warrior
4770  0.00652f, // Paladin
4771  -0.0545f, // Hunter
4772  -0.0059f, // Rogue
4773  0.03183f, // Priest
4774  0.0114f, // DK
4775  0.0167f, // Shaman
4776  0.034575f, // Mage
4777  0.02011f, // Warlock
4778  0.0f, // ??
4779  -0.0187f // Druid
4780  };
4781  // Crit/agility to dodge/agility coefficient multipliers
4782  float crit_to_dodge[MAX_CLASSES] =
4783  {
4784  1.1f, // Warrior
4785  1.0f, // Paladin
4786  1.6f, // Hunter
4787  2.0f, // Rogue
4788  1.0f, // Priest
4789  1.0f, // DK?
4790  1.0f, // Shaman
4791  1.0f, // Mage
4792  1.0f, // Warlock
4793  0.0f, // ??
4794  1.7f // Druid
4795  };
4796 
4797  uint32 level = getLevel();
4798  uint32 pclass = getClass();
4799 
4800  if (level > GT_MAX_LEVEL)
4801  level = GT_MAX_LEVEL;
4802 
4803  // Dodge per agility for most classes equal crit per agility (but for some classes need apply some multiplier)
4804  GtChanceToMeleeCritEntry const* dodgeRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1);
4805  if (dodgeRatio == NULL || pclass > MAX_CLASSES)
4806  return 0.0f;
4807 
4808  float dodge = dodge_base[pclass - 1] + GetStat(STAT_AGILITY) * dodgeRatio->ratio * crit_to_dodge[pclass - 1];
4809  return dodge * 100.0f;
4810 }
4811 
4813 {
4814  uint32 level = getLevel();
4815  uint32 pclass = getClass();
4816 
4817  if (level > GT_MAX_LEVEL)
4818  level = GT_MAX_LEVEL;
4819 
4820  GtChanceToSpellCritBaseEntry const* critBase = sGtChanceToSpellCritBaseStore.LookupEntry(pclass - 1);
4821  GtChanceToSpellCritEntry const* critRatio = sGtChanceToSpellCritStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1);
4822  if (critBase == NULL || critRatio == NULL)
4823  return 0.0f;
4824 
4825  float crit = critBase->base + GetStat(STAT_INTELLECT) * critRatio->ratio;
4826  return crit * 100.0f;
4827 }
4828 
4830 {
4831  uint32 level = getLevel();
4832 
4833  if (level > GT_MAX_LEVEL)
4834  level = GT_MAX_LEVEL;
4835 
4836  GtCombatRatingsEntry const* Rating = sGtCombatRatingsStore.LookupEntry(cr * GT_MAX_LEVEL + level - 1);
4837  if (!Rating)
4838  return 1.0f; // By default use minimum coefficient (not must be called)
4839 
4840  return Rating->ratio;
4841 }
4842 
4844 {
4846 }
4847 
4849 {
4850  float melee = GetRatingBonusValue(CR_CRIT_TAKEN_MELEE) * 2.0f;
4851  if (melee > 25.0f) melee = 25.0f;
4852  return uint32(melee * damage / 100.0f);
4853 }
4854 
4856 {
4857  float ranged = GetRatingBonusValue(CR_CRIT_TAKEN_RANGED) * 2.0f;
4858  if (ranged > 25.0f) ranged = 25.0f;
4859  return uint32(ranged * damage / 100.0f);
4860 }
4861 
4863 {
4864  float spell = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL) * 2.0f;
4865  // In wow script resilience limited to 25%
4866  if (spell > 25.0f)
4867  spell = 25.0f;
4868  return uint32(spell * damage / 100.0f);
4869 }
4870 
4872 {
4873  float spellDot = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL);
4874  // Dot resilience not limited (limit it by 100%)
4875  if (spellDot > 100.0f)
4876  spellDot = 100.0f;
4877  return uint32(spellDot * damage / 100.0f);
4878 }
4879 
4881 {
4882  switch (attType)
4883  {
4884  case BASE_ATTACK:
4885  return GetUInt32Value(PLAYER_EXPERTISE) / 4.0f;
4886  case OFF_ATTACK:
4888  default:
4889  break;
4890  }
4891  return 0.0f;
4892 }
4893 
4895 {
4896  uint32 level = getLevel();
4897  uint32 pclass = getClass();
4898 
4899  if (level > GT_MAX_LEVEL)
4900  level = GT_MAX_LEVEL;
4901 
4902  GtOCTRegenHPEntry const* baseRatio = sGtOCTRegenHPStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1);
4903  GtRegenHPPerSptEntry const* moreRatio = sGtRegenHPPerSptStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1);
4904  if (baseRatio == NULL || moreRatio == NULL)
4905  return 0.0f;
4906 
4907  // Formula from PaperDollFrame script
4908  float spirit = GetStat(STAT_SPIRIT);
4909  float baseSpirit = spirit;
4910  if (baseSpirit > 50)
4911  baseSpirit = 50;
4912  float moreSpirit = spirit - baseSpirit;
4913  float regen = baseSpirit * baseRatio->ratio + moreSpirit * moreRatio->ratio;
4914  return regen;
4915 }
4916 
4918 {
4919  uint32 level = getLevel();
4920  uint32 pclass = getClass();
4921 
4922  if (level > GT_MAX_LEVEL)
4923  level = GT_MAX_LEVEL;
4924 
4925  // GtOCTRegenMPEntry const *baseRatio = sGtOCTRegenMPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
4926  GtRegenMPPerSptEntry const* moreRatio = sGtRegenMPPerSptStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1);
4927  if (moreRatio == NULL)
4928  return 0.0f;
4929 
4930  // Formula get from PaperDollFrame script
4931  float spirit = GetStat(STAT_SPIRIT);
4932  float regen = spirit * moreRatio->ratio;
4933  return regen;
4934 }
4935 
4937 {