/src/server/game/Entities/Player/Player.cpp
C++ | 13435 lines | 10334 code | 2050 blank | 1051 comment | 3369 complexity | 52a59747d8ad70a97d9fdd1dc2962043 MD5 | raw file
Possible License(s): GPL-2.0, BSD-2-Clause
Large files files are truncated, but you can click here to view the full file
- /*
- * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "Player.h"
- #include "AccountMgr.h"
- #include "AchievementMgr.h"
- #include "ArenaTeam.h"
- #include "ArenaTeamMgr.h"
- #include "Battlefield.h"
- #include "BattlefieldMgr.h"
- #include "BattlefieldWG.h"
- #include "Battleground.h"
- #include "BattlegroundMgr.h"
- #include "BattlegroundScore.h"
- #include "CellImpl.h"
- #include "Channel.h"
- #include "ChannelMgr.h"
- #include "CharacterDatabaseCleaner.h"
- #include "Chat.h"
- #include "Common.h"
- #include "ConditionMgr.h"
- #include "CreatureAI.h"
- #include "DatabaseEnv.h"
- #include "DB2Stores.h"
- #include "DisableMgr.h"
- #include "Formulas.h"
- #include "GameEventMgr.h"
- #include "GossipDef.h"
- #include "GridNotifiers.h"
- #include "GridNotifiersImpl.h"
- #include "Group.h"
- #include "GroupMgr.h"
- #include "Guild.h"
- #include "GuildMgr.h"
- #include "InstanceSaveMgr.h"
- #include "InstanceScript.h"
- #include "LFGMgr.h"
- #include "Language.h"
- #include "Log.h"
- #include "MapInstanced.h"
- #include "MapManager.h"
- #include "ObjectAccessor.h"
- #include "ObjectMgr.h"
- #include "Opcodes.h"
- #include "OutdoorPvP.h"
- #include "OutdoorPvPMgr.h"
- #include "Pet.h"
- #include "QuestDef.h"
- #include "ReputationMgr.h"
- #include "revision.h"
- #include "SkillDiscovery.h"
- #include "SocialMgr.h"
- #include "Spell.h"
- #include "SpellAuraEffects.h"
- #include "SpellAuras.h"
- #include "SpellMgr.h"
- #include "Transport.h"
- #include "UpdateData.h"
- #include "UpdateFieldFlags.h"
- #include "UpdateMask.h"
- #include "Util.h"
- #include "Vehicle.h"
- #include "Weather.h"
- #include "WeatherMgr.h"
- #include "World.h"
- #include "WorldPacket.h"
- #include "WorldSession.h"
- #include "MovementStructures.h"
- #include "GameObjectAI.h"
- #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)
- enum CharacterFlags
- {
- CHARACTER_FLAG_NONE = 0x00000000,
- CHARACTER_FLAG_UNK1 = 0x00000001,
- CHARACTER_FLAG_UNK2 = 0x00000002,
- CHARACTER_LOCKED_FOR_TRANSFER = 0x00000004,
- CHARACTER_FLAG_UNK4 = 0x00000008,
- CHARACTER_FLAG_UNK5 = 0x00000010,
- CHARACTER_FLAG_UNK6 = 0x00000020,
- CHARACTER_FLAG_UNK7 = 0x00000040,
- CHARACTER_FLAG_UNK8 = 0x00000080,
- CHARACTER_FLAG_UNK9 = 0x00000100,
- CHARACTER_FLAG_UNK10 = 0x00000200,
- CHARACTER_FLAG_HIDE_HELM = 0x00000400,
- CHARACTER_FLAG_HIDE_CLOAK = 0x00000800,
- CHARACTER_FLAG_UNK13 = 0x00001000,
- CHARACTER_FLAG_GHOST = 0x00002000,
- CHARACTER_FLAG_RENAME = 0x00004000,
- CHARACTER_FLAG_UNK16 = 0x00008000,
- CHARACTER_FLAG_UNK17 = 0x00010000,
- CHARACTER_FLAG_UNK18 = 0x00020000,
- CHARACTER_FLAG_UNK19 = 0x00040000,
- CHARACTER_FLAG_UNK20 = 0x00080000,
- CHARACTER_FLAG_UNK21 = 0x00100000,
- CHARACTER_FLAG_UNK22 = 0x00200000,
- CHARACTER_FLAG_UNK23 = 0x00400000,
- CHARACTER_FLAG_UNK24 = 0x00800000,
- CHARACTER_FLAG_LOCKED_BY_BILLING = 0x01000000,
- CHARACTER_FLAG_DECLINED = 0x02000000,
- CHARACTER_FLAG_UNK27 = 0x04000000,
- CHARACTER_FLAG_UNK28 = 0x08000000,
- CHARACTER_FLAG_UNK29 = 0x10000000,
- CHARACTER_FLAG_UNK30 = 0x20000000,
- CHARACTER_FLAG_UNK31 = 0x40000000,
- CHARACTER_FLAG_UNK32 = 0x80000000
- };
- enum CharacterCustomizeFlags
- {
- CHAR_CUSTOMIZE_FLAG_NONE = 0x00000000,
- CHAR_CUSTOMIZE_FLAG_CUSTOMIZE = 0x00000001, // name, gender, etc...
- CHAR_CUSTOMIZE_FLAG_FACTION = 0x00010000, // name, gender, faction, etc...
- CHAR_CUSTOMIZE_FLAG_RACE = 0x00100000 // name, gender, race, etc...
- };
- // corpse reclaim times
- #define DEATH_EXPIRE_STEP (5*MINUTE)
- #define MAX_DEATH_COUNT 3
- static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
- uint32 const MasterySpells[MAX_CLASSES] =
- {
- 0,
- 87500, // Warrior
- 87494, // Paladin
- 87493, // Hunter
- 87496, // Rogue
- 87495, // Priest
- 87492, // Death Knight
- 87497, // Shaman
- 86467, // Mage
- 87498, // Warlock
- 0,
- 87491, // Druid
- };
- uint64 const MAX_MONEY_AMOUNT = 9999999999ULL;
- // == PlayerTaxi ================================================
- PlayerTaxi::PlayerTaxi()
- {
- memset(m_taximask, 0, sizeof(m_taximask));
- }
- void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level)
- {
- // class specific initial known nodes
- switch (chrClass)
- {
- case CLASS_DEATH_KNIGHT:
- {
- for (uint8 i = 0; i < TaxiMaskSize; ++i)
- m_taximask[i] |= sOldContinentsNodesMask[i];
- break;
- }
- }
- // race specific initial known nodes: capital and taxi hub masks
- switch (race)
- {
- case RACE_HUMAN: SetTaximaskNode(2); break; // Human
- case RACE_ORC: SetTaximaskNode(23); break; // Orc
- case RACE_DWARF: SetTaximaskNode(6); break; // Dwarf
- case RACE_NIGHTELF: SetTaximaskNode(26);
- SetTaximaskNode(27); break; // Night Elf
- case RACE_UNDEAD_PLAYER: SetTaximaskNode(11); break;// Undead
- case RACE_TAUREN: SetTaximaskNode(22); break; // Tauren
- case RACE_GNOME: SetTaximaskNode(6); break; // Gnome
- case RACE_TROLL: SetTaximaskNode(23); break; // Troll
- case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf
- case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei
- }
- // new continent starting masks (It will be accessible only at new map)
- switch (Player::TeamForRace(race))
- {
- case ALLIANCE: SetTaximaskNode(100); break;
- case HORDE: SetTaximaskNode(99); break;
- }
- // level dependent taxi hubs
- if (level >= 68)
- SetTaximaskNode(213); //Shattered Sun Staging Area
- }
- void PlayerTaxi::LoadTaxiMask(std::string const &data)
- {
- Tokenizer tokens(data, ' ');
- uint8 index = 0;
- for (Tokenizer::const_iterator iter = tokens.begin(); index < TaxiMaskSize && iter != tokens.end(); ++iter, ++index)
- {
- // load and set bits only for existing taxi nodes
- m_taximask[index] = sTaxiNodesMask[index] & uint32(atol(*iter));
- }
- }
- void PlayerTaxi::AppendTaximaskTo(ByteBuffer& data, bool all)
- {
- data << uint32(TaxiMaskSize);
- if (all)
- {
- for (uint8 i = 0; i < TaxiMaskSize; ++i)
- data << uint8(sTaxiNodesMask[i]); // all existed nodes
- }
- else
- {
- for (uint8 i = 0; i < TaxiMaskSize; ++i)
- data << uint8(m_taximask[i]); // known nodes
- }
- }
- bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, uint32 team)
- {
- ClearTaxiDestinations();
- Tokenizer Tokenizer(values, ' ');
- for (Tokenizer::const_iterator iter = Tokenizer.begin(); iter != Tokenizer.end(); ++iter)
- {
- uint32 node = uint32(atol(*iter));
- AddTaxiDestination(node);
- }
- if (m_TaxiDestinations.empty())
- return true;
- // Check integrity
- if (m_TaxiDestinations.size() < 2)
- return false;
- for (size_t i = 1; i < m_TaxiDestinations.size(); ++i)
- {
- uint32 cost;
- uint32 path;
- sObjectMgr->GetTaxiPath(m_TaxiDestinations[i-1], m_TaxiDestinations[i], path, cost);
- if (!path)
- return false;
- }
- // can't load taxi path without mount set (quest taxi path?)
- if (!sObjectMgr->GetTaxiMountDisplayId(GetTaxiSource(), team, true))
- return false;
- return true;
- }
- std::string PlayerTaxi::SaveTaxiDestinationsToString()
- {
- if (m_TaxiDestinations.empty())
- return "";
- std::ostringstream ss;
- for (size_t i=0; i < m_TaxiDestinations.size(); ++i)
- ss << m_TaxiDestinations[i] << ' ';
- return ss.str();
- }
- uint32 PlayerTaxi::GetCurrentTaxiPath() const
- {
- if (m_TaxiDestinations.size() < 2)
- return 0;
- uint32 path;
- uint32 cost;
- sObjectMgr->GetTaxiPath(m_TaxiDestinations[0], m_TaxiDestinations[1], path, cost);
- return path;
- }
- std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
- {
- for (uint8 i = 0; i < TaxiMaskSize; ++i)
- ss << uint32(taxi.m_taximask[i]) << ' ';
- return ss;
- }
- //== TradeData =================================================
- TradeData* TradeData::GetTraderData() const
- {
- return m_trader->GetTradeData();
- }
- Item* TradeData::GetItem(TradeSlots slot) const
- {
- return m_items[slot] ? m_player->GetItemByGuid(m_items[slot]) : NULL;
- }
- bool TradeData::HasItem(ObjectGuid itemGuid) const
- {
- for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
- if (m_items[i] == itemGuid)
- return true;
- return false;
- }
- TradeSlots TradeData::GetTradeSlotForItem(ObjectGuid itemGuid) const
- {
- for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
- if (m_items[i] == itemGuid)
- return TradeSlots(i);
- return TRADE_SLOT_INVALID;
- }
- Item* TradeData::GetSpellCastItem() const
- {
- return m_spellCastItem ? m_player->GetItemByGuid(m_spellCastItem) : NULL;
- }
- void TradeData::SetItem(TradeSlots slot, Item* item)
- {
- ObjectGuid itemGuid;
- if (item)
- itemGuid = item->GetGUID();
- if (m_items[slot] == itemGuid)
- return;
- m_items[slot] = itemGuid;
- SetAccepted(false);
- GetTraderData()->SetAccepted(false);
- Update();
- // need remove possible trader spell applied to changed item
- if (slot == TRADE_SLOT_NONTRADED)
- GetTraderData()->SetSpell(0);
- // need remove possible player spell applied (possible move reagent)
- SetSpell(0);
- }
- void TradeData::SetSpell(uint32 spell_id, Item* castItem /*= NULL*/)
- {
- ObjectGuid itemGuid = castItem ? castItem->GetGUID() : ObjectGuid::Empty;
- if (m_spell == spell_id && m_spellCastItem == itemGuid)
- return;
- m_spell = spell_id;
- m_spellCastItem = itemGuid;
- SetAccepted(false);
- GetTraderData()->SetAccepted(false);
- Update(true); // send spell info to item owner
- Update(false); // send spell info to caster self
- }
- void TradeData::SetMoney(uint64 money)
- {
- if (m_money == money)
- return;
- if (!m_player->HasEnoughMoney(money))
- {
- TradeStatusInfo info;
- info.Status = TRADE_STATUS_CLOSE_WINDOW;
- info.Result = EQUIP_ERR_NOT_ENOUGH_MONEY;
- m_player->GetSession()->SendTradeStatus(info);
- return;
- }
- m_money = money;
- SetAccepted(false);
- GetTraderData()->SetAccepted(false);
- Update(true);
- }
- void TradeData::Update(bool forTarget /*= true*/)
- {
- if (forTarget)
- m_trader->GetSession()->SendUpdateTrade(true); // player state for trader
- else
- m_player->GetSession()->SendUpdateTrade(false); // player state for player
- }
- void TradeData::SetAccepted(bool state, bool crosssend /*= false*/)
- {
- m_accepted = state;
- if (!state)
- {
- TradeStatusInfo info;
- info.Status = TRADE_STATUS_BACK_TO_TRADE;
- if (crosssend)
- m_trader->GetSession()->SendTradeStatus(info);
- else
- m_player->GetSession()->SendTradeStatus(info);
- }
- }
- // == KillRewarder ====================================================
- // KillRewarder incapsulates logic of rewarding player upon kill with:
- // * XP;
- // * honor;
- // * reputation;
- // * kill credit (for quest objectives).
- // Rewarding is initiated in two cases: when player kills unit in Unit::Kill()
- // and on battlegrounds in Battleground::RewardXPAtKill().
- //
- // Rewarding algorithm is:
- // 1. Initialize internal variables to default values.
- // 2. In case when player is in group, initialize variables necessary for group calculations:
- // 2.1. _count - number of alive group members within reward distance;
- // 2.2. _sumLevel - sum of levels of alive group members within reward distance;
- // 2.3. _maxLevel - maximum level of alive group member within reward distance;
- // 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
- // for whom victim is not gray;
- // 2.5. _isFullXP - flag identifying that for all group members victim is not gray,
- // so 100% XP will be rewarded (50% otherwise).
- // 3. Reward killer (and group, if necessary).
- // 3.1. If killer is in group, reward group.
- // 3.1.1. Initialize initial XP amount based on maximum level of group member,
- // for whom victim is not gray.
- // 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
- // 3.1.3. Reward each group member (even dead) within reward distance (see 4. for more details).
- // 3.2. Reward single killer (not group case).
- // 3.2.1. Initialize initial XP amount based on killer's level.
- // 3.2.2. Reward killer (see 4. for more details).
- // 4. Reward player.
- // 4.1. Give honor (player must be alive and not on BG).
- // 4.2. Give XP.
- // 4.2.1. If player is in group, adjust XP:
- // * set to 0 if player's level is more than maximum level of not gray member;
- // * cut XP in half if _isFullXP is false.
- // 4.2.2. Apply auras modifying rewarded XP.
- // 4.2.3. Give XP to player.
- // 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
- // 4.3. Give reputation (player must not be on BG).
- // 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
- // 5. Credit instance encounter.
- // 6. Update guild achievements.
- KillRewarder::KillRewarder(Player* killer, Unit* victim, bool isBattleGround) :
- // 1. Initialize internal variables to default values.
- _killer(killer), _victim(victim), _group(killer->GetGroup()),
- _groupRate(1.0f), _maxNotGrayMember(NULL), _count(0), _sumLevel(0), _xp(0),
- _isFullXP(false), _maxLevel(0), _isBattleGround(isBattleGround), _isPvP(false)
- {
- // mark the credit as pvp if victim is player
- if (victim->GetTypeId() == TYPEID_PLAYER)
- _isPvP = true;
- // or if its owned by player and its not a vehicle
- else if (victim->GetCharmerOrOwnerGUID().IsPlayer())
- _isPvP = !victim->IsVehicle();
- _InitGroupData();
- }
- inline void KillRewarder::_InitGroupData()
- {
- if (_group)
- {
- // 2. In case when player is in group, initialize variables necessary for group calculations:
- for (GroupReference* itr = _group->GetFirstMember(); itr != NULL; itr = itr->next())
- if (Player* member = itr->GetSource())
- if (member->IsAlive() && member->IsAtGroupRewardDistance(_victim))
- {
- const uint8 lvl = member->getLevel();
- // 2.1. _count - number of alive group members within reward distance;
- ++_count;
- // 2.2. _sumLevel - sum of levels of alive group members within reward distance;
- _sumLevel += lvl;
- // 2.3. _maxLevel - maximum level of alive group member within reward distance;
- if (_maxLevel < lvl)
- _maxLevel = lvl;
- // 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
- // for whom victim is not gray;
- uint32 grayLevel = Trinity::XP::GetGrayLevel(lvl);
- if (_victim->getLevel() > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->getLevel() < lvl))
- _maxNotGrayMember = member;
- }
- // 2.5. _isFullXP - flag identifying that for all group members victim is not gray,
- // so 100% XP will be rewarded (50% otherwise).
- _isFullXP = _maxNotGrayMember && (_maxLevel == _maxNotGrayMember->getLevel());
- }
- else
- _count = 1;
- }
- inline void KillRewarder::_InitXP(Player* player)
- {
- // Get initial value of XP for kill.
- // XP is given:
- // * on battlegrounds;
- // * otherwise, not in PvP;
- // * not if killer is on vehicle.
- if (_isBattleGround || (!_isPvP && !_killer->GetVehicle()))
- _xp = Trinity::XP::Gain(player, _victim);
- }
- inline void KillRewarder::_RewardHonor(Player* player)
- {
- // Rewarded player must be alive.
- if (player->IsAlive())
- player->RewardHonor(_victim, _count, -1, true);
- }
- inline void KillRewarder::_RewardXP(Player* player, float rate)
- {
- uint32 xp(_xp);
- if (_group)
- {
- // 4.2.1. If player is in group, adjust XP:
- // * set to 0 if player's level is more than maximum level of not gray member;
- // * cut XP in half if _isFullXP is false.
- if (_maxNotGrayMember && player->IsAlive() &&
- _maxNotGrayMember->getLevel() >= player->getLevel())
- xp = _isFullXP ?
- uint32(xp * rate) : // Reward FULL XP if all group members are not gray.
- uint32(xp * rate / 2) + 1; // Reward only HALF of XP if some of group members are gray.
- else
- xp = 0;
- }
- if (xp)
- {
- // 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT).
- Unit::AuraEffectList const& auras = player->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT);
- for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i)
- AddPct(xp, (*i)->GetAmount());
- // 4.2.3. Calculate expansion penalty
- if (_victim->GetTypeId() == TYPEID_UNIT && player->getLevel() >= GetMaxLevelForExpansion(_victim->ToCreature()->GetCreatureTemplate()->expansion))
- xp = CalculatePct(xp, 10); // Players get only 10% xp for killing creatures of lower expansion levels than himself
- // 4.2.4. Give XP to player.
- player->GiveXP(xp, _victim, _groupRate);
- if (Pet* pet = player->GetPet())
- // 4.2.5. If player has pet, reward pet with XP (100% for single player, 50% for group case).
- pet->GivePetXP(_group ? xp / 2 : xp);
- }
- }
- inline void KillRewarder::_RewardReputation(Player* player, float rate)
- {
- // 4.3. Give reputation (player must not be on BG).
- // Even dead players and corpses are rewarded.
- player->RewardReputation(_victim, rate);
- }
- inline void KillRewarder::_RewardKillCredit(Player* player)
- {
- // 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
- if (!_group || player->IsAlive() || !player->GetCorpse())
- if (Creature* target = _victim->ToCreature())
- {
- player->KilledMonster(target->GetCreatureTemplate(), target->GetGUID());
- player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE, target->GetCreatureType(), 1, 0, target);
- }
- }
- void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
- {
- // 4. Reward player.
- if (!_isBattleGround)
- {
- // 4.1. Give honor (player must be alive and not on BG).
- _RewardHonor(player);
- // 4.1.1 Send player killcredit for quests with PlayerSlain
- if (_victim->GetTypeId() == TYPEID_PLAYER)
- player->KilledPlayerCredit();
- }
- // Give XP only in PvE or in battlegrounds.
- // Give reputation and kill credit only in PvE.
- if (!_isPvP || _isBattleGround)
- {
- const float rate = _group ?
- _groupRate * float(player->getLevel()) / _sumLevel : // Group rate depends on summary level.
- 1.0f; // Personal rate is 100%.
- if (_xp)
- // 4.2. Give XP.
- _RewardXP(player, rate);
- if (!_isBattleGround)
- {
- // If killer is in dungeon then all members receive full reputation at kill.
- _RewardReputation(player, isDungeon ? 1.0f : rate);
- _RewardKillCredit(player);
- }
- }
- }
- void KillRewarder::_RewardGroup()
- {
- if (_maxLevel)
- {
- if (_maxNotGrayMember)
- // 3.1.1. Initialize initial XP amount based on maximum level of group member,
- // for whom victim is not gray.
- _InitXP(_maxNotGrayMember);
- // To avoid unnecessary calculations and calls,
- // proceed only if XP is not ZERO or player is not on battleground
- // (battleground rewards only XP, that's why).
- if (!_isBattleGround || _xp)
- {
- const bool isDungeon = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsDungeon();
- if (!_isBattleGround)
- {
- // 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
- const bool isRaid = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsRaid() && _group->isRaidGroup();
- _groupRate = Trinity::XP::xp_in_group_rate(_count, isRaid);
- }
- // 3.1.3. Reward each group member (even dead or corpse) within reward distance.
- for (GroupReference* itr = _group->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- if (Player* member = itr->GetSource())
- {
- if (member->IsAtGroupRewardDistance(_victim))
- {
- _RewardPlayer(member, isDungeon);
- member->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL, 1, 0, 0, _victim);
- }
- }
- }
- }
- }
- }
- void KillRewarder::Reward()
- {
- // 3. Reward killer (and group, if necessary).
- if (_group)
- // 3.1. If killer is in group, reward group.
- _RewardGroup();
- else
- {
- // 3.2. Reward single killer (not group case).
- // 3.2.1. Initialize initial XP amount based on killer's level.
- _InitXP(_killer);
- // To avoid unnecessary calculations and calls,
- // proceed only if XP is not ZERO or player is not on battleground
- // (battleground rewards only XP, that's why).
- if (!_isBattleGround || _xp)
- // 3.2.2. Reward killer.
- _RewardPlayer(_killer, false);
- }
- // 5. Credit instance encounter.
- // 6. Update guild achievements.
- if (Creature* victim = _victim->ToCreature())
- {
- if (victim->IsDungeonBoss())
- if (InstanceScript* instance = _victim->GetInstanceScript())
- instance->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, _victim->GetEntry(), _victim);
- if (uint32 guildId = victim->GetMap()->GetOwnerGuildId())
- if (Guild* guild = sGuildMgr->GetGuildById(guildId))
- guild->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, victim->GetEntry(), 1, 0, victim, _killer);
- }
- }
- Player::Player(WorldSession* session): Unit(true)
- {
- m_speakTime = 0;
- m_speakCount = 0;
- m_objectType |= TYPEMASK_PLAYER;
- m_objectTypeId = TYPEID_PLAYER;
- m_valuesCount = PLAYER_END;
- m_session = session;
- m_ingametime = 0;
- m_ExtraFlags = 0;
- m_spellModTakingSpell = NULL;
- //m_pad = 0;
- // players always accept
- if (!GetSession()->HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS))
- SetAcceptWhispers(true);
- m_comboPoints = 0;
- m_regenTimer = 0;
- m_regenTimerCount = 0;
- m_holyPowerRegenTimerCount = 0;
- m_focusRegenTimerCount = 0;
- m_weaponChangeTimer = 0;
- m_zoneUpdateId = uint32(-1);
- m_zoneUpdateTimer = 0;
- m_areaUpdateId = 0;
- m_team = 0;
- m_needsZoneUpdate = false;
- m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE);
- _resurrectionData = NULL;
- memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT);
- m_social = NULL;
- // group is initialized in the reference constructor
- SetGroupInvite(NULL);
- m_groupUpdateMask = 0;
- m_auraRaidUpdateMask = 0;
- m_bPassOnGroupLoot = false;
- duel = NULL;
- m_GuildIdInvited = 0;
- m_ArenaTeamIdInvited = 0;
- m_atLoginFlags = AT_LOGIN_NONE;
- mSemaphoreTeleport_Near = false;
- mSemaphoreTeleport_Far = false;
- m_DelayedOperations = 0;
- m_bCanDelayTeleport = false;
- m_bHasDelayedTeleport = false;
- m_teleport_options = 0;
- m_trade = NULL;
- m_cinematic = 0;
- PlayerTalkClass = new PlayerMenu(GetSession());
- m_currentBuybackSlot = BUYBACK_SLOT_START;
- m_DailyQuestChanged = false;
- m_lastDailyQuestTime = 0;
- // Init rune flags
- for (uint8 i = 0; i < MAX_RUNES; ++i)
- {
- SetRuneTimer(i, 0xFFFFFFFF);
- SetLastRuneGraceTimer(i, 0);
- }
- for (uint8 i=0; i < MAX_TIMERS; i++)
- m_MirrorTimer[i] = DISABLED_MIRROR_TIMER;
- m_MirrorTimerFlags = UNDERWATER_NONE;
- m_MirrorTimerFlagsLast = UNDERWATER_NONE;
- m_isInWater = false;
- m_drunkTimer = 0;
- m_restTime = 0;
- m_deathTimer = 0;
- m_deathExpireTime = 0;
- m_swingErrorMsg = 0;
- for (uint8 j = 0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j)
- {
- m_bgBattlegroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE;
- m_bgBattlegroundQueueID[j].invitedToInstance = 0;
- }
- m_logintime = time(NULL);
- m_Last_tick = m_logintime;
- m_Played_time[PLAYED_TIME_TOTAL] = 0;
- m_Played_time[PLAYED_TIME_LEVEL] = 0;
- m_WeaponProficiency = 0;
- m_ArmorProficiency = 0;
- m_canParry = false;
- m_canBlock = false;
- m_canTitanGrip = false;
- m_temporaryUnsummonedPetNumber = 0;
- //cache for UNIT_CREATED_BY_SPELL to allow
- //returning reagents for temporarily removed pets
- //when dying/logging out
- m_oldpetspell = 0;
- m_lastpetnumber = 0;
- ////////////////////Rest System/////////////////////
- time_inn_enter = 0;
- inn_pos_mapid = 0;
- inn_pos_x = 0.0f;
- inn_pos_y = 0.0f;
- inn_pos_z = 0.0f;
- m_rest_bonus = 0;
- rest_type = REST_TYPE_NO;
- ////////////////////Rest System/////////////////////
- m_mailsLoaded = false;
- m_mailsUpdated = false;
- unReadMails = 0;
- m_nextMailDelivereTime = 0;
- m_itemUpdateQueueBlocked = false;
- for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
- m_forced_speed_changes[i] = 0;
- m_stableSlots = 0;
- /////////////////// Instance System /////////////////////
- m_HomebindTimer = 0;
- m_InstanceValid = true;
- m_dungeonDifficulty = DUNGEON_DIFFICULTY_NORMAL;
- m_raidDifficulty = RAID_DIFFICULTY_10MAN_NORMAL;
- m_raidMapDifficulty = RAID_DIFFICULTY_10MAN_NORMAL;
- m_lastPotionId = 0;
- _talentMgr = new PlayerTalentInfo();
- for (uint8 i = 0; i < BASEMOD_END; ++i)
- {
- m_auraBaseMod[i][FLAT_MOD] = 0.0f;
- m_auraBaseMod[i][PCT_MOD] = 1.0f;
- }
- for (uint8 i = 0; i < MAX_COMBAT_RATING; i++)
- m_baseRatingValue[i] = 0;
- m_baseSpellPower = 0;
- m_baseManaRegen = 0;
- m_baseHealthRegen = 0;
- m_spellPenetrationItemMod = 0;
- // Honor System
- m_lastHonorUpdateTime = time(NULL);
- m_IsBGRandomWinner = false;
- // Player summoning
- m_summon_expire = 0;
- m_summon_mapid = 0;
- m_summon_x = 0.0f;
- m_summon_y = 0.0f;
- m_summon_z = 0.0f;
- m_mover = this;
- m_movedPlayer = this;
- m_seer = this;
- m_recallMap = 0;
- m_recallX = 0;
- m_recallY = 0;
- m_recallZ = 0;
- m_recallO = 0;
- m_homebindMapId = 0;
- m_homebindAreaId = 0;
- m_homebindX = 0;
- m_homebindY = 0;
- m_homebindZ = 0;
- m_contestedPvPTimer = 0;
- m_declinedname = NULL;
- m_isActive = true;
- m_runes = NULL;
- m_lastFallTime = 0;
- m_lastFallZ = 0;
- m_grantableLevels = 0;
- m_ControlledByPlayer = true;
- sWorld->IncreasePlayerCount();
- m_ChampioningFaction = 0;
- m_timeSyncTimer = 0;
- m_timeSyncClient = 0;
- m_timeSyncServer = 0;
- for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i)
- m_powerFraction[i] = 0;
- isDebugAreaTriggers = false;
- m_WeeklyQuestChanged = false;
- m_MonthlyQuestChanged = false;
- m_SeasonalQuestChanged = false;
- SetPendingBind(0, 0);
- _activeCheats = CHEAT_NONE;
- _maxPersonalArenaRate = 0;
- memset(_voidStorageItems, 0, VOID_STORAGE_MAX_SLOT * sizeof(VoidStorageItem*));
- memset(_CUFProfiles, 0, MAX_CUF_PROFILES * sizeof(CUFProfile*));
- m_achievementMgr = new AchievementMgr<Player>(this);
- m_reputationMgr = new ReputationMgr(this);
- }
- Player::~Player()
- {
- // it must be unloaded already in PlayerLogout and accessed only for loggined player
- //m_social = NULL;
- // Note: buy back item already deleted from DB when player was saved
- for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; ++i)
- delete m_items[i];
- for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- delete itr->second;
- delete _talentMgr;
- //all mailed items should be deleted, also all mail should be deallocated
- for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
- delete *itr;
- for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
- delete iter->second; //if item is duplicated... then server may crash ... but that item should be deallocated
- delete PlayerTalkClass;
- for (size_t x = 0; x < ItemSetEff.size(); x++)
- delete ItemSetEff[x];
- delete m_declinedname;
- delete m_runes;
- delete m_achievementMgr;
- delete m_reputationMgr;
- for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
- delete _voidStorageItems[i];
- for (uint8 i = 0; i < MAX_CUF_PROFILES; ++i)
- delete _CUFProfiles[i];
- ClearResurrectRequestData();
- sWorld->DecreasePlayerCount();
- }
- void Player::CleanupsBeforeDelete(bool finalCleanup)
- {
- TradeCancel(false);
- DuelComplete(DUEL_INTERRUPTED);
- Unit::CleanupsBeforeDelete(finalCleanup);
- // clean up player-instance binds, may unload some instance saves
- for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
- for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
- itr->second.save->RemovePlayer(this);
- }
- bool Player::Create(uint32 guidlow, CharacterCreateInfo* createInfo)
- {
- //FIXME: outfitId not used in player creating
- /// @todo need more checks against packet modifications
- // should check that skin, face, hair* are valid via DBC per race/class
- // also do it in Player::BuildEnumData, Player::LoadFromDB
- Object::_Create(guidlow, 0, HIGHGUID_PLAYER);
- m_name = createInfo->Name;
- PlayerInfo const* info = sObjectMgr->GetPlayerInfo(createInfo->Race, createInfo->Class);
- if (!info)
- {
- TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid race/class pair (%u/%u) - refusing to do so.",
- GetSession()->GetAccountId(), m_name.c_str(), createInfo->Race, createInfo->Class);
- return false;
- }
- for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; i++)
- m_items[i] = NULL;
- Relocate(info->positionX, info->positionY, info->positionZ, info->orientation);
- ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(createInfo->Class);
- if (!cEntry)
- {
- TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid character class (%u) - refusing to do so (wrong DBC-files?)",
- GetSession()->GetAccountId(), m_name.c_str(), createInfo->Class);
- return false;
- }
- SetMap(sMapMgr->CreateMap(info->mapId, this));
- uint8 powertype = cEntry->powerType;
- SetObjectScale(1.0f);
- setFactionForRace(createInfo->Race);
- if (!IsValidGender(createInfo->Gender))
- {
- TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid gender (%u) - refusing to do so",
- GetSession()->GetAccountId(), m_name.c_str(), createInfo->Gender);
- return false;
- }
- uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Gender << 16);
- SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24)));
- InitDisplayIds();
- if (sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP)
- {
- SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP);
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
- }
- SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER);
- SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // default for players in 3.0.3
- SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); // -1 is default value
- SetUInt32Value(PLAYER_BYTES, (createInfo->Skin | (createInfo->Face << 8) | (createInfo->HairStyle << 16) | (createInfo->HairColor << 24)));
- SetUInt32Value(PLAYER_BYTES_2, (createInfo->FacialHair |
- (0x00 << 8) |
- (0x00 << 16) |
- (((GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0) ? REST_STATE_RAF_LINKED : REST_STATE_NOT_RAF_LINKED) << 24)));
- SetByteValue(PLAYER_BYTES_3, 0, createInfo->Gender);
- SetByteValue(PLAYER_BYTES_3, 3, 0); // BattlefieldArenaFaction (0 or 1)
- SetUInt64Value(OBJECT_FIELD_DATA, 0);
- SetUInt32Value(PLAYER_GUILDRANK, 0);
- SetGuildLevel(0);
- SetUInt32Value(PLAYER_GUILD_TIMESTAMP, 0);
- for (int i = 0; i < KNOWN_TITLES_SIZE; ++i)
- SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES + i, 0); // 0=disabled
- SetUInt32Value(PLAYER_CHOSEN_TITLE, 0);
- SetUInt32Value(PLAYER_FIELD_KILLS, 0);
- SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, 0);
- // set starting level
- uint32 start_level = getClass() != CLASS_DEATH_KNIGHT
- ? sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL)
- : sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL);
- if (m_session->HasPermission(rbac::RBAC_PERM_USE_START_GM_LEVEL))
- {
- uint32 gm_level = sWorld->getIntConfig(CONFIG_START_GM_LEVEL);
- if (gm_level > start_level)
- start_level = gm_level;
- }
- SetUInt32Value(UNIT_FIELD_LEVEL, start_level);
- InitRunes();
- SetUInt32Value(PLAYER_FIELD_COINAGE, sWorld->getIntConfig(CONFIG_START_PLAYER_MONEY));
- SetCurrency(CURRENCY_TYPE_HONOR_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_HONOR_POINTS));
- SetCurrency(CURRENCY_TYPE_JUSTICE_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_JUSTICE_POINTS));
- SetCurrency(CURRENCY_TYPE_CONQUEST_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_CONQUEST_POINTS));
- // start with every map explored
- if (sWorld->getBoolConfig(CONFIG_START_ALL_EXPLORED))
- {
- for (uint8 i=0; i<PLAYER_EXPLORED_ZONES_SIZE; i++)
- SetFlag(PLAYER_EXPLORED_ZONES_1+i, 0xFFFFFFFF);
- }
- //Reputations if "StartAllReputation" is enabled, -- @todo Fix this in a better way
- if (sWorld->getBoolConfig(CONFIG_START_ALL_REP))
- {
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(942), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(935), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(936), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1011), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(970), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(967), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(989), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(932), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(934), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1038), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1077), 42999);
- // Factions depending on team, like cities and some more stuff
- switch (GetTeam())
- {
- case ALLIANCE:
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(72), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(47), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(69), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(930), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(730), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(978), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(54), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(946), 42999);
- break;
- case HORDE:
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(76), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(68), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(81), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(911), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(729), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(941), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(530), 42999);
- GetReputationMgr().SetReputation(sFactionStore.LookupEntry(947), 42999);
- break;
- default:
- break;
- }
- }
- // Played time
- m_Last_tick = time(NULL);
- m_Played_time[PLAYED_TIME_TOTAL] = 0;
- m_Played_time[PLAYED_TIME_LEVEL] = 0;
- // base stats and related field values
- InitStatsForLevel();
- InitTaxiNodesForLevel();
- InitGlyphsForLevel();
- InitTalentForLevel();
- InitPrimaryProfessions(); // to max set before any spell added
- // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
- UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
- SetFullHealth();
- if (getPowerType() == POWER_MANA)
- {
- UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intellect)
- SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
- }
- if (getPowerType() == POWER_RUNIC_POWER)
- {
- SetPower(POWER_RUNES, 8);
- SetMaxPower(POWER_RUNES, 8);
- SetPower(POWER_RUNIC_POWER, 0);
- SetMaxPower(POWER_RUNIC_POWER, 1000);
- }
- // original spells
- LearnDefaultSkills();
- LearnCustomSpells();
- // original action bar
- for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr)
- addActionButton(action_itr->button, action_itr->action, action_itr->type);
- // original items
- if (CharStartOutfitEntry const* oEntry = GetCharStartOutfitEntry(createInfo->Race, createInfo->Class, createInfo->Gender))
- {
- for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
- {
- if (oEntry->ItemId[j] <= 0)
- continue;
- uint32 itemId = oEntry->ItemId[j];
- // just skip, reported in ObjectMgr::LoadItemTemplates
- ItemTemplate const* iProto = sObjectMgr->GetItemTemplate(itemId);
- if (!iProto)
- continue;
- // BuyCount by default
- uint32 count = iProto->BuyCount;
- // special amount for food/drink
- if (iProto->Class == ITEM_CLASS_CONSUMABLE && iProto->SubClass == ITEM_SUBCLASS_FOOD_DRINK)
- {
- switch (iProto->Spells[0].SpellCategory)
- {
- case SPELL_CATEGORY_FOOD: // food
- count = getClass() == CLASS_DEATH_KNIGHT ? 10 : 4;
- break;
- case SPELL_CATEGORY_DRINK: // drink
- count = 2;
- break;
- }
- if (iProto->GetMaxStackSize() < count)
- count = iProto->GetMaxStackSize();
- }
- StoreNewItemInBestSlots(itemId, count);
- }
- }
- for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr != info->item.end(); ++item_id_itr)
- StoreNewItemInBestSlots(item_id_itr->item_id, item_id_itr->item_amount);
- // bags and main-hand weapon must equipped at this moment
- // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon)
- // or ammo not equipped in special bag
- for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
- {
- uint16 eDest;
- // equip offhand weapon/shield if it attempt equipped before main-hand weapon
- InventoryResult msg = CanEquipItem(NULL_SLOT, eDest, pItem, false);
- if (msg == EQUIP_ERR_OK)
- {
- RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
- EquipItem(eDest, pItem, true);
- }
- // move other items to more appropriate slots
- else
- {
- ItemPosCountVec sDest;
- msg = CanStoreItem(NULL_BAG, NULL_SLOT, sDest, pItem, false);
- if (msg == EQUIP_ERR_OK)
- {
- RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
- pItem = StoreItem(sDest, pItem, true);
- }
- }
- }
- }
- // all item positions resolved
- return true;
- }
- bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount)
- {
- TC_LOG_DEBUG("entities.player.items", "STORAGE: Creating initial item, itemId = %u, count = %u", titem_id, titem_amount);
- // attempt equip by one
- while (titem_amount > 0)
- {
- uint16 eDest;
- InventoryResult msg = CanEquipNewItem(NULL_SLOT, eDest, titem_id, false);
- if (msg != EQUIP_ERR_OK)
- break;
- EquipNewItem(eDest, titem_id, true);
- AutoUnequipOffhandIfNeed();
- --titem_amount;
- }
- if (titem_amount == 0)
- return true; // equipped
- // attempt store
- ItemPosCountVec sDest;
- // store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
- InventoryResult msg = CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount);
- if (msg == EQUIP_ERR_OK)
- {
- StoreNewItem(sDest, titem_id, true, Item::GenerateItemRandomPropertyId(titem_id));
- return true; // stored
- }
- // item can't be added
- TC_LOG_ERROR("entities.player.items", "STORAGE: Can't equip or store initial item %u for race %u class %u, error msg = %u", titem_id, getRace(), getClass(), msg);
- return false;
- }
- void Player::SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, int32 Regen)
- {
- if (int(MaxValue) == DISABLED_MIRROR_TIMER)
- {
- if (int(CurrentValue) != DISABLED_MIRROR_TIMER)
- StopMirrorTimer(Type);
- return;
- }
- WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
- data << (uint32)Type;
- data << CurrentValue;
- data << MaxValue;
- data << Regen;
- data << (uint8)0;
- data << (uint32)0; // spell id
- GetSession()->SendPacket(&data);
- }
- void Player::StopMirrorTimer(MirrorTimerType Type)
- {
- m_MirrorTimer[Type] = DISABLED_MIRROR_TIMER;
- WorldPacket data(SMSG_STOP_MIRROR_TIMER, 4);
- data << (uint32)Type;
- GetSession()->SendPacket(&data);
- }
- bool Player::IsImmuneToEnvironmentalDamage()
- {
- // check for GM and death state included in isAttackableByAOE
- return (!isTargetableForAttack(false));
- }
- uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage)
- {
- if (IsImmuneToEnvironmentalDamage())
- return 0;
- // Absorb, resist some environmental damage type
- uint32 absorb = 0;
- uint32 resist = 0;
- if (type == DAMAGE_LAVA)
- CalcAbsorbResist(this, SPELL_SCHOOL_MASK_FIRE, DIRECT_DAMAGE, damage, &absorb, &resist);
- else if (type == DAMAGE_SLIME)
- CalcAbsorbResist(this, SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, damage, &absorb, &resist);
- damage -= absorb + resist;
- DealDamageMods(this, damage, &absorb);
- WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21));
- data << uint64(GetGUID());
- data << uint8(type != DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL);
- data << uint32(damage);
- data << uint32(absorb);
- data << uint32(resist);
- SendMessageToSet(&data, true);
- uint32 final_damage = DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
- if (!IsAlive())
- {
- if (type == DAMAGE_FALL) // DealDamage not apply item durability loss at self damage
- {
- TC_LOG_DEBUG("entities.player", "We are fall to death, loosing 10 percents durability");
- DurabilityLossAll(0.10f, false);
- // durability lost message
- SendDurabilityLoss(this, 10);
- }
- UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM, 1, type);
- }
- return final_damage;
- }
- int32 Player::getMaxTimer(MirrorTimerType timer)
- {
- switch (timer)
- {
- case FATIGUE_TIMER:
- return MINUTE * IN_MILLISECONDS;
- case BREATH_TIMER:
- {
- if (!IsAlive() || HasAuraType(SPELL_AURA_WATER_BREATHING) || GetSession()->GetSecurity() >= AccountTypes(sWorld->getIntConfig(CONFIG_DISABLE_BREATHING)))
- return DISABLED_MIRROR_TIMER;
- int32 UnderWaterTime = 3 * MINUTE * IN_MILLISECONDS;
- AuraEffectList const& mModWaterBreathing = GetAuraEffectsByType(SPELL_AURA_MOD_WATER_BREATHING);
- for (AuraEffectList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i)
- AddPct(UnderWaterTime, (*i)->GetAmount());
- return UnderWaterTime;
- }
- case FIRE_TIMER:
- {
- if (!IsAlive())
- return DISABLED_MIRROR_TIMER;
- return 1 * IN_MILLISECONDS;
- }
- default:
- return 0;
- }
- }
- void Player::UpdateMirrorTimers()
- {
- // Desync flags for update on next HandleDrowning
- if (m_MirrorTimerFlags)
- m_MirrorTimerFlagsLast = ~m_MirrorTimerFlags;
- }
- void Player::StopMirrorTimers()
- {
- StopMirrorTimer(FATIGUE_TIMER);
- StopMirrorTimer(BREATH_TIMER);
- StopMirrorTimer(FIRE_TIMER);
- }
- bool Player::IsMirrorTimerActive(MirrorTimerType type)
- {
- return m_MirrorTimer[type] == getMaxTimer(type);
- }
- void Player::HandleDrowning(uint32 time_diff)
- {
- if (!m_MirrorTimerFlags)
- return;
- // In water
- if (m_MirrorTimerFlags & UNDERWATER_INWATER)
- {
- // Breath timer not activated - activate it
- if (m_MirrorTimer[BREATH_TIMER] == DISABLED_MIRROR_TIMER)
- {
- m_MirrorTimer[BREATH_TIMER] = getMaxTimer(BREATH_TIMER);
- SendMirrorTimer(BREATH_TIMER, m_MirrorTimer[BREATH_TIMER], m_MirrorTimer[BREATH_TIMER], -1);
- }
- else // If activated - do tick
- {
- m_MirrorTimer[BREATH_TIMER]-=time_diff;
- // Timer limit - need deal damage
- if (m_MirrorTimer[BREATH_TIMER] < 0)
- {
- m_MirrorTimer[BREATH_TIMER]+= 1*IN_MILLISECONDS;
- // Calculate and deal damage
- /// @todo Check this formula
- uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1);
- EnvironmentalDamage(DAMAGE_DROWNING, damage);
- }
- else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INWATER)) // Update time in client if need
- SendMirrorTimer(BREATH_TIMER, getMaxTimer(BREATH_TIMER), m_MirrorTimer[BREATH_TIMER], -1);
- }
- }
- else if (m_MirrorTimer[BREATH_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
- {
- int32 UnderWaterTime = getMaxTimer(BREATH_TIMER);
- // Need breath regen
- m_MirrorTimer[BREATH_TIMER]+=10*time_diff;
- if (m_MirrorTimer[BREATH_TIMER] >= UnderWaterTime || !IsAlive())
- StopMirrorTimer(BREATH_TIMER);
- else if (m_MirrorTimerFlagsLast & UNDERWATER_INWATER)
- SendMirrorTimer(BREATH_TIMER, UnderWaterTime, m_MirrorTimer[BREATH_TIMER], 10);
- }
- // In dark water
- if (m_MirrorTimerFlags & UNDERWARER_INDARKWATER)
- {
- // Fatigue timer not activated - activate it
- if (m_MirrorTimer[FATIGUE_TIMER] == DISABLED_MIRROR_TIMER)
- {
- m_MirrorTimer[FATIGUE_TIMER] = getMaxTimer(FATIGUE_TIMER);
- SendMirrorTimer(FATIGUE_TIMER, m_MirrorTimer[FATIGUE_TIMER], m_MirrorTimer[FATIGUE_TIMER], -1);
- }
- else
- {
- m_MirrorTimer[FATIGUE_TIMER]-=time_diff;
- // Timer limit - need deal damage or teleport ghost t…
Large files files are truncated, but you can click here to view the full file