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