Core/Player: Added missing change in 8bf7fa369fcbda3a8830dbc45b291634bacfd744
[trinitycore] / src / server / game / Entities / Player / Player.cpp
1 /*
2 * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
3 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "Player.h"
20 #include "AreaTrigger.h"
21 #include "AccountMgr.h"
22 #include "AchievementMgr.h"
23 #include "ArenaTeam.h"
24 #include "ArenaTeamMgr.h"
25 #include "Bag.h"
26 #include "Battlefield.h"
27 #include "BattlefieldMgr.h"
28 #include "BattlefieldTB.h"
29 #include "BattlefieldWG.h"
30 #include "Battleground.h"
31 #include "BattlegroundMgr.h"
32 #include "BattlegroundPackets.h"
33 #include "BattlegroundScore.h"
34 #include "BattlePetMgr.h"
35 #include "CellImpl.h"
36 #include "Channel.h"
37 #include "ChannelMgr.h"
38 #include "CharacterDatabaseCleaner.h"
39 #include "CharacterTemplateDataStore.h"
40 #include "CharacterPackets.h"
41 #include "Chat.h"
42 #include "ChatPackets.h"
43 #include "CinematicMgr.h"
44 #include "CombatLogPackets.h"
45 #include "CombatPackets.h"
46 #include "Common.h"
47 #include "ConditionMgr.h"
48 #include "CreatureAI.h"
49 #include "DB2Stores.h"
50 #include "DatabaseEnv.h"
51 #include "DisableMgr.h"
52 #include "DuelPackets.h"
53 #include "EquipmentSetPackets.h"
54 #include "Formulas.h"
55 #include "GameEventMgr.h"
56 #include "GameObjectAI.h"
57 #include "Garrison.h"
58 #include "GitRevision.h"
59 #include "GossipDef.h"
60 #include "GridNotifiers.h"
61 #include "GridNotifiersImpl.h"
62 #include "Group.h"
63 #include "GroupMgr.h"
64 #include "GameTables.h"
65 #include "Guild.h"
66 #include "GuildMgr.h"
67 #include "InstancePackets.h"
68 #include "InstanceSaveMgr.h"
69 #include "InstanceScript.h"
70 #include "ItemPackets.h"
71 #include "KillRewarder.h"
72 #include "LFGMgr.h"
73 #include "Language.h"
74 #include "Log.h"
75 #include "LootMgr.h"
76 #include "LootPackets.h"
77 #include "Mail.h"
78 #include "MailPackets.h"
79 #include "MapManager.h"
80 #include "MiscPackets.h"
81 #include "MotionMaster.h"
82 #include "MovementPackets.h"
83 #include "ObjectAccessor.h"
84 #include "ObjectMgr.h"
85 #include "Opcodes.h"
86 #include "OutdoorPvP.h"
87 #include "OutdoorPvPMgr.h"
88 #include "Pet.h"
89 #include "PetPackets.h"
90 #include "PhasingHandler.h"
91 #include "QueryHolder.h"
92 #include "QuestDef.h"
93 #include "QuestObjectiveCriteriaMgr.h"
94 #include "QuestPackets.h"
95 #include "Realm.h"
96 #include "ReputationMgr.h"
97 #include "RestMgr.h"
98 #include "Scenario.h"
99 #include "SkillDiscovery.h"
100 #include "SocialMgr.h"
101 #include "Spell.h"
102 #include "SpellAuraEffects.h"
103 #include "SpellAuras.h"
104 #include "SpellHistory.h"
105 #include "SpellMgr.h"
106 #include "SpellPackets.h"
107 #include "TalentPackets.h"
108 #include "ToyPackets.h"
109 #include "TradeData.h"
110 #include "TransmogrificationPackets.h"
111 #include "Transport.h"
112 #include "UpdateData.h"
113 #include "UpdateFieldFlags.h"
114 #include "Util.h"
115 #include "Vehicle.h"
116 #include "VehiclePackets.h"
117 #include "Weather.h"
118 #include "WeatherMgr.h"
119 #include "World.h"
120 #include "WorldPacket.h"
121 #include "WorldSession.h"
122 #include "WorldStatePackets.h"
123 #include <G3D/g3dmath.h>
124
125 #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)
126
127 // corpse reclaim times
128 #define DEATH_EXPIRE_STEP (5*MINUTE)
129 #define MAX_DEATH_COUNT 3
130
131 static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
132
133 uint64 const MAX_MONEY_AMOUNT = 99999999999ULL;
134
135 Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this)
136 {
137 m_speakTime = 0;
138 m_speakCount = 0;
139
140 m_objectType |= TYPEMASK_PLAYER;
141 m_objectTypeId = TYPEID_PLAYER;
142
143 m_valuesCount = PLAYER_END;
144 _dynamicValuesCount = PLAYER_DYNAMIC_END;
145
146 m_session = session;
147
148 m_ingametime = 0;
149
150 m_ExtraFlags = 0;
151
152 m_spellModTakingSpell = nullptr;
153
154 // players always accept
155 if (!GetSession()->HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS))
156 SetAcceptWhispers(true);
157
158 m_combatExitTime = 0;
159 m_regenTimer = 0;
160 m_regenTimerCount = 0;
161 m_weaponChangeTimer = 0;
162
163 m_zoneUpdateId = uint32(-1);
164 m_zoneUpdateTimer = 0;
165
166 m_areaUpdateId = 0;
167 m_team = 0;
168
169 m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE);
170
171 memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT);
172
173 m_social = nullptr;
174
175 // group is initialized in the reference constructor
176 SetGroupInvite(nullptr);
177 m_groupUpdateMask = 0;
178 m_bPassOnGroupLoot = false;
179
180 duel = nullptr;
181
182 m_GuildIdInvited = UI64LIT(0);
183 m_ArenaTeamIdInvited = 0;
184
185 m_atLoginFlags = AT_LOGIN_NONE;
186
187 mSemaphoreTeleport_Near = false;
188 mSemaphoreTeleport_Far = false;
189
190 m_DelayedOperations = 0;
191 m_bCanDelayTeleport = false;
192 m_bHasDelayedTeleport = false;
193 m_teleport_options = 0;
194
195 m_trade = nullptr;
196
197 m_cinematic = 0;
198
199 m_movie = 0;
200
201 PlayerTalkClass = new PlayerMenu(GetSession());
202 m_currentBuybackSlot = BUYBACK_SLOT_START;
203
204 m_DailyQuestChanged = false;
205 m_lastDailyQuestTime = 0;
206
207 for (uint8 i=0; i < MAX_TIMERS; i++)
208 m_MirrorTimer[i] = DISABLED_MIRROR_TIMER;
209
210 m_MirrorTimerFlags = UNDERWATER_NONE;
211 m_MirrorTimerFlagsLast = UNDERWATER_NONE;
212 m_isInWater = false;
213 m_drunkTimer = 0;
214 m_deathTimer = 0;
215 m_deathExpireTime = 0;
216
217 m_swingErrorMsg = 0;
218
219 for (uint8 j = 0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j)
220 {
221 m_bgBattlegroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE;
222 m_bgBattlegroundQueueID[j].invitedToInstance = 0;
223 m_bgBattlegroundQueueID[j].joinTime = 0;
224 }
225
226 m_logintime = time(nullptr);
227 m_Last_tick = m_logintime;
228 m_Played_time[PLAYED_TIME_TOTAL] = 0;
229 m_Played_time[PLAYED_TIME_LEVEL] = 0;
230 m_WeaponProficiency = 0;
231 m_ArmorProficiency = 0;
232 m_canParry = false;
233 m_canBlock = false;
234 m_canTitanGrip = false;
235 m_titanGripPenaltySpellId = 0;
236
237 m_temporaryUnsummonedPetNumber = 0;
238 //cache for UNIT_CREATED_BY_SPELL to allow
239 //returning reagents for temporarily removed pets
240 //when dying/logging out
241 m_oldpetspell = 0;
242 m_lastpetnumber = 0;
243
244 m_mailsLoaded = false;
245 m_mailsUpdated = false;
246 unReadMails = 0;
247 m_nextMailDelivereTime = 0;
248
249 m_itemUpdateQueueBlocked = false;
250
251 for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
252 m_forced_speed_changes[i] = 0;
253
254 m_stableSlots = 0;
255
256 /////////////////// Instance System /////////////////////
257
258 m_HomebindTimer = 0;
259 m_InstanceValid = true;
260 m_dungeonDifficulty = DIFFICULTY_NORMAL;
261 m_raidDifficulty = DIFFICULTY_NORMAL_RAID;
262 m_legacyRaidDifficulty = DIFFICULTY_10_N;
263 m_prevMapDifficulty = DIFFICULTY_NORMAL_RAID;
264
265 m_lastPotionId = 0;
266
267 for (uint8 i = 0; i < BASEMOD_END; ++i)
268 {
269 m_auraBaseMod[i][FLAT_MOD] = 0.0f;
270 m_auraBaseMod[i][PCT_MOD] = 1.0f;
271 }
272
273 for (uint8 i = 0; i < MAX_COMBAT_RATING; i++)
274 m_baseRatingValue[i] = 0;
275
276 m_baseSpellPower = 0;
277 m_baseManaRegen = 0;
278 m_baseHealthRegen = 0;
279 m_spellPenetrationItemMod = 0;
280
281 // Honor System
282 m_lastHonorUpdateTime = time(nullptr);
283
284 m_IsBGRandomWinner = false;
285
286 // Player summoning
287 m_summon_expire = 0;
288
289 m_unitMovedByMe = this;
290 m_playerMovingMe = this;
291 m_seer = this;
292
293 m_homebindMapId = 0;
294 m_homebindAreaId = 0;
295 m_homebindX = 0;
296 m_homebindY = 0;
297 m_homebindZ = 0;
298
299 m_contestedPvPTimer = 0;
300
301 m_declinedname = nullptr;
302
303 m_isActive = true;
304
305 m_runes = nullptr;
306
307 m_lastFallTime = 0;
308 m_lastFallZ = 0;
309
310 m_grantableLevels = 0;
311 m_fishingSteps = 0;
312
313 m_ControlledByPlayer = true;
314
315 sWorld->IncreasePlayerCount();
316
317 m_ChampioningFaction = 0;
318
319 m_timeSyncTimer = 0;
320 m_timeSyncClient = 0;
321 m_timeSyncServer = 0;
322
323 for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i)
324 m_powerFraction[i] = 0;
325
326 isDebugAreaTriggers = false;
327
328 m_WeeklyQuestChanged = false;
329 m_MonthlyQuestChanged = false;
330 m_SeasonalQuestChanged = false;
331
332 SetPendingBind(0, 0);
333
334 _activeCheats = CHEAT_NONE;
335 healthBeforeDuel = 0;
336 manaBeforeDuel = 0;
337
338 memset(_voidStorageItems, 0, VOID_STORAGE_MAX_SLOT * sizeof(VoidStorageItem*));
339
340 _cinematicMgr = new CinematicMgr(this);
341
342 m_achievementMgr = new PlayerAchievementMgr(this);
343 m_reputationMgr = new ReputationMgr(this);
344 m_questObjectiveCriteriaMgr = Trinity::make_unique<QuestObjectiveCriteriaMgr>(this);
345
346 for (uint8 i = 0; i < MAX_CUF_PROFILES; ++i)
347 _CUFProfiles[i] = nullptr;
348
349 _advancedCombatLoggingEnabled = false;
350
351 _restMgr = Trinity::make_unique<RestMgr>(this);
352
353 _usePvpItemLevels = false;
354 }
355
356 Player::~Player()
357 {
358 // it must be unloaded already in PlayerLogout and accessed only for logged in player
359 //m_social = NULL;
360
361 // Note: buy back item already deleted from DB when player was saved
362 for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; ++i)
363 delete m_items[i];
364
365 for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
366 delete itr->second;
367
368 //all mailed items should be deleted, also all mail should be deallocated
369 for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
370 delete *itr;
371
372 for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
373 delete iter->second; //if item is duplicated... then server may crash ... but that item should be deallocated
374
375 delete PlayerTalkClass;
376
377 for (size_t x = 0; x < ItemSetEff.size(); x++)
378 delete ItemSetEff[x];
379
380 delete m_declinedname;
381 delete m_runes;
382 delete m_achievementMgr;
383 delete m_reputationMgr;
384 delete _cinematicMgr;
385
386 for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
387 delete _voidStorageItems[i];
388
389 sWorld->DecreasePlayerCount();
390 }
391
392 void Player::CleanupsBeforeDelete(bool finalCleanup)
393 {
394 TradeCancel(false);
395 DuelComplete(DUEL_INTERRUPTED);
396
397 Unit::CleanupsBeforeDelete(finalCleanup);
398
399 // clean up player-instance binds, may unload some instance saves
400 for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
401 for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
402 itr->second.save->RemovePlayer(this);
403 }
404
405 bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::CharacterCreateInfo const* createInfo)
406 {
407 //FIXME: outfitId not used in player creating
408 /// @todo need more checks against packet modifications
409
410 Object::_Create(ObjectGuid::Create<HighGuid::Player>(guidlow));
411
412 m_name = createInfo->Name;
413
414 PlayerInfo const* info = sObjectMgr->GetPlayerInfo(createInfo->Race, createInfo->Class);
415 if (!info)
416 {
417 TC_LOG_ERROR("entities.player.cheat", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with an invalid race/class pair (%u/%u) - refusing to do so.",
418 GetSession()->GetAccountId(), m_name.c_str(), createInfo->Race, createInfo->Class);
419 return false;
420 }
421
422 for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; i++)
423 m_items[i] = nullptr;
424
425 Relocate(info->positionX, info->positionY, info->positionZ, info->orientation);
426
427 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(createInfo->Class);
428 if (!cEntry)
429 {
430 TC_LOG_ERROR("entities.player.cheat", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with an invalid character class (%u) - refusing to do so (wrong DBC-files?)",
431 GetSession()->GetAccountId(), m_name.c_str(), createInfo->Class);
432 return false;
433 }
434
435 if (!ValidateAppearance(createInfo->Race, createInfo->Class, createInfo->Sex, createInfo->HairStyle, createInfo->HairColor, createInfo->Face, createInfo->FacialHairStyle, createInfo->Skin, createInfo->CustomDisplay, true))
436 {
437 TC_LOG_ERROR("entities.player.cheat", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with invalid appearance attributes - refusing to do so",
438 GetSession()->GetAccountId(), m_name.c_str());
439 return false;
440 }
441
442 SetMap(sMapMgr->CreateMap(info->mapId, this));
443
444 uint8 powertype = cEntry->DisplayPower;
445
446 SetObjectScale(1.0f);
447
448 setFactionForRace(createInfo->Race);
449
450 if (!IsValidGender(createInfo->Sex))
451 {
452 TC_LOG_ERROR("entities.player.cheat", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with an invalid gender (%u) - refusing to do so",
453 GetSession()->GetAccountId(), m_name.c_str(), createInfo->Sex);
454 return false;
455 }
456
457 SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE, createInfo->Race);
458 SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, createInfo->Class);
459 SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, createInfo->Sex);
460 SetUInt32Value(UNIT_FIELD_DISPLAY_POWER, powertype);
461 InitDisplayIds();
462 if (sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP)
463 {
464 SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_PVP);
465 SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
466 }
467
468 SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER);
469 SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // default for players in 3.0.3
470
471 SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); // -1 is default value
472
473 SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID, createInfo->Skin);
474 SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID, createInfo->Face);
475 SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID, createInfo->HairStyle);
476 SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID, createInfo->HairColor);
477 SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE, createInfo->FacialHairStyle);
478 for (uint32 i = 0; i < PLAYER_CUSTOM_DISPLAY_SIZE; ++i)
479 SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_CUSTOM_DISPLAY_OPTION + i, createInfo->CustomDisplay[i]);
480 SetUInt32Value(PLAYER_FIELD_REST_INFO + REST_STATE_XP, (GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0) ? REST_STATE_RAF_LINKED : REST_STATE_NOT_RAF_LINKED);
481 SetUInt32Value(PLAYER_FIELD_REST_INFO + REST_STATE_HONOR, REST_STATE_NOT_RAF_LINKED);
482 SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER, createInfo->Sex);
483 SetByteValue(PLAYER_BYTES_4, PLAYER_BYTES_4_OFFSET_ARENA_FACTION, 0);
484 SetInventorySlotCount(INVENTORY_DEFAULT_SIZE);
485
486 SetGuidValue(OBJECT_FIELD_DATA, ObjectGuid::Empty);
487 SetUInt32Value(PLAYER_GUILDRANK, 0);
488 SetGuildLevel(0);
489 SetUInt32Value(PLAYER_GUILD_TIMESTAMP, 0);
490
491 for (int i = 0; i < KNOWN_TITLES_SIZE; ++i)
492 SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES + i, 0); // 0=disabled
493 SetUInt32Value(PLAYER_CHOSEN_TITLE, 0);
494
495 SetUInt32Value(PLAYER_FIELD_KILLS, 0);
496 SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, 0);
497
498 // set starting level
499 uint32 start_level = sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL);
500 if (getClass() == CLASS_DEATH_KNIGHT)
501 start_level = sWorld->getIntConfig(CONFIG_START_DEATH_KNIGHT_PLAYER_LEVEL);
502 else if (getClass() == CLASS_DEMON_HUNTER)
503 start_level = sWorld->getIntConfig(CONFIG_START_DEMON_HUNTER_PLAYER_LEVEL);
504
505 if (createInfo->TemplateSet)
506 {
507 if (m_session->HasPermission(rbac::RBAC_PERM_USE_CHARACTER_TEMPLATES))
508 {
509 if (CharacterTemplate const* charTemplate = sCharacterTemplateDataStore->GetCharacterTemplate(*createInfo->TemplateSet))
510 {
511 if (charTemplate->Level > start_level)
512 start_level = charTemplate->Level;
513 }
514 }
515 else
516 TC_LOG_WARN("cheat", "Account: %u (IP: %s) tried to use a character template without given permission. Possible cheating attempt.", m_session->GetAccountId(), m_session->GetRemoteAddress().c_str());
517 }
518
519 if (m_session->HasPermission(rbac::RBAC_PERM_USE_START_GM_LEVEL))
520 {
521 uint32 gm_level = sWorld->getIntConfig(CONFIG_START_GM_LEVEL);
522 if (gm_level > start_level)
523 start_level = gm_level;
524 }
525
526 SetUInt32Value(UNIT_FIELD_LEVEL, start_level);
527
528 InitRunes();
529
530 SetUInt64Value(PLAYER_FIELD_COINAGE, sWorld->getIntConfig(CONFIG_START_PLAYER_MONEY));
531 SetCurrency(CURRENCY_TYPE_APEXIS_CRYSTALS, sWorld->getIntConfig(CONFIG_CURRENCY_START_APEXIS_CRYSTALS));
532 SetCurrency(CURRENCY_TYPE_JUSTICE_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_JUSTICE_POINTS));
533
534 // start with every map explored
535 if (sWorld->getBoolConfig(CONFIG_START_ALL_EXPLORED))
536 {
537 for (uint16 i=0; i<PLAYER_EXPLORED_ZONES_SIZE; i++)
538 SetFlag(PLAYER_EXPLORED_ZONES_1+i, 0xFFFFFFFF);
539 }
540
541 //Reputations if "StartAllReputation" is enabled, -- @todo Fix this in a better way
542 if (sWorld->getBoolConfig(CONFIG_START_ALL_REP))
543 {
544 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(942), 42999);
545 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(935), 42999);
546 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(936), 42999);
547 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1011), 42999);
548 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(970), 42999);
549 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(967), 42999);
550 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(989), 42999);
551 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(932), 42999);
552 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(934), 42999);
553 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1038), 42999);
554 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1077), 42999);
555
556 // Factions depending on team, like cities and some more stuff
557 switch (GetTeam())
558 {
559 case ALLIANCE:
560 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(72), 42999);
561 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(47), 42999);
562 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(69), 42999);
563 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(930), 42999);
564 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(730), 42999);
565 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(978), 42999);
566 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(54), 42999);
567 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(946), 42999);
568 break;
569 case HORDE:
570 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(76), 42999);
571 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(68), 42999);
572 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(81), 42999);
573 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(911), 42999);
574 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(729), 42999);
575 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(941), 42999);
576 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(530), 42999);
577 GetReputationMgr().SetReputation(sFactionStore.LookupEntry(947), 42999);
578 break;
579 default:
580 break;
581 }
582 }
583
584 // Played time
585 m_Last_tick = time(nullptr);
586 m_Played_time[PLAYED_TIME_TOTAL] = 0;
587 m_Played_time[PLAYED_TIME_LEVEL] = 0;
588
589 // base stats and related field values
590 InitStatsForLevel();
591 InitTaxiNodesForLevel();
592 InitTalentForLevel();
593 InitPrimaryProfessions(); // to max set before any spell added
594
595 // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
596 UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
597 SetFullHealth();
598 SetFullPower(POWER_MANA);
599
600 // original spells
601 LearnDefaultSkills();
602 LearnCustomSpells();
603
604 // Original action bar. Do not use Player::AddActionButton because we do not have skill spells loaded at this time
605 // but checks will still be performed later when loading character from db in Player::_LoadActions
606 for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr)
607 {
608 // create new button
609 ActionButton& ab = m_actionButtons[action_itr->button];
610
611 // set data
612 ab.SetActionAndType(action_itr->action, ActionButtonType(action_itr->type));
613 }
614
615 // original items
616 if (CharStartOutfitEntry const* oEntry = sDB2Manager.GetCharStartOutfitEntry(createInfo->Race, createInfo->Class, createInfo->Sex))
617 {
618 for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
619 {
620 if (oEntry->ItemID[j] <= 0)
621 continue;
622
623 uint32 itemId = oEntry->ItemID[j];
624
625 // just skip, reported in ObjectMgr::LoadItemTemplates
626 ItemTemplate const* iProto = sObjectMgr->GetItemTemplate(itemId);
627 if (!iProto)
628 continue;
629
630 // BuyCount by default
631 uint32 count = iProto->GetBuyCount();
632
633 // special amount for food/drink
634 if (iProto->GetClass() == ITEM_CLASS_CONSUMABLE && iProto->GetSubClass() == ITEM_SUBCLASS_FOOD_DRINK)
635 {
636 if (iProto->Effects.size() >= 1)
637 {
638 switch (iProto->Effects[0]->SpellCategoryID)
639 {
640 case SPELL_CATEGORY_FOOD: // food
641 count = getClass() == CLASS_DEATH_KNIGHT ? 10 : 4;
642 break;
643 case SPELL_CATEGORY_DRINK: // drink
644 count = 2;
645 break;
646 }
647 }
648 if (iProto->GetMaxStackSize() < count)
649 count = iProto->GetMaxStackSize();
650 }
651 StoreNewItemInBestSlots(itemId, count);
652 }
653 }
654
655 for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr != info->item.end(); ++item_id_itr)
656 StoreNewItemInBestSlots(item_id_itr->item_id, item_id_itr->item_amount);
657
658 // bags and main-hand weapon must equipped at this moment
659 // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon)
660 // or ammo not equipped in special bag
661 uint8 inventoryEnd = INVENTORY_SLOT_ITEM_START + GetInventorySlotCount();
662 for (uint8 i = INVENTORY_SLOT_ITEM_START; i < inventoryEnd; i++)
663 {
664 if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
665 {
666 uint16 eDest;
667 // equip offhand weapon/shield if it attempt equipped before main-hand weapon
668 InventoryResult msg = CanEquipItem(NULL_SLOT, eDest, pItem, false);
669 if (msg == EQUIP_ERR_OK)
670 {
671 RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
672 EquipItem(eDest, pItem, true);
673 }
674 // move other items to more appropriate slots
675 else
676 {
677 ItemPosCountVec sDest;
678 msg = CanStoreItem(NULL_BAG, NULL_SLOT, sDest, pItem, false);
679 if (msg == EQUIP_ERR_OK)
680 {
681 RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
682 StoreItem(sDest, pItem, true);
683 }
684 }
685 }
686 }
687 // all item positions resolved
688
689 if (ChrSpecializationEntry const* defaultSpec = sDB2Manager.GetDefaultChrSpecializationForClass(getClass()))
690 {
691 SetActiveTalentGroup(defaultSpec->OrderIndex);
692 SetPrimarySpecialization(defaultSpec->ID);
693 }
694
695 return true;
696 }
697
698 bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount)
699 {
700 TC_LOG_DEBUG("entities.player.items", "Player::StoreNewItemInBestSlots: Player '%s' (%s) creates initial item (ItemID: %u, Count: %u)",
701 GetName().c_str(), GetGUID().ToString().c_str(), titem_id, titem_amount);
702
703 // attempt equip by one
704 while (titem_amount > 0)
705 {
706 uint16 eDest;
707 InventoryResult msg = CanEquipNewItem(NULL_SLOT, eDest, titem_id, false);
708 if (msg != EQUIP_ERR_OK)
709 break;
710
711 EquipNewItem(eDest, titem_id, true);
712 AutoUnequipOffhandIfNeed();
713 --titem_amount;
714 }
715
716 if (titem_amount == 0)
717 return true; // equipped
718
719 // attempt store
720 ItemPosCountVec sDest;
721 // store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
722 InventoryResult msg = CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount);
723 if (msg == EQUIP_ERR_OK)
724 {
725 StoreNewItem(sDest, titem_id, true, GenerateItemRandomPropertyId(titem_id));
726 return true; // stored
727 }
728
729 // item can't be added
730 TC_LOG_ERROR("entities.player.items", "Player::StoreNewItemInBestSlots: Player '%s' (%s) can't equip or store initial item (ItemID: %u, Race: %u, Class: %u, InventoryResult: %u)",
731 GetName().c_str(), GetGUID().ToString().c_str(), titem_id, getRace(), getClass(), msg);
732 return false;
733 }
734
735 void Player::SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, int32 Regen)
736 {
737 if (int(MaxValue) == DISABLED_MIRROR_TIMER)
738 {
739 if (int(CurrentValue) != DISABLED_MIRROR_TIMER)
740 StopMirrorTimer(Type);
741 return;
742 }
743
744 GetSession()->SendPacket(WorldPackets::Misc::StartMirrorTimer(Type, CurrentValue, MaxValue, Regen, 0, 0).Write());
745 }
746
747 void Player::StopMirrorTimer(MirrorTimerType Type)
748 {
749 m_MirrorTimer[Type] = DISABLED_MIRROR_TIMER;
750 GetSession()->SendPacket(WorldPackets::Misc::StopMirrorTimer(Type).Write());
751 }
752
753 bool Player::IsImmuneToEnvironmentalDamage() const
754 {
755 // check for GM and death state included in isAttackableByAOE
756 return !isTargetableForAttack(false);
757 }
758
759 uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage)
760 {
761 if (IsImmuneToEnvironmentalDamage())
762 return 0;
763
764 // Absorb, resist some environmental damage type
765 uint32 absorb = 0;
766 uint32 resist = 0;
767 switch (type)
768 {
769 case DAMAGE_LAVA:
770 case DAMAGE_SLIME:
771 {
772 DamageInfo dmgInfo(this, this, damage, nullptr, type == DAMAGE_LAVA ? SPELL_SCHOOL_MASK_FIRE : SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, BASE_ATTACK);
773 CalcAbsorbResist(dmgInfo);
774 absorb = dmgInfo.GetAbsorb();
775 resist = dmgInfo.GetResist();
776 damage = dmgInfo.GetDamage();
777 break;
778 }
779 default:
780 break;
781 }
782
783 DealDamageMods(this, damage, &absorb);
784
785 WorldPackets::CombatLog::EnvironmentalDamageLog packet;
786 packet.Victim = GetGUID();
787 packet.Type = type != DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL;
788 packet.Amount = damage;
789 packet.Absorbed = absorb;
790 packet.Resisted = resist;
791
792 uint32 final_damage = DealDamage(this, damage, nullptr, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
793
794 packet.LogData.Initialize(this);
795 SendCombatLogMessage(&packet);
796
797 if (!IsAlive())
798 {
799 if (type == DAMAGE_FALL) // DealDamage does not apply item durability loss from self-induced damage.
800 {
801 TC_LOG_DEBUG("entities.player", "Player::EnvironmentalDamage: Player '%s' (%s) fall to death, loosing 10%% durability",
802 GetName().c_str(), GetGUID().ToString().c_str());
803 DurabilityLossAll(0.10f, false);
804 // durability lost message
805 SendDurabilityLoss(this, 10);
806 }
807
808 UpdateCriteria(CRITERIA_TYPE_DEATHS_FROM, 1, type);
809 }
810
811 return final_damage;
812 }
813
814 int32 Player::getMaxTimer(MirrorTimerType timer) const
815 {
816 switch (timer)
817 {
818 case FATIGUE_TIMER:
819 return MINUTE * IN_MILLISECONDS;
820 case BREATH_TIMER:
821 {
822 if (!IsAlive() || HasAuraType(SPELL_AURA_WATER_BREATHING) || GetSession()->GetSecurity() >= AccountTypes(sWorld->getIntConfig(CONFIG_DISABLE_BREATHING)))
823 return DISABLED_MIRROR_TIMER;
824
825 int32 UnderWaterTime = 3 * MINUTE * IN_MILLISECONDS;
826 UnderWaterTime *= GetTotalAuraMultiplier(SPELL_AURA_MOD_WATER_BREATHING);
827 return UnderWaterTime;
828 }
829 case FIRE_TIMER:
830 {
831 if (!IsAlive())
832 return DISABLED_MIRROR_TIMER;
833 return 1 * IN_MILLISECONDS;
834 }
835 default:
836 return 0;
837 }
838 }
839
840 void Player::UpdateMirrorTimers()
841 {
842 // Desync flags for update on next HandleDrowning
843 if (m_MirrorTimerFlags)
844 m_MirrorTimerFlagsLast = ~m_MirrorTimerFlags;
845 }
846
847 void Player::StopMirrorTimers()
848 {
849 StopMirrorTimer(FATIGUE_TIMER);
850 StopMirrorTimer(BREATH_TIMER);
851 StopMirrorTimer(FIRE_TIMER);
852 }
853
854 bool Player::IsMirrorTimerActive(MirrorTimerType type) const
855 {
856 return m_MirrorTimer[type] == getMaxTimer(type);
857 }
858
859 void Player::HandleDrowning(uint32 time_diff)
860 {
861 if (!m_MirrorTimerFlags)
862 return;
863
864 // In water
865 if (m_MirrorTimerFlags & UNDERWATER_INWATER)
866 {
867 // Breath timer not activated - activate it
868 if (m_MirrorTimer[BREATH_TIMER] == DISABLED_MIRROR_TIMER)
869 {
870 m_MirrorTimer[BREATH_TIMER] = getMaxTimer(BREATH_TIMER);
871 SendMirrorTimer(BREATH_TIMER, m_MirrorTimer[BREATH_TIMER], m_MirrorTimer[BREATH_TIMER], -1);
872 }
873 else // If activated - do tick
874 {
875 m_MirrorTimer[BREATH_TIMER]-=time_diff;
876 // Timer limit - need deal damage
877 if (m_MirrorTimer[BREATH_TIMER] < 0)
878 {
879 m_MirrorTimer[BREATH_TIMER]+= 1*IN_MILLISECONDS;
880 // Calculate and deal damage
881 /// @todo Check this formula
882 uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1);
883 EnvironmentalDamage(DAMAGE_DROWNING, damage);
884 }
885 else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INWATER)) // Update time in client if need
886 SendMirrorTimer(BREATH_TIMER, getMaxTimer(BREATH_TIMER), m_MirrorTimer[BREATH_TIMER], -1);
887 }
888 }
889 else if (m_MirrorTimer[BREATH_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
890 {
891 int32 UnderWaterTime = getMaxTimer(BREATH_TIMER);
892 // Need breath regen
893 m_MirrorTimer[BREATH_TIMER]+=10*time_diff;
894 if (m_MirrorTimer[BREATH_TIMER] >= UnderWaterTime || !IsAlive())
895 StopMirrorTimer(BREATH_TIMER);
896 else if (m_MirrorTimerFlagsLast & UNDERWATER_INWATER)
897 SendMirrorTimer(BREATH_TIMER, UnderWaterTime, m_MirrorTimer[BREATH_TIMER], 10);
898 }
899
900 // In dark water
901 if (m_MirrorTimerFlags & UNDERWARER_INDARKWATER)
902 {
903 // Fatigue timer not activated - activate it
904 if (m_MirrorTimer[FATIGUE_TIMER] == DISABLED_MIRROR_TIMER)
905 {
906 m_MirrorTimer[FATIGUE_TIMER] = getMaxTimer(FATIGUE_TIMER);
907 SendMirrorTimer(FATIGUE_TIMER, m_MirrorTimer[FATIGUE_TIMER], m_MirrorTimer[FATIGUE_TIMER], -1);
908 }
909 else
910 {
911 m_MirrorTimer[FATIGUE_TIMER]-=time_diff;
912 // Timer limit - need deal damage or teleport ghost to graveyard
913 if (m_MirrorTimer[FATIGUE_TIMER] < 0)
914 {
915 m_MirrorTimer[FATIGUE_TIMER]+= 1*IN_MILLISECONDS;
916 if (IsAlive()) // Calculate and deal damage
917 {
918 uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1);
919 EnvironmentalDamage(DAMAGE_EXHAUSTED, damage);
920 }
921 else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) // Teleport ghost to graveyard
922 RepopAtGraveyard();
923 }
924 else if (!(m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER))
925 SendMirrorTimer(FATIGUE_TIMER, getMaxTimer(FATIGUE_TIMER), m_MirrorTimer[FATIGUE_TIMER], -1);
926 }
927 }
928 else if (m_MirrorTimer[FATIGUE_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
929 {
930 int32 DarkWaterTime = getMaxTimer(FATIGUE_TIMER);
931 m_MirrorTimer[FATIGUE_TIMER]+=10*time_diff;
932 if (m_MirrorTimer[FATIGUE_TIMER] >= DarkWaterTime || !IsAlive())
933 StopMirrorTimer(FATIGUE_TIMER);
934 else if (m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER)
935 SendMirrorTimer(FATIGUE_TIMER, DarkWaterTime, m_MirrorTimer[FATIGUE_TIMER], 10);
936 }
937
938 if (m_MirrorTimerFlags & (UNDERWATER_INLAVA /*| UNDERWATER_INSLIME*/) && !(_lastLiquid && _lastLiquid->SpellID))
939 {
940 // Breath timer not activated - activate it
941 if (m_MirrorTimer[FIRE_TIMER] == DISABLED_MIRROR_TIMER)
942 m_MirrorTimer[FIRE_TIMER] = getMaxTimer(FIRE_TIMER);
943 else
944 {
945 m_MirrorTimer[FIRE_TIMER] -= time_diff;
946 if (m_MirrorTimer[FIRE_TIMER] < 0)
947 {
948 m_MirrorTimer[FIRE_TIMER]+= 1*IN_MILLISECONDS;
949 // Calculate and deal damage
950 /// @todo Check this formula
951 uint32 damage = urand(600, 700);
952 if (m_MirrorTimerFlags & UNDERWATER_INLAVA)
953 EnvironmentalDamage(DAMAGE_LAVA, damage);
954 // need to skip Slime damage in Undercity,
955 // maybe someone can find better way to handle environmental damage
956 //else if (m_zoneUpdateId != 1497)
957 // EnvironmentalDamage(DAMAGE_SLIME, damage);
958 }
959 }
960 }
961 else
962 m_MirrorTimer[FIRE_TIMER] = DISABLED_MIRROR_TIMER;
963
964 // Recheck timers flag
965 m_MirrorTimerFlags&=~UNDERWATER_EXIST_TIMERS;
966 for (uint8 i = 0; i< MAX_TIMERS; ++i)
967 if (m_MirrorTimer[i] != DISABLED_MIRROR_TIMER)
968 {
969 m_MirrorTimerFlags|=UNDERWATER_EXIST_TIMERS;
970 break;
971 }
972 m_MirrorTimerFlagsLast = m_MirrorTimerFlags;
973 }
974
975 ///The player sobers by 1% every 9 seconds
976 void Player::HandleSobering()
977 {
978 m_drunkTimer = 0;
979
980 uint8 currentDrunkValue = GetDrunkValue();
981 uint8 drunk = currentDrunkValue ? --currentDrunkValue : 0;
982 SetDrunkValue(drunk);
983 }
984
985 DrunkenState Player::GetDrunkenstateByValue(uint8 value)
986 {
987 if (value >= 90)
988 return DRUNKEN_SMASHED;
989 if (value >= 50)
990 return DRUNKEN_DRUNK;
991 if (value)
992 return DRUNKEN_TIPSY;
993 return DRUNKEN_SOBER;
994 }
995
996 void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/)
997 {
998 bool isSobering = newDrunkValue < GetDrunkValue();
999 uint32 oldDrunkenState = Player::GetDrunkenstateByValue(GetDrunkValue());
1000 if (newDrunkValue > 100)
1001 newDrunkValue = 100;
1002
1003 // select drunk percent or total SPELL_AURA_MOD_FAKE_INEBRIATE amount, whichever is higher for visibility updates
1004 int32 drunkPercent = std::max<int32>(newDrunkValue, GetTotalAuraModifier(SPELL_AURA_MOD_FAKE_INEBRIATE));
1005 if (drunkPercent)
1006 {
1007 m_invisibilityDetect.AddFlag(INVISIBILITY_DRUNK);
1008 m_invisibilityDetect.SetValue(INVISIBILITY_DRUNK, drunkPercent);
1009 }
1010 else if (!HasAuraType(SPELL_AURA_MOD_FAKE_INEBRIATE) && !newDrunkValue)
1011 m_invisibilityDetect.DelFlag(INVISIBILITY_DRUNK);
1012
1013 uint32 newDrunkenState = Player::GetDrunkenstateByValue(newDrunkValue);
1014 SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION, newDrunkValue);
1015 UpdateObjectVisibility();
1016
1017 if (!isSobering)
1018 m_drunkTimer = 0; // reset sobering timer
1019
1020 if (newDrunkenState == oldDrunkenState)
1021 return;
1022
1023 WorldPackets::Misc::CrossedInebriationThreshold data;
1024 data.Guid = GetGUID();
1025 data.Threshold = newDrunkenState;
1026 data.ItemID = itemId;
1027
1028 SendMessageToSet(data.Write(), true);
1029 }
1030
1031 void Player::Update(uint32 p_time)
1032 {
1033 if (!IsInWorld())
1034 return;
1035
1036 // undelivered mail
1037 if (m_nextMailDelivereTime && m_nextMailDelivereTime <= time(nullptr))
1038 {
1039 SendNewMail();
1040 ++unReadMails;
1041
1042 // It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated)
1043 m_nextMailDelivereTime = 0;
1044 }
1045
1046 // If this is set during update SetSpellModTakingSpell call is missing somewhere in the code
1047 // Having this would prevent more aura charges to be dropped, so let's crash
1048 //ASSERT (!m_spellModTakingSpell);
1049 if (m_spellModTakingSpell)
1050 {
1051 //TC_LOG_FATAL("entities.player", "Player has m_pad %u during update!", m_pad);
1052 //if (m_spellModTakingSpell)
1053 TC_LOG_FATAL("spells", "Player::Update: Player '%s' (%s) has m_spellModTakingSpell (SpellID: %u) during update!",
1054 GetName().c_str(), GetGUID().ToString().c_str(), m_spellModTakingSpell->m_spellInfo->Id);
1055 m_spellModTakingSpell = nullptr;
1056 }
1057
1058 // Update cinematic location, if 500ms have passed and we're doing a cinematic now.
1059 _cinematicMgr->m_cinematicDiff += p_time;
1060 if (_cinematicMgr->m_activeCinematicCameraId != 0 && GetMSTimeDiffToNow(_cinematicMgr->m_lastCinematicCheck) > CINEMATIC_UPDATEDIFF)
1061 {
1062 _cinematicMgr->m_lastCinematicCheck = getMSTime();
1063 _cinematicMgr->UpdateCinematicLocation(p_time);
1064 }
1065
1066 //used to implement delayed far teleport
1067 SetCanDelayTeleport(true);
1068 Unit::Update(p_time);
1069 SetCanDelayTeleport(false);
1070
1071 time_t now = time(nullptr);
1072
1073 UpdatePvPFlag(now);
1074
1075 UpdateContestedPvP(p_time);
1076
1077 UpdateDuelFlag(now);
1078
1079 CheckDuelDistance(now);
1080
1081 UpdateAfkReport(now);
1082
1083 if (GetCombatTimer()) // Only set when in pvp combat
1084 if (Aura* aura = GetAura(SPELL_PVP_RULES_ENABLED))
1085 if (!aura->IsPermanent())
1086 aura->SetDuration(aura->GetSpellInfo()->GetMaxDuration());
1087
1088 if (IsAIEnabled && GetAI())
1089 GetAI()->UpdateAI(p_time);
1090 else if (NeedChangeAI)
1091 {
1092 UpdateCharmAI();
1093 NeedChangeAI = false;
1094 IsAIEnabled = (GetAI() != nullptr);
1095 }
1096
1097 // Update items that have just a limited lifetime
1098 if (now > m_Last_tick)
1099 UpdateItemDuration(uint32(now - m_Last_tick));
1100
1101 // check every second
1102 if (now > m_Last_tick + 1)
1103 UpdateSoulboundTradeItems();
1104
1105 // If mute expired, remove it from the DB
1106 if (GetSession()->m_muteTime && GetSession()->m_muteTime < now)
1107 {
1108 GetSession()->m_muteTime = 0;
1109 PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME);
1110 stmt->setInt64(0, 0); // Set the mute time to 0
1111 stmt->setString(1, "");
1112 stmt->setString(2, "");
1113 stmt->setUInt32(3, GetSession()->GetAccountId());
1114 LoginDatabase.Execute(stmt);
1115 }
1116
1117 if (!m_timedquests.empty())
1118 {
1119 QuestSet::iterator iter = m_timedquests.begin();
1120 while (iter != m_timedquests.end())
1121 {
1122 QuestStatusData& q_status = m_QuestStatus[*iter];
1123 if (q_status.Timer <= p_time)
1124 {
1125 uint32 quest_id = *iter;
1126 ++iter; // current iter will be removed in FailQuest
1127 FailQuest(quest_id);
1128 }
1129 else
1130 {
1131 q_status.Timer -= p_time;
1132 m_QuestStatusSave[*iter] = QUEST_DEFAULT_SAVE_TYPE;
1133 ++iter;
1134 }
1135 }
1136 }
1137
1138 m_achievementMgr->UpdateTimedCriteria(p_time);
1139
1140 if (HasUnitState(UNIT_STATE_MELEE_ATTACKING) && !HasUnitState(UNIT_STATE_CASTING))
1141 {
1142 if (Unit* victim = GetVictim())
1143 {
1144 // default combat reach 10
1145 /// @todo add weapon, skill check
1146
1147 if (isAttackReady(BASE_ATTACK))
1148 {
1149 if (!IsWithinMeleeRange(victim))
1150 {
1151 setAttackTimer(BASE_ATTACK, 100);
1152 if (m_swingErrorMsg != 1) // send single time (client auto repeat)
1153 {
1154 SendAttackSwingNotInRange();
1155 m_swingErrorMsg = 1;
1156 }
1157 }
1158 //120 degrees of radiant range, if player is not in boundary radius
1159 else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim))
1160 {
1161 setAttackTimer(BASE_ATTACK, 100);
1162 if (m_swingErrorMsg != 2) // send single time (client auto repeat)
1163 {
1164 SendAttackSwingBadFacingAttack();
1165 m_swingErrorMsg = 2;
1166 }
1167 }
1168 else
1169 {
1170 m_swingErrorMsg = 0; // reset swing error state
1171
1172 // prevent base and off attack in same time, delay attack at 0.2 sec
1173 if (haveOffhandWeapon())
1174 if (getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
1175 setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
1176
1177 // do attack
1178 AttackerStateUpdate(victim, BASE_ATTACK);
1179 resetAttackTimer(BASE_ATTACK);
1180 }
1181 }
1182
1183 if (!IsInFeralForm() && haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
1184 {
1185 if (!IsWithinMeleeRange(victim))
1186 setAttackTimer(OFF_ATTACK, 100);
1187 else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim))
1188 {
1189 setAttackTimer(BASE_ATTACK, 100);
1190 }
1191 else
1192 {
1193 // prevent base and off attack in same time, delay attack at 0.2 sec
1194 if (getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY)
1195 setAttackTimer(BASE_ATTACK, ATTACK_DISPLAY_DELAY);
1196
1197 // do attack
1198 AttackerStateUpdate(victim, OFF_ATTACK);
1199 resetAttackTimer(OFF_ATTACK);
1200 }
1201 }
1202
1203 /*Unit* owner = victim->GetOwner();
1204 Unit* u = owner ? owner : victim;
1205 if (u->IsPvP() && (!duel || duel->opponent != u))
1206 {
1207 UpdatePvP(true);
1208 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
1209 }*/
1210 }
1211 }
1212
1213 if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
1214 _restMgr->Update(now);
1215
1216 if (m_weaponChangeTimer > 0)
1217 {
1218 if (p_time >= m_weaponChangeTimer)
1219 m_weaponChangeTimer = 0;
1220 else
1221 m_weaponChangeTimer -= p_time;
1222 }
1223
1224 if (m_zoneUpdateTimer > 0)
1225 {
1226 if (p_time >= m_zoneUpdateTimer)
1227 {
1228 // On zone update tick check if we are still in an inn if we are supposed to be in one
1229 if (_restMgr->HasRestFlag(REST_FLAG_IN_TAVERN))
1230 {
1231 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(_restMgr->GetInnTriggerID());
1232 if (!atEntry || !IsInAreaTriggerRadius(atEntry))
1233 _restMgr->RemoveRestFlag(REST_FLAG_IN_TAVERN);
1234 }
1235
1236 uint32 newzone, newarea;
1237 GetZoneAndAreaId(newzone, newarea);
1238
1239 if (m_zoneUpdateId != newzone)
1240 UpdateZone(newzone, newarea); // also update area
1241 else
1242 {
1243 // use area updates as well
1244 // needed for free far all arenas for example
1245 if (m_areaUpdateId != newarea)
1246 UpdateArea(newarea);
1247
1248 m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
1249 }
1250 }
1251 else
1252 m_zoneUpdateTimer -= p_time;
1253 }
1254
1255 if (m_timeSyncTimer > 0 && !IsBeingTeleportedFar())
1256 {
1257 if (p_time >= m_timeSyncTimer)
1258 SendTimeSync();
1259 else
1260 m_timeSyncTimer -= p_time;
1261 }
1262
1263 if (IsAlive())
1264 {
1265 m_regenTimer += p_time;
1266 RegenerateAll();
1267 }
1268
1269 if (m_deathState == JUST_DIED)
1270 KillPlayer();
1271
1272 if (m_nextSave > 0)
1273 {
1274 if (p_time >= m_nextSave)
1275 {
1276 // m_nextSave reset in SaveToDB call
1277 SaveToDB();
1278 TC_LOG_DEBUG("entities.player", "Player::Update: Player '%s' (%s) saved", GetName().c_str(), GetGUID().ToString().c_str());
1279 }
1280 else
1281 m_nextSave -= p_time;
1282 }
1283
1284 //Handle Water/drowning
1285 HandleDrowning(p_time);
1286
1287 // Played time
1288 if (now > m_Last_tick)
1289 {
1290 uint32 elapsed = uint32(now - m_Last_tick);
1291 m_Played_time[PLAYED_TIME_TOTAL] += elapsed; // Total played time
1292 m_Played_time[PLAYED_TIME_LEVEL] += elapsed; // Level played time
1293 m_Last_tick = now;
1294 }
1295
1296 if (GetDrunkValue())
1297 {
1298 m_drunkTimer += p_time;
1299 if (m_drunkTimer > 9 * IN_MILLISECONDS)
1300 HandleSobering();
1301 }
1302
1303 if (HasPendingBind())
1304 {
1305 if (_pendingBindTimer <= p_time)
1306 {
1307 // Player left the instance
1308 if (_pendingBindId == GetInstanceId())
1309 BindToInstance();
1310 SetPendingBind(0, 0);
1311 }
1312 else
1313 _pendingBindTimer -= p_time;
1314 }
1315
1316 // not auto-free ghost from body in instances
1317 if (m_deathTimer > 0 && !GetMap()->Instanceable() && !HasAuraType(SPELL_AURA_PREVENT_RESURRECTION))
1318 {
1319 if (p_time >= m_deathTimer)
1320 {
1321 m_deathTimer = 0;
1322 BuildPlayerRepop();
1323 RepopAtGraveyard();
1324 }
1325 else
1326 m_deathTimer -= p_time;
1327 }
1328
1329 UpdateEnchantTime(p_time);
1330 UpdateHomebindTime(p_time);
1331
1332 if (!_instanceResetTimes.empty())
1333 {
1334 for (InstanceTimeMap::iterator itr = _instanceResetTimes.begin(); itr != _instanceResetTimes.end();)
1335 {
1336 if (itr->second < now)
1337 _instanceResetTimes.erase(itr++);
1338 else
1339 ++itr;
1340 }
1341 }
1342
1343 // group update
1344 SendUpdateToOutOfRangeGroupMembers();
1345
1346 Pet* pet = GetPet();
1347 if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityRange()) && !pet->isPossessed())
1348 //if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID())))
1349 RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
1350
1351 //we should execute delayed teleports only for alive(!) players
1352 //because we don't want player's ghost teleported from graveyard
1353 if (IsHasDelayedTeleport() && IsAlive())
1354 TeleportTo(m_teleport_dest, m_teleport_options);
1355
1356 }
1357
1358 void Player::setDeathState(DeathState s)
1359 {
1360 bool oldIsAlive = IsAlive();
1361
1362 if (s == JUST_DIED)
1363 {
1364 if (!oldIsAlive)
1365 {
1366 TC_LOG_ERROR("entities.player", "Player::setDeathState: Attempted to kill a dead player '%s' (%s)", GetName().c_str(), GetGUID().ToString().c_str());
1367 return;
1368 }
1369
1370 // drunken state is cleared on death
1371 SetDrunkValue(0);
1372 // lost combo points at any target (targeted combo points clear in Unit::setDeathState)
1373 ClearComboPoints();
1374
1375 ClearResurrectRequestData();
1376
1377 //FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
1378 RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT, true);
1379
1380 InitializeSelfResurrectionSpells();
1381
1382 UpdateCriteria(CRITERIA_TYPE_DEATH_AT_MAP, 1);
1383 UpdateCriteria(CRITERIA_TYPE_DEATH, 1);
1384 UpdateCriteria(CRITERIA_TYPE_DEATH_IN_DUNGEON, 1);
1385 ResetCriteria(CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, CRITERIA_CONDITION_NO_DEATH);
1386 ResetCriteria(CRITERIA_TYPE_HONORABLE_KILL, CRITERIA_CONDITION_NO_DEATH);
1387 ResetCriteria(CRITERIA_TYPE_GET_KILLING_BLOWS, CRITERIA_CONDITION_NO_DEATH);
1388 }
1389
1390 Unit::setDeathState(s);
1391
1392 if (IsAlive() && !oldIsAlive)
1393 //clear aura case after resurrection by another way (spells will be applied before next death)
1394 ClearDynamicValue(PLAYER_DYNAMIC_FIELD_SELF_RES_SPELLS);
1395 }
1396
1397 void Player::ToggleAFK()
1398 {
1399 ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);
1400
1401 // afk player not allowed in battleground
1402 if (!IsGameMaster() && isAFK() && InBattleground() && !InArena())
1403 LeaveBattleground();
1404 }
1405
1406 void Player::ToggleDND()
1407 {
1408 ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);
1409 }
1410
1411 uint8 Player::GetChatFlags() const
1412 {
1413 uint8 tag = CHAT_FLAG_NONE;
1414
1415 if (isGMChat())
1416 tag |= CHAT_FLAG_GM;
1417 if (isDND())
1418 tag |= CHAT_FLAG_DND;
1419 if (isAFK())
1420 tag |= CHAT_FLAG_AFK;
1421 if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_DEVELOPER))
1422 tag |= CHAT_FLAG_DEV;
1423
1424 return tag;
1425 }
1426
1427 bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options)
1428 {
1429 if (!MapManager::IsValidMapCoord(mapid, x, y, z, orientation))
1430 {
1431 TC_LOG_ERROR("maps", "Player::TeleportTo: Invalid map (%d) or invalid coordinates (X: %f, Y: %f, Z: %f, O: %f) given when teleporting player '%s' (%s, MapID: %d, X: %f, Y: %f, Z: %f, O: %f).",
1432 mapid, x, y, z, orientation, GetGUID().ToString().c_str(), GetName().c_str(), GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
1433 return false;
1434 }
1435
1436 if (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_DISABLE_MAP) && DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, mapid, this))
1437 {
1438 TC_LOG_ERROR("entities.player.cheat", "Player::TeleportTo: Player '%s' (%s) tried to enter a forbidden map (MapID: %u)", GetGUID().ToString().c_str(), GetName().c_str(), mapid);
1439 SendTransferAborted(mapid, TRANSFER_ABORT_MAP_NOT_ALLOWED);
1440 return false;
1441 }
1442
1443 // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
1444 Pet* pet = GetPet();
1445
1446 MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
1447
1448 // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)...
1449 // don't let gm level > 1 either
1450 if (!InBattleground() && mEntry->IsBattlegroundOrArena())
1451 return false;
1452
1453 // client without expansion support
1454 if (GetSession()->GetExpansion() < mEntry->Expansion())
1455 {
1456 TC_LOG_DEBUG("maps", "Player '%s' (%s) using client without required expansion tried teleporting to non accessible map (MapID: %u)",
1457 GetName().c_str(), GetGUID().ToString().c_str(), mapid);
1458
1459 if (Transport* transport = GetTransport())
1460 {
1461 transport->RemovePassenger(this);
1462 RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :)
1463 }
1464
1465 SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL, mEntry->Expansion());
1466
1467 return false; // normal client can't teleport to this map...
1468 }
1469 else
1470 TC_LOG_DEBUG("maps", "Player %s (%s) is being teleported to map (MapID: %u)", GetName().c_str(), GetGUID().ToString().c_str(), mapid);
1471
1472 if (m_vehicle)
1473 ExitVehicle();
1474
1475 // reset movement flags at teleport, because player will continue move with these flags after teleport
1476 SetUnitMovementFlags(GetUnitMovementFlags() & MOVEMENTFLAG_MASK_HAS_PLAYER_STATUS_OPCODE);
1477 m_movementInfo.ResetJump();
1478 DisableSpline();
1479
1480 if (Transport* transport = GetTransport())
1481 {
1482 if (!(options & TELE_TO_NOT_LEAVE_TRANSPORT))
1483 transport->RemovePassenger(this);
1484 }
1485
1486 // The player was ported to another map and loses the duel immediately.
1487 // We have to perform this check before the teleport, otherwise the
1488 // ObjectAccessor won't find the flag.
1489 if (duel && GetMapId() != mapid && GetMap()->GetGameObject(GetGuidValue(PLAYER_DUEL_ARBITER)))
1490 DuelComplete(DUEL_FLED);
1491
1492 if (GetMapId() == mapid)
1493 {
1494 //lets reset far teleport flag if it wasn't reset during chained teleport
1495 SetSemaphoreTeleportFar(false);
1496 //setup delayed teleport flag
1497 SetDelayedTeleportFlag(IsCanDelayTeleport());
1498 //if teleport spell is cast in Unit::Update() func
1499 //then we need to delay it until update process will be finished
1500 if (IsHasDelayedTeleport())
1501 {
1502 SetSemaphoreTeleportNear(true);
1503 //lets save teleport destination for player
1504 m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
1505 m_teleport_options = options;
1506 return true;
1507 }
1508
1509 if (!(options & TELE_TO_NOT_UNSUMMON_PET))
1510 {
1511 //same map, only remove pet if out of range for new position
1512 if (pet && !pet->IsWithinDist3d(x, y, z, GetMap()->GetVisibilityRange()))
1513 UnsummonPetTemporaryIfAny();
1514 }
1515
1516 if (!(options & TELE_TO_NOT_LEAVE_COMBAT))
1517 CombatStop();
1518
1519 // this will be used instead of the current location in SaveToDB
1520 m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
1521 m_teleport_options = options;
1522 SetFallInformation(0, z);
1523
1524 // code for finish transfer called in WorldSession::HandleMovementOpcodes()
1525 // at client packet CMSG_MOVE_TELEPORT_ACK
1526 SetSemaphoreTeleportNear(true);
1527 // near teleport, triggering send CMSG_MOVE_TELEPORT_ACK from client at landing
1528 if (!GetSession()->PlayerLogout())
1529 SendTeleportPacket(m_teleport_dest);
1530 }
1531 else
1532 {
1533 if (getClass() == CLASS_DEATH_KNIGHT && GetMapId() == 609 && !IsGameMaster() && !HasSpell(50977))
1534 return false;
1535
1536 // far teleport to another map
1537 Map* oldmap = IsInWorld() ? GetMap() : NULL;
1538 // check if we can enter before stopping combat / removing pet / totems / interrupting spells
1539
1540 // Check enter rights before map getting to avoid creating instance copy for player
1541 // this check not dependent from map instance copy and same for all instance copies of selected map
1542 if (sMapMgr->PlayerCannotEnter(mapid, this, false))
1543 return false;
1544
1545 // Seamless teleport can happen only if cosmetic maps match
1546 if (!oldmap ||
1547 (oldmap->GetEntry()->CosmeticParentMapID != int32(mapid) && int32(GetMapId()) != mEntry->CosmeticParentMapID &&
1548 !((oldmap->GetEntry()->CosmeticParentMapID != -1) ^ (oldmap->GetEntry()->CosmeticParentMapID != mEntry->CosmeticParentMapID))))
1549 options &= ~TELE_TO_SEAMLESS;
1550
1551 //I think this always returns true. Correct me if I am wrong.
1552 // If the map is not created, assume it is possible to enter it.
1553 // It will be created in the WorldPortAck.
1554 //Map* map = sMapMgr->FindBaseNonInstanceMap(mapid);
1555 //if (!map || map->CanEnter(this))
1556 {
1557 //lets reset near teleport flag if it wasn't reset during chained teleports
1558 SetSemaphoreTeleportNear(false);
1559 //setup delayed teleport flag
1560 SetDelayedTeleportFlag(IsCanDelayTeleport());
1561 //if teleport spell is cast in Unit::Update() func
1562 //then we need to delay it until update process will be finished
1563 if (IsHasDelayedTeleport())
1564 {
1565 SetSemaphoreTeleportFar(true);
1566 //lets save teleport destination for player
1567 m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
1568 m_teleport_options = options;
1569 return true;
1570 }
1571
1572 SetSelection(ObjectGuid::Empty);
1573
1574 CombatStop();
1575
1576 ResetContestedPvP();
1577
1578 // remove player from battleground on far teleport (when changing maps)
1579 if (Battleground const* bg = GetBattleground())
1580 {
1581 // Note: at battleground join battleground id set before teleport
1582 // and we already will found "current" battleground
1583 // just need check that this is targeted map or leave
1584 if (bg->GetMapId() != mapid)
1585 LeaveBattleground(false); // don't teleport to entry point
1586 }
1587
1588 // remove arena spell coldowns/buffs now to also remove pet's cooldowns before it's temporarily unsummoned
1589 if (mEntry->IsBattleArena())
1590 {
1591 RemoveArenaSpellCooldowns(true);
1592 RemoveArenaAuras();
1593 if (pet)
1594 pet->RemoveArenaAuras();
1595 }
1596
1597 // remove pet on map change
1598 if (pet)
1599 UnsummonPetTemporaryIfAny();
1600
1601 // remove all dyn objects
1602 RemoveAllDynObjects();
1603
1604 // remove all areatriggers entities
1605 RemoveAllAreaTriggers();
1606
1607 // stop spellcasting
1608 // not attempt interrupt teleportation spell at caster teleport
1609 if (!(options & TELE_TO_SPELL))
1610 if (IsNonMeleeSpellCast(true))
1611 InterruptNonMeleeSpells(true);
1612
1613 //remove auras before removing from map...
1614 RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING));
1615
1616 if (!GetSession()->PlayerLogout() && !(options & TELE_TO_SEAMLESS))
1617 {
1618 // send transfer packets
1619 WorldPackets::Movement::TransferPending transferPending;
1620 transferPending.MapID = mapid;
1621 transferPending.OldMapPosition = GetPosition();
1622 if (Transport* transport = GetTransport())
1623 {
1624 transferPending.Ship = boost::in_place();
1625 transferPending.Ship->ID = transport->GetEntry();
1626 transferPending.Ship->OriginMapID = GetMapId();
1627 }
1628
1629 GetSession()->SendPacket(transferPending.Write());
1630 }
1631
1632 // remove from old map now
1633 if (oldmap)
1634 oldmap->RemovePlayerFromMap(this, false);
1635
1636 m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
1637 m_teleport_options = options;
1638 SetFallInformation(0, z);
1639 // if the player is saved before worldportack (at logout for example)
1640 // this will be used instead of the current location in SaveToDB
1641
1642 if (!GetSession()->PlayerLogout())
1643 {
1644 WorldPackets::Movement::SuspendToken suspendToken;
1645 suspendToken.SequenceIndex = m_movementCounter; // not incrementing
1646 suspendToken.Reason = options & TELE_TO_SEAMLESS ? 2 : 1;
1647 SendDirectMessage(suspendToken.Write());
1648 }
1649
1650 // move packet sent by client always after far teleport
1651 // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
1652 SetSemaphoreTeleportFar(true);
1653 }
1654 //else
1655 // return false;
1656 }
1657 return true;
1658 }
1659
1660 bool Player::TeleportTo(WorldLocation const &loc, uint32 options /*= 0*/)
1661 {
1662 return TeleportTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation(), options);
1663 }
1664
1665 bool Player::TeleportToBGEntryPoint()
1666 {
1667 if (m_bgData.joinPos.m_mapId == MAPID_INVALID)
1668 return false;
1669
1670 ScheduleDelayedOperation(DELAYED_BG_MOUNT_RESTORE);
1671 ScheduleDelayedOperation(DELAYED_BG_TAXI_RESTORE);
1672 ScheduleDelayedOperation(DELAYED_BG_GROUP_RESTORE);
1673 return TeleportTo(m_bgData.joinPos);
1674 }
1675
1676 void Player::ProcessDelayedOperations()
1677 {
1678 if (m_DelayedOperations == 0)
1679 return;
1680
1681 if (m_DelayedOperations & DELAYED_RESURRECT_PLAYER)
1682 ResurrectUsingRequestDataImpl();
1683
1684 if (m_DelayedOperations & DELAYED_SAVE_PLAYER)
1685 SaveToDB();
1686
1687 if (m_DelayedOperations & DELAYED_SPELL_CAST_DESERTER)
1688 CastSpell(this, 26013, true); // Deserter
1689
1690 if (m_DelayedOperations & DELAYED_BG_MOUNT_RESTORE)
1691 {
1692 if (m_bgData.mountSpell)
1693 {
1694 CastSpell(this, m_bgData.mountSpell, true);
1695 m_bgData.mountSpell = 0;
1696 }
1697 }
1698
1699 if (m_DelayedOperations & DELAYED_BG_TAXI_RESTORE)
1700 {
1701 if (m_bgData.HasTaxiPath())
1702 {
1703 m_taxi.AddTaxiDestination(m_bgData.taxiPath[0]);
1704 m_taxi.AddTaxiDestination(m_bgData.taxiPath[1]);
1705 m_bgData.ClearTaxiPath();
1706
1707 ContinueTaxiFlight();
1708 }
1709 }
1710
1711 if (m_DelayedOperations & DELAYED_BG_GROUP_RESTORE)
1712 {
1713 if (Group* g = GetGroup())
1714 g->SendUpdateToPlayer(GetGUID());
1715 }
1716
1717 //we have executed ALL delayed ops, so clear the flag
1718 m_DelayedOperations = 0;
1719 }
1720
1721 void Player::AddToWorld()
1722 {
1723 ///- Do not add/remove the player from the object storage
1724 ///- It will crash when updating the ObjectAccessor
1725 ///- The player should only be added when logging in
1726 Unit::AddToWorld();
1727
1728 for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
1729 if (m_items[i])
1730 m_items[i]->AddToWorld();
1731 }
1732
1733 void Player::RemoveFromWorld()
1734 {
1735 // cleanup
1736 if (IsInWorld())
1737 {
1738 ///- Release charmed creatures, unsummon totems and remove pets/guardians
1739 StopCastingCharm();
1740 StopCastingBindSight();
1741 UnsummonPetTemporaryIfAny();
1742 sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
1743 sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
1744 }
1745
1746 // Remove items from world before self - player must be found in Item::RemoveFromObjectUpdate
1747 for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
1748 if (m_items[i])
1749 m_items[i]->RemoveFromWorld();
1750
1751 ///- Do not add/remove the player from the object storage
1752 ///- It will crash when updating the ObjectAccessor
1753 ///- The player should only be removed when logging out
1754 Unit::RemoveFromWorld();
1755
1756 for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
1757 iter->second->RemoveFromWorld();
1758
1759 if (m_uint32Values)
1760 {
1761 if (WorldObject* viewpoint = GetViewpoint())
1762 {
1763 TC_LOG_ERROR("entities.player", "Player::RemoveFromWorld: Player '%s' (%s) has viewpoint (Entry:%u, Type: %u) when removed from world",
1764 GetName().c_str(), GetGUID().ToString().c_str(), viewpoint->GetEntry(), viewpoint->GetTypeId());
1765 SetViewpoint(viewpoint, false);
1766 }
1767 }
1768 }
1769
1770 void Player::SetObjectScale(float scale)
1771 {
1772 Unit::SetObjectScale(scale);
1773 SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, scale * DEFAULT_WORLD_OBJECT_SIZE);
1774 SetFloatValue(UNIT_FIELD_COMBATREACH, scale * DEFAULT_COMBAT_REACH);
1775 if (IsInWorld())
1776 SendMovementSetCollisionHeight(scale * GetCollisionHeight(IsMounted()));
1777 }
1778
1779 bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const
1780 {
1781 SpellEffectInfo const* effect = spellInfo->GetEffect(GetMap()->GetDifficultyID(), index);
1782 if (!effect || !effect->IsEffect())
1783 return false;
1784
1785 // players are immune to taunt (the aura and the spell effect).
1786 if (effect->IsAura(SPELL_AURA_MOD_TAUNT))
1787 return true;
1788 if (effect->IsEffect(SPELL_EFFECT_ATTACK_ME))
1789 return true;
1790
1791 return Unit::IsImmunedToSpellEffect(spellInfo, index, caster);
1792 }
1793
1794 void Player::RegenerateAll()
1795 {
1796 m_regenTimerCount += m_regenTimer;
1797
1798 for (Powers power = POWER_MANA; power < MAX_POWERS; power = Powers(power + 1))
1799 if (power != POWER_RUNES)
1800 Regenerate(power);
1801
1802 // Runes act as cooldowns, and they don't need to send any data
1803 if (getClass() == CLASS_DEATH_KNIGHT)
1804 {
1805 uint32 regeneratedRunes = 0;
1806 uint32 regenIndex = 0;
1807 while (regeneratedRunes < MAX_RECHARGING_RUNES && m_runes->CooldownOrder.size() > regenIndex)
1808 {
1809 uint8 runeToRegen = m_runes->CooldownOrder[regenIndex];
1810 uint32 runeCooldown = GetRuneCooldown(runeToRegen);
1811 if (runeCooldown > m_regenTimer)
1812 {
1813 SetRuneCooldown(runeToRegen, runeCooldown - m_regenTimer);
1814 ++regenIndex;
1815 }
1816 else
1817 SetRuneCooldown(runeToRegen, 0);
1818
1819 ++regeneratedRunes;
1820 }
1821 }
1822
1823 if (m_regenTimerCount >= 2000)
1824 {
1825 // Not in combat or they have regeneration
1826 if (!IsInCombat() || IsPolymorphed() || m_baseHealthRegen || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) || HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT))
1827 RegenerateHealth();
1828
1829 m_regenTimerCount -= 2000;
1830 }
1831
1832 m_regenTimer = 0;
1833 }
1834
1835 void Player::Regenerate(Powers power)
1836 {
1837 // Skip regeneration for power type we cannot have
1838 uint32 powerIndex = GetPowerIndex(power);
1839 if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
1840 return;
1841
1842 /// @todo possible use of miscvalueb instead of amount
1843 if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER, power))
1844 return;
1845
1846 int32 curValue = GetPower(power);
1847
1848 // TODO: updating haste should update UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER for certain power types
1849 PowerTypeEntry const* powerType = sDB2Manager.GetPowerTypeEntry(power);
1850 if (!powerType)
1851 return;
1852
1853 float addvalue = 0.0f;
1854 if (!IsInCombat())
1855 {
1856 if (powerType->RegenInterruptTimeMS && GetMSTimeDiffToNow(m_combatExitTime) < uint32(powerType->RegenInterruptTimeMS))
1857 return;
1858
1859 addvalue = (powerType->RegenPeace + GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER + powerIndex)) * 0.001f * m_regenTimer;
1860 }
1861 else
1862 addvalue = (powerType->RegenCombat + GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex)) * 0.001f * m_regenTimer;
1863
1864 static Rates const RatesForPower[MAX_POWERS] =
1865 {
1866 RATE_POWER_MANA,
1867 RATE_POWER_RAGE_LOSS,
1868 RATE_POWER_FOCUS,
1869 RATE_POWER_ENERGY,
1870 RATE_POWER_COMBO_POINTS_LOSS,
1871 MAX_RATES, // runes
1872 RATE_POWER_RUNIC_POWER_LOSS,
1873 RATE_POWER_SOUL_SHARDS,
1874 RATE_POWER_LUNAR_POWER,
1875 RATE_POWER_HOLY_POWER,
1876 MAX_RATES, // alternate
1877 RATE_POWER_MAELSTROM,
1878 RATE_POWER_CHI,
1879 RATE_POWER_INSANITY,
1880 MAX_RATES, // burning embers, unused
1881 MAX_RATES, // demonic fury, unused
1882 RATE_POWER_ARCANE_CHARGES,
1883 RATE_POWER_FURY,
1884 RATE_POWER_PAIN,
1885 };
1886
1887 if (RatesForPower[power] != MAX_RATES)
1888 addvalue *= sWorld->getRate(RatesForPower[power]);
1889
1890 // Mana regen calculated in Player::UpdateManaRegen()
1891 if (power != POWER_MANA)
1892 {
1893 addvalue *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, power);
1894
1895 addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * ((power != POWER_ENERGY) ? m_regenTimerCount : m_regenTimer) / (5 * IN_MILLISECONDS);
1896 }
1897
1898 int32 minPower = powerType->MinPower;
1899 int32 maxPower = GetMaxPower(power);
1900
1901 if (addvalue < 0.0f)
1902 {
1903 if (curValue <= minPower)
1904 return;
1905 }
1906 else if (addvalue > 0.0f)
1907 {
1908 if (curValue >= maxPower)
1909 return;
1910 }
1911 else
1912 return;
1913
1914 addvalue += m_powerFraction[powerIndex];
1915 int32 integerValue = int32(std::fabs(addvalue));
1916
1917 if (powerType->CenterPower)
1918 {
1919 if (curValue > powerType->CenterPower)
1920 {
1921 addvalue = -std::abs(addvalue);
1922 minPower = powerType->CenterPower;
1923 }
1924 else if (curValue < powerType->CenterPower)
1925 {
1926 addvalue = std::abs(addvalue);
1927 maxPower = powerType->CenterPower;
1928 }
1929 else
1930 return;
1931 }
1932
1933 if (addvalue < 0.0f)
1934 {
1935 if (curValue > minPower + integerValue)
1936 {
1937 curValue -= integerValue;
1938 m_powerFraction[powerIndex] = addvalue + integerValue;
1939 }
1940 else
1941 {
1942 curValue = minPower;
1943 m_powerFraction[powerIndex] = 0;
1944 }
1945 }
1946 else
1947 {
1948 if (curValue + integerValue <= maxPower)
1949 {
1950 curValue += integerValue;
1951 m_powerFraction[powerIndex] = addvalue - integerValue;
1952 }
1953 else
1954 {
1955 curValue = maxPower;
1956 m_powerFraction[powerIndex] = 0;
1957 }
1958 }
1959
1960 if (m_regenTimerCount >= 2000)
1961 SetPower(power, curValue);
1962 else
1963 UpdateUInt32Value(UNIT_FIELD_POWER + powerIndex, curValue);
1964 }
1965
1966 void Player::RegenerateHealth()
1967 {
1968 uint32 curValue = GetHealth();
1969 uint32 maxValue = GetMaxHealth();
1970
1971 if (curValue >= maxValue)
1972 return;
1973
1974 float HealthIncreaseRate = sWorld->getRate(RATE_HEALTH);
1975 float addValue = 0.0f;
1976
1977 // polymorphed case
1978 if (IsPolymorphed())
1979 addValue = float(GetMaxHealth()) / 3.0f;
1980 // normal regen case (maybe partly in combat case)
1981 else if (!IsInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT))
1982 {
1983 addValue = HealthIncreaseRate;
1984
1985 if (!IsInCombat())
1986 {
1987 if (getLevel() < 15)
1988 addValue = (0.20f * ((float)GetMaxHealth()) / getLevel() * HealthIncreaseRate);
1989 else
1990 addValue = 0.015f * ((float)GetMaxHealth()) * HealthIncreaseRate;
1991
1992 addValue *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
1993
1994 addValue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * 0.4f;
1995 }
1996 else if (HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT))
1997 ApplyPct(addValue, GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT));
1998
1999 if (!IsStandState())
2000 addValue *= 1.5f;
2001 }
2002
2003 // always regeneration bonus (including combat)
2004 addValue += GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT);
2005 addValue += m_baseHealthRegen / 2.5f;
2006
2007 if (addValue < 0.0f)
2008 addValue = 0.0f;
2009
2010 ModifyHealth(int32(addValue));
2011 }
2012
2013 void Player::ResetAllPowers()
2014 {
2015 SetFullHealth();
2016
2017 switch (GetPowerType())
2018 {
2019 case POWER_MANA:
2020 SetFullPower(POWER_MANA);
2021 break;
2022 case POWER_RAGE:
2023 SetPower(POWER_RAGE, 0);
2024 break;
2025 case POWER_ENERGY:
2026 SetFullPower(POWER_ENERGY);
2027 break;
2028 case POWER_RUNIC_POWER:
2029 SetPower(POWER_RUNIC_POWER, 0);
2030 break;
2031 case POWER_LUNAR_POWER:
2032 SetPower(POWER_LUNAR_POWER, 0);
2033 break;
2034 default:
2035 break;
2036 }
2037 }
2038
2039 bool Player::CanInteractWithQuestGiver(Object* questGiver) const
2040 {
2041 switch (questGiver->GetTypeId())
2042 {
2043 case TYPEID_UNIT:
2044 return GetNPCIfCanInteractWith(questGiver->GetGUID(), UNIT_NPC_FLAG_QUESTGIVER) != nullptr;
2045 case TYPEID_GAMEOBJECT:
2046 return GetGameObjectIfCanInteractWith(questGiver->GetGUID(), GAMEOBJECT_TYPE_QUESTGIVER) != nullptr;
2047 case TYPEID_PLAYER:
2048 return IsAlive() && questGiver->ToPlayer()->IsAlive();
2049 case TYPEID_ITEM:
2050 return IsAlive();
2051 default:
2052 break;
2053 }
2054 return false;
2055 }
2056
2057 Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, uint64 npcflagmask) const
2058 {
2059 // unit checks
2060 if (!guid)
2061 return nullptr;
2062
2063 if (!IsInWorld())
2064 return nullptr;
2065
2066 if (IsInFlight())
2067 return nullptr;
2068
2069 // exist (we need look pets also for some interaction (quest/etc)
2070 Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid);
2071 if (!creature)
2072 return nullptr;
2073
2074 // Deathstate checks
2075 if (!IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_GHOST_VISIBLE))
2076 return nullptr;
2077
2078 // alive or spirit healer
2079 if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_INTERACT_WHILE_DEAD))
2080 return nullptr;
2081
2082 // appropriate npc type
2083 if (npcflagmask && !creature->HasFlag64(UNIT_NPC_FLAGS, npcflagmask))
2084 return nullptr;
2085
2086 // not allow interaction under control, but allow with own pets
2087 if (!creature->GetCharmerGUID().IsEmpty())
2088 return nullptr;
2089
2090 // not unfriendly/hostile
2091 if (creature->GetReactionTo(this) <= REP_UNFRIENDLY)
2092 return nullptr;
2093
2094 // not too far
2095 if (!creature->IsWithinDistInMap(this, INTERACTION_DISTANCE))
2096 return nullptr;
2097
2098 return creature;
2099 }
2100
2101 GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const
2102 {
2103 if (GameObject* go = GetMap()->GetGameObject(guid))
2104 {
2105 if (go->IsWithinDistInMap(this, go->GetInteractionDistance()))
2106 return go;
2107
2108 TC_LOG_DEBUG("maps", "Player::GetGameObjectIfCanInteractWith: GameObject '%s' (%s) is too far away from player '%s' (%s) to be used by him (Distance: %f, maximal %f is allowed)",
2109 go->GetGOInfo()->name.c_str(), go->GetGUID().ToString().c_str(), GetName().c_str(), GetGUID().ToString().c_str(), go->GetDistance(this), go->GetInteractionDistance());
2110 }
2111
2112 return nullptr;
2113 }
2114
2115 GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid, GameobjectTypes type) const
2116 {
2117 if (GameObject* go = GetMap()->GetGameObject(guid))
2118 {
2119 if (go->GetGoType() == type)
2120 {
2121 if (go->IsWithinDistInMap(this, go->GetInteractionDistance()))
2122 return go;
2123
2124 TC_LOG_DEBUG("maps", "Player::GetGameObjectIfCanInteractWith: GameObject '%s' (%s) is too far away from player '%s' (%s) to be used by him (Distance: %f, maximal %f is allowed)",
2125 go->GetGOInfo()->name.c_str(), go->GetGUID().ToString().c_str(), GetName().c_str(), GetGUID().ToString().c_str(), go->GetDistance(this), go->GetInteractionDistance());
2126 }
2127 }
2128
2129 return nullptr;
2130 }
2131
2132 bool Player::IsUnderWater() const
2133 {
2134 return IsInWater() &&
2135 GetPositionZ() < (GetMap()->GetWaterLevel(GetPhaseShift(), GetPositionX(), GetPositionY()) - 2);
2136 }
2137
2138 void Player::SetInWater(bool apply)
2139 {
2140 if (m_isInWater == apply)
2141 return;
2142
2143 //define player in water by opcodes
2144 //move player's guid into HateOfflineList of those mobs
2145 //which can't swim and move guid back into ThreatList when
2146 //on surface.
2147 /// @todo exist also swimming mobs, and function must be symmetric to enter/leave water
2148 m_isInWater = apply;
2149
2150 // remove auras that need water/land
2151 RemoveAurasWithInterruptFlags(apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
2152
2153 getHostileRefManager().updateThreatTables();
2154 }
2155
2156 bool Player::IsInAreaTriggerRadius(const AreaTriggerEntry* trigger) const
2157 {
2158 if (!trigger)
2159 return false;
2160
2161 if (int32(GetMapId()) != trigger->ContinentID && !GetPhaseShift().HasVisibleMapId(trigger->ContinentID))
2162 return false;
2163
2164 if (trigger->PhaseID || trigger->PhaseGroupID || trigger->PhaseUseFlags)
2165 if (!PhasingHandler::InDbPhaseShift(this, trigger->PhaseUseFlags, trigger->PhaseID, trigger->PhaseGroupID))
2166 return false;
2167
2168 if (trigger->Radius > 0.f)
2169 {
2170 // if we have radius check it
2171 float dist = GetDistance(trigger->Pos.X, trigger->Pos.Y, trigger->Pos.Z);
2172 if (dist > trigger->Radius)
2173 return false;
2174 }
2175 else
2176 {
2177 Position center(trigger->Pos.X, trigger->Pos.Y, trigger->Pos.Z, trigger->BoxYaw);
2178 if (!IsWithinBox(center, trigger->BoxLength / 2.f, trigger->BoxWidth / 2.f, trigger->BoxHeight / 2.f))
2179 return false;
2180 }
2181
2182 return true;
2183 }
2184
2185 void Player::SetGameMaster(bool on)
2186 {
2187 if (on)
2188 {
2189 m_ExtraFlags |= PLAYER_EXTRA_GM_ON;
2190 setFaction(35);
2191 SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
2192 SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS);
2193
2194 if (Pet* pet = GetPet())
2195 {
2196 pet->setFaction(35);
2197 pet->getHostileRefManager().setOnlineOfflineState(false);
2198 }
2199
2200 RemoveByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP);
2201 ResetContestedPvP();
2202
2203 getHostileRefManager().setOnlineOfflineState(false);
2204 CombatStopWithPets();
2205
2206 PhasingHandler::SetAlwaysVisible(GetPhaseShift(), true);
2207 m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity());
2208 }
2209 else
2210 {
2211 PhasingHandler::SetAlwaysVisible(GetPhaseShift(), false);
2212
2213 m_ExtraFlags &= ~ PLAYER_EXTRA_GM_ON;
2214 setFactionForRace(getRace());
2215 RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
2216 RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS);
2217
2218 if (Pet* pet = GetPet())
2219 {
2220 pet->setFaction(getFaction());
2221 pet->getHostileRefManager().setOnlineOfflineState(true);
2222 }
2223
2224 // restore FFA PvP Server state
2225 if (sWorld->IsFFAPvPRealm())
2226 SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP);
2227
2228 // restore FFA PvP area state, remove not allowed for GM mounts
2229 UpdateArea(m_areaUpdateId);
2230
2231 getHostileRefManager().setOnlineOfflineState(true);
2232 m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER);
2233 }
2234
2235 UpdateObjectVisibility();
2236 }
2237
2238 bool Player::CanBeGameMaster() const
2239 {
2240 return GetSession()->HasPermission(rbac::RBAC_PERM_COMMAND_GM);
2241 }
2242
2243 void Player::SetGMVisible(bool on)
2244 {
2245 if (on)
2246 {
2247 m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; //remove flag
2248 m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER);
2249 }
2250 else
2251 {
2252 m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE; //add flag
2253
2254 SetAcceptWhispers(false);
2255 SetGameMaster(true);
2256
2257 m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity());
2258 }
2259
2260 for (Channel* channel : m_channels)
2261 channel->SetInvisible(this, !on);
2262 }
2263
2264 bool Player::IsGroupVisibleFor(Player const* p) const
2265 {
2266 switch (sWorld->getIntConfig(CONFIG_GROUP_VISIBILITY))
2267 {
2268 default: return IsInSameGroupWith(p);
2269 case 1: return IsInSameRaidWith(p);
2270 case 2: return GetTeam() == p->GetTeam();
2271 }
2272 }
2273
2274 bool Player::IsInSameGroupWith(Player const* p) const
2275 {
2276 return p == this || (GetGroup() != nullptr &&
2277 GetGroup() == p->GetGroup() &&
2278 GetGroup()->SameSubGroup(this, p));
2279 }
2280
2281 bool Player::IsInSameRaidWith(Player const* p) const
2282 {
2283 return p == this || (GetGroup() != nullptr && GetGroup() == p->GetGroup());
2284 }
2285
2286 ///- If the player is invited, remove him. If the group if then only 1 person, disband the group.
2287 /// @todo Shouldn't we also check if there is no other invitees before disbanding the group?
2288 void Player::UninviteFromGroup()
2289 {
2290 Group* group = GetGroupInvite();
2291 if (!group)
2292 return;
2293
2294 group->RemoveInvite(this);
2295
2296 if (group->GetMembersCount() <= 1) // group has just 1 member => disband
2297 {
2298 if (group->IsCreated())
2299 {
2300 group->Disband(true);
2301 }
2302 else
2303 {
2304 group->RemoveAllInvites();
2305 delete group;
2306 }
2307 }
2308 }
2309
2310 void Player::RemoveFromGroup(Group* group, ObjectGuid guid, RemoveMethod method /* = GROUP_REMOVEMETHOD_DEFAULT*/, ObjectGuid kicker /* = ObjectGuid::Empty */, const char* reason /* = NULL */)
2311 {
2312 if (!group)
2313 return;
2314
2315 group->RemoveMember(guid, method, kicker, reason);
2316 }
2317
2318 void Player::SetXP(uint32 xp)
2319 {
2320 SetUInt32Value(PLAYER_XP, xp);
2321
2322 int32 playerLevelDelta = 0;
2323
2324 // If XP < 50%, player should see scaling creature with -1 level except for level max
2325 if (getLevel() < MAX_LEVEL && xp < (GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 2))
2326 playerLevelDelta = -1;
2327
2328 SetInt32Value(PLAYER_FIELD_SCALING_PLAYER_LEVEL_DELTA, playerLevelDelta);
2329 }
2330
2331 void Player::GiveXP(uint32 xp, Unit* victim, float group_rate)
2332 {
2333 if (xp < 1)
2334 return;
2335
2336 if (!IsAlive() && !GetBattlegroundId())
2337 return;
2338
2339 if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_NO_XP_GAIN))
2340 return;
2341
2342 if (victim && victim->GetTypeId() == TYPEID_UNIT && !victim->ToCreature()->hasLootRecipient())
2343 return;
2344
2345 uint8 level = getLevel();
2346
2347 sScriptMgr->OnGivePlayerXP(this, xp, victim);
2348
2349 // XP to money conversion processed in Player::RewardQuest
2350 if (level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
2351 return;
2352
2353 uint32 bonus_xp;
2354 bool recruitAFriend = GetsRecruitAFriendBonus(true);
2355
2356 // RaF does NOT stack with rested experience
2357 if (recruitAFriend)
2358 bonus_xp = 2 * xp; // xp + bonus_xp must add up to 3 * xp for RaF; calculation for quests done client-side
2359 else
2360 bonus_xp = victim ? _restMgr->GetRestBonusFor(REST_TYPE_XP, xp) : 0; // XP resting bonus
2361
2362 WorldPackets::Character::LogXPGain packet;
2363 packet.Victim = victim ? victim->GetGUID() : ObjectGuid::Empty;
2364 packet.Original = xp + bonus_xp;
2365 packet.Reason = victim ? LOG_XP_REASON_KILL : LOG_XP_REASON_NO_KILL;
2366 packet.Amount = xp;
2367 packet.GroupBonus = group_rate;
2368 packet.ReferAFriendBonusType = recruitAFriend ? 1 : 0;
2369 GetSession()->SendPacket(packet.Write());
2370
2371 uint32 curXP = GetUInt32Value(PLAYER_XP);
2372 uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
2373 uint32 newXP = curXP + xp + bonus_xp;
2374
2375 while (newXP >= nextLvlXP && level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
2376 {
2377 newXP -= nextLvlXP;
2378
2379 if (level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
2380 GiveLevel(level + 1);
2381
2382 level = getLevel();
2383 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
2384 }
2385
2386 SetXP(newXP);
2387 }
2388
2389 // Update player to next level
2390 // Current player experience not update (must be update by caller)
2391 void Player::GiveLevel(uint8 level)
2392 {
2393 uint8 oldLevel = getLevel();
2394 if (level == oldLevel)
2395 return;
2396
2397 if (Guild* guild = GetGuild())
2398 guild->UpdateMemberData(this, GUILD_MEMBER_DATA_LEVEL, level);
2399
2400 PlayerLevelInfo info;
2401 sObjectMgr->GetPlayerLevelInfo(getRace(), getClass(), level, &info);
2402
2403 uint32 basemana = 0;
2404 sObjectMgr->GetPlayerClassLevelInfo(getClass(), level, basemana);
2405
2406 WorldPackets::Misc::LevelUpInfo packet;
2407 packet.Level = level;
2408 packet.HealthDelta = 0;
2409
2410 /// @todo find some better solution
2411 // for (int i = 0; i < MAX_STORED_POWERS; ++i)
2412 packet.PowerDelta[0] = int32(basemana) - int32(GetCreateMana());
2413 packet.PowerDelta[1] = 0;
2414 packet.PowerDelta[2] = 0;
2415 packet.PowerDelta[3] = 0;
2416 packet.PowerDelta[4] = 0;
2417 packet.PowerDelta[5] = 0;
2418
2419 for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
2420 packet.StatDelta[i] = int32(info.stats[i]) - GetCreateStat(Stats(i));
2421
2422 uint32 const* rowLevels = (getClass() != CLASS_DEATH_KNIGHT) ? DefaultTalentRowLevels : DKTalentRowLevels;
2423
2424 packet.Cp = std::find(rowLevels, rowLevels + MAX_TALENT_TIERS, level) != (rowLevels + MAX_TALENT_TIERS);
2425
2426 GetSession()->SendPacket(packet.Write());
2427
2428 SetUInt32Value(PLAYER_NEXT_LEVEL_XP, sObjectMgr->GetXPForLevel(level));
2429
2430 //update level, max level of skills
2431 m_Played_time[PLAYED_TIME_LEVEL] = 0; // Level Played Time reset
2432
2433 _ApplyAllLevelScaleItemMods(false);
2434
2435 SetLevel(level);
2436
2437 UpdateSkillsForLevel();
2438 LearnDefaultSkills();
2439 LearnSpecializationSpells();
2440
2441 // save base values (bonuses already included in stored stats
2442 for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
2443 SetCreateStat(Stats(i), info.stats[i]);
2444
2445 SetCreateHealth(0);
2446 SetCreateMana(basemana);
2447
2448 InitTalentForLevel();
2449 InitTaxiNodesForLevel();
2450
2451 if (level < PLAYER_LEVEL_MIN_HONOR)
2452 ResetPvpTalents();
2453
2454 UpdateAllStats();
2455
2456 if (sWorld->getBoolConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up
2457 UpdateSkillsToMaxSkillsForLevel();
2458
2459 _ApplyAllLevelScaleItemMods(true); // Moved to above SetFullHealth so player will have full health from Heirlooms
2460
2461 // Only health and mana are set to maximum.
2462 SetFullHealth();
2463 SetFullPower(POWER_MANA);
2464
2465 // update level to hunter/summon pet
2466 if (Pet* pet = GetPet())
2467 pet->SynchronizeLevelWithOwner();
2468
2469 if (MailLevelReward const* mailReward = sObjectMgr->GetMailLevelReward(level, getRaceMask()))
2470 {
2471 /// @todo Poor design of mail system
2472 SQLTransaction trans = CharacterDatabase.BeginTransaction();
2473 MailDraft(mailReward->mailTemplateId).SendMailTo(trans, this, MailSender(MAIL_CREATURE, uint64(mailReward->senderEntry)));
2474 CharacterDatabase.CommitTransaction(trans);
2475 }
2476
2477 UpdateCriteria(CRITERIA_TYPE_REACH_LEVEL);
2478
2479 // Refer-A-Friend
2480 if (GetSession()->GetRecruiterId())
2481 {
2482 if (level < sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL))
2483 {
2484 if (level % 2 == 0)
2485 {
2486 ++m_grantableLevels;
2487
2488 if (!HasByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01))
2489 SetByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01);
2490 }
2491 }
2492 }
2493
2494 sScriptMgr->OnPlayerLevelChanged(this, oldLevel);
2495 }
2496
2497 void Player::InitTalentForLevel()
2498 {
2499 uint8 level = getLevel();
2500 // talents base at level diff (talents = level - 9 but some can be used already)
2501 if (level < MIN_SPECIALIZATION_LEVEL)
2502 ResetTalentSpecialization();
2503
2504 uint32 talentTiers = CalculateTalentsTiers();
2505 if (level < 15)
2506 {
2507 // Remove all talent points
2508 ResetTalents(true);
2509 }
2510 else
2511 {
2512 if (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_MORE_TALENTS_THAN_ALLOWED))
2513 for (uint32 t = talentTiers; t < MAX_TALENT_TIERS; ++t)
2514 for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c)
2515 for (TalentEntry const* talent : sDB2Manager.GetTalentsByPosition(getClass(), t, c))
2516 RemoveTalent(talent);
2517 }
2518
2519 SetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS, talentTiers);
2520
2521 if (!GetSession()->PlayerLoading())
2522 SendTalentsInfoData(); // update at client
2523 }
2524
2525 void Player::InitStatsForLevel(bool reapplyMods)
2526 {
2527 if (reapplyMods) //reapply stats values only on .reset stats (level) command
2528 _RemoveAllStatBonuses();
2529
2530 uint32 basemana = 0;
2531 sObjectMgr->GetPlayerClassLevelInfo(getClass(), getLevel(), basemana);
2532
2533 PlayerLevelInfo info;
2534 sObjectMgr->GetPlayerLevelInfo(getRace(), getClass(), getLevel(), &info);
2535
2536 SetUInt32Value(PLAYER_FIELD_MAX_LEVEL, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
2537 SetUInt32Value(PLAYER_NEXT_LEVEL_XP, sObjectMgr->GetXPForLevel(getLevel()));
2538
2539 // reset before any aura state sources (health set/aura apply)
2540 SetUInt32Value(UNIT_FIELD_AURASTATE, 0);
2541
2542 UpdateSkillsForLevel();
2543
2544 // set default cast time multiplier
2545 SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
2546 SetFloatValue(UNIT_MOD_CAST_HASTE, 1.0f);
2547 SetFloatValue(UNIT_FIELD_MOD_HASTE, 1.0f);
2548 SetFloatValue(UNIT_FIELD_MOD_RANGED_HASTE, 1.0f);
2549 SetFloatValue(UNIT_FIELD_MOD_HASTE_REGEN, 1.0f);
2550 SetFloatValue(UNIT_FIELD_MOD_TIME_RATE, 1.0f);
2551
2552 // reset size before reapply auras
2553 SetObjectScale(1.0f);
2554
2555 // save base values (bonuses already included in stored stats
2556 for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
2557 SetCreateStat(Stats(i), info.stats[i]);
2558
2559 for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
2560 SetStat(Stats(i), info.stats[i]);
2561
2562 SetCreateHealth(0);
2563
2564 //set create powers
2565 SetCreateMana(basemana);
2566
2567 SetArmor(int32(m_createStats[STAT_AGILITY]*2));
2568
2569 InitStatBuffMods();
2570
2571 //reset rating fields values
2572 for (uint16 index = PLAYER_FIELD_COMBAT_RATING_1; index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index)
2573 SetUInt32Value(index, 0);
2574
2575 SetUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, 0);
2576 SetFloatValue(PLAYER_FIELD_MOD_HEALING_PCT, 1.0f);
2577 SetFloatValue(PLAYER_FIELD_MOD_HEALING_DONE_PCT, 1.0f);
2578 SetFloatValue(PLAYER_FIELD_MOD_PERIODIC_HEALING_DONE_PERCENT, 1.0f);
2579 for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
2580 {
2581 SetInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + i, 0);
2582 SetInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, 0);
2583 SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT + i, 1.00f);
2584 }
2585
2586 SetFloatValue(PLAYER_FIELD_MOD_SPELL_POWER_PCT, 1.0f);
2587
2588 //reset attack power, damage and attack speed fields
2589 for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i)
2590 SetFloatValue(UNIT_FIELD_BASEATTACKTIME + i, float(BASE_ATTACK_TIME));
2591
2592 SetFloatValue(UNIT_FIELD_MINDAMAGE, 0.0f);
2593 SetFloatValue(UNIT_FIELD_MAXDAMAGE, 0.0f);
2594 SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, 0.0f);
2595 SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, 0.0f);
2596 SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, 0.0f);
2597 SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, 0.0f);
2598 for (uint16 i = 0; i < 3; ++i)
2599 {
2600 SetFloatValue(PLAYER_FIELD_WEAPON_DMG_MULTIPLIERS + i, 1.0f);
2601 SetFloatValue(PLAYER_FIELD_WEAPON_ATK_SPEED_MULTIPLIERS + i, 1.0f);
2602 }
2603
2604 SetInt32Value(UNIT_FIELD_ATTACK_POWER, 0);
2605 SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, 0.0f);
2606 SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0);
2607 SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER, 0.0f);
2608
2609 // Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
2610 SetFloatValue(PLAYER_CRIT_PERCENTAGE, 0.0f);
2611 SetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE, 0.0f);
2612 SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE, 0.0f);
2613
2614 // Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
2615 SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1, 0.0f);
2616
2617 SetFloatValue(PLAYER_PARRY_PERCENTAGE, 0.0f);
2618 SetFloatValue(PLAYER_BLOCK_PERCENTAGE, 0.0f);
2619
2620 // Static 30% damage blocked
2621 SetUInt32Value(PLAYER_SHIELD_BLOCK, 30);
2622
2623 // Dodge percentage
2624 SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f);
2625
2626 // set armor (resistance 0) to original value (create_agility*2)
2627 SetArmor(int32(m_createStats[STAT_AGILITY]*2));
2628 SetResistanceBuffMods(SPELL_SCHOOL_NORMAL, true, 0.0f);
2629 SetResistanceBuffMods(SPELL_SCHOOL_NORMAL, false, 0.0f);
2630 // set other resistance to original value (0)
2631 for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
2632 {
2633 SetResistance(SpellSchools(i), 0);
2634 SetResistanceBuffMods(SpellSchools(i), true, 0.0f);
2635 SetResistanceBuffMods(SpellSchools(i), false, 0.0f);
2636 }
2637
2638 SetUInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, 0);
2639 SetUInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE, 0);
2640 for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
2641 {
2642 SetUInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + i, 0);
2643 SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER + i, 0.0f);
2644 }
2645 // Reset no reagent cost field
2646 for (uint8 i = 0; i < 3; ++i)
2647 SetUInt32Value(PLAYER_NO_REAGENT_COST_1 + i, 0);
2648 // Init data for form but skip reapply item mods for form
2649 InitDataForForm(reapplyMods);
2650
2651 // save new stats
2652 for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
2653 SetMaxPower(Powers(i), uint32(GetCreatePowers(Powers(i))));
2654
2655 SetMaxHealth(0); // stamina bonus will applied later
2656
2657 // cleanup mounted state (it will set correctly at aura loading if player saved at mount.
2658 SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
2659
2660 // cleanup unit flags (will be re-applied if need at aura load).
2661 RemoveFlag(UNIT_FIELD_FLAGS,
2662 UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_ATTACKABLE_1 |
2663 UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_LOOTING |
2664 UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED |
2665 UNIT_FLAG_STUNNED | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED |
2666 UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_NOT_SELECTABLE |
2667 UNIT_FLAG_SKINNABLE | UNIT_FLAG_MOUNT | UNIT_FLAG_TAXI_FLIGHT );
2668 SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); // must be set
2669
2670 SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER);// must be set
2671
2672 // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example.
2673 RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST | PLAYER_ALLOW_ONLY_ABILITY);
2674
2675 RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes
2676 RemoveByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SANCTUARY);
2677
2678 // restore if need some important flags
2679 SetByteValue(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_IGNORE_POWER_REGEN_PREDICTION_MASK, 0);
2680 SetByteValue(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, 0);
2681 SetByteValue(PLAYER_FIELD_BYTES2, 3, 0);
2682
2683 if (reapplyMods) // reapply stats values only on .reset stats (level) command
2684 _ApplyAllStatBonuses();
2685
2686 // set current level health and mana/energy to maximum after applying all mods.
2687 SetFullHealth();
2688 SetFullPower(POWER_MANA);
2689 SetFullPower(POWER_ENERGY);
2690 if (GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
2691 SetFullPower(POWER_RAGE);
2692 SetFullPower(POWER_FOCUS);
2693 SetPower(POWER_RUNIC_POWER, 0);
2694
2695 // update level to hunter/summon pet
2696 if (Pet* pet = GetPet())
2697 pet->SynchronizeLevelWithOwner();
2698 }
2699
2700 void Player::SendKnownSpells()
2701 {
2702 WorldPackets::Spells::SendKnownSpells knownSpells;
2703 knownSpells.InitialLogin = false; /// @todo
2704
2705 knownSpells.KnownSpells.reserve(m_spells.size());
2706 for (PlayerSpellMap::value_type const& spell : m_spells)
2707 {
2708 if (spell.second->state == PLAYERSPELL_REMOVED)
2709 continue;
2710
2711 if (!spell.second->active || spell.second->disabled)
2712 continue;
2713
2714 knownSpells.KnownSpells.push_back(spell.first);
2715 }
2716
2717 SendDirectMessage(knownSpells.Write());
2718 }
2719
2720 void Player::RemoveMail(uint32 id)
2721 {
2722 for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
2723 {
2724 if ((*itr)->messageID == id)
2725 {
2726 //do not delete item, because Player::removeMail() is called when returning mail to sender.
2727 m_mail.erase(itr);
2728 return;
2729 }
2730 }
2731 }
2732
2733 void Player::SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError, ObjectGuid::LowType item_guid, uint32 item_count) const
2734 {
2735 WorldPackets::Mail::MailCommandResult result;
2736
2737 result.MailID = mailId;
2738 result.Command = mailAction;
2739 result.ErrorCode = mailError;
2740
2741 if (mailError == MAIL_ERR_EQUIP_ERROR)
2742 result.BagResult = equipError;
2743 else if (mailAction == MAIL_ITEM_TAKEN)
2744 {
2745 result.AttachID = item_guid;
2746 result.QtyInInventory = item_count;
2747 }
2748 GetSession()->SendPacket(result.Write());
2749 }
2750
2751 void Player::SendNewMail() const
2752 {
2753 // deliver undelivered mail
2754 WorldPackets::Mail::NotifyRecievedMail notify;
2755 notify.Delay = 0.0f;
2756
2757 GetSession()->SendPacket(notify.Write());
2758 }
2759
2760 void Player::UpdateNextMailTimeAndUnreads()
2761 {
2762 // calculate next delivery time (min. from non-delivered mails
2763 // and recalculate unReadMail
2764 time_t cTime = time(nullptr);
2765 m_nextMailDelivereTime = 0;
2766 unReadMails = 0;
2767 for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
2768 {
2769 if ((*itr)->deliver_time > cTime)
2770 {
2771 if (!m_nextMailDelivereTime || m_nextMailDelivereTime > (*itr)->deliver_time)
2772 m_nextMailDelivereTime = (*itr)->deliver_time;
2773 }
2774 else if (((*itr)->checked & MAIL_CHECK_MASK_READ) == 0)
2775 ++unReadMails;
2776 }
2777 }
2778
2779 void Player::AddNewMailDeliverTime(time_t deliver_time)
2780 {
2781 if (deliver_time <= time(nullptr)) // ready now
2782 {
2783 ++unReadMails;
2784 SendNewMail();
2785 }
2786 else // not ready and no have ready mails
2787 {
2788 if (!m_nextMailDelivereTime || m_nextMailDelivereTime > deliver_time)
2789 m_nextMailDelivereTime = deliver_time;
2790 }
2791 }
2792
2793 void DeleteSpellFromAllPlayers(uint32 spellId)
2794 {
2795 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_SPELL_SPELLS);
2796 stmt->setUInt32(0, spellId);
2797 CharacterDatabase.Execute(stmt);
2798 }
2799
2800 bool Player::AddTalent(TalentEntry const* talent, uint8 spec, bool learning)
2801 {
2802 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellID);
2803 if (!spellInfo)
2804 {
2805 TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: %u) does not exist.", talent->SpellID);
2806 return false;
2807 }
2808
2809 if (!SpellMgr::IsSpellValid(spellInfo, this, false))
2810 {
2811 TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: %u) is invalid", talent->SpellID);
2812 return false;
2813 }
2814
2815 if (talent->OverridesSpellID)
2816 AddOverrideSpell(talent->OverridesSpellID, talent->SpellID);
2817
2818 PlayerTalentMap::iterator itr = GetTalentMap(spec)->find(talent->ID);
2819 if (itr != GetTalentMap(spec)->end())
2820 itr->second = PLAYERSPELL_UNCHANGED;
2821 else
2822 (*GetTalentMap(spec))[talent->ID] = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
2823
2824 return true;
2825 }
2826
2827 void Player::RemoveTalent(TalentEntry const* talent)
2828 {
2829 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellID);
2830 if (!spellInfo)
2831 return;
2832
2833 RemoveSpell(talent->SpellID, true);
2834
2835 // search for spells that the talent teaches and unlearn them
2836 for (SpellEffectInfo const* effect : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE))
2837 if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
2838 RemoveSpell(effect->TriggerSpell, true);
2839
2840 if (talent->OverridesSpellID)
2841 RemoveOverrideSpell(talent->OverridesSpellID, talent->SpellID);
2842
2843 // if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
2844 PlayerTalentMap::iterator plrTalent = GetTalentMap(GetActiveTalentGroup())->find(talent->ID);
2845 if (plrTalent != GetTalentMap(GetActiveTalentGroup())->end())
2846 plrTalent->second = PLAYERSPELL_REMOVED;
2847 }
2848
2849 bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading /*= false*/, int32 fromSkill /*= 0*/)
2850 {
2851 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
2852 if (!spellInfo)
2853 {
2854 // do character spell book cleanup (all characters)
2855 if (!IsInWorld() && !learning) // spell load case
2856 {
2857 TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) does not exist. deleting for all characters in `character_spell`.", spellId);
2858
2859 DeleteSpellFromAllPlayers(spellId);
2860 }
2861 else
2862 TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) does not exist", spellId);
2863
2864 return false;
2865 }
2866
2867 if (!SpellMgr::IsSpellValid(spellInfo, this, false))
2868 {
2869 // do character spell book cleanup (all characters)
2870 if (!IsInWorld() && !learning) // spell load case
2871 {
2872 TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) is invalid. deleting for all characters in `character_spell`.", spellId);
2873
2874 DeleteSpellFromAllPlayers(spellId);
2875 }
2876 else
2877 TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) is invalid", spellId);
2878
2879 return false;
2880 }
2881
2882 PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
2883
2884 bool dependent_set = false;
2885 bool disabled_case = false;
2886 bool superceded_old = false;
2887
2888 PlayerSpellMap::iterator itr = m_spells.find(spellId);
2889
2890 // Remove temporary spell if found to prevent conflicts
2891 if (itr != m_spells.end() && itr->second->state == PLAYERSPELL_TEMPORARY)
2892 RemoveTemporarySpell(spellId);
2893 else if (itr != m_spells.end())
2894 {
2895 uint32 next_active_spell_id = 0;
2896 // fix activate state for non-stackable low rank (and find next spell for !active case)
2897 if (spellInfo->IsRanked())
2898 {
2899 if (uint32 next = sSpellMgr->GetNextSpellInChain(spellId))
2900 {
2901 if (HasSpell(next))
2902 {
2903 // high rank already known so this must !active
2904 active = false;
2905 next_active_spell_id = next;
2906 }
2907 }
2908 }
2909
2910 // not do anything if already known in expected state
2911 if (itr->second->state != PLAYERSPELL_REMOVED && itr->second->active == active &&
2912 itr->second->dependent == dependent && itr->second->disabled == disabled)
2913 {
2914 if (!IsInWorld() && !learning) // explicitly load from DB and then exist in it already and set correctly
2915 itr->second->state = PLAYERSPELL_UNCHANGED;
2916
2917 return false;
2918 }
2919
2920 // dependent spell known as not dependent, overwrite state
2921 if (itr->second->state != PLAYERSPELL_REMOVED && !itr->second->dependent && dependent)
2922 {
2923 itr->second->dependent = dependent;
2924 if (itr->second->state != PLAYERSPELL_NEW)
2925 itr->second->state = PLAYERSPELL_CHANGED;
2926 dependent_set = true;
2927 }
2928
2929 // update active state for known spell
2930 if (itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled)
2931 {
2932 itr->second->active = active;
2933
2934 if (!IsInWorld() && !learning && !dependent_set) // explicitly load from DB and then exist in it already and set correctly
2935 itr->second->state = PLAYERSPELL_UNCHANGED;
2936 else if (itr->second->state != PLAYERSPELL_NEW)
2937 itr->second->state = PLAYERSPELL_CHANGED;
2938
2939 if (active)
2940 {
2941 if (spellInfo->IsPassive() && IsNeedCastPassiveSpellAtLearn(spellInfo))
2942 CastSpell(this, spellId, true);
2943 }
2944 else if (IsInWorld())
2945 {
2946 if (next_active_spell_id)
2947 SendSupercededSpell(spellId, next_active_spell_id);
2948 else
2949 {
2950 WorldPackets::Spells::UnlearnedSpells unlearnedSpells;
2951 unlearnedSpells.SpellID.push_back(spellId);
2952 SendDirectMessage(unlearnedSpells.Write());
2953 }
2954 }
2955
2956 return active; // learn (show in spell book if active now)
2957 }
2958
2959 if (itr->second->disabled != disabled && itr->second->state != PLAYERSPELL_REMOVED)
2960 {
2961 if (itr->second->state != PLAYERSPELL_NEW)
2962 itr->second->state = PLAYERSPELL_CHANGED;
2963 itr->second->disabled = disabled;
2964
2965 if (disabled)
2966 return false;
2967
2968 disabled_case = true;
2969 }
2970 else switch (itr->second->state)
2971 {
2972 case PLAYERSPELL_UNCHANGED: // known saved spell
2973 return false;
2974 case PLAYERSPELL_REMOVED: // re-learning removed not saved spell
2975 {
2976 delete itr->second;
2977 m_spells.erase(itr);
2978 state = PLAYERSPELL_CHANGED;
2979 break; // need re-add
2980 }
2981 default: // known not saved yet spell (new or modified)
2982 {
2983 // can be in case spell loading but learned at some previous spell loading
2984 if (!IsInWorld() && !learning && !dependent_set)
2985 itr->second->state = PLAYERSPELL_UNCHANGED;
2986
2987 return false;
2988 }
2989 }
2990 }
2991
2992 if (!disabled_case) // skip new spell adding if spell already known (disabled spells case)
2993 {
2994 // non talent spell: learn low ranks (recursive call)
2995 if (uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spellId))
2996 {
2997 if (!IsInWorld() || disabled) // at spells loading, no output, but allow save
2998 AddSpell(prev_spell, active, true, true, disabled, false, fromSkill);
2999 else // at normal learning
3000 LearnSpell(prev_spell, true, fromSkill);
3001 }
3002
3003 PlayerSpell* newspell = new PlayerSpell;
3004 newspell->state = state;
3005 newspell->active = active;
3006 newspell->dependent = dependent;
3007 newspell->disabled = disabled;
3008
3009 // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
3010 if (newspell->active && !newspell->disabled && spellInfo->IsRanked())
3011 {
3012 for (PlayerSpellMap::iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2)
3013 {
3014 if (itr2->second->state == PLAYERSPELL_REMOVED)
3015 continue;
3016
3017 SpellInfo const* i_spellInfo = sSpellMgr->GetSpellInfo(itr2->first);
3018 if (!i_spellInfo)
3019 continue;
3020
3021 if (spellInfo->IsDifferentRankOf(i_spellInfo))
3022 {
3023 if (itr2->second->active)
3024 {
3025 if (spellInfo->IsHighRankOf(i_spellInfo))
3026 {
3027 if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
3028 SendSupercededSpell(itr2->first, spellId);
3029
3030 // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
3031 itr2->second->active = false;
3032 if (itr2->second->state != PLAYERSPELL_NEW)
3033 itr2->second->state = PLAYERSPELL_CHANGED;
3034 superceded_old = true; // new spell replace old in action bars and spell book.
3035 }
3036 else
3037 {
3038 if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
3039 SendSupercededSpell(spellId, itr2->first);
3040
3041 // mark new spell as disable (not learned yet for client and will not learned)
3042 newspell->active = false;
3043 if (newspell->state != PLAYERSPELL_NEW)
3044 newspell->state = PLAYERSPELL_CHANGED;
3045 }
3046 }
3047 }
3048 }
3049 }
3050
3051 m_spells[spellId] = newspell;
3052
3053 // return false if spell disabled
3054 if (newspell->disabled)
3055 return false;
3056 }
3057
3058 // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
3059 // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
3060 if (!loading && spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT) && spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL))
3061 {
3062 // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
3063 CastSpell(this, spellId, true);
3064 }
3065 // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
3066 else if (spellInfo->IsPassive())
3067 {
3068 if (IsNeedCastPassiveSpellAtLearn(spellInfo))
3069 CastSpell(this, spellId, true);
3070 }
3071 else if (spellInfo->HasEffect(SPELL_EFFECT_SKILL_STEP))
3072 {
3073 CastSpell(this, spellId, true);
3074 return false;
3075 }
3076
3077 // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
3078 if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
3079 {
3080 if (spellInfo->IsPrimaryProfessionFirstRank())
3081 SetFreePrimaryProfessions(freeProfs - 1);
3082 }
3083
3084 SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
3085
3086 if (SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId))
3087 {
3088 // add dependent skills if this spell is not learned from adding skill already
3089 if (spellLearnSkill->skill != fromSkill)
3090 {
3091 uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
3092 uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
3093
3094 if (skill_value < spellLearnSkill->value)
3095 skill_value = spellLearnSkill->value;
3096
3097 uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : spellLearnSkill->maxvalue;
3098
3099 if (skill_max_value < new_skill_max_value)
3100 skill_max_value = new_skill_max_value;
3101
3102 SetSkill(spellLearnSkill->skill, spellLearnSkill->step, skill_value, skill_max_value);
3103 }
3104 }
3105 else
3106 {
3107 // not ranked skills
3108 for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
3109 {
3110 SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
3111 if (!pSkill)
3112 continue;
3113
3114 if (_spell_idx->second->SkillLine == fromSkill)
3115 continue;
3116
3117 // Runeforging special case
3118 if ((_spell_idx->second->AcquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(_spell_idx->second->SkillLine)) || ((_spell_idx->second->SkillLine == SKILL_RUNEFORGING) && _spell_idx->second->TrivialSkillLineRankHigh == 0))
3119 if (SkillRaceClassInfoEntry const* rcInfo = sDB2Manager.GetSkillRaceClassInfo(_spell_idx->second->SkillLine, getRace(), getClass()))
3120 LearnDefaultSkill(rcInfo);
3121 }
3122 }
3123
3124 // learn dependent spells
3125 SpellLearnSpellMapBounds spell_bounds = sSpellMgr->GetSpellLearnSpellMapBounds(spellId);
3126
3127 for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
3128 {
3129 if (!itr2->second.AutoLearned)
3130 {
3131 if (!IsInWorld() || !itr2->second.Active) // at spells loading, no output, but allow save
3132 AddSpell(itr2->second.Spell, itr2->second.Active, true, true, false);
3133 else // at normal learning
3134 LearnSpell(itr2->second.Spell, true);
3135 }
3136
3137 if (itr2->second.OverridesSpell && itr2->second.Active)
3138 AddOverrideSpell(itr2->second.OverridesSpell, itr2->second.Spell);
3139 }
3140
3141 if (!GetSession()->PlayerLoading())
3142 {
3143 // not ranked skills
3144 for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
3145 {
3146 UpdateCriteria(CRITERIA_TYPE_LEARN_SKILL_LINE, _spell_idx->second->SkillLine);
3147 UpdateCriteria(CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS, _spell_idx->second->SkillLine);
3148 }
3149
3150 UpdateCriteria(CRITERIA_TYPE_LEARN_SPELL, spellId);
3151 }
3152
3153 // needs to be when spell is already learned, to prevent infinite recursion crashes
3154 if (sDB2Manager.GetMount(spellId))
3155 GetSession()->GetCollectionMgr()->AddMount(spellId, MOUNT_STATUS_NONE, false, IsInWorld() ? false : true);
3156
3157 // need to add Battle pets automatically into pet journal
3158 for (BattlePetSpeciesEntry const* entry : sBattlePetSpeciesStore)
3159 {
3160 if (entry->SummonSpellID == int32(spellId) && GetSession()->GetBattlePetMgr()->GetPetCount(entry->ID) == 0)
3161 {
3162 GetSession()->GetBattlePetMgr()->AddPet(entry->ID, entry->CreatureID, BattlePetMgr::RollPetBreed(entry->ID), BattlePetMgr::GetDefaultPetQuality(entry->ID));
3163 UpdateCriteria(CRITERIA_TYPE_OWN_BATTLE_PET_COUNT);
3164 break;
3165 }
3166 }
3167
3168 // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell
3169 return active && !disabled && !superceded_old;
3170 }
3171
3172 void Player::AddTemporarySpell(uint32 spellId)
3173 {
3174 PlayerSpellMap::iterator itr = m_spells.find(spellId);
3175 // spell already added - do not do anything
3176 if (itr != m_spells.end())
3177 return;
3178 PlayerSpell* newspell = new PlayerSpell;
3179 newspell->state = PLAYERSPELL_TEMPORARY;
3180 newspell->active = true;
3181 newspell->dependent = false;
3182 newspell->disabled = false;
3183 m_spells[spellId] = newspell;
3184 }
3185
3186 void Player::RemoveTemporarySpell(uint32 spellId)
3187 {
3188 PlayerSpellMap::iterator itr = m_spells.find(spellId);
3189 // spell already not in list - do not do anything
3190 if (itr == m_spells.end())
3191 return;
3192 // spell has other state than temporary - do not change it
3193 if (itr->second->state != PLAYERSPELL_TEMPORARY)
3194 return;
3195 delete itr->second;
3196 m_spells.erase(itr);
3197 }
3198
3199 bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const
3200 {
3201 // note: form passives activated with shapeshift spells be implemented by HandleShapeshiftBoosts instead of spell_learn_spell
3202 // talent dependent passives activated at form apply have proper stance data
3203 ShapeshiftForm form = GetShapeshiftForm();
3204 bool need_cast = (!spellInfo->Stances || (form && (spellInfo->Stances & (UI64LIT(1) << (form - 1)))) ||
3205 (!form && (spellInfo->HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT))));
3206
3207 if (spellInfo->HasAttribute(SPELL_ATTR8_MASTERY_SPECIALIZATION))
3208 need_cast &= IsCurrentSpecMasterySpell(spellInfo);
3209
3210 // Check EquippedItemClass
3211 // passive spells which apply aura and have an item requirement are to be added in Player::ApplyItemDependentAuras
3212 if (spellInfo->IsPassive() && spellInfo->EquippedItemClass >= 0)
3213 {
3214 for (SpellEffectInfo const* effectInfo : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE))
3215 if (effectInfo && effectInfo->IsAura())
3216 return false;
3217 }
3218
3219 //Check CasterAuraStates
3220 return need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraStateType(spellInfo->CasterAuraState)));
3221 }
3222
3223 bool Player::IsCurrentSpecMasterySpell(SpellInfo const* spellInfo) const
3224 {
3225 if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID)))
3226 return spellInfo->Id == uint32(chrSpec->MasterySpellID[0]) || spellInfo->Id == uint32(chrSpec->MasterySpellID[1]);
3227
3228 return false;
3229 }
3230
3231 void Player::LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill /*= 0*/)
3232 {
3233 PlayerSpellMap::iterator itr = m_spells.find(spell_id);
3234
3235 bool disabled = (itr != m_spells.end()) ? itr->second->disabled : false;
3236 bool active = disabled ? itr->second->active : true;
3237
3238 bool learning = AddSpell(spell_id, active, true, dependent, false, false, fromSkill);
3239
3240 // prevent duplicated entires in spell book, also not send if not in world (loading)
3241 if (learning && IsInWorld())
3242 {
3243 WorldPackets::Spells::LearnedSpells packet;
3244 packet.SpellID.push_back(spell_id);
3245 GetSession()->SendPacket(packet.Write());
3246 }
3247
3248 // learn all disabled higher ranks and required spells (recursive)
3249 if (disabled)
3250 {
3251 if (uint32 nextSpell = sSpellMgr->GetNextSpellInChain(spell_id))
3252 {
3253 PlayerSpellMap::iterator iter = m_spells.find(nextSpell);
3254 if (iter != m_spells.end() && iter->second->disabled)
3255 LearnSpell(nextSpell, false, fromSkill);
3256 }
3257
3258 SpellsRequiringSpellMapBounds spellsRequiringSpell = sSpellMgr->GetSpellsRequiringSpellBounds(spell_id);
3259 for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequiringSpell.first; itr2 != spellsRequiringSpell.second; ++itr2)
3260 {
3261 PlayerSpellMap::iterator iter2 = m_spells.find(itr2->second);
3262 if (iter2 != m_spells.end() && iter2->second->disabled)
3263 LearnSpell(itr2->second, false, fromSkill);
3264 }
3265 }
3266 }
3267
3268 void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
3269 {
3270 PlayerSpellMap::iterator itr = m_spells.find(spell_id);
3271 if (itr == m_spells.end())
3272 return;
3273
3274 if (itr->second->state == PLAYERSPELL_REMOVED || (disabled && itr->second->disabled) || itr->second->state == PLAYERSPELL_TEMPORARY)
3275 return;
3276
3277 // unlearn non talent higher ranks (recursive)
3278 if (uint32 nextSpell = sSpellMgr->GetNextSpellInChain(spell_id))
3279 {
3280 SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(nextSpell);
3281 if (HasSpell(nextSpell) && !spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT))
3282 RemoveSpell(nextSpell, disabled, false);
3283 }
3284 //unlearn spells dependent from recently removed spells
3285 SpellsRequiringSpellMapBounds spellsRequiringSpell = sSpellMgr->GetSpellsRequiringSpellBounds(spell_id);
3286 for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequiringSpell.first; itr2 != spellsRequiringSpell.second; ++itr2)
3287 RemoveSpell(itr2->second, disabled);
3288
3289 // re-search, it can be corrupted in prev loop
3290 itr = m_spells.find(spell_id);
3291 if (itr == m_spells.end())
3292 return; // already unleared
3293
3294 bool cur_active = itr->second->active;
3295 bool cur_dependent = itr->second->dependent;
3296
3297 if (disabled)
3298 {
3299 itr->second->disabled = disabled;
3300 if (itr->second->state != PLAYERSPELL_NEW)
3301 itr->second->state = PLAYERSPELL_CHANGED;
3302 }
3303 else
3304 {
3305 if (itr->second->state == PLAYERSPELL_NEW)
3306 {
3307 delete itr->second;
3308 m_spells.erase(itr);
3309 }
3310 else
3311 itr->second->state = PLAYERSPELL_REMOVED;
3312 }
3313
3314 RemoveOwnedAura(spell_id, GetGUID());
3315
3316 // remove pet auras
3317 for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
3318 if (PetAura const* petSpell = sSpellMgr->GetPetAura(spell_id, i))
3319 RemovePetAura(petSpell);
3320
3321 // update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
3322 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
3323 if (spellInfo && spellInfo->IsPrimaryProfessionFirstRank())
3324 {
3325 uint32 freeProfs = GetFreePrimaryProfessionPoints()+1;
3326 if (freeProfs <= sWorld->getIntConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL))
3327 SetFreePrimaryProfessions(freeProfs);
3328 }
3329
3330 // remove dependent skill
3331 SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spell_id);
3332 if (spellLearnSkill)
3333 {
3334 uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spell_id);
3335 if (!prev_spell) // first rank, remove skill
3336 SetSkill(spellLearnSkill->skill, 0, 0, 0);
3337 else
3338 {
3339 // search prev. skill setting by spell ranks chain
3340 SpellLearnSkillNode const* prevSkill = sSpellMgr->GetSpellLearnSkill(prev_spell);
3341 while (!prevSkill && prev_spell)
3342 {
3343 prev_spell = sSpellMgr->GetPrevSpellInChain(prev_spell);
3344 prevSkill = sSpellMgr->GetSpellLearnSkill(sSpellMgr->GetFirstSpellInChain(prev_spell));
3345 }
3346
3347 if (!prevSkill) // not found prev skill setting, remove skill
3348 SetSkill(spellLearnSkill->skill, 0, 0, 0);
3349 else // set to prev. skill setting values
3350 {
3351 uint32 skill_value = GetPureSkillValue(prevSkill->skill);
3352 uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill);
3353
3354 if (skill_value > prevSkill->value)
3355 skill_value = prevSkill->value;
3356
3357 uint32 new_skill_max_value = prevSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : prevSkill->maxvalue;
3358
3359 if (skill_max_value > new_skill_max_value)
3360 skill_max_value = new_skill_max_value;
3361
3362 SetSkill(prevSkill->skill, prevSkill->step, skill_value, skill_max_value);
3363 }
3364 }
3365 }
3366
3367 // remove dependent spells
3368 SpellLearnSpellMapBounds spell_bounds = sSpellMgr->GetSpellLearnSpellMapBounds(spell_id);
3369
3370 for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
3371 {
3372 RemoveSpell(itr2->second.Spell, disabled);
3373 if (itr2->second.OverridesSpell)
3374 RemoveOverrideSpell(itr2->second.OverridesSpell, itr2->second.Spell);
3375 }
3376
3377 // activate lesser rank in spellbook/action bar, and cast it if need
3378 bool prev_activate = false;
3379
3380 if (uint32 prev_id = sSpellMgr->GetPrevSpellInChain(spell_id))
3381 {
3382 // if ranked non-stackable spell: need activate lesser rank and update dendence state
3383 /// No need to check for spellInfo != NULL here because if cur_active is true, then that means that the spell was already in m_spells, and only valid spells can be pushed there.
3384 if (cur_active && spellInfo->IsRanked())
3385 {
3386 // need manually update dependence state (learn spell ignore like attempts)
3387 PlayerSpellMap::iterator prev_itr = m_spells.find(prev_id);
3388 if (prev_itr != m_spells.end())
3389 {
3390 if (prev_itr->second->dependent != cur_dependent)
3391 {
3392 prev_itr->second->dependent = cur_dependent;
3393 if (prev_itr->second->state != PLAYERSPELL_NEW)
3394 prev_itr->second->state = PLAYERSPELL_CHANGED;
3395 }
3396
3397 // now re-learn if need re-activate
3398 if (cur_active && !prev_itr->second->active && learn_low_rank)
3399 {
3400 if (AddSpell(prev_id, true, false, prev_itr->second->dependent, prev_itr->second->disabled))
3401 {
3402 // downgrade spell ranks in spellbook and action bar
3403 SendSupercededSpell(spell_id, prev_id);
3404 prev_activate = true;
3405 }
3406 }
3407 }
3408 }
3409 }
3410
3411 m_overrideSpells.erase(spell_id);
3412
3413 if (m_canTitanGrip)
3414 {
3415 if (spellInfo && spellInfo->IsPassive() && spellInfo->HasEffect(SPELL_EFFECT_TITAN_GRIP))
3416 {
3417 RemoveAurasDueToSpell(m_titanGripPenaltySpellId);
3418 SetCanTitanGrip(false);
3419 }
3420 }
3421
3422 if (m_canDualWield)
3423 {
3424 if (spellInfo && spellInfo->IsPassive() && spellInfo->HasEffect(SPELL_EFFECT_DUAL_WIELD))
3425 SetCanDualWield(false);
3426 }
3427
3428 if (sWorld->getBoolConfig(CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN))
3429 AutoUnequipOffhandIfNeed();
3430
3431 // remove from spell book if not replaced by lesser rank
3432 if (!prev_activate)
3433 {
3434 WorldPackets::Spells::UnlearnedSpells unlearnedSpells;
3435 unlearnedSpells.SpellID.push_back(spell_id);
3436 SendDirectMessage(unlearnedSpells.Write());
3437 }
3438 }
3439
3440 void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns)
3441 {
3442 // remove cooldowns on spells that have < 10 min CD
3443 GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr)
3444 {
3445 SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
3446 return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS;
3447 }, true);
3448
3449 // pet cooldowns
3450 if (removeActivePetCooldowns)
3451 if (Pet* pet = GetPet())
3452 pet->GetSpellHistory()->ResetAllCooldowns();
3453 }
3454
3455 uint32 Player::GetNextResetTalentsCost() const
3456 {
3457 // The first time reset costs 1 gold
3458 if (GetTalentResetCost() < 1*GOLD)
3459 return 1*GOLD;
3460 // then 5 gold
3461 else if (GetTalentResetCost() < 5