2 * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
3 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
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.
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
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/>.
20 #include "AreaTrigger.h"
21 #include "AccountMgr.h"
22 #include "AchievementMgr.h"
23 #include "ArenaTeam.h"
24 #include "ArenaTeamMgr.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"
37 #include "ChannelMgr.h"
38 #include "CharacterDatabaseCleaner.h"
39 #include "CharacterTemplateDataStore.h"
40 #include "CharacterPackets.h"
42 #include "ChatPackets.h"
43 #include "CinematicMgr.h"
44 #include "CombatLogPackets.h"
45 #include "CombatPackets.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"
55 #include "GameEventMgr.h"
56 #include "GameObjectAI.h"
58 #include "GitRevision.h"
59 #include "GossipDef.h"
60 #include "GridNotifiers.h"
61 #include "GridNotifiersImpl.h"
64 #include "GameTables.h"
67 #include "InstancePackets.h"
68 #include "InstanceSaveMgr.h"
69 #include "InstanceScript.h"
70 #include "ItemPackets.h"
71 #include "KillRewarder.h"
76 #include "LootPackets.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"
86 #include "OutdoorPvP.h"
87 #include "OutdoorPvPMgr.h"
89 #include "PetPackets.h"
90 #include "PhasingHandler.h"
91 #include "QueryHolder.h"
93 #include "QuestObjectiveCriteriaMgr.h"
94 #include "QuestPackets.h"
96 #include "ReputationMgr.h"
99 #include "SkillDiscovery.h"
100 #include "SocialMgr.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"
116 #include "VehiclePackets.h"
118 #include "WeatherMgr.h"
120 #include "WorldPacket.h"
121 #include "WorldSession.h"
122 #include "WorldStatePackets.h"
123 #include <G3D/g3dmath.h>
125 #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)
127 // corpse reclaim times
128 #define DEATH_EXPIRE_STEP (5*MINUTE)
129 #define MAX_DEATH_COUNT 3
131 static uint32 copseReclaimDelay
[MAX_DEATH_COUNT
] = { 30, 60, 120 };
133 uint64
const MAX_MONEY_AMOUNT
= 99999999999ULL;
135 Player::Player(WorldSession
* session
) : Unit(true), m_sceneMgr(this)
140 m_objectType
|= TYPEMASK_PLAYER
;
141 m_objectTypeId
= TYPEID_PLAYER
;
143 m_valuesCount
= PLAYER_END
;
144 _dynamicValuesCount
= PLAYER_DYNAMIC_END
;
152 m_spellModTakingSpell
= nullptr;
154 // players always accept
155 if (!GetSession()->HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS
))
156 SetAcceptWhispers(true);
158 m_combatExitTime
= 0;
160 m_regenTimerCount
= 0;
161 m_weaponChangeTimer
= 0;
163 m_zoneUpdateId
= uint32(-1);
164 m_zoneUpdateTimer
= 0;
169 m_nextSave
= sWorld
->getIntConfig(CONFIG_INTERVAL_SAVE
);
171 memset(m_items
, 0, sizeof(Item
*)*PLAYER_SLOTS_COUNT
);
175 // group is initialized in the reference constructor
176 SetGroupInvite(nullptr);
177 m_groupUpdateMask
= 0;
178 m_bPassOnGroupLoot
= false;
182 m_GuildIdInvited
= UI64LIT(0);
183 m_ArenaTeamIdInvited
= 0;
185 m_atLoginFlags
= AT_LOGIN_NONE
;
187 mSemaphoreTeleport_Near
= false;
188 mSemaphoreTeleport_Far
= false;
190 m_DelayedOperations
= 0;
191 m_bCanDelayTeleport
= false;
192 m_bHasDelayedTeleport
= false;
193 m_teleport_options
= 0;
201 PlayerTalkClass
= new PlayerMenu(GetSession());
202 m_currentBuybackSlot
= BUYBACK_SLOT_START
;
204 m_DailyQuestChanged
= false;
205 m_lastDailyQuestTime
= 0;
207 for (uint8 i
=0; i
< MAX_TIMERS
; i
++)
208 m_MirrorTimer
[i
] = DISABLED_MIRROR_TIMER
;
210 m_MirrorTimerFlags
= UNDERWATER_NONE
;
211 m_MirrorTimerFlagsLast
= UNDERWATER_NONE
;
215 m_deathExpireTime
= 0;
219 for (uint8 j
= 0; j
< PLAYER_MAX_BATTLEGROUND_QUEUES
; ++j
)
221 m_bgBattlegroundQueueID
[j
].bgQueueTypeId
= BATTLEGROUND_QUEUE_NONE
;
222 m_bgBattlegroundQueueID
[j
].invitedToInstance
= 0;
223 m_bgBattlegroundQueueID
[j
].joinTime
= 0;
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;
234 m_canTitanGrip
= false;
235 m_titanGripPenaltySpellId
= 0;
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
244 m_mailsLoaded
= false;
245 m_mailsUpdated
= false;
247 m_nextMailDelivereTime
= 0;
249 m_itemUpdateQueueBlocked
= false;
251 for (uint8 i
= 0; i
< MAX_MOVE_TYPE
; ++i
)
252 m_forced_speed_changes
[i
] = 0;
256 /////////////////// Instance System /////////////////////
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
;
267 for (uint8 i
= 0; i
< BASEMOD_END
; ++i
)
269 m_auraBaseMod
[i
][FLAT_MOD
] = 0.0f
;
270 m_auraBaseMod
[i
][PCT_MOD
] = 1.0f
;
273 for (uint8 i
= 0; i
< MAX_COMBAT_RATING
; i
++)
274 m_baseRatingValue
[i
] = 0;
276 m_baseSpellPower
= 0;
278 m_baseHealthRegen
= 0;
279 m_spellPenetrationItemMod
= 0;
282 m_lastHonorUpdateTime
= time(nullptr);
284 m_IsBGRandomWinner
= false;
289 m_unitMovedByMe
= this;
290 m_playerMovingMe
= this;
294 m_homebindAreaId
= 0;
299 m_contestedPvPTimer
= 0;
301 m_declinedname
= nullptr;
310 m_grantableLevels
= 0;
313 m_ControlledByPlayer
= true;
315 sWorld
->IncreasePlayerCount();
317 m_ChampioningFaction
= 0;
320 m_timeSyncClient
= 0;
321 m_timeSyncServer
= 0;
323 for (uint8 i
= 0; i
< MAX_POWERS_PER_CLASS
; ++i
)
324 m_powerFraction
[i
] = 0;
326 isDebugAreaTriggers
= false;
328 m_WeeklyQuestChanged
= false;
329 m_MonthlyQuestChanged
= false;
330 m_SeasonalQuestChanged
= false;
332 SetPendingBind(0, 0);
334 _activeCheats
= CHEAT_NONE
;
335 healthBeforeDuel
= 0;
338 memset(_voidStorageItems
, 0, VOID_STORAGE_MAX_SLOT
* sizeof(VoidStorageItem
*));
340 _cinematicMgr
= new CinematicMgr(this);
342 m_achievementMgr
= new PlayerAchievementMgr(this);
343 m_reputationMgr
= new ReputationMgr(this);
344 m_questObjectiveCriteriaMgr
= Trinity::make_unique
<QuestObjectiveCriteriaMgr
>(this);
346 for (uint8 i
= 0; i
< MAX_CUF_PROFILES
; ++i
)
347 _CUFProfiles
[i
] = nullptr;
349 _advancedCombatLoggingEnabled
= false;
351 _restMgr
= Trinity::make_unique
<RestMgr
>(this);
353 _usePvpItemLevels
= false;
358 // it must be unloaded already in PlayerLogout and accessed only for logged in player
361 // Note: buy back item already deleted from DB when player was saved
362 for (uint8 i
= 0; i
< PLAYER_SLOTS_COUNT
; ++i
)
365 for (PlayerSpellMap::const_iterator itr
= m_spells
.begin(); itr
!= m_spells
.end(); ++itr
)
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
)
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
375 delete PlayerTalkClass
;
377 for (size_t x
= 0; x
< ItemSetEff
.size(); x
++)
378 delete ItemSetEff
[x
];
380 delete m_declinedname
;
382 delete m_achievementMgr
;
383 delete m_reputationMgr
;
384 delete _cinematicMgr
;
386 for (uint8 i
= 0; i
< VOID_STORAGE_MAX_SLOT
; ++i
)
387 delete _voidStorageItems
[i
];
389 sWorld
->DecreasePlayerCount();
392 void Player::CleanupsBeforeDelete(bool finalCleanup
)
395 DuelComplete(DUEL_INTERRUPTED
);
397 Unit::CleanupsBeforeDelete(finalCleanup
);
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);
405 bool Player::Create(ObjectGuid::LowType guidlow
, WorldPackets::Character::CharacterCreateInfo
const* createInfo
)
407 //FIXME: outfitId not used in player creating
408 /// @todo need more checks against packet modifications
410 Object::_Create(ObjectGuid::Create
<HighGuid::Player
>(guidlow
));
412 m_name
= createInfo
->Name
;
414 PlayerInfo
const* info
= sObjectMgr
->GetPlayerInfo(createInfo
->Race
, createInfo
->Class
);
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
);
422 for (uint8 i
= 0; i
< PLAYER_SLOTS_COUNT
; i
++)
423 m_items
[i
] = nullptr;
425 Relocate(info
->positionX
, info
->positionY
, info
->positionZ
, info
->orientation
);
427 ChrClassesEntry
const* cEntry
= sChrClassesStore
.LookupEntry(createInfo
->Class
);
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
);
435 if (!ValidateAppearance(createInfo
->Race
, createInfo
->Class
, createInfo
->Sex
, createInfo
->HairStyle
, createInfo
->HairColor
, createInfo
->Face
, createInfo
->FacialHairStyle
, createInfo
->Skin
, createInfo
->CustomDisplay
, true))
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());
442 SetMap(sMapMgr
->CreateMap(info
->mapId
, this));
444 uint8 powertype
= cEntry
->DisplayPower
;
446 SetObjectScale(1.0f
);
448 setFactionForRace(createInfo
->Race
);
450 if (!IsValidGender(createInfo
->Sex
))
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
);
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
);
462 if (sWorld
->getIntConfig(CONFIG_GAME_TYPE
) == REALM_TYPE_PVP
|| sWorld
->getIntConfig(CONFIG_GAME_TYPE
) == REALM_TYPE_RPPVP
)
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
);
468 SetFlag(UNIT_FIELD_FLAGS_2
, UNIT_FLAG2_REGENERATE_POWER
);
469 SetFloatValue(UNIT_FIELD_HOVERHEIGHT
, 1.0f
); // default for players in 3.0.3
471 SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX
, uint32(-1)); // -1 is default value
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
);
486 SetGuidValue(OBJECT_FIELD_DATA
, ObjectGuid::Empty
);
487 SetUInt32Value(PLAYER_GUILDRANK
, 0);
489 SetUInt32Value(PLAYER_GUILD_TIMESTAMP
, 0);
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);
495 SetUInt32Value(PLAYER_FIELD_KILLS
, 0);
496 SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS
, 0);
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
);
505 if (createInfo
->TemplateSet
)
507 if (m_session
->HasPermission(rbac::RBAC_PERM_USE_CHARACTER_TEMPLATES
))
509 if (CharacterTemplate
const* charTemplate
= sCharacterTemplateDataStore
->GetCharacterTemplate(*createInfo
->TemplateSet
))
511 if (charTemplate
->Level
> start_level
)
512 start_level
= charTemplate
->Level
;
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());
519 if (m_session
->HasPermission(rbac::RBAC_PERM_USE_START_GM_LEVEL
))
521 uint32 gm_level
= sWorld
->getIntConfig(CONFIG_START_GM_LEVEL
);
522 if (gm_level
> start_level
)
523 start_level
= gm_level
;
526 SetUInt32Value(UNIT_FIELD_LEVEL
, start_level
);
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
));
534 // start with every map explored
535 if (sWorld
->getBoolConfig(CONFIG_START_ALL_EXPLORED
))
537 for (uint16 i
=0; i
<PLAYER_EXPLORED_ZONES_SIZE
; i
++)
538 SetFlag(PLAYER_EXPLORED_ZONES_1
+i
, 0xFFFFFFFF);
541 //Reputations if "StartAllReputation" is enabled, -- @todo Fix this in a better way
542 if (sWorld
->getBoolConfig(CONFIG_START_ALL_REP
))
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);
556 // Factions depending on team, like cities and some more stuff
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);
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);
585 m_Last_tick
= time(nullptr);
586 m_Played_time
[PLAYED_TIME_TOTAL
] = 0;
587 m_Played_time
[PLAYED_TIME_LEVEL
] = 0;
589 // base stats and related field values
591 InitTaxiNodesForLevel();
592 InitTalentForLevel();
593 InitPrimaryProfessions(); // to max set before any spell added
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)
598 SetFullPower(POWER_MANA
);
601 LearnDefaultSkills();
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
)
609 ActionButton
& ab
= m_actionButtons
[action_itr
->button
];
612 ab
.SetActionAndType(action_itr
->action
, ActionButtonType(action_itr
->type
));
616 if (CharStartOutfitEntry
const* oEntry
= sDB2Manager
.GetCharStartOutfitEntry(createInfo
->Race
, createInfo
->Class
, createInfo
->Sex
))
618 for (int j
= 0; j
< MAX_OUTFIT_ITEMS
; ++j
)
620 if (oEntry
->ItemID
[j
] <= 0)
623 uint32 itemId
= oEntry
->ItemID
[j
];
625 // just skip, reported in ObjectMgr::LoadItemTemplates
626 ItemTemplate
const* iProto
= sObjectMgr
->GetItemTemplate(itemId
);
630 // BuyCount by default
631 uint32 count
= iProto
->GetBuyCount();
633 // special amount for food/drink
634 if (iProto
->GetClass() == ITEM_CLASS_CONSUMABLE
&& iProto
->GetSubClass() == ITEM_SUBCLASS_FOOD_DRINK
)
636 if (iProto
->Effects
.size() >= 1)
638 switch (iProto
->Effects
[0]->SpellCategoryID
)
640 case SPELL_CATEGORY_FOOD
: // food
641 count
= getClass() == CLASS_DEATH_KNIGHT
? 10 : 4;
643 case SPELL_CATEGORY_DRINK
: // drink
648 if (iProto
->GetMaxStackSize() < count
)
649 count
= iProto
->GetMaxStackSize();
651 StoreNewItemInBestSlots(itemId
, count
);
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
);
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
++)
664 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
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
)
671 RemoveItem(INVENTORY_SLOT_BAG_0
, i
, true);
672 EquipItem(eDest
, pItem
, true);
674 // move other items to more appropriate slots
677 ItemPosCountVec sDest
;
678 msg
= CanStoreItem(NULL_BAG
, NULL_SLOT
, sDest
, pItem
, false);
679 if (msg
== EQUIP_ERR_OK
)
681 RemoveItem(INVENTORY_SLOT_BAG_0
, i
, true);
682 StoreItem(sDest
, pItem
, true);
687 // all item positions resolved
689 if (ChrSpecializationEntry
const* defaultSpec
= sDB2Manager
.GetDefaultChrSpecializationForClass(getClass()))
691 SetActiveTalentGroup(defaultSpec
->OrderIndex
);
692 SetPrimarySpecialization(defaultSpec
->ID
);
698 bool Player::StoreNewItemInBestSlots(uint32 titem_id
, uint32 titem_amount
)
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
);
703 // attempt equip by one
704 while (titem_amount
> 0)
707 InventoryResult msg
= CanEquipNewItem(NULL_SLOT
, eDest
, titem_id
, false);
708 if (msg
!= EQUIP_ERR_OK
)
711 EquipNewItem(eDest
, titem_id
, true);
712 AutoUnequipOffhandIfNeed();
716 if (titem_amount
== 0)
717 return true; // equipped
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
)
725 StoreNewItem(sDest
, titem_id
, true, GenerateItemRandomPropertyId(titem_id
));
726 return true; // stored
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
);
735 void Player::SendMirrorTimer(MirrorTimerType Type
, uint32 MaxValue
, uint32 CurrentValue
, int32 Regen
)
737 if (int(MaxValue
) == DISABLED_MIRROR_TIMER
)
739 if (int(CurrentValue
) != DISABLED_MIRROR_TIMER
)
740 StopMirrorTimer(Type
);
744 GetSession()->SendPacket(WorldPackets::Misc::StartMirrorTimer(Type
, CurrentValue
, MaxValue
, Regen
, 0, 0).Write());
747 void Player::StopMirrorTimer(MirrorTimerType Type
)
749 m_MirrorTimer
[Type
] = DISABLED_MIRROR_TIMER
;
750 GetSession()->SendPacket(WorldPackets::Misc::StopMirrorTimer(Type
).Write());
753 bool Player::IsImmuneToEnvironmentalDamage() const
755 // check for GM and death state included in isAttackableByAOE
756 return !isTargetableForAttack(false);
759 uint32
Player::EnvironmentalDamage(EnviromentalDamage type
, uint32 damage
)
761 if (IsImmuneToEnvironmentalDamage())
764 // Absorb, resist some environmental damage type
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();
783 DealDamageMods(this, damage
, &absorb
);
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
;
792 uint32 final_damage
= DealDamage(this, damage
, nullptr, SELF_DAMAGE
, SPELL_SCHOOL_MASK_NORMAL
, nullptr, false);
794 packet
.LogData
.Initialize(this);
795 SendCombatLogMessage(&packet
);
799 if (type
== DAMAGE_FALL
) // DealDamage does not apply item durability loss from self-induced damage.
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);
808 UpdateCriteria(CRITERIA_TYPE_DEATHS_FROM
, 1, type
);
814 int32
Player::getMaxTimer(MirrorTimerType timer
) const
819 return MINUTE
* IN_MILLISECONDS
;
822 if (!IsAlive() || HasAuraType(SPELL_AURA_WATER_BREATHING
) || GetSession()->GetSecurity() >= AccountTypes(sWorld
->getIntConfig(CONFIG_DISABLE_BREATHING
)))
823 return DISABLED_MIRROR_TIMER
;
825 int32 UnderWaterTime
= 3 * MINUTE
* IN_MILLISECONDS
;
826 UnderWaterTime
*= GetTotalAuraMultiplier(SPELL_AURA_MOD_WATER_BREATHING
);
827 return UnderWaterTime
;
832 return DISABLED_MIRROR_TIMER
;
833 return 1 * IN_MILLISECONDS
;
840 void Player::UpdateMirrorTimers()
842 // Desync flags for update on next HandleDrowning
843 if (m_MirrorTimerFlags
)
844 m_MirrorTimerFlagsLast
= ~m_MirrorTimerFlags
;
847 void Player::StopMirrorTimers()
849 StopMirrorTimer(FATIGUE_TIMER
);
850 StopMirrorTimer(BREATH_TIMER
);
851 StopMirrorTimer(FIRE_TIMER
);
854 bool Player::IsMirrorTimerActive(MirrorTimerType type
) const
856 return m_MirrorTimer
[type
] == getMaxTimer(type
);
859 void Player::HandleDrowning(uint32 time_diff
)
861 if (!m_MirrorTimerFlags
)
865 if (m_MirrorTimerFlags
& UNDERWATER_INWATER
)
867 // Breath timer not activated - activate it
868 if (m_MirrorTimer
[BREATH_TIMER
] == DISABLED_MIRROR_TIMER
)
870 m_MirrorTimer
[BREATH_TIMER
] = getMaxTimer(BREATH_TIMER
);
871 SendMirrorTimer(BREATH_TIMER
, m_MirrorTimer
[BREATH_TIMER
], m_MirrorTimer
[BREATH_TIMER
], -1);
873 else // If activated - do tick
875 m_MirrorTimer
[BREATH_TIMER
]-=time_diff
;
876 // Timer limit - need deal damage
877 if (m_MirrorTimer
[BREATH_TIMER
] < 0)
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
);
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);
889 else if (m_MirrorTimer
[BREATH_TIMER
] != DISABLED_MIRROR_TIMER
) // Regen timer
891 int32 UnderWaterTime
= getMaxTimer(BREATH_TIMER
);
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);
901 if (m_MirrorTimerFlags
& UNDERWARER_INDARKWATER
)
903 // Fatigue timer not activated - activate it
904 if (m_MirrorTimer
[FATIGUE_TIMER
] == DISABLED_MIRROR_TIMER
)
906 m_MirrorTimer
[FATIGUE_TIMER
] = getMaxTimer(FATIGUE_TIMER
);
907 SendMirrorTimer(FATIGUE_TIMER
, m_MirrorTimer
[FATIGUE_TIMER
], m_MirrorTimer
[FATIGUE_TIMER
], -1);
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)
915 m_MirrorTimer
[FATIGUE_TIMER
]+= 1*IN_MILLISECONDS
;
916 if (IsAlive()) // Calculate and deal damage
918 uint32 damage
= GetMaxHealth() / 5 + urand(0, getLevel()-1);
919 EnvironmentalDamage(DAMAGE_EXHAUSTED
, damage
);
921 else if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GHOST
)) // Teleport ghost to graveyard
924 else if (!(m_MirrorTimerFlagsLast
& UNDERWARER_INDARKWATER
))
925 SendMirrorTimer(FATIGUE_TIMER
, getMaxTimer(FATIGUE_TIMER
), m_MirrorTimer
[FATIGUE_TIMER
], -1);
928 else if (m_MirrorTimer
[FATIGUE_TIMER
] != DISABLED_MIRROR_TIMER
) // Regen timer
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);
938 if (m_MirrorTimerFlags
& (UNDERWATER_INLAVA
/*| UNDERWATER_INSLIME*/) && !(_lastLiquid
&& _lastLiquid
->SpellID
))
940 // Breath timer not activated - activate it
941 if (m_MirrorTimer
[FIRE_TIMER
] == DISABLED_MIRROR_TIMER
)
942 m_MirrorTimer
[FIRE_TIMER
] = getMaxTimer(FIRE_TIMER
);
945 m_MirrorTimer
[FIRE_TIMER
] -= time_diff
;
946 if (m_MirrorTimer
[FIRE_TIMER
] < 0)
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);
962 m_MirrorTimer
[FIRE_TIMER
] = DISABLED_MIRROR_TIMER
;
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
)
969 m_MirrorTimerFlags
|=UNDERWATER_EXIST_TIMERS
;
972 m_MirrorTimerFlagsLast
= m_MirrorTimerFlags
;
975 ///The player sobers by 1% every 9 seconds
976 void Player::HandleSobering()
980 uint8 currentDrunkValue
= GetDrunkValue();
981 uint8 drunk
= currentDrunkValue
? --currentDrunkValue
: 0;
982 SetDrunkValue(drunk
);
985 DrunkenState
Player::GetDrunkenstateByValue(uint8 value
)
988 return DRUNKEN_SMASHED
;
990 return DRUNKEN_DRUNK
;
992 return DRUNKEN_TIPSY
;
993 return DRUNKEN_SOBER
;
996 void Player::SetDrunkValue(uint8 newDrunkValue
, uint32 itemId
/*= 0*/)
998 bool isSobering
= newDrunkValue
< GetDrunkValue();
999 uint32 oldDrunkenState
= Player::GetDrunkenstateByValue(GetDrunkValue());
1000 if (newDrunkValue
> 100)
1001 newDrunkValue
= 100;
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
));
1007 m_invisibilityDetect
.AddFlag(INVISIBILITY_DRUNK
);
1008 m_invisibilityDetect
.SetValue(INVISIBILITY_DRUNK
, drunkPercent
);
1010 else if (!HasAuraType(SPELL_AURA_MOD_FAKE_INEBRIATE
) && !newDrunkValue
)
1011 m_invisibilityDetect
.DelFlag(INVISIBILITY_DRUNK
);
1013 uint32 newDrunkenState
= Player::GetDrunkenstateByValue(newDrunkValue
);
1014 SetByteValue(PLAYER_BYTES_3
, PLAYER_BYTES_3_OFFSET_INEBRIATION
, newDrunkValue
);
1015 UpdateObjectVisibility();
1018 m_drunkTimer
= 0; // reset sobering timer
1020 if (newDrunkenState
== oldDrunkenState
)
1023 WorldPackets::Misc::CrossedInebriationThreshold data
;
1024 data
.Guid
= GetGUID();
1025 data
.Threshold
= newDrunkenState
;
1026 data
.ItemID
= itemId
;
1028 SendMessageToSet(data
.Write(), true);
1031 void Player::Update(uint32 p_time
)
1037 if (m_nextMailDelivereTime
&& m_nextMailDelivereTime
<= time(nullptr))
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;
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
)
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;
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
)
1062 _cinematicMgr
->m_lastCinematicCheck
= getMSTime();
1063 _cinematicMgr
->UpdateCinematicLocation(p_time
);
1066 //used to implement delayed far teleport
1067 SetCanDelayTeleport(true);
1068 Unit::Update(p_time
);
1069 SetCanDelayTeleport(false);
1071 time_t now
= time(nullptr);
1075 UpdateContestedPvP(p_time
);
1077 UpdateDuelFlag(now
);
1079 CheckDuelDistance(now
);
1081 UpdateAfkReport(now
);
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());
1088 if (IsAIEnabled
&& GetAI())
1089 GetAI()->UpdateAI(p_time
);
1090 else if (NeedChangeAI
)
1093 NeedChangeAI
= false;
1094 IsAIEnabled
= (GetAI() != nullptr);
1097 // Update items that have just a limited lifetime
1098 if (now
> m_Last_tick
)
1099 UpdateItemDuration(uint32(now
- m_Last_tick
));
1101 // check every second
1102 if (now
> m_Last_tick
+ 1)
1103 UpdateSoulboundTradeItems();
1105 // If mute expired, remove it from the DB
1106 if (GetSession()->m_muteTime
&& GetSession()->m_muteTime
< now
)
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
);
1117 if (!m_timedquests
.empty())
1119 QuestSet::iterator iter
= m_timedquests
.begin();
1120 while (iter
!= m_timedquests
.end())
1122 QuestStatusData
& q_status
= m_QuestStatus
[*iter
];
1123 if (q_status
.Timer
<= p_time
)
1125 uint32 quest_id
= *iter
;
1126 ++iter
; // current iter will be removed in FailQuest
1127 FailQuest(quest_id
);
1131 q_status
.Timer
-= p_time
;
1132 m_QuestStatusSave
[*iter
] = QUEST_DEFAULT_SAVE_TYPE
;
1138 m_achievementMgr
->UpdateTimedCriteria(p_time
);
1140 if (HasUnitState(UNIT_STATE_MELEE_ATTACKING
) && !HasUnitState(UNIT_STATE_CASTING
))
1142 if (Unit
* victim
= GetVictim())
1144 // default combat reach 10
1145 /// @todo add weapon, skill check
1147 if (isAttackReady(BASE_ATTACK
))
1149 if (!IsWithinMeleeRange(victim
))
1151 setAttackTimer(BASE_ATTACK
, 100);
1152 if (m_swingErrorMsg
!= 1) // send single time (client auto repeat)
1154 SendAttackSwingNotInRange();
1155 m_swingErrorMsg
= 1;
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
))
1161 setAttackTimer(BASE_ATTACK
, 100);
1162 if (m_swingErrorMsg
!= 2) // send single time (client auto repeat)
1164 SendAttackSwingBadFacingAttack();
1165 m_swingErrorMsg
= 2;
1170 m_swingErrorMsg
= 0; // reset swing error state
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
);
1178 AttackerStateUpdate(victim
, BASE_ATTACK
);
1179 resetAttackTimer(BASE_ATTACK
);
1183 if (!IsInFeralForm() && haveOffhandWeapon() && isAttackReady(OFF_ATTACK
))
1185 if (!IsWithinMeleeRange(victim
))
1186 setAttackTimer(OFF_ATTACK
, 100);
1187 else if (!IsWithinBoundaryRadius(victim
) && !HasInArc(2 * float(M_PI
) / 3, victim
))
1189 setAttackTimer(BASE_ATTACK
, 100);
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
);
1198 AttackerStateUpdate(victim
, OFF_ATTACK
);
1199 resetAttackTimer(OFF_ATTACK
);
1203 /*Unit* owner = victim->GetOwner();
1204 Unit* u = owner ? owner : victim;
1205 if (u->IsPvP() && (!duel || duel->opponent != u))
1208 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
1213 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_RESTING
))
1214 _restMgr
->Update(now
);
1216 if (m_weaponChangeTimer
> 0)
1218 if (p_time
>= m_weaponChangeTimer
)
1219 m_weaponChangeTimer
= 0;
1221 m_weaponChangeTimer
-= p_time
;
1224 if (m_zoneUpdateTimer
> 0)
1226 if (p_time
>= m_zoneUpdateTimer
)
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
))
1231 AreaTriggerEntry
const* atEntry
= sAreaTriggerStore
.LookupEntry(_restMgr
->GetInnTriggerID());
1232 if (!atEntry
|| !IsInAreaTriggerRadius(atEntry
))
1233 _restMgr
->RemoveRestFlag(REST_FLAG_IN_TAVERN
);
1236 uint32 newzone
, newarea
;
1237 GetZoneAndAreaId(newzone
, newarea
);
1239 if (m_zoneUpdateId
!= newzone
)
1240 UpdateZone(newzone
, newarea
); // also update area
1243 // use area updates as well
1244 // needed for free far all arenas for example
1245 if (m_areaUpdateId
!= newarea
)
1246 UpdateArea(newarea
);
1248 m_zoneUpdateTimer
= ZONE_UPDATE_INTERVAL
;
1252 m_zoneUpdateTimer
-= p_time
;
1255 if (m_timeSyncTimer
> 0 && !IsBeingTeleportedFar())
1257 if (p_time
>= m_timeSyncTimer
)
1260 m_timeSyncTimer
-= p_time
;
1265 m_regenTimer
+= p_time
;
1269 if (m_deathState
== JUST_DIED
)
1274 if (p_time
>= m_nextSave
)
1276 // m_nextSave reset in SaveToDB call
1278 TC_LOG_DEBUG("entities.player", "Player::Update: Player '%s' (%s) saved", GetName().c_str(), GetGUID().ToString().c_str());
1281 m_nextSave
-= p_time
;
1284 //Handle Water/drowning
1285 HandleDrowning(p_time
);
1288 if (now
> m_Last_tick
)
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
1296 if (GetDrunkValue())
1298 m_drunkTimer
+= p_time
;
1299 if (m_drunkTimer
> 9 * IN_MILLISECONDS
)
1303 if (HasPendingBind())
1305 if (_pendingBindTimer
<= p_time
)
1307 // Player left the instance
1308 if (_pendingBindId
== GetInstanceId())
1310 SetPendingBind(0, 0);
1313 _pendingBindTimer
-= p_time
;
1316 // not auto-free ghost from body in instances
1317 if (m_deathTimer
> 0 && !GetMap()->Instanceable() && !HasAuraType(SPELL_AURA_PREVENT_RESURRECTION
))
1319 if (p_time
>= m_deathTimer
)
1326 m_deathTimer
-= p_time
;
1329 UpdateEnchantTime(p_time
);
1330 UpdateHomebindTime(p_time
);
1332 if (!_instanceResetTimes
.empty())
1334 for (InstanceTimeMap::iterator itr
= _instanceResetTimes
.begin(); itr
!= _instanceResetTimes
.end();)
1336 if (itr
->second
< now
)
1337 _instanceResetTimes
.erase(itr
++);
1344 SendUpdateToOutOfRangeGroupMembers();
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);
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
);
1358 void Player::setDeathState(DeathState s
)
1360 bool oldIsAlive
= IsAlive();
1366 TC_LOG_ERROR("entities.player", "Player::setDeathState: Attempted to kill a dead player '%s' (%s)", GetName().c_str(), GetGUID().ToString().c_str());
1370 // drunken state is cleared on death
1372 // lost combo points at any target (targeted combo points clear in Unit::setDeathState)
1375 ClearResurrectRequestData();
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);
1380 InitializeSelfResurrectionSpells();
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
);
1390 Unit::setDeathState(s
);
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
);
1397 void Player::ToggleAFK()
1399 ToggleFlag(PLAYER_FLAGS
, PLAYER_FLAGS_AFK
);
1401 // afk player not allowed in battleground
1402 if (!IsGameMaster() && isAFK() && InBattleground() && !InArena())
1403 LeaveBattleground();
1406 void Player::ToggleDND()
1408 ToggleFlag(PLAYER_FLAGS
, PLAYER_FLAGS_DND
);
1411 uint8
Player::GetChatFlags() const
1413 uint8 tag
= CHAT_FLAG_NONE
;
1416 tag
|= CHAT_FLAG_GM
;
1418 tag
|= CHAT_FLAG_DND
;
1420 tag
|= CHAT_FLAG_AFK
;
1421 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_DEVELOPER
))
1422 tag
|= CHAT_FLAG_DEV
;
1427 bool Player::TeleportTo(uint32 mapid
, float x
, float y
, float z
, float orientation
, uint32 options
)
1429 if (!MapManager::IsValidMapCoord(mapid
, x
, y
, z
, orientation
))
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());
1436 if (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_DISABLE_MAP
) && DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP
, mapid
, this))
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
);
1443 // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
1444 Pet
* pet
= GetPet();
1446 MapEntry
const* mEntry
= sMapStore
.LookupEntry(mapid
);
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())
1453 // client without expansion support
1454 if (GetSession()->GetExpansion() < mEntry
->Expansion())
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
);
1459 if (Transport
* transport
= GetTransport())
1461 transport
->RemovePassenger(this);
1462 RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :)
1465 SendTransferAborted(mapid
, TRANSFER_ABORT_INSUF_EXPAN_LVL
, mEntry
->Expansion());
1467 return false; // normal client can't teleport to this map...
1470 TC_LOG_DEBUG("maps", "Player %s (%s) is being teleported to map (MapID: %u)", GetName().c_str(), GetGUID().ToString().c_str(), mapid
);
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();
1480 if (Transport
* transport
= GetTransport())
1482 if (!(options
& TELE_TO_NOT_LEAVE_TRANSPORT
))
1483 transport
->RemovePassenger(this);
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
);
1492 if (GetMapId() == mapid
)
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())
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
;
1509 if (!(options
& TELE_TO_NOT_UNSUMMON_PET
))
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();
1516 if (!(options
& TELE_TO_NOT_LEAVE_COMBAT
))
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
);
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
);
1533 if (getClass() == CLASS_DEATH_KNIGHT
&& GetMapId() == 609 && !IsGameMaster() && !HasSpell(50977))
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
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))
1545 // Seamless teleport can happen only if cosmetic maps match
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
;
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))
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())
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
;
1572 SetSelection(ObjectGuid::Empty
);
1576 ResetContestedPvP();
1578 // remove player from battleground on far teleport (when changing maps)
1579 if (Battleground
const* bg
= GetBattleground())
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
1588 // remove arena spell coldowns/buffs now to also remove pet's cooldowns before it's temporarily unsummoned
1589 if (mEntry
->IsBattleArena())
1591 RemoveArenaSpellCooldowns(true);
1594 pet
->RemoveArenaAuras();
1597 // remove pet on map change
1599 UnsummonPetTemporaryIfAny();
1601 // remove all dyn objects
1602 RemoveAllDynObjects();
1604 // remove all areatriggers entities
1605 RemoveAllAreaTriggers();
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);
1613 //remove auras before removing from map...
1614 RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP
| AURA_INTERRUPT_FLAG_MOVE
| AURA_INTERRUPT_FLAG_TURNING
));
1616 if (!GetSession()->PlayerLogout() && !(options
& TELE_TO_SEAMLESS
))
1618 // send transfer packets
1619 WorldPackets::Movement::TransferPending transferPending
;
1620 transferPending
.MapID
= mapid
;
1621 transferPending
.OldMapPosition
= GetPosition();
1622 if (Transport
* transport
= GetTransport())
1624 transferPending
.Ship
= boost::in_place();
1625 transferPending
.Ship
->ID
= transport
->GetEntry();
1626 transferPending
.Ship
->OriginMapID
= GetMapId();
1629 GetSession()->SendPacket(transferPending
.Write());
1632 // remove from old map now
1634 oldmap
->RemovePlayerFromMap(this, false);
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
1642 if (!GetSession()->PlayerLogout())
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());
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);
1660 bool Player::TeleportTo(WorldLocation
const &loc
, uint32 options
/*= 0*/)
1662 return TeleportTo(loc
.GetMapId(), loc
.GetPositionX(), loc
.GetPositionY(), loc
.GetPositionZ(), loc
.GetOrientation(), options
);
1665 bool Player::TeleportToBGEntryPoint()
1667 if (m_bgData
.joinPos
.m_mapId
== MAPID_INVALID
)
1670 ScheduleDelayedOperation(DELAYED_BG_MOUNT_RESTORE
);
1671 ScheduleDelayedOperation(DELAYED_BG_TAXI_RESTORE
);
1672 ScheduleDelayedOperation(DELAYED_BG_GROUP_RESTORE
);
1673 return TeleportTo(m_bgData
.joinPos
);
1676 void Player::ProcessDelayedOperations()
1678 if (m_DelayedOperations
== 0)
1681 if (m_DelayedOperations
& DELAYED_RESURRECT_PLAYER
)
1682 ResurrectUsingRequestDataImpl();
1684 if (m_DelayedOperations
& DELAYED_SAVE_PLAYER
)
1687 if (m_DelayedOperations
& DELAYED_SPELL_CAST_DESERTER
)
1688 CastSpell(this, 26013, true); // Deserter
1690 if (m_DelayedOperations
& DELAYED_BG_MOUNT_RESTORE
)
1692 if (m_bgData
.mountSpell
)
1694 CastSpell(this, m_bgData
.mountSpell
, true);
1695 m_bgData
.mountSpell
= 0;
1699 if (m_DelayedOperations
& DELAYED_BG_TAXI_RESTORE
)
1701 if (m_bgData
.HasTaxiPath())
1703 m_taxi
.AddTaxiDestination(m_bgData
.taxiPath
[0]);
1704 m_taxi
.AddTaxiDestination(m_bgData
.taxiPath
[1]);
1705 m_bgData
.ClearTaxiPath();
1707 ContinueTaxiFlight();
1711 if (m_DelayedOperations
& DELAYED_BG_GROUP_RESTORE
)
1713 if (Group
* g
= GetGroup())
1714 g
->SendUpdateToPlayer(GetGUID());
1717 //we have executed ALL delayed ops, so clear the flag
1718 m_DelayedOperations
= 0;
1721 void Player::AddToWorld()
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
1728 for (uint8 i
= PLAYER_SLOT_START
; i
< PLAYER_SLOT_END
; ++i
)
1730 m_items
[i
]->AddToWorld();
1733 void Player::RemoveFromWorld()
1738 ///- Release charmed creatures, unsummon totems and remove pets/guardians
1740 StopCastingBindSight();
1741 UnsummonPetTemporaryIfAny();
1742 sOutdoorPvPMgr
->HandlePlayerLeaveZone(this, m_zoneUpdateId
);
1743 sBattlefieldMgr
->HandlePlayerLeaveZone(this, m_zoneUpdateId
);
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
)
1749 m_items
[i
]->RemoveFromWorld();
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();
1756 for (ItemMap::iterator iter
= mMitems
.begin(); iter
!= mMitems
.end(); ++iter
)
1757 iter
->second
->RemoveFromWorld();
1761 if (WorldObject
* viewpoint
= GetViewpoint())
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);
1770 void Player::SetObjectScale(float scale
)
1772 Unit::SetObjectScale(scale
);
1773 SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS
, scale
* DEFAULT_WORLD_OBJECT_SIZE
);
1774 SetFloatValue(UNIT_FIELD_COMBATREACH
, scale
* DEFAULT_COMBAT_REACH
);
1776 SendMovementSetCollisionHeight(scale
* GetCollisionHeight(IsMounted()));
1779 bool Player::IsImmunedToSpellEffect(SpellInfo
const* spellInfo
, uint32 index
, Unit
* caster
) const
1781 SpellEffectInfo
const* effect
= spellInfo
->GetEffect(GetMap()->GetDifficultyID(), index
);
1782 if (!effect
|| !effect
->IsEffect())
1785 // players are immune to taunt (the aura and the spell effect).
1786 if (effect
->IsAura(SPELL_AURA_MOD_TAUNT
))
1788 if (effect
->IsEffect(SPELL_EFFECT_ATTACK_ME
))
1791 return Unit::IsImmunedToSpellEffect(spellInfo
, index
, caster
);
1794 void Player::RegenerateAll()
1796 m_regenTimerCount
+= m_regenTimer
;
1798 for (Powers power
= POWER_MANA
; power
< MAX_POWERS
; power
= Powers(power
+ 1))
1799 if (power
!= POWER_RUNES
)
1802 // Runes act as cooldowns, and they don't need to send any data
1803 if (getClass() == CLASS_DEATH_KNIGHT
)
1805 uint32 regeneratedRunes
= 0;
1806 uint32 regenIndex
= 0;
1807 while (regeneratedRunes
< MAX_RECHARGING_RUNES
&& m_runes
->CooldownOrder
.size() > regenIndex
)
1809 uint8 runeToRegen
= m_runes
->CooldownOrder
[regenIndex
];
1810 uint32 runeCooldown
= GetRuneCooldown(runeToRegen
);
1811 if (runeCooldown
> m_regenTimer
)
1813 SetRuneCooldown(runeToRegen
, runeCooldown
- m_regenTimer
);
1817 SetRuneCooldown(runeToRegen
, 0);
1823 if (m_regenTimerCount
>= 2000)
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
))
1829 m_regenTimerCount
-= 2000;
1835 void Player::Regenerate(Powers power
)
1837 // Skip regeneration for power type we cannot have
1838 uint32 powerIndex
= GetPowerIndex(power
);
1839 if (powerIndex
== MAX_POWERS
|| powerIndex
>= MAX_POWERS_PER_CLASS
)
1842 /// @todo possible use of miscvalueb instead of amount
1843 if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER
, power
))
1846 int32 curValue
= GetPower(power
);
1848 // TODO: updating haste should update UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER for certain power types
1849 PowerTypeEntry
const* powerType
= sDB2Manager
.GetPowerTypeEntry(power
);
1853 float addvalue
= 0.0f
;
1856 if (powerType
->RegenInterruptTimeMS
&& GetMSTimeDiffToNow(m_combatExitTime
) < uint32(powerType
->RegenInterruptTimeMS
))
1859 addvalue
= (powerType
->RegenPeace
+ GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER
+ powerIndex
)) * 0.001f
* m_regenTimer
;
1862 addvalue
= (powerType
->RegenCombat
+ GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER
+ powerIndex
)) * 0.001f
* m_regenTimer
;
1864 static Rates
const RatesForPower
[MAX_POWERS
] =
1867 RATE_POWER_RAGE_LOSS
,
1870 RATE_POWER_COMBO_POINTS_LOSS
,
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
,
1879 RATE_POWER_INSANITY
,
1880 MAX_RATES
, // burning embers, unused
1881 MAX_RATES
, // demonic fury, unused
1882 RATE_POWER_ARCANE_CHARGES
,
1887 if (RatesForPower
[power
] != MAX_RATES
)
1888 addvalue
*= sWorld
->getRate(RatesForPower
[power
]);
1890 // Mana regen calculated in Player::UpdateManaRegen()
1891 if (power
!= POWER_MANA
)
1893 addvalue
*= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT
, power
);
1895 addvalue
+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN
, power
) * ((power
!= POWER_ENERGY
) ? m_regenTimerCount
: m_regenTimer
) / (5 * IN_MILLISECONDS
);
1898 int32 minPower
= powerType
->MinPower
;
1899 int32 maxPower
= GetMaxPower(power
);
1901 if (addvalue
< 0.0f
)
1903 if (curValue
<= minPower
)
1906 else if (addvalue
> 0.0f
)
1908 if (curValue
>= maxPower
)
1914 addvalue
+= m_powerFraction
[powerIndex
];
1915 int32 integerValue
= int32(std::fabs(addvalue
));
1917 if (powerType
->CenterPower
)
1919 if (curValue
> powerType
->CenterPower
)
1921 addvalue
= -std::abs(addvalue
);
1922 minPower
= powerType
->CenterPower
;
1924 else if (curValue
< powerType
->CenterPower
)
1926 addvalue
= std::abs(addvalue
);
1927 maxPower
= powerType
->CenterPower
;
1933 if (addvalue
< 0.0f
)
1935 if (curValue
> minPower
+ integerValue
)
1937 curValue
-= integerValue
;
1938 m_powerFraction
[powerIndex
] = addvalue
+ integerValue
;
1942 curValue
= minPower
;
1943 m_powerFraction
[powerIndex
] = 0;
1948 if (curValue
+ integerValue
<= maxPower
)
1950 curValue
+= integerValue
;
1951 m_powerFraction
[powerIndex
] = addvalue
- integerValue
;
1955 curValue
= maxPower
;
1956 m_powerFraction
[powerIndex
] = 0;
1960 if (m_regenTimerCount
>= 2000)
1961 SetPower(power
, curValue
);
1963 UpdateUInt32Value(UNIT_FIELD_POWER
+ powerIndex
, curValue
);
1966 void Player::RegenerateHealth()
1968 uint32 curValue
= GetHealth();
1969 uint32 maxValue
= GetMaxHealth();
1971 if (curValue
>= maxValue
)
1974 float HealthIncreaseRate
= sWorld
->getRate(RATE_HEALTH
);
1975 float addValue
= 0.0f
;
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
))
1983 addValue
= HealthIncreaseRate
;
1987 if (getLevel() < 15)
1988 addValue
= (0.20f
* ((float)GetMaxHealth()) / getLevel() * HealthIncreaseRate
);
1990 addValue
= 0.015f
* ((float)GetMaxHealth()) * HealthIncreaseRate
;
1992 addValue
*= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT
);
1994 addValue
+= GetTotalAuraModifier(SPELL_AURA_MOD_REGEN
) * 0.4f
;
1996 else if (HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT
))
1997 ApplyPct(addValue
, GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT
));
1999 if (!IsStandState())
2003 // always regeneration bonus (including combat)
2004 addValue
+= GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT
);
2005 addValue
+= m_baseHealthRegen
/ 2.5f
;
2007 if (addValue
< 0.0f
)
2010 ModifyHealth(int32(addValue
));
2013 void Player::ResetAllPowers()
2017 switch (GetPowerType())
2020 SetFullPower(POWER_MANA
);
2023 SetPower(POWER_RAGE
, 0);
2026 SetFullPower(POWER_ENERGY
);
2028 case POWER_RUNIC_POWER
:
2029 SetPower(POWER_RUNIC_POWER
, 0);
2031 case POWER_LUNAR_POWER
:
2032 SetPower(POWER_LUNAR_POWER
, 0);
2039 bool Player::CanInteractWithQuestGiver(Object
* questGiver
) const
2041 switch (questGiver
->GetTypeId())
2044 return GetNPCIfCanInteractWith(questGiver
->GetGUID(), UNIT_NPC_FLAG_QUESTGIVER
) != nullptr;
2045 case TYPEID_GAMEOBJECT
:
2046 return GetGameObjectIfCanInteractWith(questGiver
->GetGUID(), GAMEOBJECT_TYPE_QUESTGIVER
) != nullptr;
2048 return IsAlive() && questGiver
->ToPlayer()->IsAlive();
2057 Creature
* Player::GetNPCIfCanInteractWith(ObjectGuid
const& guid
, uint64 npcflagmask
) const
2069 // exist (we need look pets also for some interaction (quest/etc)
2070 Creature
* creature
= ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid
);
2074 // Deathstate checks
2075 if (!IsAlive() && !(creature
->GetCreatureTemplate()->type_flags
& CREATURE_TYPE_FLAG_GHOST_VISIBLE
))
2078 // alive or spirit healer
2079 if (!creature
->IsAlive() && !(creature
->GetCreatureTemplate()->type_flags
& CREATURE_TYPE_FLAG_CAN_INTERACT_WHILE_DEAD
))
2082 // appropriate npc type
2083 if (npcflagmask
&& !creature
->HasFlag64(UNIT_NPC_FLAGS
, npcflagmask
))
2086 // not allow interaction under control, but allow with own pets
2087 if (!creature
->GetCharmerGUID().IsEmpty())
2090 // not unfriendly/hostile
2091 if (creature
->GetReactionTo(this) <= REP_UNFRIENDLY
)
2095 if (!creature
->IsWithinDistInMap(this, INTERACTION_DISTANCE
))
2101 GameObject
* Player::GetGameObjectIfCanInteractWith(ObjectGuid
const& guid
) const
2103 if (GameObject
* go
= GetMap()->GetGameObject(guid
))
2105 if (go
->IsWithinDistInMap(this, go
->GetInteractionDistance()))
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());
2115 GameObject
* Player::GetGameObjectIfCanInteractWith(ObjectGuid
const& guid
, GameobjectTypes type
) const
2117 if (GameObject
* go
= GetMap()->GetGameObject(guid
))
2119 if (go
->GetGoType() == type
)
2121 if (go
->IsWithinDistInMap(this, go
->GetInteractionDistance()))
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());
2132 bool Player::IsUnderWater() const
2134 return IsInWater() &&
2135 GetPositionZ() < (GetMap()->GetWaterLevel(GetPhaseShift(), GetPositionX(), GetPositionY()) - 2);
2138 void Player::SetInWater(bool apply
)
2140 if (m_isInWater
== apply
)
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
2147 /// @todo exist also swimming mobs, and function must be symmetric to enter/leave water
2148 m_isInWater
= apply
;
2150 // remove auras that need water/land
2151 RemoveAurasWithInterruptFlags(apply
? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER
: AURA_INTERRUPT_FLAG_NOT_UNDERWATER
);
2153 getHostileRefManager().updateThreatTables();
2156 bool Player::IsInAreaTriggerRadius(const AreaTriggerEntry
* trigger
) const
2161 if (int32(GetMapId()) != trigger
->ContinentID
&& !GetPhaseShift().HasVisibleMapId(trigger
->ContinentID
))
2164 if (trigger
->PhaseID
|| trigger
->PhaseGroupID
|| trigger
->PhaseUseFlags
)
2165 if (!PhasingHandler::InDbPhaseShift(this, trigger
->PhaseUseFlags
, trigger
->PhaseID
, trigger
->PhaseGroupID
))
2168 if (trigger
->Radius
> 0.f
)
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
)
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
))
2185 void Player::SetGameMaster(bool on
)
2189 m_ExtraFlags
|= PLAYER_EXTRA_GM_ON
;
2191 SetFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GM
);
2192 SetFlag(UNIT_FIELD_FLAGS_2
, UNIT_FLAG2_ALLOW_CHEAT_SPELLS
);
2194 if (Pet
* pet
= GetPet())
2196 pet
->setFaction(35);
2197 pet
->getHostileRefManager().setOnlineOfflineState(false);
2200 RemoveByteFlag(UNIT_FIELD_BYTES_2
, UNIT_BYTES_2_OFFSET_PVP_FLAG
, UNIT_BYTE2_FLAG_FFA_PVP
);
2201 ResetContestedPvP();
2203 getHostileRefManager().setOnlineOfflineState(false);
2204 CombatStopWithPets();
2206 PhasingHandler::SetAlwaysVisible(GetPhaseShift(), true);
2207 m_serverSideVisibilityDetect
.SetValue(SERVERSIDE_VISIBILITY_GM
, GetSession()->GetSecurity());
2211 PhasingHandler::SetAlwaysVisible(GetPhaseShift(), false);
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
);
2218 if (Pet
* pet
= GetPet())
2220 pet
->setFaction(getFaction());
2221 pet
->getHostileRefManager().setOnlineOfflineState(true);
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
);
2228 // restore FFA PvP area state, remove not allowed for GM mounts
2229 UpdateArea(m_areaUpdateId
);
2231 getHostileRefManager().setOnlineOfflineState(true);
2232 m_serverSideVisibilityDetect
.SetValue(SERVERSIDE_VISIBILITY_GM
, SEC_PLAYER
);
2235 UpdateObjectVisibility();
2238 bool Player::CanBeGameMaster() const
2240 return GetSession()->HasPermission(rbac::RBAC_PERM_COMMAND_GM
);
2243 void Player::SetGMVisible(bool on
)
2247 m_ExtraFlags
&= ~PLAYER_EXTRA_GM_INVISIBLE
; //remove flag
2248 m_serverSideVisibility
.SetValue(SERVERSIDE_VISIBILITY_GM
, SEC_PLAYER
);
2252 m_ExtraFlags
|= PLAYER_EXTRA_GM_INVISIBLE
; //add flag
2254 SetAcceptWhispers(false);
2255 SetGameMaster(true);
2257 m_serverSideVisibility
.SetValue(SERVERSIDE_VISIBILITY_GM
, GetSession()->GetSecurity());
2260 for (Channel
* channel
: m_channels
)
2261 channel
->SetInvisible(this, !on
);
2264 bool Player::IsGroupVisibleFor(Player
const* p
) const
2266 switch (sWorld
->getIntConfig(CONFIG_GROUP_VISIBILITY
))
2268 default: return IsInSameGroupWith(p
);
2269 case 1: return IsInSameRaidWith(p
);
2270 case 2: return GetTeam() == p
->GetTeam();
2274 bool Player::IsInSameGroupWith(Player
const* p
) const
2276 return p
== this || (GetGroup() != nullptr &&
2277 GetGroup() == p
->GetGroup() &&
2278 GetGroup()->SameSubGroup(this, p
));
2281 bool Player::IsInSameRaidWith(Player
const* p
) const
2283 return p
== this || (GetGroup() != nullptr && GetGroup() == p
->GetGroup());
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()
2290 Group
* group
= GetGroupInvite();
2294 group
->RemoveInvite(this);
2296 if (group
->GetMembersCount() <= 1) // group has just 1 member => disband
2298 if (group
->IsCreated())
2300 group
->Disband(true);
2304 group
->RemoveAllInvites();
2310 void Player::RemoveFromGroup(Group
* group
, ObjectGuid guid
, RemoveMethod method
/* = GROUP_REMOVEMETHOD_DEFAULT*/, ObjectGuid kicker
/* = ObjectGuid::Empty */, const char* reason
/* = NULL */)
2315 group
->RemoveMember(guid
, method
, kicker
, reason
);
2318 void Player::SetXP(uint32 xp
)
2320 SetUInt32Value(PLAYER_XP
, xp
);
2322 int32 playerLevelDelta
= 0;
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;
2328 SetInt32Value(PLAYER_FIELD_SCALING_PLAYER_LEVEL_DELTA
, playerLevelDelta
);
2331 void Player::GiveXP(uint32 xp
, Unit
* victim
, float group_rate
)
2336 if (!IsAlive() && !GetBattlegroundId())
2339 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_NO_XP_GAIN
))
2342 if (victim
&& victim
->GetTypeId() == TYPEID_UNIT
&& !victim
->ToCreature()->hasLootRecipient())
2345 uint8 level
= getLevel();
2347 sScriptMgr
->OnGivePlayerXP(this, xp
, victim
);
2349 // XP to money conversion processed in Player::RewardQuest
2350 if (level
>= sWorld
->getIntConfig(CONFIG_MAX_PLAYER_LEVEL
))
2354 bool recruitAFriend
= GetsRecruitAFriendBonus(true);
2356 // RaF does NOT stack with rested experience
2358 bonus_xp
= 2 * xp
; // xp + bonus_xp must add up to 3 * xp for RaF; calculation for quests done client-side
2360 bonus_xp
= victim
? _restMgr
->GetRestBonusFor(REST_TYPE_XP
, xp
) : 0; // XP resting bonus
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
;
2367 packet
.GroupBonus
= group_rate
;
2368 packet
.ReferAFriendBonusType
= recruitAFriend
? 1 : 0;
2369 GetSession()->SendPacket(packet
.Write());
2371 uint32 curXP
= GetUInt32Value(PLAYER_XP
);
2372 uint32 nextLvlXP
= GetUInt32Value(PLAYER_NEXT_LEVEL_XP
);
2373 uint32 newXP
= curXP
+ xp
+ bonus_xp
;
2375 while (newXP
>= nextLvlXP
&& level
< sWorld
->getIntConfig(CONFIG_MAX_PLAYER_LEVEL
))
2379 if (level
< sWorld
->getIntConfig(CONFIG_MAX_PLAYER_LEVEL
))
2380 GiveLevel(level
+ 1);
2383 nextLvlXP
= GetUInt32Value(PLAYER_NEXT_LEVEL_XP
);
2389 // Update player to next level
2390 // Current player experience not update (must be update by caller)
2391 void Player::GiveLevel(uint8 level
)
2393 uint8 oldLevel
= getLevel();
2394 if (level
== oldLevel
)
2397 if (Guild
* guild
= GetGuild())
2398 guild
->UpdateMemberData(this, GUILD_MEMBER_DATA_LEVEL
, level
);
2400 PlayerLevelInfo info
;
2401 sObjectMgr
->GetPlayerLevelInfo(getRace(), getClass(), level
, &info
);
2403 uint32 basemana
= 0;
2404 sObjectMgr
->GetPlayerClassLevelInfo(getClass(), level
, basemana
);
2406 WorldPackets::Misc::LevelUpInfo packet
;
2407 packet
.Level
= level
;
2408 packet
.HealthDelta
= 0;
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;
2419 for (uint8 i
= STAT_STRENGTH
; i
< MAX_STATS
; ++i
)
2420 packet
.StatDelta
[i
] = int32(info
.stats
[i
]) - GetCreateStat(Stats(i
));
2422 uint32
const* rowLevels
= (getClass() != CLASS_DEATH_KNIGHT
) ? DefaultTalentRowLevels
: DKTalentRowLevels
;
2424 packet
.Cp
= std::find(rowLevels
, rowLevels
+ MAX_TALENT_TIERS
, level
) != (rowLevels
+ MAX_TALENT_TIERS
);
2426 GetSession()->SendPacket(packet
.Write());
2428 SetUInt32Value(PLAYER_NEXT_LEVEL_XP
, sObjectMgr
->GetXPForLevel(level
));
2430 //update level, max level of skills
2431 m_Played_time
[PLAYED_TIME_LEVEL
] = 0; // Level Played Time reset
2433 _ApplyAllLevelScaleItemMods(false);
2437 UpdateSkillsForLevel();
2438 LearnDefaultSkills();
2439 LearnSpecializationSpells();
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
]);
2446 SetCreateMana(basemana
);
2448 InitTalentForLevel();
2449 InitTaxiNodesForLevel();
2451 if (level
< PLAYER_LEVEL_MIN_HONOR
)
2456 if (sWorld
->getBoolConfig(CONFIG_ALWAYS_MAXSKILL
)) // Max weapon skill when leveling up
2457 UpdateSkillsToMaxSkillsForLevel();
2459 _ApplyAllLevelScaleItemMods(true); // Moved to above SetFullHealth so player will have full health from Heirlooms
2461 // Only health and mana are set to maximum.
2463 SetFullPower(POWER_MANA
);
2465 // update level to hunter/summon pet
2466 if (Pet
* pet
= GetPet())
2467 pet
->SynchronizeLevelWithOwner();
2469 if (MailLevelReward
const* mailReward
= sObjectMgr
->GetMailLevelReward(level
, getRaceMask()))
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
);
2477 UpdateCriteria(CRITERIA_TYPE_REACH_LEVEL
);
2480 if (GetSession()->GetRecruiterId())
2482 if (level
< sWorld
->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL
))
2486 ++m_grantableLevels
;
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);
2494 sScriptMgr
->OnPlayerLevelChanged(this, oldLevel
);
2497 void Player::InitTalentForLevel()
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();
2504 uint32 talentTiers
= CalculateTalentsTiers();
2507 // Remove all talent points
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
);
2519 SetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS
, talentTiers
);
2521 if (!GetSession()->PlayerLoading())
2522 SendTalentsInfoData(); // update at client
2525 void Player::InitStatsForLevel(bool reapplyMods
)
2527 if (reapplyMods
) //reapply stats values only on .reset stats (level) command
2528 _RemoveAllStatBonuses();
2530 uint32 basemana
= 0;
2531 sObjectMgr
->GetPlayerClassLevelInfo(getClass(), getLevel(), basemana
);
2533 PlayerLevelInfo info
;
2534 sObjectMgr
->GetPlayerLevelInfo(getRace(), getClass(), getLevel(), &info
);
2536 SetUInt32Value(PLAYER_FIELD_MAX_LEVEL
, sWorld
->getIntConfig(CONFIG_MAX_PLAYER_LEVEL
));
2537 SetUInt32Value(PLAYER_NEXT_LEVEL_XP
, sObjectMgr
->GetXPForLevel(getLevel()));
2539 // reset before any aura state sources (health set/aura apply)
2540 SetUInt32Value(UNIT_FIELD_AURASTATE
, 0);
2542 UpdateSkillsForLevel();
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
);
2552 // reset size before reapply auras
2553 SetObjectScale(1.0f
);
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
]);
2559 for (uint8 i
= STAT_STRENGTH
; i
< MAX_STATS
; ++i
)
2560 SetStat(Stats(i
), info
.stats
[i
]);
2565 SetCreateMana(basemana
);
2567 SetArmor(int32(m_createStats
[STAT_AGILITY
]*2));
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);
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
)
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
);
2586 SetFloatValue(PLAYER_FIELD_MOD_SPELL_POWER_PCT
, 1.0f
);
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
));
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
)
2600 SetFloatValue(PLAYER_FIELD_WEAPON_DMG_MULTIPLIERS
+ i
, 1.0f
);
2601 SetFloatValue(PLAYER_FIELD_WEAPON_ATK_SPEED_MULTIPLIERS
+ i
, 1.0f
);
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
);
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
);
2614 // Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
2615 SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1
, 0.0f
);
2617 SetFloatValue(PLAYER_PARRY_PERCENTAGE
, 0.0f
);
2618 SetFloatValue(PLAYER_BLOCK_PERCENTAGE
, 0.0f
);
2620 // Static 30% damage blocked
2621 SetUInt32Value(PLAYER_SHIELD_BLOCK
, 30);
2624 SetFloatValue(PLAYER_DODGE_PERCENTAGE
, 0.0f
);
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
)
2633 SetResistance(SpellSchools(i
), 0);
2634 SetResistanceBuffMods(SpellSchools(i
), true, 0.0f
);
2635 SetResistanceBuffMods(SpellSchools(i
), false, 0.0f
);
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
)
2642 SetUInt32Value(UNIT_FIELD_POWER_COST_MODIFIER
+ i
, 0);
2643 SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER
+ i
, 0.0f
);
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
);
2652 for (uint8 i
= POWER_MANA
; i
< MAX_POWERS
; ++i
)
2653 SetMaxPower(Powers(i
), uint32(GetCreatePowers(Powers(i
))));
2655 SetMaxHealth(0); // stamina bonus will applied later
2657 // cleanup mounted state (it will set correctly at aura loading if player saved at mount.
2658 SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID
, 0);
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
2670 SetFlag(UNIT_FIELD_FLAGS_2
, UNIT_FLAG2_REGENERATE_POWER
);// must be set
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
);
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
);
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);
2683 if (reapplyMods
) // reapply stats values only on .reset stats (level) command
2684 _ApplyAllStatBonuses();
2686 // set current level health and mana/energy to maximum after applying all mods.
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);
2695 // update level to hunter/summon pet
2696 if (Pet
* pet
= GetPet())
2697 pet
->SynchronizeLevelWithOwner();
2700 void Player::SendKnownSpells()
2702 WorldPackets::Spells::SendKnownSpells knownSpells
;
2703 knownSpells
.InitialLogin
= false; /// @todo
2705 knownSpells
.KnownSpells
.reserve(m_spells
.size());
2706 for (PlayerSpellMap::value_type
const& spell
: m_spells
)
2708 if (spell
.second
->state
== PLAYERSPELL_REMOVED
)
2711 if (!spell
.second
->active
|| spell
.second
->disabled
)
2714 knownSpells
.KnownSpells
.push_back(spell
.first
);
2717 SendDirectMessage(knownSpells
.Write());
2720 void Player::RemoveMail(uint32 id
)
2722 for (PlayerMails::iterator itr
= m_mail
.begin(); itr
!= m_mail
.end(); ++itr
)
2724 if ((*itr
)->messageID
== id
)
2726 //do not delete item, because Player::removeMail() is called when returning mail to sender.
2733 void Player::SendMailResult(uint32 mailId
, MailResponseType mailAction
, MailResponseResult mailError
, uint32 equipError
, ObjectGuid::LowType item_guid
, uint32 item_count
) const
2735 WorldPackets::Mail::MailCommandResult result
;
2737 result
.MailID
= mailId
;
2738 result
.Command
= mailAction
;
2739 result
.ErrorCode
= mailError
;
2741 if (mailError
== MAIL_ERR_EQUIP_ERROR
)
2742 result
.BagResult
= equipError
;
2743 else if (mailAction
== MAIL_ITEM_TAKEN
)
2745 result
.AttachID
= item_guid
;
2746 result
.QtyInInventory
= item_count
;
2748 GetSession()->SendPacket(result
.Write());
2751 void Player::SendNewMail() const
2753 // deliver undelivered mail
2754 WorldPackets::Mail::NotifyRecievedMail notify
;
2755 notify
.Delay
= 0.0f
;
2757 GetSession()->SendPacket(notify
.Write());
2760 void Player::UpdateNextMailTimeAndUnreads()
2762 // calculate next delivery time (min. from non-delivered mails
2763 // and recalculate unReadMail
2764 time_t cTime
= time(nullptr);
2765 m_nextMailDelivereTime
= 0;
2767 for (PlayerMails::iterator itr
= m_mail
.begin(); itr
!= m_mail
.end(); ++itr
)
2769 if ((*itr
)->deliver_time
> cTime
)
2771 if (!m_nextMailDelivereTime
|| m_nextMailDelivereTime
> (*itr
)->deliver_time
)
2772 m_nextMailDelivereTime
= (*itr
)->deliver_time
;
2774 else if (((*itr
)->checked
& MAIL_CHECK_MASK_READ
) == 0)
2779 void Player::AddNewMailDeliverTime(time_t deliver_time
)
2781 if (deliver_time
<= time(nullptr)) // ready now
2786 else // not ready and no have ready mails
2788 if (!m_nextMailDelivereTime
|| m_nextMailDelivereTime
> deliver_time
)
2789 m_nextMailDelivereTime
= deliver_time
;
2793 void DeleteSpellFromAllPlayers(uint32 spellId
)
2795 PreparedStatement
* stmt
= CharacterDatabase
.GetPreparedStatement(CHAR_DEL_INVALID_SPELL_SPELLS
);
2796 stmt
->setUInt32(0, spellId
);
2797 CharacterDatabase
.Execute(stmt
);
2800 bool Player::AddTalent(TalentEntry
const* talent
, uint8 spec
, bool learning
)
2802 SpellInfo
const* spellInfo
= sSpellMgr
->GetSpellInfo(talent
->SpellID
);
2805 TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: %u) does not exist.", talent
->SpellID
);
2809 if (!SpellMgr::IsSpellValid(spellInfo
, this, false))
2811 TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: %u) is invalid", talent
->SpellID
);
2815 if (talent
->OverridesSpellID
)
2816 AddOverrideSpell(talent
->OverridesSpellID
, talent
->SpellID
);
2818 PlayerTalentMap::iterator itr
= GetTalentMap(spec
)->find(talent
->ID
);
2819 if (itr
!= GetTalentMap(spec
)->end())
2820 itr
->second
= PLAYERSPELL_UNCHANGED
;
2822 (*GetTalentMap(spec
))[talent
->ID
] = learning
? PLAYERSPELL_NEW
: PLAYERSPELL_UNCHANGED
;
2827 void Player::RemoveTalent(TalentEntry
const* talent
)
2829 SpellInfo
const* spellInfo
= sSpellMgr
->GetSpellInfo(talent
->SpellID
);
2833 RemoveSpell(talent
->SpellID
, true);
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);
2840 if (talent
->OverridesSpellID
)
2841 RemoveOverrideSpell(talent
->OverridesSpellID
, talent
->SpellID
);
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
;
2849 bool Player::AddSpell(uint32 spellId
, bool active
, bool learning
, bool dependent
, bool disabled
, bool loading
/*= false*/, int32 fromSkill
/*= 0*/)
2851 SpellInfo
const* spellInfo
= sSpellMgr
->GetSpellInfo(spellId
);
2854 // do character spell book cleanup (all characters)
2855 if (!IsInWorld() && !learning
) // spell load case
2857 TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) does not exist. deleting for all characters in `character_spell`.", spellId
);
2859 DeleteSpellFromAllPlayers(spellId
);
2862 TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) does not exist", spellId
);
2867 if (!SpellMgr::IsSpellValid(spellInfo
, this, false))
2869 // do character spell book cleanup (all characters)
2870 if (!IsInWorld() && !learning
) // spell load case
2872 TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) is invalid. deleting for all characters in `character_spell`.", spellId
);
2874 DeleteSpellFromAllPlayers(spellId
);
2877 TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) is invalid", spellId
);
2882 PlayerSpellState state
= learning
? PLAYERSPELL_NEW
: PLAYERSPELL_UNCHANGED
;
2884 bool dependent_set
= false;
2885 bool disabled_case
= false;
2886 bool superceded_old
= false;
2888 PlayerSpellMap::iterator itr
= m_spells
.find(spellId
);
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())
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())
2899 if (uint32 next
= sSpellMgr
->GetNextSpellInChain(spellId
))
2903 // high rank already known so this must !active
2905 next_active_spell_id
= next
;
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
)
2914 if (!IsInWorld() && !learning
) // explicitly load from DB and then exist in it already and set correctly
2915 itr
->second
->state
= PLAYERSPELL_UNCHANGED
;
2920 // dependent spell known as not dependent, overwrite state
2921 if (itr
->second
->state
!= PLAYERSPELL_REMOVED
&& !itr
->second
->dependent
&& dependent
)
2923 itr
->second
->dependent
= dependent
;
2924 if (itr
->second
->state
!= PLAYERSPELL_NEW
)
2925 itr
->second
->state
= PLAYERSPELL_CHANGED
;
2926 dependent_set
= true;
2929 // update active state for known spell
2930 if (itr
->second
->active
!= active
&& itr
->second
->state
!= PLAYERSPELL_REMOVED
&& !itr
->second
->disabled
)
2932 itr
->second
->active
= active
;
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
;
2941 if (spellInfo
->IsPassive() && IsNeedCastPassiveSpellAtLearn(spellInfo
))
2942 CastSpell(this, spellId
, true);
2944 else if (IsInWorld())
2946 if (next_active_spell_id
)
2947 SendSupercededSpell(spellId
, next_active_spell_id
);
2950 WorldPackets::Spells::UnlearnedSpells unlearnedSpells
;
2951 unlearnedSpells
.SpellID
.push_back(spellId
);
2952 SendDirectMessage(unlearnedSpells
.Write());
2956 return active
; // learn (show in spell book if active now)
2959 if (itr
->second
->disabled
!= disabled
&& itr
->second
->state
!= PLAYERSPELL_REMOVED
)
2961 if (itr
->second
->state
!= PLAYERSPELL_NEW
)
2962 itr
->second
->state
= PLAYERSPELL_CHANGED
;
2963 itr
->second
->disabled
= disabled
;
2968 disabled_case
= true;
2970 else switch (itr
->second
->state
)
2972 case PLAYERSPELL_UNCHANGED
: // known saved spell
2974 case PLAYERSPELL_REMOVED
: // re-learning removed not saved spell
2977 m_spells
.erase(itr
);
2978 state
= PLAYERSPELL_CHANGED
;
2979 break; // need re-add
2981 default: // known not saved yet spell (new or modified)
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
;
2992 if (!disabled_case
) // skip new spell adding if spell already known (disabled spells case)
2994 // non talent spell: learn low ranks (recursive call)
2995 if (uint32 prev_spell
= sSpellMgr
->GetPrevSpellInChain(spellId
))
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
);
3003 PlayerSpell
* newspell
= new PlayerSpell
;
3004 newspell
->state
= state
;
3005 newspell
->active
= active
;
3006 newspell
->dependent
= dependent
;
3007 newspell
->disabled
= disabled
;
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())
3012 for (PlayerSpellMap::iterator itr2
= m_spells
.begin(); itr2
!= m_spells
.end(); ++itr2
)
3014 if (itr2
->second
->state
== PLAYERSPELL_REMOVED
)
3017 SpellInfo
const* i_spellInfo
= sSpellMgr
->GetSpellInfo(itr2
->first
);
3021 if (spellInfo
->IsDifferentRankOf(i_spellInfo
))
3023 if (itr2
->second
->active
)
3025 if (spellInfo
->IsHighRankOf(i_spellInfo
))
3027 if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
3028 SendSupercededSpell(itr2
->first
, spellId
);
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.
3038 if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
3039 SendSupercededSpell(spellId
, itr2
->first
);
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
;
3051 m_spells
[spellId
] = newspell
;
3053 // return false if spell disabled
3054 if (newspell
->disabled
)
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
))
3062 // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
3063 CastSpell(this, spellId
, true);
3065 // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
3066 else if (spellInfo
->IsPassive())
3068 if (IsNeedCastPassiveSpellAtLearn(spellInfo
))
3069 CastSpell(this, spellId
, true);
3071 else if (spellInfo
->HasEffect(SPELL_EFFECT_SKILL_STEP
))
3073 CastSpell(this, spellId
, true);
3077 // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
3078 if (uint32 freeProfs
= GetFreePrimaryProfessionPoints())
3080 if (spellInfo
->IsPrimaryProfessionFirstRank())
3081 SetFreePrimaryProfessions(freeProfs
- 1);
3084 SkillLineAbilityMapBounds skill_bounds
= sSpellMgr
->GetSkillLineAbilityMapBounds(spellId
);
3086 if (SpellLearnSkillNode
const* spellLearnSkill
= sSpellMgr
->GetSpellLearnSkill(spellId
))
3088 // add dependent skills if this spell is not learned from adding skill already
3089 if (spellLearnSkill
->skill
!= fromSkill
)
3091 uint32 skill_value
= GetPureSkillValue(spellLearnSkill
->skill
);
3092 uint32 skill_max_value
= GetPureMaxSkillValue(spellLearnSkill
->skill
);
3094 if (skill_value
< spellLearnSkill
->value
)
3095 skill_value
= spellLearnSkill
->value
;
3097 uint32 new_skill_max_value
= spellLearnSkill
->maxvalue
== 0 ? GetMaxSkillValueForLevel() : spellLearnSkill
->maxvalue
;
3099 if (skill_max_value
< new_skill_max_value
)
3100 skill_max_value
= new_skill_max_value
;
3102 SetSkill(spellLearnSkill
->skill
, spellLearnSkill
->step
, skill_value
, skill_max_value
);
3107 // not ranked skills
3108 for (SkillLineAbilityMap::const_iterator _spell_idx
= skill_bounds
.first
; _spell_idx
!= skill_bounds
.second
; ++_spell_idx
)
3110 SkillLineEntry
const* pSkill
= sSkillLineStore
.LookupEntry(_spell_idx
->second
->SkillLine
);
3114 if (_spell_idx
->second
->SkillLine
== fromSkill
)
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
);
3124 // learn dependent spells
3125 SpellLearnSpellMapBounds spell_bounds
= sSpellMgr
->GetSpellLearnSpellMapBounds(spellId
);
3127 for (SpellLearnSpellMap::const_iterator itr2
= spell_bounds
.first
; itr2
!= spell_bounds
.second
; ++itr2
)
3129 if (!itr2
->second
.AutoLearned
)
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);
3137 if (itr2
->second
.OverridesSpell
&& itr2
->second
.Active
)
3138 AddOverrideSpell(itr2
->second
.OverridesSpell
, itr2
->second
.Spell
);
3141 if (!GetSession()->PlayerLoading())
3143 // not ranked skills
3144 for (SkillLineAbilityMap::const_iterator _spell_idx
= skill_bounds
.first
; _spell_idx
!= skill_bounds
.second
; ++_spell_idx
)
3146 UpdateCriteria(CRITERIA_TYPE_LEARN_SKILL_LINE
, _spell_idx
->second
->SkillLine
);
3147 UpdateCriteria(CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
, _spell_idx
->second
->SkillLine
);
3150 UpdateCriteria(CRITERIA_TYPE_LEARN_SPELL
, spellId
);
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);
3157 // need to add Battle pets automatically into pet journal
3158 for (BattlePetSpeciesEntry
const* entry
: sBattlePetSpeciesStore
)
3160 if (entry
->SummonSpellID
== int32(spellId
) && GetSession()->GetBattlePetMgr()->GetPetCount(entry
->ID
) == 0)
3162 GetSession()->GetBattlePetMgr()->AddPet(entry
->ID
, entry
->CreatureID
, BattlePetMgr::RollPetBreed(entry
->ID
), BattlePetMgr::GetDefaultPetQuality(entry
->ID
));
3163 UpdateCriteria(CRITERIA_TYPE_OWN_BATTLE_PET_COUNT
);
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
;
3172 void Player::AddTemporarySpell(uint32 spellId
)
3174 PlayerSpellMap::iterator itr
= m_spells
.find(spellId
);
3175 // spell already added - do not do anything
3176 if (itr
!= m_spells
.end())
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
;
3186 void Player::RemoveTemporarySpell(uint32 spellId
)
3188 PlayerSpellMap::iterator itr
= m_spells
.find(spellId
);
3189 // spell already not in list - do not do anything
3190 if (itr
== m_spells
.end())
3192 // spell has other state than temporary - do not change it
3193 if (itr
->second
->state
!= PLAYERSPELL_TEMPORARY
)
3196 m_spells
.erase(itr
);
3199 bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo
const* spellInfo
) const
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
))));
3207 if (spellInfo
->HasAttribute(SPELL_ATTR8_MASTERY_SPECIALIZATION
))
3208 need_cast
&= IsCurrentSpecMasterySpell(spellInfo
);
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)
3214 for (SpellEffectInfo
const* effectInfo
: spellInfo
->GetEffectsForDifficulty(DIFFICULTY_NONE
))
3215 if (effectInfo
&& effectInfo
->IsAura())
3219 //Check CasterAuraStates
3220 return need_cast
&& (!spellInfo
->CasterAuraState
|| HasAuraState(AuraStateType(spellInfo
->CasterAuraState
)));
3223 bool Player::IsCurrentSpecMasterySpell(SpellInfo
const* spellInfo
) const
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]);
3231 void Player::LearnSpell(uint32 spell_id
, bool dependent
, int32 fromSkill
/*= 0*/)
3233 PlayerSpellMap::iterator itr
= m_spells
.find(spell_id
);
3235 bool disabled
= (itr
!= m_spells
.end()) ? itr
->second
->disabled
: false;
3236 bool active
= disabled
? itr
->second
->active
: true;
3238 bool learning
= AddSpell(spell_id
, active
, true, dependent
, false, false, fromSkill
);
3240 // prevent duplicated entires in spell book, also not send if not in world (loading)
3241 if (learning
&& IsInWorld())
3243 WorldPackets::Spells::LearnedSpells packet
;
3244 packet
.SpellID
.push_back(spell_id
);
3245 GetSession()->SendPacket(packet
.Write());
3248 // learn all disabled higher ranks and required spells (recursive)
3251 if (uint32 nextSpell
= sSpellMgr
->GetNextSpellInChain(spell_id
))
3253 PlayerSpellMap::iterator iter
= m_spells
.find(nextSpell
);
3254 if (iter
!= m_spells
.end() && iter
->second
->disabled
)
3255 LearnSpell(nextSpell
, false, fromSkill
);
3258 SpellsRequiringSpellMapBounds spellsRequiringSpell
= sSpellMgr
->GetSpellsRequiringSpellBounds(spell_id
);
3259 for (SpellsRequiringSpellMap::const_iterator itr2
= spellsRequiringSpell
.first
; itr2
!= spellsRequiringSpell
.second
; ++itr2
)
3261 PlayerSpellMap::iterator iter2
= m_spells
.find(itr2
->second
);
3262 if (iter2
!= m_spells
.end() && iter2
->second
->disabled
)
3263 LearnSpell(itr2
->second
, false, fromSkill
);
3268 void Player::RemoveSpell(uint32 spell_id
, bool disabled
, bool learn_low_rank
)
3270 PlayerSpellMap::iterator itr
= m_spells
.find(spell_id
);
3271 if (itr
== m_spells
.end())
3274 if (itr
->second
->state
== PLAYERSPELL_REMOVED
|| (disabled
&& itr
->second
->disabled
) || itr
->second
->state
== PLAYERSPELL_TEMPORARY
)
3277 // unlearn non talent higher ranks (recursive)
3278 if (uint32 nextSpell
= sSpellMgr
->GetNextSpellInChain(spell_id
))
3280 SpellInfo
const* spellInfo
= sSpellMgr
->AssertSpellInfo(nextSpell
);
3281 if (HasSpell(nextSpell
) && !spellInfo
->HasAttribute(SPELL_ATTR0_CU_IS_TALENT
))
3282 RemoveSpell(nextSpell
, disabled
, false);
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
);
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
3294 bool cur_active
= itr
->second
->active
;
3295 bool cur_dependent
= itr
->second
->dependent
;
3299 itr
->second
->disabled
= disabled
;
3300 if (itr
->second
->state
!= PLAYERSPELL_NEW
)
3301 itr
->second
->state
= PLAYERSPELL_CHANGED
;
3305 if (itr
->second
->state
== PLAYERSPELL_NEW
)
3308 m_spells
.erase(itr
);
3311 itr
->second
->state
= PLAYERSPELL_REMOVED
;
3314 RemoveOwnedAura(spell_id
, GetGUID());
3317 for (uint8 i
= 0; i
< MAX_SPELL_EFFECTS
; ++i
)
3318 if (PetAura
const* petSpell
= sSpellMgr
->GetPetAura(spell_id
, i
))
3319 RemovePetAura(petSpell
);
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())
3325 uint32 freeProfs
= GetFreePrimaryProfessionPoints()+1;
3326 if (freeProfs
<= sWorld
->getIntConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL
))
3327 SetFreePrimaryProfessions(freeProfs
);
3330 // remove dependent skill
3331 SpellLearnSkillNode
const* spellLearnSkill
= sSpellMgr
->GetSpellLearnSkill(spell_id
);
3332 if (spellLearnSkill
)
3334 uint32 prev_spell
= sSpellMgr
->GetPrevSpellInChain(spell_id
);
3335 if (!prev_spell
) // first rank, remove skill
3336 SetSkill(spellLearnSkill
->skill
, 0, 0, 0);
3339 // search prev. skill setting by spell ranks chain
3340 SpellLearnSkillNode
const* prevSkill
= sSpellMgr
->GetSpellLearnSkill(prev_spell
);
3341 while (!prevSkill
&& prev_spell
)
3343 prev_spell
= sSpellMgr
->GetPrevSpellInChain(prev_spell
);
3344 prevSkill
= sSpellMgr
->GetSpellLearnSkill(sSpellMgr
->GetFirstSpellInChain(prev_spell
));
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
3351 uint32 skill_value
= GetPureSkillValue(prevSkill
->skill
);
3352 uint32 skill_max_value
= GetPureMaxSkillValue(prevSkill
->skill
);
3354 if (skill_value
> prevSkill
->value
)
3355 skill_value
= prevSkill
->value
;
3357 uint32 new_skill_max_value
= prevSkill
->maxvalue
== 0 ? GetMaxSkillValueForLevel() : prevSkill
->maxvalue
;
3359 if (skill_max_value
> new_skill_max_value
)
3360 skill_max_value
= new_skill_max_value
;
3362 SetSkill(prevSkill
->skill
, prevSkill
->step
, skill_value
, skill_max_value
);
3367 // remove dependent spells
3368 SpellLearnSpellMapBounds spell_bounds
= sSpellMgr
->GetSpellLearnSpellMapBounds(spell_id
);
3370 for (SpellLearnSpellMap::const_iterator itr2
= spell_bounds
.first
; itr2
!= spell_bounds
.second
; ++itr2
)
3372 RemoveSpell(itr2
->second
.Spell
, disabled
);
3373 if (itr2
->second
.OverridesSpell
)
3374 RemoveOverrideSpell(itr2
->second
.OverridesSpell
, itr2
->second
.Spell
);
3377 // activate lesser rank in spellbook/action bar, and cast it if need
3378 bool prev_activate
= false;
3380 if (uint32 prev_id
= sSpellMgr
->GetPrevSpellInChain(spell_id
))
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())
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())
3390 if (prev_itr
->second
->dependent
!= cur_dependent
)
3392 prev_itr
->second
->dependent
= cur_dependent
;
3393 if (prev_itr
->second
->state
!= PLAYERSPELL_NEW
)
3394 prev_itr
->second
->state
= PLAYERSPELL_CHANGED
;
3397 // now re-learn if need re-activate
3398 if (cur_active
&& !prev_itr
->second
->active
&& learn_low_rank
)
3400 if (AddSpell(prev_id
, true, false, prev_itr
->second
->dependent
, prev_itr
->second
->disabled
))
3402 // downgrade spell ranks in spellbook and action bar
3403 SendSupercededSpell(spell_id
, prev_id
);
3404 prev_activate
= true;
3411 m_overrideSpells
.erase(spell_id
);
3415 if (spellInfo
&& spellInfo
->IsPassive() && spellInfo
->HasEffect(SPELL_EFFECT_TITAN_GRIP
))
3417 RemoveAurasDueToSpell(m_titanGripPenaltySpellId
);
3418 SetCanTitanGrip(false);
3424 if (spellInfo
&& spellInfo
->IsPassive() && spellInfo
->HasEffect(SPELL_EFFECT_DUAL_WIELD
))
3425 SetCanDualWield(false);
3428 if (sWorld
->getBoolConfig(CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN
))
3429 AutoUnequipOffhandIfNeed();
3431 // remove from spell book if not replaced by lesser rank
3434 WorldPackets::Spells::UnlearnedSpells unlearnedSpells
;
3435 unlearnedSpells
.SpellID
.push_back(spell_id
);
3436 SendDirectMessage(unlearnedSpells
.Write());
3440 void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns
)
3442 // remove cooldowns on spells that have < 10 min CD
3443 GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr
)
3445 SpellInfo
const* spellInfo
= sSpellMgr
->AssertSpellInfo(itr
->first
);
3446 return spellInfo
->RecoveryTime
< 10 * MINUTE
* IN_MILLISECONDS
&& spellInfo
->CategoryRecoveryTime
< 10 * MINUTE
* IN_MILLISECONDS
;
3450 if (removeActivePetCooldowns
)
3451 if (Pet
* pet
= GetPet())
3452 pet
->GetSpellHistory()->ResetAllCooldowns();