141#include <boost/dynamic_bitset.hpp>
142#include <G3D/g3dmath.h>
145#define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)
148#define DEATH_EXPIRE_STEP (5*MINUTE)
149#define MAX_DEATH_COUNT 3
323 sWorld->IncreasePlayerCount();
354 _restMgr = std::make_unique<RestMgr>(
this);
369 for (PlayerMails::iterator itr =
m_mail.begin(); itr !=
m_mail.end(); ++itr)
372 for (ItemMap::iterator iter =
mMitems.begin(); iter !=
mMitems.end(); ++iter)
375 for (
size_t x = 0; x <
ItemSetEff.size(); x++)
381 sWorld->DecreasePlayerCount();
404 TC_LOG_ERROR(
"entities.player.cheat",
"Player::Create: Possible hacking attempt: Account {} tried to create a character named '{}' with an invalid race/class pair ({}/{}) - refusing to do so.",
415 TC_LOG_ERROR(
"entities.player.cheat",
"Player::Create: Possible hacking attempt: Account {} tried to create a character named '{}' with an invalid character class ({}) - refusing to do so (wrong DBC-files?)",
422 TC_LOG_ERROR(
"entities.player.cheat",
"Player::Create: Possible hacking-attempt: Account {} tried creating a character named '{}' with invalid appearance attributes - refusing to do so",
440 transport->AddPassenger(
this);
444 transport->CalculatePassengerPosition(x, y, z, &o);
460 TC_LOG_ERROR(
"entities.player.cheat",
"Player::Create: Possible hacking attempt: Account {} tried to create a character named '{}' with an invalid gender ({}) - refusing to do so",
516 for (PlayerCreateInfoActions::const_iterator action_itr = info->
action.begin(); action_itr != info->
action.end(); ++action_itr)
573 TC_LOG_DEBUG(
"entities.player.items",
"Player::StoreNewItemInBestSlots: Player '{}' ({}) creates initial item (ItemID: {}, Count: {})",
603 TC_LOG_ERROR(
"entities.player.items",
"Player::StoreNewItemInBestSlots: Player '{}' ({}) can't equip or store initial item (ItemID: {}, Race: {}, Class: {}, InventoryResult: {})",
676 TC_LOG_DEBUG(
"entities.player",
"Player::EnvironmentalDamage: Player '{}' ({}) fall to death, losing {}% durability",
702 return UnderWaterTime;
741 uint8 damagePercent = 10;
748 damage +=
urand(0, pow(10, std::max(0, (
int32)log10(damage) - 1)));
870 uint8 drunk = currentDrunkValue ? --currentDrunkValue : 0;
889 if (newDrunkValue > 100)
909 if (newDrunkenState == oldDrunkenState)
966 if (!aura->IsPermanent())
967 aura->SetDuration(aura->GetSpellInfo()->GetMaxDuration());
997 if (q_status.
Timer <= p_time)
1005 q_status.
Timer -= p_time;
1131 if (itr->second < now)
1148 if (!
GetMap()->IsDungeon())
1255 return TeleportTo({ .Location =
WorldLocation(mapid, x, y, z, orientation), .InstanceId = instanceId }, options);
1260 return TeleportTo({ .Location = loc, .InstanceId = instanceId }, options);
1267 TC_LOG_ERROR(
"maps",
"Player::TeleportTo: Invalid map ({}) or invalid coordinates ({}) given when teleporting player '{}' ({}, MapID: {}, {}).",
1292 TC_LOG_DEBUG(
"maps",
"Player '{}' ({}) using client without required expansion tried teleporting to non accessible map (MapID: {})",
1297 transport->RemovePassenger(
this);
1320 transport->RemovePassenger(
this);
1394 options &= ~TELE_TO_SEAMLESS;
1463 transferPending.
Ship.emplace();
1466 transferPending.
Ship->ID = transportSpawn->TransportGameObjectId;
1470 transferPending.
Ship->OriginMapID = -1;
1556 g->SendUpdateToPlayer(
GetGUID());
1602 for (ItemMap::iterator iter =
mMitems.begin(); iter !=
mMitems.end(); ++iter)
1603 iter->second->RemoveFromWorld();
1607 TC_LOG_ERROR(
"entities.player",
"Player::RemoveFromWorld: Player '{}' ({}) has viewpoint (Entry:{}, Type: {}) when removed from world",
1623 bool requireImmunityPurgesEffectAttribute )
const
1645 uint32 regeneratedRunes = 0;
1647 while (regeneratedRunes < MAX_RECHARGING_RUNES && m_runes->CooldownOrder.size() > regenIndex)
1649 uint8 runeToRegen =
m_runes->CooldownOrder[regenIndex];
1693 float addvalue = 0.0f;
1735 addvalue *=
sWorld->getRate(RatesForPower[power]);
1752 addvalue = -std::abs(addvalue);
1755 else if (curValue < powerType->CenterPower)
1757 addvalue = std::abs(addvalue);
1765 int32 integerValue =
int32(std::fabs(addvalue));
1767 if (addvalue < 0.0f)
1769 if (curValue <= minPower)
1772 else if (addvalue > 0.0f)
1774 if (curValue >= maxPower)
1780 bool forcesSetPower =
false;
1781 if (addvalue < 0.0f)
1783 if (curValue > minPower + integerValue)
1785 curValue -= integerValue;
1790 curValue = minPower;
1792 forcesSetPower =
true;
1797 if (curValue + integerValue <= maxPower)
1799 curValue += integerValue;
1804 curValue = maxPower;
1806 forcesSetPower =
true;
1811 curValue = maxPower;
1842 if (curValue >= maxValue)
1846 float addValue = 0.0f;
1854 addValue = HealthIncreaseRate;
1861 addValue = 0.015f * ((float)
GetMaxHealth()) * HealthIncreaseRate;
1878 if (addValue < 0.0f)
1954 auto hasNpcFlags = [&]()
1956 if (!npcFlags && !npcFlags2)
2038 return areaTriggerActionSet->GetFlags().HasFlag(flag);
2118 if (
sWorld->IsFFAPvPRealm())
2153 channel->SetInvisible(
this, !on);
2163 case 3:
return false;
2169 return p ==
this || (
GetGroup() !=
nullptr &&
2215 int32 playerLevelDelta = 0;
2219 playerLevelDelta = -1;
2240 sScriptMgr->OnGivePlayerXP(
this, xp, victim);
2285 if (level == oldLevel)
2298 packet.
Level = level;
2348 artifact->CheckArtifactRelicSlotUnlock(
this);
2358 pet->SynchronizeLevelWithOwner();
2371 if (level > oldLevel)
2376 sScriptMgr->OnPlayerLevelChanged(
this, oldLevel);
2499 for (
uint16 i = 0; i < 3; ++i)
2592 pet->SynchronizeLevelWithOwner();
2601 for (PlayerSpellMap::value_type
const& spell :
m_spells)
2606 if (!spell.second.active || spell.second.disabled)
2610 if (spell.second.favorite)
2625 for (PlayerMails::iterator itr =
m_mail.begin(); itr !=
m_mail.end(); ++itr)
2627 if ((*itr)->messageID ==
id)
2658 notify.
Delay = 0.0f;
2670 for (PlayerMails::iterator itr =
m_mail.begin(); itr !=
m_mail.end(); ++itr)
2672 if ((*itr)->deliver_time > cTime)
2708 TC_LOG_ERROR(
"spells",
"Player::AddTalent: Spell (ID: {}) does not exist.", talent->
SpellID);
2718 PlayerTalentMap::iterator itr =
GetTalentMap(spec)->find(talent->
ID);
2775 return &auraLocation->Loc;
2788 TC_LOG_ERROR(
"spells",
"Player::AddSpell: Spell (ID: {}) does not exist. deleting for all characters in `character_spell`.", spellId);
2793 TC_LOG_ERROR(
"spells",
"Player::AddSpell: Spell (ID: {}) does not exist", spellId);
2803 TC_LOG_ERROR(
"spells",
"Player::AddSpell: Spell (ID: {}) is invalid. deleting for all characters in `character_spell`.", spellId);
2808 TC_LOG_ERROR(
"spells",
"Player::AddSpell: Spell (ID: {}) is invalid", spellId);
2815 bool dependent_set =
false;
2816 bool disabled_case =
false;
2817 bool superceded_old =
false;
2819 PlayerSpellMap::iterator itr =
m_spells.find(spellId);
2826 uint32 next_active_spell_id = 0;
2836 next_active_spell_id = next;
2843 itr->second.dependent == dependent && itr->second.disabled == disabled)
2854 itr->second.dependent = dependent;
2857 dependent_set =
true;
2860 if (itr->second.TraitDefinitionId != traitDefinitionId)
2862 if (itr->second.TraitDefinitionId)
2866 itr->second.TraitDefinitionId = traitDefinitionId;
2869 itr->second.favorite = favorite;
2872 if (itr->second.active != active && itr->second.state !=
PLAYERSPELL_REMOVED && !itr->second.disabled)
2874 itr->second.active = active;
2876 if (!
IsInWorld() && !learning && !dependent_set)
2888 if (next_active_spell_id)
2893 unlearnedSpells.
SpellID.push_back(spellId);
2905 itr->second.disabled = disabled;
2910 disabled_case =
true;
2912 else switch (itr->second.state)
2925 if (!
IsInWorld() && !learning && !dependent_set)
2939 AddSpell(prev_spell, active,
true,
true, disabled,
false, fromSkill);
2944 std::pair<PlayerSpellMap::iterator, bool> inserted =
m_spells.emplace(std::piecewise_construct, std::forward_as_tuple(spellId), std::forward_as_tuple());
2949 newspell.
active = active;
2953 if (traitDefinitionId)
2959 for (PlayerSpellMap::iterator itr2 =
m_spells.begin(); itr2 !=
m_spells.end(); ++itr2)
2970 if (itr2->second.active)
2978 itr2->second.active =
false;
2981 superceded_old =
true;
3003 bool castSpell =
false;
3023 if (traitDefinitionId)
3027 int32 traitEntryIndex = traitConfig->Entries.FindIndexIf([traitDefinitionId](
UF::TraitEntry const& traitEntry)
3032 if (traitEntryIndex >= 0)
3033 rank = traitConfig->Entries[traitEntryIndex].Rank + traitConfig->Entries[traitEntryIndex].GrantedRanks;
3041 if (traitDefinitionEffectPoint->EffectIndex >=
int32(spellInfo->
GetEffects().size()))
3044 float basePoints =
sDB2Manager.GetCurveValueAt(traitDefinitionEffectPoint->CurveID, rank);
3060 if (traitDefinitionId)
3062 if (traitDefinition->OverridesSpellID)
3077 if (spellLearnSkill->skill != fromSkill)
3082 if (skill_value < spellLearnSkill->value)
3083 skill_value = spellLearnSkill->value;
3085 uint16 new_skill_max_value = spellLearnSkill->maxvalue;
3087 if (new_skill_max_value == 0)
3095 new_skill_max_value = 300;
3101 new_skill_max_value = 1;
3114 skill_value = new_skill_max_value;
3118 if (skill_max_value < new_skill_max_value)
3119 skill_max_value = new_skill_max_value;
3121 SetSkill(spellLearnSkill->skill, spellLearnSkill->step, skill_value, skill_max_value);
3127 for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
3133 if (_spell_idx->second->SkillLine == fromSkill)
3146 for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
3148 if (!itr2->second.AutoLearned)
3150 if (!
IsInWorld() || !itr2->second.Active)
3151 AddSpell(itr2->second.Spell, itr2->second.Active,
true,
true,
false);
3156 if (itr2->second.OverridesSpell && itr2->second.Active)
3163 for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
3177 return active && !disabled && !superceded_old;
3182 PlayerSpellMap::iterator itr =
m_spells.find(spellId);
3195 PlayerSpellMap::iterator itr =
m_spells.find(spellId);
3210 bool need_cast = (!spellInfo->
Stances || (form && (spellInfo->
Stances & (
UI64LIT(1) << (form - 1)))) ||
3219 if (spellEffectInfo.IsAura())
3234 PlayerSpellMap::iterator itr =
m_spells.find(spell_id);
3236 bool disabled = (itr !=
m_spells.end()) ? itr->second.disabled :
false;
3237 bool active = disabled ? itr->second.active :
true;
3238 bool favorite = itr !=
m_spells.end() ? itr->second.favorite :
false;
3240 bool learning =
AddSpell(spell_id, active,
true, dependent,
false,
false, fromSkill, favorite, traitDefinitionId);
3247 learnedSpellInfo.
SpellID = spell_id;
3259 PlayerSpellMap::iterator iter =
m_spells.find(nextSpell);
3260 if (iter !=
m_spells.end() && iter->second.disabled)
3265 for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequiringSpell.first; itr2 != spellsRequiringSpell.second; ++itr2)
3267 PlayerSpellMap::iterator iter2 =
m_spells.find(itr2->second);
3268 if (iter2 !=
m_spells.end() && iter2->second.disabled)
3278 PlayerSpellMap::iterator itr =
m_spells.find(spell_id);
3294 for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequiringSpell.first; itr2 != spellsRequiringSpell.second; ++itr2)
3302 bool cur_active = itr->second.active;
3303 bool cur_dependent = itr->second.dependent;
3308 itr->second.disabled = disabled;
3338 if (spellLearnSkill)
3347 while (!prevSkill && prev_spell)
3349 prev_spell =
sSpellMgr->GetPrevSpellInChain(prev_spell);
3350 prevSkill =
sSpellMgr->GetSpellLearnSkill(
sSpellMgr->GetFirstSpellInChain(prev_spell));
3362 if (new_skill_max_value == 0)
3370 new_skill_max_value = 300;
3376 new_skill_max_value = 1;
3389 skill_value = new_skill_max_value;
3392 else if (skill_value > prevSkill->
value)
3393 skill_value = prevSkill->
value;
3395 if (skill_max_value > new_skill_max_value)
3396 skill_max_value = new_skill_max_value;
3398 if (skill_value > new_skill_max_value)
3399 skill_value = new_skill_max_value;
3409 for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
3411 bool hasOtherSpellTeachingThis = std::ranges::any_of(
sSpellMgr->GetSpellLearnedBySpellMapBounds(itr2->second.Spell), [&](
SpellLearnSpellNode const* learnNode)
3413 if (learnNode->SourceSpell == spell_id)
3415 if (!learnNode->Active)
3417 return HasSpell(learnNode->SourceSpell);
3418 }, &SpellLearnedBySpellMap::value_type::second);
3420 if (hasOtherSpellTeachingThis)
3424 if (itr2->second.OverridesSpell)
3429 bool prev_activate =
false;
3435 if (cur_active && spellInfo->
IsRanked())
3438 PlayerSpellMap::iterator prev_itr =
m_spells.find(prev_id);
3441 if (prev_itr->second.dependent != cur_dependent)
3443 prev_itr->second.dependent = cur_dependent;
3449 if (!prev_itr->second.active && learn_low_rank)
3451 if (
AddSpell(prev_id,
true,
false, prev_itr->second.dependent, prev_itr->second.disabled))
3455 prev_activate =
true;
3462 if (traitDefinitionId)
3490 unlearnedSpells.
SpellID.push_back(spell_id);
3502 itr->second.favorite = favorite;
3519 if (removeActivePetCooldowns)
3521 pet->GetSpellHistory()->ResetAllCooldowns();
3543 return (new_cost < 10*
GOLD ? 10*
GOLD : new_cost);
3550 if (new_cost > 50*
GOLD)
3559 sScriptMgr->OnPlayerTalentsReset(
this, noCost);
3635 for (PlayerMails::iterator itr =
m_mail.begin(); itr !=
m_mail.end(); ++itr)
3636 if ((*itr)->messageID ==
id)
3678 std::size_t sizePos = data->
wpos();
3693 std::size_t sizePos = data->
wpos();
3718 std::size_t sizePos = data->
wpos();
3724 m_unitData->WriteUpdate(*data, mask,
true,
this, target);
3728 m_playerData->WriteUpdate(*data, mask2,
true,
this, target);
3739 if (requestedObjectMask.IsAnySet())
3744 if (unitMask.IsAnySet())
3749 if (playerMask.IsAnySet())
3752 if (target ==
this && requestedActivePlayerMask.IsAnySet())
3756 std::size_t sizePos = buffer.
wpos();
3761 m_objectData->WriteUpdate(buffer, requestedObjectMask,
true,
this, target);
3764 m_unitData->WriteUpdate(buffer, unitMask,
true,
this, target);
3767 m_playerData->WriteUpdate(buffer, playerMask,
true,
this, target);
3770 m_activePlayerData->WriteUpdate(buffer, requestedActivePlayerMask,
true,
this, target);
3822 PlayerSpellMap::const_iterator itr =
m_spells.find(spell);
3824 !itr->second.disabled);
3829 PlayerTalentMap::const_iterator itr =
GetTalentMap(group)->find(talentId);
3835 PlayerSpellMap::const_iterator itr =
m_spells.find(spell);
3837 itr->second.active && !itr->second.disabled);
3856 updateRealmChars =
false;
3864 name = characterInfo->
Name;
3868 else if (characterInfo)
3882 if (characterInfo->
Level < charDeleteMinLvl)
3892 guild->DeleteMember(trans, playerguid,
false,
false);
3903 if (
Group* group =
sGroupMgr->GetGroupByDbStoreId((*resultGroup)[0].GetUInt32()))
3909 switch (charDeleteMethod)
3920 std::unordered_map<uint64, std::vector<Item*>> itemsByMail;
3948 std::unordered_map<ObjectGuid::LowType, ItemAdditionalLoadInfo> additionalData;
3950 azeriteItemUnlockedEssencesResult, azeriteEmpoweredItemResult);
3954 Field* fields = resultItems->Fetch();
3957 itemsByMail[mailId].push_back(mailItem);
3959 }
while (resultItems->NextRow());
3964 Field* mailFields = resultMail->Fetch();
3970 std::string subject = mailFields[4].
GetString();
3971 std::string body = mailFields[5].
GetString();
3973 bool has_items = mailFields[7].
GetBool();
3979 trans->Append(stmt);
3988 trans->Append(stmt);
3995 draft =
MailDraft(mailTemplateId,
false);
3997 auto itemsItr = itemsByMail.find(mail_id);
3998 if (itemsItr != itemsByMail.end())
4000 for (
Item* item : itemsItr->second)
4004 itemsByMail.erase(itemsItr);
4009 trans->Append(stmt);
4015 while (resultMail->NextRow());
4018 for (
auto&& kvp : itemsByMail)
4019 for (
Item* item : kvp.second)
4033 uint32 petguidlow = (*resultPets)[0].GetUInt32();
4035 }
while (resultPets->NextRow());
4048 playerFriend->GetSocial()->RemoveFromSocialList(playerguid,
SOCIAL_FLAG_ALL);
4051 }
while (resultFriends->NextRow());
4056 trans->Append(stmt);
4060 trans->Append(stmt);
4064 trans->Append(stmt);
4068 trans->Append(stmt);
4072 trans->Append(stmt);
4076 trans->Append(stmt);
4080 trans->Append(stmt);
4084 trans->Append(stmt);
4088 trans->Append(stmt);
4092 trans->Append(stmt);
4096 trans->Append(stmt);
4100 trans->Append(stmt);
4104 trans->Append(stmt);
4108 trans->Append(stmt);
4112 trans->Append(stmt);
4116 trans->Append(stmt);
4120 trans->Append(stmt);
4124 trans->Append(stmt);
4128 trans->Append(stmt);
4132 trans->Append(stmt);
4136 trans->Append(stmt);
4140 trans->Append(stmt);
4144 trans->Append(stmt);
4148 trans->Append(stmt);
4152 trans->Append(stmt);
4156 trans->Append(stmt);
4160 trans->Append(stmt);
4164 trans->Append(stmt);
4168 trans->Append(stmt);
4172 trans->Append(stmt);
4176 trans->Append(stmt);
4180 trans->Append(stmt);
4184 trans->Append(stmt);
4188 trans->Append(stmt);
4192 trans->Append(stmt);
4196 trans->Append(stmt);
4200 trans->Append(stmt);
4204 trans->Append(stmt);
4208 trans->Append(stmt);
4212 trans->Append(stmt);
4216 trans->Append(stmt);
4220 trans->Append(stmt);
4224 trans->Append(stmt);
4229 trans->Append(stmt);
4233 trans->Append(stmt);
4237 trans->Append(stmt);
4241 trans->Append(stmt);
4245 trans->Append(stmt);
4249 trans->Append(stmt);
4253 trans->Append(stmt);
4257 trans->Append(stmt);
4261 trans->Append(stmt);
4265 trans->Append(stmt);
4269 trans->Append(stmt);
4273 trans->Append(stmt);
4277 trans->Append(stmt);
4281 trans->Append(stmt);
4286 loginTransaction->Append(loginStmt);
4291 loginTransaction->Append(loginStmt);
4299 trans->Append(stmt);
4303 trans->Append(stmt);
4313 trans->Append(stmt);
4318 TC_LOG_ERROR(
"entities.player.cheat",
"Player::DeleteFromDB: Tried to delete player ({}) with unsupported delete method ({}).",
4319 playerguid.
ToString(), charDeleteMethod);
4321 if (trans->GetSize() > 0)
4329 if (updateRealmChars)
4330 sWorld->UpdateRealmCharCount(accountId);
4356 TC_LOG_INFO(
"entities.player",
"Player::DeleteOldCharacters: Deleting all characters which have been deleted {} days before...", keepDays);
4364 TC_LOG_DEBUG(
"entities.player",
"Player::DeleteOldCharacters: Found {} character(s) to delete", result->GetRowCount());
4367 Field* fields = result->Fetch();
4368 Player::DeleteFromDB(ObjectGuid::Create<HighGuid::Player>(fields[0].GetUInt64()), fields[1].GetUInt32(),
true,
true);
4370 while (result->NextRow());
4424 if (corpseReclaimDelay >= 0)
4467 if (restore_percent > 0.0f)
4542 if (corpseReclaimDelay >= 0)
4568 if (!corpse->
Create(
GetMap()->GenerateLowGuid<HighGuid::Corpse>(),
this))
4597 uint32 itemInventoryType;
4599 itemInventoryType = itemEntry->InventoryType;
4603 corpse->
SetItem(i, itemDisplayId | (itemInventoryType << 24));
4614 if (!
GetMap()->Instanceable())
4624 if (triggerSave && !
GetSession()->PlayerLogoutWithSave())
4651 for (
uint32 j = 0; j < pBag->GetBagSize(); j++)
4664 if (!pMaxDurability)
4669 uint32 pDurabilityLoss =
uint32(pMaxDurability*percent);
4671 if (pDurabilityLoss < 1)
4672 pDurabilityLoss = 1;
4695 for (
uint32 j = 0; j < pBag->GetBagSize(); j++)
4708 int32 pNewDurability = pOldDurability - points;
4710 if (pNewDurability < 0)
4712 else if (pNewDurability > pMaxDurability)
4713 pNewDurability = pMaxDurability;
4715 if (pOldDurability != pNewDurability)
4718 if (pNewDurability == 0 && pOldDurability > 0 && item->
IsEquipped())
4724 if (pNewDurability > 0 && pOldDurability == 0 && item->
IsEquipped())
4743 std::list<std::pair<Item*, uint64>> itemRepairCostStore;
4749 if (
uint64 cost = item->CalculateDurabilityRepairCost(discountMod))
4750 itemRepairCostStore.push_back(std::make_pair(item, cost));
4758 if (
uint64 cost = item->CalculateDurabilityRepairCost(discountMod))
4759 itemRepairCostStore.push_back(std::make_pair(item, cost));
4764 for (
auto const& [item, cost] : itemRepairCostStore)