2 * Copyright (C) 2005-2013 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "Database/DatabaseEnv.h"
26 #include "WorldPacket.h"
27 #include "WorldSession.h"
28 #include "UpdateMask.h"
29 #include "SkillDiscovery.h"
31 #include "GossipDef.h"
32 #include "UpdateData.h"
34 #include "ChannelMgr.h"
35 #include "MapManager.h"
36 #include "MapPersistentStateMgr.h"
37 #include "InstanceData.h"
38 #include "GridNotifiers.h"
39 #include "GridNotifiersImpl.h"
41 #include "ObjectMgr.h"
42 #include "ObjectAccessor.h"
43 #include "CreatureAI.h"
50 #include "Transports.h"
52 #include "BattleGround/BattleGround.h"
53 #include "BattleGround/BattleGroundMgr.h"
54 #include "BattleGround/BattleGroundAV.h"
55 #include "OutdoorPvP/OutdoorPvP.h"
56 #include "ArenaTeam.h"
58 #include "Database/DatabaseImpl.h"
60 #include "ScriptMgr.h"
61 #include "SocialMgr.h"
62 #include "AchievementMgr.h"
64 #include "SpellAuras.h"
65 #include "DBCStores.h"
66 #include "DB2Stores.h"
67 #include "SQLStorages.h"
72 #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)
76 CHARACTER_FLAG_NONE
= 0x00000000,
77 CHARACTER_FLAG_UNK1
= 0x00000001,
78 CHARACTER_FLAG_UNK2
= 0x00000002,
79 CHARACTER_LOCKED_FOR_TRANSFER
= 0x00000004,
80 CHARACTER_FLAG_UNK4
= 0x00000008,
81 CHARACTER_FLAG_UNK5
= 0x00000010,
82 CHARACTER_FLAG_UNK6
= 0x00000020,
83 CHARACTER_FLAG_UNK7
= 0x00000040,
84 CHARACTER_FLAG_UNK8
= 0x00000080,
85 CHARACTER_FLAG_UNK9
= 0x00000100,
86 CHARACTER_FLAG_UNK10
= 0x00000200,
87 CHARACTER_FLAG_HIDE_HELM
= 0x00000400,
88 CHARACTER_FLAG_HIDE_CLOAK
= 0x00000800,
89 CHARACTER_FLAG_UNK13
= 0x00001000,
90 CHARACTER_FLAG_GHOST
= 0x00002000,
91 CHARACTER_FLAG_RENAME
= 0x00004000,
92 CHARACTER_FLAG_UNK16
= 0x00008000,
93 CHARACTER_FLAG_UNK17
= 0x00010000,
94 CHARACTER_FLAG_UNK18
= 0x00020000,
95 CHARACTER_FLAG_UNK19
= 0x00040000,
96 CHARACTER_FLAG_UNK20
= 0x00080000,
97 CHARACTER_FLAG_UNK21
= 0x00100000,
98 CHARACTER_FLAG_UNK22
= 0x00200000,
99 CHARACTER_FLAG_UNK23
= 0x00400000,
100 CHARACTER_FLAG_UNK24
= 0x00800000,
101 CHARACTER_FLAG_LOCKED_BY_BILLING
= 0x01000000,
102 CHARACTER_FLAG_DECLINED
= 0x02000000,
103 CHARACTER_FLAG_UNK27
= 0x04000000,
104 CHARACTER_FLAG_UNK28
= 0x08000000,
105 CHARACTER_FLAG_UNK29
= 0x10000000,
106 CHARACTER_FLAG_UNK30
= 0x20000000,
107 CHARACTER_FLAG_UNK31
= 0x40000000,
108 CHARACTER_FLAG_UNK32
= 0x80000000
111 enum CharacterCustomizeFlags
113 CHAR_CUSTOMIZE_FLAG_NONE
= 0x00000000,
114 CHAR_CUSTOMIZE_FLAG_CUSTOMIZE
= 0x00000001, // name, gender, etc...
115 CHAR_CUSTOMIZE_FLAG_FACTION
= 0x00010000, // name, gender, faction, etc...
116 CHAR_CUSTOMIZE_FLAG_RACE
= 0x00100000 // name, gender, race, etc...
119 // corpse reclaim times
120 #define DEATH_EXPIRE_STEP (5*MINUTE)
121 #define MAX_DEATH_COUNT 3
123 static const uint32 corpseReclaimDelay
[MAX_DEATH_COUNT
] = {30, 60, 120};
125 //== PlayerTaxi ================================================
127 PlayerTaxi::PlayerTaxi()
130 memset(m_taximask
, 0, sizeof(m_taximask
));
133 void PlayerTaxi::InitTaxiNodesForLevel(uint32 race
, uint32 chrClass
, uint32 level
)
135 // class specific initial known nodes
138 case CLASS_DEATH_KNIGHT
:
140 for (int i
= 0; i
< TaxiMaskSize
; ++i
)
141 m_taximask
[i
] |= sOldContinentsNodesMask
[i
];
146 // race specific initial known nodes: capital and taxi hub masks
149 case RACE_HUMAN
: SetTaximaskNode(2); break; // Human
150 case RACE_ORC
: SetTaximaskNode(23); break; // Orc
151 case RACE_DWARF
: SetTaximaskNode(6); break; // Dwarf
152 case RACE_NIGHTELF
: SetTaximaskNode(26);
153 SetTaximaskNode(27); break; // Night Elf
154 case RACE_UNDEAD
: SetTaximaskNode(11); break; // Undead
155 case RACE_TAUREN
: SetTaximaskNode(22); break; // Tauren
156 case RACE_GNOME
: SetTaximaskNode(6); break; // Gnome
157 case RACE_TROLL
: SetTaximaskNode(23); break; // Troll
158 case RACE_BLOODELF
: SetTaximaskNode(82); break; // Blood Elf
159 case RACE_DRAENEI
: SetTaximaskNode(94); break; // Draenei
162 // new continent starting masks (It will be accessible only at new map)
163 switch (Player::TeamForRace(race
))
165 case ALLIANCE
: SetTaximaskNode(100); break;
166 case HORDE
: SetTaximaskNode(99); break;
169 // level dependent taxi hubs
171 SetTaximaskNode(213); // Shattered Sun Staging Area
174 void PlayerTaxi::LoadTaxiMask(const char* data
)
176 Tokens tokens
= StrSplit(data
, " ");
179 Tokens::iterator iter
;
180 for (iter
= tokens
.begin(), index
= 0;
181 (index
< TaxiMaskSize
) && (iter
!= tokens
.end()); ++iter
, ++index
)
183 // load and set bits only for existing taxi nodes
184 m_taximask
[index
] = sTaxiNodesMask
[index
] & uint8(atol((*iter
).c_str()));
188 void PlayerTaxi::AppendTaximaskTo(ByteBuffer
& data
, bool all
)
190 data
<< uint32(TaxiMaskSize
);
193 for (uint8 i
= 0; i
< TaxiMaskSize
; ++i
)
194 data
<< uint8(sTaxiNodesMask
[i
]); // all existing nodes
198 for (uint8 i
= 0; i
< TaxiMaskSize
; ++i
)
199 data
<< uint8(m_taximask
[i
]); // known nodes
203 bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string
& values
, Team team
)
205 ClearTaxiDestinations();
207 Tokens tokens
= StrSplit(values
, " ");
209 for (Tokens::iterator iter
= tokens
.begin(); iter
!= tokens
.end(); ++iter
)
211 uint32 node
= uint32(atol(iter
->c_str()));
212 AddTaxiDestination(node
);
215 if (m_TaxiDestinations
.empty())
219 if (m_TaxiDestinations
.size() < 2)
222 for (size_t i
= 1; i
< m_TaxiDestinations
.size(); ++i
)
226 sObjectMgr
.GetTaxiPath(m_TaxiDestinations
[i
- 1], m_TaxiDestinations
[i
], path
, cost
);
231 // can't load taxi path without mount set (quest taxi path?)
232 if (!sObjectMgr
.GetTaxiMountDisplayId(GetTaxiSource(), team
, true))
238 std::string
PlayerTaxi::SaveTaxiDestinationsToString()
240 if (m_TaxiDestinations
.empty())
243 std::ostringstream ss
;
245 for (size_t i
= 0; i
< m_TaxiDestinations
.size(); ++i
)
246 ss
<< m_TaxiDestinations
[i
] << " ";
251 uint32
PlayerTaxi::GetCurrentTaxiPath() const
253 if (m_TaxiDestinations
.size() < 2)
259 sObjectMgr
.GetTaxiPath(m_TaxiDestinations
[0], m_TaxiDestinations
[1], path
, cost
);
264 std::ostringstream
& operator<< (std::ostringstream
& ss
, PlayerTaxi
const& taxi
)
266 for (int i
= 0; i
< TaxiMaskSize
; ++i
)
267 ss
<< uint32(taxi
.m_taximask
[i
]) << " "; // cast to prevent conversion to char
271 //== TradeData =================================================
273 TradeData
* TradeData::GetTraderData() const
275 return m_trader
->GetTradeData();
278 Item
* TradeData::GetItem(TradeSlots slot
) const
280 return m_items
[slot
] ? m_player
->GetItemByGuid(m_items
[slot
]) : NULL
;
283 bool TradeData::HasItem(ObjectGuid item_guid
) const
285 for (int i
= 0; i
< TRADE_SLOT_COUNT
; ++i
)
286 if (m_items
[i
] == item_guid
)
292 Item
* TradeData::GetSpellCastItem() const
294 return m_spellCastItem
? m_player
->GetItemByGuid(m_spellCastItem
) : NULL
;
297 void TradeData::SetItem(TradeSlots slot
, Item
* item
)
299 ObjectGuid itemGuid
= item
? item
->GetObjectGuid() : ObjectGuid();
301 if (m_items
[slot
] == itemGuid
)
304 m_items
[slot
] = itemGuid
;
307 GetTraderData()->SetAccepted(false);
311 // need remove possible trader spell applied to changed item
312 if (slot
== TRADE_SLOT_NONTRADED
)
313 GetTraderData()->SetSpell(0);
315 // need remove possible player spell applied (possible move reagent)
319 void TradeData::SetSpell(uint32 spell_id
, Item
* castItem
/*= NULL*/)
321 ObjectGuid itemGuid
= castItem
? castItem
->GetObjectGuid() : ObjectGuid();
323 if (m_spell
== spell_id
&& m_spellCastItem
== itemGuid
)
327 m_spellCastItem
= itemGuid
;
330 GetTraderData()->SetAccepted(false);
332 Update(true); // send spell info to item owner
333 Update(false); // send spell info to caster self
336 void TradeData::SetMoney(uint64 money
)
338 if (m_money
== money
)
344 GetTraderData()->SetAccepted(false);
349 void TradeData::Update(bool for_trader
/*= true*/)
352 m_trader
->GetSession()->SendUpdateTrade(true); // player state for trader
354 m_player
->GetSession()->SendUpdateTrade(false); // player state for player
357 void TradeData::SetAccepted(bool state
, bool crosssend
/*= false*/)
364 m_trader
->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE
);
366 m_player
->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE
);
370 //== Player ====================================================
372 UpdateMask
Player::updateVisualBits
;
374 Player::Player(WorldSession
* session
): Unit(), m_mover(this), m_camera(this), m_achievementMgr(this), m_reputationMgr(this)
381 m_objectType
|= TYPEMASK_PLAYER
;
382 m_objectTypeId
= TYPEID_PLAYER
;
384 m_valuesCount
= PLAYER_END
;
386 SetActiveObjectState(true); // player is always active object
391 if (GetSession()->GetSecurity() >= SEC_GAMEMASTER
)
392 SetAcceptTicket(true);
394 // players always accept
395 if (GetSession()->GetSecurity() == SEC_PLAYER
)
396 SetAcceptWhispers(true);
400 m_usedTalentCount
= 0;
401 m_questRewardTalentCount
= 0;
402 m_freeTalentPoints
= 0;
405 m_holyPowerRegenTimer
= REGEN_TIME_HOLY_POWER
;
406 m_weaponChangeTimer
= 0;
409 m_zoneUpdateTimer
= 0;
413 m_nextSave
= sWorld
.getConfig(CONFIG_UINT32_INTERVAL_SAVE
);
415 // randomize first save time in range [CONFIG_UINT32_INTERVAL_SAVE] around [CONFIG_UINT32_INTERVAL_SAVE]
416 // this must help in case next save after mass player load after server startup
417 m_nextSave
= urand(m_nextSave
/ 2, m_nextSave
* 3 / 2);
419 clearResurrectRequestData();
421 memset(m_items
, 0, sizeof(Item
*)*PLAYER_SLOTS_COUNT
);
425 // group is initialized in the reference constructor
426 SetGroupInvite(NULL
);
427 m_groupUpdateMask
= 0;
428 m_auraUpdateMask
= 0;
432 m_GuildIdInvited
= 0;
433 m_ArenaTeamIdInvited
= 0;
435 m_atLoginFlags
= AT_LOGIN_NONE
;
437 mSemaphoreTeleport_Near
= false;
438 mSemaphoreTeleport_Far
= false;
440 m_DelayedOperations
= 0;
441 m_bCanDelayTeleport
= false;
442 m_bHasDelayedTeleport
= false;
443 m_bHasBeenAliveAtDelayedTeleport
= true; // overwrite always at setup teleport data, so not used infact
444 m_teleport_options
= 0;
450 PlayerTalkClass
= new PlayerMenu(GetSession());
451 m_currentBuybackSlot
= BUYBACK_SLOT_START
;
453 m_DailyQuestChanged
= false;
454 m_WeeklyQuestChanged
= false;
456 for (int i
= 0; i
< MAX_TIMERS
; ++i
)
457 m_MirrorTimer
[i
] = DISABLED_MIRROR_TIMER
;
459 m_MirrorTimerFlags
= UNDERWATER_NONE
;
460 m_MirrorTimerFlagsLast
= UNDERWATER_NONE
;
467 m_deathExpireTime
= 0;
471 m_DetectInvTimer
= 1 * IN_MILLISECONDS
;
473 for (int j
= 0; j
< PLAYER_MAX_BATTLEGROUND_QUEUES
; ++j
)
475 m_bgBattleGroundQueueID
[j
].bgQueueTypeId
= BATTLEGROUND_QUEUE_NONE
;
476 m_bgBattleGroundQueueID
[j
].invitedToInstance
= 0;
479 m_logintime
= time(NULL
);
480 m_Last_tick
= m_logintime
;
481 m_WeaponProficiency
= 0;
482 m_ArmorProficiency
= 0;
485 m_canDualWield
= false;
486 m_canTitanGrip
= false;
489 m_temporaryUnsummonedPetNumber
= 0;
491 //////////////////// Rest System/////////////////////
495 rest_type
= REST_TYPE_NO
;
496 //////////////////// Rest System/////////////////////
498 m_mailsUpdated
= false;
500 m_nextMailDelivereTime
= 0;
502 m_resetTalentsCost
= 0;
503 m_resetTalentsTime
= 0;
504 m_itemUpdateQueueBlocked
= false;
506 for (int i
= 0; i
< MAX_MOVE_TYPE
; ++i
)
507 m_forced_speed_changes
[i
] = 0;
511 /////////////////// Instance System /////////////////////
514 m_InstanceValid
= true;
515 m_dungeonDifficulty
= DUNGEON_DIFFICULTY_NORMAL
;
516 m_raidDifficulty
= RAID_DIFFICULTY_10MAN_NORMAL
;
522 for (int i
= 0; i
< MAX_TALENT_SPEC_COUNT
; ++i
)
523 m_talentsPrimaryTree
[i
] = 0;
525 for (int i
= 0; i
< BASEMOD_END
; ++i
)
527 m_auraBaseMod
[i
][FLAT_MOD
] = 0.0f
;
528 m_auraBaseMod
[i
][PCT_MOD
] = 1.0f
;
531 for (int i
= 0; i
< MAX_COMBAT_RATING
; ++i
)
532 m_baseRatingValue
[i
] = 0;
534 m_baseSpellPower
= 0;
535 m_baseHealthRegen
= 0;
537 m_armorPenetrationPct
= 0.0f
;
538 m_spellPenetrationItemMod
= 0;
540 m_lastHonorKillsUpdateTime
= time(NULL
);
549 m_contestedPvPTimer
= 0;
551 m_declinedname
= NULL
;
564 CleanupsBeforeDelete();
566 // it must be unloaded already in PlayerLogout and accessed only for loggined player
569 // Note: buy back item already deleted from DB when player was saved
570 for (int i
= 0; i
< PLAYER_SLOTS_COUNT
; ++i
)
576 // all mailed items should be deleted, also all mail should be deallocated
577 for (PlayerMails::const_iterator itr
= m_mail
.begin(); itr
!= m_mail
.end(); ++itr
)
580 for (ItemMap::const_iterator iter
= mMitems
.begin(); iter
!= mMitems
.end(); ++iter
)
581 delete iter
->second
; // if item is duplicated... then server may crash ... but that item should be deallocated
583 delete PlayerTalkClass
;
587 m_transport
->RemovePassenger(this);
590 for (size_t x
= 0; x
< ItemSetEff
.size(); ++x
)
591 delete ItemSetEff
[x
];
593 // clean up player-instance binds, may unload some instance saves
594 for (uint8 i
= 0; i
< MAX_DIFFICULTY
; ++i
)
595 for (BoundInstancesMap::iterator itr
= m_boundInstances
[i
].begin(); itr
!= m_boundInstances
[i
].end(); ++itr
)
596 itr
->second
.state
->RemovePlayer(this);
598 delete m_declinedname
;
602 void Player::CleanupsBeforeDelete()
604 if (m_uint32Values
) // only for fully created Object
607 DuelComplete(DUEL_INTERRUPTED
);
610 // notify zone scripts for player logout
611 sOutdoorPvPMgr
.HandlePlayerLeaveZone(this, m_zoneUpdateId
);
613 Unit::CleanupsBeforeDelete();
616 bool Player::Create(uint32 guidlow
, const std::string
& name
, uint8 race
, uint8 class_
, uint8 gender
, uint8 skin
, uint8 face
, uint8 hairStyle
, uint8 hairColor
, uint8 facialHair
, uint8
/*outfitId */)
618 // FIXME: outfitId not used in player creating
620 Object::_Create(guidlow
, 0, HIGHGUID_PLAYER
);
624 PlayerInfo
const* info
= sObjectMgr
.GetPlayerInfo(race
, class_
);
627 sLog
.outError("Player have incorrect race/class pair. Can't be loaded.");
631 ChrClassesEntry
const* cEntry
= sChrClassesStore
.LookupEntry(class_
);
634 sLog
.outError("Class %u not found in DBC (Wrong DBC files?)", class_
);
638 // player store gender in single bit
639 if (gender
!= uint8(GENDER_MALE
) && gender
!= uint8(GENDER_FEMALE
))
641 sLog
.outError("Invalid gender %u at player creating", uint32(gender
));
645 for (int i
= 0; i
< PLAYER_SLOTS_COUNT
; ++i
)
648 SetLocationMapId(info
->mapId
);
649 Relocate(info
->positionX
, info
->positionY
, info
->positionZ
, info
->orientation
);
651 SetMap(sMapMgr
.CreateMap(info
->mapId
, this));
653 uint8 powertype
= cEntry
->powerType
;
655 setFactionForRace(race
);
657 SetByteValue(UNIT_FIELD_BYTES_0
, 0, race
);
658 SetByteValue(UNIT_FIELD_BYTES_0
, 1, class_
);
659 SetByteValue(UNIT_FIELD_BYTES_0
, 2, gender
);
660 SetByteValue(UNIT_FIELD_BYTES_0
, 3, powertype
);
662 InitDisplayIds(); // model, scale and model data
664 SetByteFlag(UNIT_FIELD_BYTES_2
, 1, UNIT_BYTE2_FLAG_PVP
);
665 SetFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_PVP_ATTACKABLE
);
666 SetFlag(UNIT_FIELD_FLAGS_2
, UNIT_FLAG2_REGENERATE_POWER
);
667 SetFloatValue(UNIT_MOD_CAST_SPEED
, 1.0f
); // fix cast time showed in spell tooltip on client
668 SetFloatValue(UNIT_FIELD_HOVERHEIGHT
, 1.0f
); // default for players in 3.0.3
670 SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX
, -1); // -1 is default value
672 SetByteValue(PLAYER_BYTES
, 0, skin
);
673 SetByteValue(PLAYER_BYTES
, 1, face
);
674 SetByteValue(PLAYER_BYTES
, 2, hairStyle
);
675 SetByteValue(PLAYER_BYTES
, 3, hairColor
);
677 SetByteValue(PLAYER_BYTES_2
, 0, facialHair
);
678 SetByteValue(PLAYER_BYTES_2
, 3, REST_STATE_NORMAL
);
680 SetUInt16Value(PLAYER_BYTES_3
, 0, gender
); // only GENDER_MALE/GENDER_FEMALE (1 bit) allowed, drunk state = 0
681 SetByteValue(PLAYER_BYTES_3
, 3, 0); // BattlefieldArenaFaction (0 or 1)
685 SetUInt32Value(PLAYER_GUILDRANK
, 0);
686 SetUInt32Value(PLAYER_GUILD_TIMESTAMP
, 0);
688 for (int i
= 0; i
< KNOWN_TITLES_SIZE
; ++i
)
689 SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES
+ i
, 0); // 0=disabled
690 SetUInt32Value(PLAYER_CHOSEN_TITLE
, 0);
692 SetUInt32Value(PLAYER_FIELD_KILLS
, 0);
693 SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS
, 0);
695 // set starting level
696 uint32 start_level
= getClass() != CLASS_DEATH_KNIGHT
697 ? sWorld
.getConfig(CONFIG_UINT32_START_PLAYER_LEVEL
)
698 : sWorld
.getConfig(CONFIG_UINT32_START_HEROIC_PLAYER_LEVEL
);
700 if (GetSession()->GetSecurity() >= SEC_MODERATOR
)
702 uint32 gm_level
= sWorld
.getConfig(CONFIG_UINT32_START_GM_LEVEL
);
703 if (gm_level
> start_level
)
704 start_level
= gm_level
;
707 SetUInt32Value(UNIT_FIELD_LEVEL
, start_level
);
711 SetUInt32Value(PLAYER_FIELD_COINAGE
, sWorld
.getConfig(CONFIG_UINT32_START_PLAYER_MONEY
));
712 SetCurrencyCount(CURRENCY_HONOR_POINTS
,sWorld
.getConfig(CONFIG_UINT32_CURRENCY_START_HONOR_POINTS
));
713 SetCurrencyCount(CURRENCY_CONQUEST_POINTS
, sWorld
.getConfig(CONFIG_UINT32_CURRENCY_START_CONQUEST_POINTS
));
716 m_Last_tick
= time(NULL
);
717 m_Played_time
[PLAYED_TIME_TOTAL
] = 0;
718 m_Played_time
[PLAYED_TIME_LEVEL
] = 0;
720 // base stats and related field values
722 InitTaxiNodesForLevel();
723 InitGlyphsForLevel();
724 InitTalentForLevel();
725 InitPrimaryProfessions(); // to max set before any spell added
727 // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
728 UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
729 SetHealth(GetMaxHealth());
731 if (getPowerType() == POWER_MANA
)
733 UpdateMaxPower(POWER_MANA
); // Update max Mana (for add bonus from intellect)
734 SetPower(POWER_MANA
, GetMaxPower(POWER_MANA
));
737 if (getPowerType() != POWER_MANA
) // hide additional mana bar if we have no mana
739 SetPower(POWER_MANA
, 0);
740 SetMaxPower(POWER_MANA
, 0);
744 learnDefaultSpells();
746 // original action bar
747 for (PlayerCreateInfoActions::const_iterator action_itr
= info
->action
.begin(); action_itr
!= info
->action
.end(); ++action_itr
)
748 addActionButton(0, action_itr
->button
, action_itr
->action
, action_itr
->type
);
751 uint32 raceClassGender
= GetUInt32Value(UNIT_FIELD_BYTES_0
) & 0x00FFFFFF;
753 CharStartOutfitEntry
const* oEntry
= NULL
;
754 for (uint32 i
= 1; i
< sCharStartOutfitStore
.GetNumRows(); ++i
)
756 if (CharStartOutfitEntry
const* entry
= sCharStartOutfitStore
.LookupEntry(i
))
758 if (entry
->RaceClassGender
== raceClassGender
)
768 for (int j
= 0; j
< MAX_OUTFIT_ITEMS
; ++j
)
770 if (oEntry
->ItemId
[j
] <= 0)
773 uint32 item_id
= oEntry
->ItemId
[j
];
775 // just skip, reported in ObjectMgr::LoadItemPrototypes
776 ItemPrototype
const* iProto
= ObjectMgr::GetItemPrototype(item_id
);
780 // BuyCount by default
781 int32 count
= iProto
->BuyCount
;
783 // special amount for foor/drink
784 if (iProto
->Class
== ITEM_CLASS_CONSUMABLE
&& iProto
->SubClass
== ITEM_SUBCLASS_FOOD
)
786 switch (iProto
->Spells
[0].SpellCategory
)
789 count
= getClass() == CLASS_DEATH_KNIGHT
? 10 : 4;
795 if (iProto
->Stackable
< count
)
796 count
= iProto
->Stackable
;
799 StoreNewItemInBestSlots(item_id
, count
);
803 for (PlayerCreateInfoItems::const_iterator item_id_itr
= info
->item
.begin(); item_id_itr
!= info
->item
.end(); ++item_id_itr
)
804 StoreNewItemInBestSlots(item_id_itr
->item_id
, item_id_itr
->item_amount
);
806 // bags and main-hand weapon must equipped at this moment
807 // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon)
808 // or ammo not equipped in special bag
809 for (int i
= INVENTORY_SLOT_ITEM_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
811 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
814 // equip offhand weapon/shield if it attempt equipped before main-hand weapon
815 InventoryResult msg
= CanEquipItem(NULL_SLOT
, eDest
, pItem
, false);
816 if (msg
== EQUIP_ERR_OK
)
818 RemoveItem(INVENTORY_SLOT_BAG_0
, i
, true);
819 EquipItem(eDest
, pItem
, true);
821 // move other items to more appropriate slots (ammo not equipped in special bag)
824 ItemPosCountVec sDest
;
825 msg
= CanStoreItem(NULL_BAG
, NULL_SLOT
, sDest
, pItem
, false);
826 if (msg
== EQUIP_ERR_OK
)
828 RemoveItem(INVENTORY_SLOT_BAG_0
, i
, true);
829 pItem
= StoreItem(sDest
, pItem
, true);
832 // if this is ammo then use it
833 msg
= CanUseAmmo(pItem
->GetEntry());
834 if (msg
== EQUIP_ERR_OK
)
835 SetAmmo(pItem
->GetEntry());
839 // all item positions resolved
841 if (info
->phaseMap
!= 0)
842 CharacterDatabase
.PExecute("REPLACE INTO `character_phase_data` (`guid`, `map`) VALUES (%u, %u)", guidlow
, info
->phaseMap
);
847 bool Player::StoreNewItemInBestSlots(uint32 titem_id
, uint32 titem_amount
)
849 DEBUG_LOG("STORAGE: Creating initial item, itemId = %u, count = %u", titem_id
, titem_amount
);
851 // attempt equip by one
852 while (titem_amount
> 0)
855 uint8 msg
= CanEquipNewItem(NULL_SLOT
, eDest
, titem_id
, false);
856 if (msg
!= EQUIP_ERR_OK
)
859 EquipNewItem(eDest
, titem_id
, true);
860 AutoUnequipOffhandIfNeed();
864 if (titem_amount
== 0)
865 return true; // equipped
868 ItemPosCountVec sDest
;
869 // store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
870 uint8 msg
= CanStoreNewItem(INVENTORY_SLOT_BAG_0
, NULL_SLOT
, sDest
, titem_id
, titem_amount
);
871 if (msg
== EQUIP_ERR_OK
)
873 StoreNewItem(sDest
, titem_id
, true, Item::GenerateItemRandomPropertyId(titem_id
));
874 return true; // stored
877 // item can't be added
878 sLog
.outError("STORAGE: Can't equip or store initial item %u for race %u class %u , error msg = %u", titem_id
, getRace(), getClass(), msg
);
882 // helper function, mainly for script side, but can be used for simple task in mangos also.
883 Item
* Player::StoreNewItemInInventorySlot(uint32 itemEntry
, uint32 amount
)
885 ItemPosCountVec vDest
;
887 uint8 msg
= CanStoreNewItem(INVENTORY_SLOT_BAG_0
, NULL_SLOT
, vDest
, itemEntry
, amount
);
889 if (msg
== EQUIP_ERR_OK
)
891 if (Item
* pItem
= StoreNewItem(vDest
, itemEntry
, true, Item::GenerateItemRandomPropertyId(itemEntry
)))
898 void Player::SendMirrorTimer(MirrorTimerType Type
, uint32 MaxValue
, uint32 CurrentValue
, int32 Regen
)
900 if (int(MaxValue
) == DISABLED_MIRROR_TIMER
)
902 if (int(CurrentValue
) != DISABLED_MIRROR_TIMER
)
903 StopMirrorTimer(Type
);
906 WorldPacket
data(SMSG_START_MIRROR_TIMER
, (21));
907 data
<< (uint32
)Type
;
908 data
<< CurrentValue
;
912 data
<< (uint32
)0; // spell id
913 GetSession()->SendPacket(&data
);
916 void Player::StopMirrorTimer(MirrorTimerType Type
)
918 m_MirrorTimer
[Type
] = DISABLED_MIRROR_TIMER
;
919 WorldPacket
data(SMSG_STOP_MIRROR_TIMER
, 4);
920 data
<< (uint32
)Type
;
921 GetSession()->SendPacket(&data
);
924 uint32
Player::EnvironmentalDamage(EnviromentalDamage type
, uint32 damage
)
926 if (!isAlive() || isGameMaster())
929 // Absorb, resist some environmental damage type
932 if (type
== DAMAGE_LAVA
)
933 CalculateDamageAbsorbAndResist(this, SPELL_SCHOOL_MASK_FIRE
, DIRECT_DAMAGE
, damage
, &absorb
, &resist
);
934 else if (type
== DAMAGE_SLIME
)
935 CalculateDamageAbsorbAndResist(this, SPELL_SCHOOL_MASK_NATURE
, DIRECT_DAMAGE
, damage
, &absorb
, &resist
);
937 damage
-= absorb
+ resist
;
939 DealDamageMods(this, damage
, &absorb
);
941 WorldPacket
data(SMSG_ENVIRONMENTALDAMAGELOG
, (21));
942 data
<< GetObjectGuid();
943 data
<< uint8(type
!= DAMAGE_FALL_TO_VOID
? type
: DAMAGE_FALL
);
944 data
<< uint32(damage
);
945 data
<< uint32(absorb
);
946 data
<< uint32(resist
);
947 SendMessageToSet(&data
, true);
949 uint32 final_damage
= DealDamage(this, damage
, NULL
, SELF_DAMAGE
, SPELL_SCHOOL_MASK_NORMAL
, NULL
, false);
953 if (type
== DAMAGE_FALL
) // DealDamage not apply item durability loss at self damage
955 DEBUG_LOG("We are fall to death, loosing 10 percents durability");
956 DurabilityLossAll(0.10f
, false);
957 // durability lost message
958 WorldPacket
data2(SMSG_DURABILITY_DAMAGE_DEATH
, 0);
959 GetSession()->SendPacket(&data2
);
962 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM
, 1, type
);
968 int32
Player::getMaxTimer(MirrorTimerType timer
)
973 if (GetSession()->GetSecurity() >= (AccountTypes
)sWorld
.getConfig(CONFIG_UINT32_TIMERBAR_FATIGUE_GMLEVEL
))
974 return DISABLED_MIRROR_TIMER
;
975 return sWorld
.getConfig(CONFIG_UINT32_TIMERBAR_FATIGUE_MAX
) * IN_MILLISECONDS
;
978 if (!isAlive() || HasAuraType(SPELL_AURA_WATER_BREATHING
) ||
979 GetSession()->GetSecurity() >= (AccountTypes
)sWorld
.getConfig(CONFIG_UINT32_TIMERBAR_BREATH_GMLEVEL
))
980 return DISABLED_MIRROR_TIMER
;
981 int32 UnderWaterTime
= sWorld
.getConfig(CONFIG_UINT32_TIMERBAR_BREATH_MAX
) * IN_MILLISECONDS
;
982 AuraList
const& mModWaterBreathing
= GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING
);
983 for (AuraList::const_iterator i
= mModWaterBreathing
.begin(); i
!= mModWaterBreathing
.end(); ++i
)
984 UnderWaterTime
= uint32(UnderWaterTime
* (100.0f
+ (*i
)->GetModifier()->m_amount
) / 100.0f
);
985 return UnderWaterTime
;
989 if (!isAlive() || GetSession()->GetSecurity() >= (AccountTypes
)sWorld
.getConfig(CONFIG_UINT32_TIMERBAR_FIRE_GMLEVEL
))
990 return DISABLED_MIRROR_TIMER
;
991 return sWorld
.getConfig(CONFIG_UINT32_TIMERBAR_FIRE_MAX
) * IN_MILLISECONDS
;
999 void Player::UpdateMirrorTimers()
1001 // Desync flags for update on next HandleDrowning
1002 if (m_MirrorTimerFlags
)
1003 m_MirrorTimerFlagsLast
= ~m_MirrorTimerFlags
;
1006 void Player::HandleDrowning(uint32 time_diff
)
1008 if (!m_MirrorTimerFlags
)
1012 if (m_MirrorTimerFlags
& UNDERWATER_INWATER
)
1014 // Breath timer not activated - activate it
1015 if (m_MirrorTimer
[BREATH_TIMER
] == DISABLED_MIRROR_TIMER
)
1017 m_MirrorTimer
[BREATH_TIMER
] = getMaxTimer(BREATH_TIMER
);
1018 SendMirrorTimer(BREATH_TIMER
, m_MirrorTimer
[BREATH_TIMER
], m_MirrorTimer
[BREATH_TIMER
], -1);
1022 m_MirrorTimer
[BREATH_TIMER
] -= time_diff
;
1023 // Timer limit - need deal damage
1024 if (m_MirrorTimer
[BREATH_TIMER
] < 0)
1026 m_MirrorTimer
[BREATH_TIMER
] += 2 * IN_MILLISECONDS
;
1027 // Calculate and deal damage
1028 // TODO: Check this formula
1029 uint32 damage
= GetMaxHealth() / 5 + urand(0, getLevel() - 1);
1030 EnvironmentalDamage(DAMAGE_DROWNING
, damage
);
1032 else if (!(m_MirrorTimerFlagsLast
& UNDERWATER_INWATER
)) // Update time in client if need
1033 SendMirrorTimer(BREATH_TIMER
, getMaxTimer(BREATH_TIMER
), m_MirrorTimer
[BREATH_TIMER
], -1);
1036 else if (m_MirrorTimer
[BREATH_TIMER
] != DISABLED_MIRROR_TIMER
) // Regen timer
1038 int32 UnderWaterTime
= getMaxTimer(BREATH_TIMER
);
1039 // Need breath regen
1040 m_MirrorTimer
[BREATH_TIMER
] += 10 * time_diff
;
1041 if (m_MirrorTimer
[BREATH_TIMER
] >= UnderWaterTime
|| !isAlive())
1042 StopMirrorTimer(BREATH_TIMER
);
1043 else if (m_MirrorTimerFlagsLast
& UNDERWATER_INWATER
)
1044 SendMirrorTimer(BREATH_TIMER
, UnderWaterTime
, m_MirrorTimer
[BREATH_TIMER
], 10);
1048 if (m_MirrorTimerFlags
& UNDERWATER_INDARKWATER
)
1050 // Fatigue timer not activated - activate it
1051 if (m_MirrorTimer
[FATIGUE_TIMER
] == DISABLED_MIRROR_TIMER
)
1053 m_MirrorTimer
[FATIGUE_TIMER
] = getMaxTimer(FATIGUE_TIMER
);
1054 SendMirrorTimer(FATIGUE_TIMER
, m_MirrorTimer
[FATIGUE_TIMER
], m_MirrorTimer
[FATIGUE_TIMER
], -1);
1058 m_MirrorTimer
[FATIGUE_TIMER
] -= time_diff
;
1059 // Timer limit - need deal damage or teleport ghost to graveyard
1060 if (m_MirrorTimer
[FATIGUE_TIMER
] < 0)
1062 m_MirrorTimer
[FATIGUE_TIMER
] += 2 * IN_MILLISECONDS
;
1063 if (isAlive()) // Calculate and deal damage
1065 uint32 damage
= GetMaxHealth() / 5 + urand(0, getLevel() - 1);
1066 EnvironmentalDamage(DAMAGE_EXHAUSTED
, damage
);
1068 else if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GHOST
)) // Teleport ghost to graveyard
1071 else if (!(m_MirrorTimerFlagsLast
& UNDERWATER_INDARKWATER
))
1072 SendMirrorTimer(FATIGUE_TIMER
, getMaxTimer(FATIGUE_TIMER
), m_MirrorTimer
[FATIGUE_TIMER
], -1);
1075 else if (m_MirrorTimer
[FATIGUE_TIMER
] != DISABLED_MIRROR_TIMER
) // Regen timer
1077 int32 DarkWaterTime
= getMaxTimer(FATIGUE_TIMER
);
1078 m_MirrorTimer
[FATIGUE_TIMER
] += 10 * time_diff
;
1079 if (m_MirrorTimer
[FATIGUE_TIMER
] >= DarkWaterTime
|| !isAlive())
1080 StopMirrorTimer(FATIGUE_TIMER
);
1081 else if (m_MirrorTimerFlagsLast
& UNDERWATER_INDARKWATER
)
1082 SendMirrorTimer(FATIGUE_TIMER
, DarkWaterTime
, m_MirrorTimer
[FATIGUE_TIMER
], 10);
1085 if (m_MirrorTimerFlags
& (UNDERWATER_INLAVA
| UNDERWATER_INSLIME
))
1087 // Breath timer not activated - activate it
1088 if (m_MirrorTimer
[FIRE_TIMER
] == DISABLED_MIRROR_TIMER
)
1089 m_MirrorTimer
[FIRE_TIMER
] = getMaxTimer(FIRE_TIMER
);
1092 m_MirrorTimer
[FIRE_TIMER
] -= time_diff
;
1093 if (m_MirrorTimer
[FIRE_TIMER
] < 0)
1095 m_MirrorTimer
[FIRE_TIMER
] += 2 * IN_MILLISECONDS
;
1096 // Calculate and deal damage
1097 // TODO: Check this formula
1098 uint32 damage
= urand(600, 700);
1099 if (m_MirrorTimerFlags
& UNDERWATER_INLAVA
)
1100 EnvironmentalDamage(DAMAGE_LAVA
, damage
);
1101 // need to skip Slime damage in Undercity,
1102 // maybe someone can find better way to handle environmental damage
1103 else if (m_zoneUpdateId
!= 1497)
1104 EnvironmentalDamage(DAMAGE_SLIME
, damage
);
1109 m_MirrorTimer
[FIRE_TIMER
] = DISABLED_MIRROR_TIMER
;
1111 // Recheck timers flag
1112 m_MirrorTimerFlags
&= ~UNDERWATER_EXIST_TIMERS
;
1113 for (int i
= 0; i
< MAX_TIMERS
; ++i
)
1114 if (m_MirrorTimer
[i
] != DISABLED_MIRROR_TIMER
)
1116 m_MirrorTimerFlags
|= UNDERWATER_EXIST_TIMERS
;
1119 m_MirrorTimerFlagsLast
= m_MirrorTimerFlags
;
1122 /// The player sobers by 256 every 10 seconds
1123 void Player::HandleSobering()
1127 uint32 drunk
= (m_drunk
<= 256) ? 0 : (m_drunk
- 256);
1128 SetDrunkValue(drunk
);
1131 DrunkenState
Player::GetDrunkenstateByValue(uint16 value
)
1134 return DRUNKEN_SMASHED
;
1136 return DRUNKEN_DRUNK
;
1138 return DRUNKEN_TIPSY
;
1139 return DRUNKEN_SOBER
;
1142 void Player::SetDrunkValue(uint16 newDrunkenValue
, uint32 itemId
)
1144 uint32 oldDrunkenState
= Player::GetDrunkenstateByValue(m_drunk
);
1146 m_drunk
= newDrunkenValue
;
1147 SetUInt16Value(PLAYER_BYTES_3
, 0, uint16(getGender()) | (m_drunk
& 0xFFFE));
1149 uint32 newDrunkenState
= Player::GetDrunkenstateByValue(m_drunk
);
1151 // special drunk invisibility detection
1152 if (newDrunkenState
>= DRUNKEN_DRUNK
)
1153 m_detectInvisibilityMask
|= (1 << 6);
1155 m_detectInvisibilityMask
&= ~(1 << 6);
1157 if (newDrunkenState
== oldDrunkenState
)
1160 WorldPacket
data(SMSG_CROSSED_INEBRIATION_THRESHOLD
, (8 + 4 + 4));
1161 data
<< GetObjectGuid();
1162 data
<< uint32(newDrunkenState
);
1163 data
<< uint32(itemId
);
1165 SendMessageToSet(&data
, true);
1168 void Player::Update(uint32 update_diff
, uint32 p_time
)
1173 // Remove failed timed Achievements
1174 GetAchievementMgr().DoFailedTimedAchievementCriterias();
1177 if (m_nextMailDelivereTime
&& m_nextMailDelivereTime
<= time(NULL
))
1182 // It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated)
1183 m_nextMailDelivereTime
= 0;
1186 // Used to implement delayed far teleports
1187 SetCanDelayTeleport(true);
1188 Unit::Update(update_diff
, p_time
);
1189 SetCanDelayTeleport(false);
1191 // Update player only attacks
1192 if (uint32 ranged_att
= getAttackTimer(RANGED_ATTACK
))
1193 setAttackTimer(RANGED_ATTACK
, (update_diff
>= ranged_att
? 0 : ranged_att
- update_diff
));
1195 time_t now
= time(NULL
);
1199 UpdateContestedPvP(update_diff
);
1201 UpdateDuelFlag(now
);
1203 CheckDuelDistance(now
);
1205 UpdateAfkReport(now
);
1207 // Update items that have just a limited lifetime
1208 if (now
> m_Last_tick
)
1209 UpdateItemDuration(uint32(now
- m_Last_tick
));
1211 if (!m_timedquests
.empty())
1213 QuestSet::iterator iter
= m_timedquests
.begin();
1214 while (iter
!= m_timedquests
.end())
1216 QuestStatusData
& q_status
= mQuestStatus
[*iter
];
1217 if (q_status
.m_timer
<= update_diff
)
1219 uint32 quest_id
= *iter
;
1220 ++iter
; // Current iter will be removed in FailQuest
1221 FailQuest(quest_id
);
1225 q_status
.m_timer
-= update_diff
;
1226 if (q_status
.uState
!= QUEST_NEW
) q_status
.uState
= QUEST_CHANGED
;
1232 if (hasUnitState(UNIT_STAT_MELEE_ATTACKING
))
1234 UpdateMeleeAttackingState();
1236 Unit
* pVictim
= getVictim();
1237 if (pVictim
&& !IsNonMeleeSpellCasted(false))
1239 Player
* vOwner
= pVictim
->GetCharmerOrOwnerPlayerOrPlayerItself();
1240 if (vOwner
&& vOwner
->IsPvP() && !IsInDuelWith(vOwner
))
1243 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT
);
1248 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_RESTING
))
1250 if (roll_chance_i(3) && GetTimeInnEnter() > 0) // Freeze update
1252 time_t time_inn
= time(NULL
) - GetTimeInnEnter();
1253 if (time_inn
>= 10) // Freeze update
1255 float bubble
= 0.125f
* sWorld
.getConfig(CONFIG_FLOAT_RATE_REST_INGAME
);
1256 // Speed collect rest bonus (section/in hour)
1257 SetRestBonus(float(GetRestBonus() + time_inn
* (GetUInt32Value(PLAYER_NEXT_LEVEL_XP
) / 72000) * bubble
));
1258 UpdateInnerTime(time(NULL
));
1265 if (update_diff
>= m_regenTimer
)
1268 m_regenTimer
-= update_diff
;
1271 if (m_weaponChangeTimer
> 0)
1273 if (update_diff
>= m_weaponChangeTimer
)
1274 m_weaponChangeTimer
= 0;
1276 m_weaponChangeTimer
-= update_diff
;
1279 if (m_zoneUpdateTimer
> 0)
1281 if (update_diff
>= m_zoneUpdateTimer
)
1283 uint32 newzone
, newarea
;
1284 GetZoneAndAreaId(newzone
, newarea
);
1286 if (m_zoneUpdateId
!= newzone
)
1287 UpdateZone(newzone
, newarea
); // Also update area
1290 // Use area updates as well
1291 // Needed for free for all arenas for example
1292 if (m_areaUpdateId
!= newarea
)
1293 UpdateArea(newarea
);
1295 m_zoneUpdateTimer
= ZONE_UPDATE_INTERVAL
;
1299 m_zoneUpdateTimer
-= update_diff
;
1302 if (m_timeSyncTimer
> 0)
1304 if (update_diff
>= m_timeSyncTimer
)
1307 m_timeSyncTimer
-= update_diff
;
1312 if (!HasAuraType(SPELL_AURA_STOP_NATURAL_MANA_REGEN
))
1313 SetFlag(UNIT_FIELD_FLAGS_2
, UNIT_FLAG2_REGENERATE_POWER
);
1319 if (m_deathState
== JUST_DIED
)
1324 if (update_diff
>= m_nextSave
)
1326 // m_nextSave reseted in SaveToDB call
1328 DETAIL_LOG("Player '%s' (GUID: %u) saved", GetName(), GetGUIDLow());
1331 m_nextSave
-= update_diff
;
1334 // Handle Water/drowning
1335 HandleDrowning(update_diff
);
1337 // Handle detect stealth players
1338 if (m_DetectInvTimer
> 0)
1340 if (update_diff
>= m_DetectInvTimer
)
1342 HandleStealthedUnitsDetection();
1343 m_DetectInvTimer
= 3000;
1346 m_DetectInvTimer
-= update_diff
;
1350 if (now
> m_Last_tick
)
1352 uint32 elapsed
= uint32(now
- m_Last_tick
);
1353 m_Played_time
[PLAYED_TIME_TOTAL
] += elapsed
; // Total played time
1354 m_Played_time
[PLAYED_TIME_LEVEL
] += elapsed
; // Level played time
1360 m_drunkTimer
+= update_diff
;
1362 if (m_drunkTimer
> 10 * IN_MILLISECONDS
)
1366 // Not auto-free ghost from body in instances
1367 if (m_deathTimer
> 0 && !GetMap()->Instanceable())
1369 if (p_time
>= m_deathTimer
)
1376 m_deathTimer
-= p_time
;
1379 UpdateEnchantTime(update_diff
);
1380 UpdateHomebindTime(update_diff
);
1383 SendUpdateToOutOfRangeGroupMembers();
1385 Pet
* pet
= GetPet();
1386 if (pet
&& !pet
->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGuid() && (pet
->GetObjectGuid() != GetCharmGuid())))
1387 pet
->Unsummon(PET_SAVE_REAGENTS
, this);
1389 if (IsHasDelayedTeleport())
1390 TeleportTo(m_teleport_dest
, m_teleport_options
);
1393 void Player::SetDeathState(DeathState s
)
1395 uint32 ressSpellId
= 0;
1397 bool cur
= isAlive();
1399 if (s
== JUST_DIED
&& cur
)
1401 // drunken state is cleared on death
1403 // lost combo points at any target (targeted combo points clear in Unit::SetDeathState)
1406 clearResurrectRequestData();
1408 // remove form before other mods to prevent incorrect stats calculation
1409 RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT
);
1411 // FIXME: is pet dismissed at dying or releasing spirit? if second, add SetDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
1412 RemovePet(PET_SAVE_REAGENTS
);
1414 // save value before aura remove in Unit::SetDeathState
1415 ressSpellId
= GetUInt32Value(PLAYER_SELF_RES_SPELL
);
1419 ressSpellId
= GetResurrectionSpellId();
1421 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
, 1);
1422 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH
, 1);
1423 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON
, 1);
1425 if (InstanceData
* mapInstance
= GetInstanceData())
1426 mapInstance
->OnPlayerDeath(this);
1429 Unit::SetDeathState(s
);
1431 // restore resurrection spell id for player after aura remove
1432 if (s
== JUST_DIED
&& cur
&& ressSpellId
)
1433 SetUInt32Value(PLAYER_SELF_RES_SPELL
, ressSpellId
);
1435 if (isAlive() && !cur
)
1437 // clear aura case after resurrection by another way (spells will be applied before next death)
1438 SetUInt32Value(PLAYER_SELF_RES_SPELL
, 0);
1440 // restore default warrior stance
1441 if (getClass() == CLASS_WARRIOR
)
1442 CastSpell(this, SPELL_ID_PASSIVE_BATTLE_STANCE
, true);
1446 bool Player::BuildEnumData(QueryResult
* result
, ByteBuffer
* data
, ByteBuffer
* buffer
)
1449 // "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
1450 // 8 9 10 11 12 13 14
1451 // "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
1452 // 15 16 17 18 19 20 21
1453 // "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.equipmentCache, characters.slot, character_declinedname.genitive "
1455 Field
* fields
= result
->Fetch();
1456 ObjectGuid guid
= ObjectGuid(HIGHGUID_PLAYER
, fields
[0].GetUInt32());
1457 uint8 pRace
= fields
[2].GetUInt8();
1458 uint8 pClass
= fields
[3].GetUInt8();
1460 PlayerInfo
const* info
= sObjectMgr
.GetPlayerInfo(pRace
, pClass
);
1463 sLog
.outError("Player %u has incorrect race/class pair. Don't build enum.", guid
);
1467 ObjectGuid guildGuid
= ObjectGuid(HIGHGUID_GUILD
, fields
[13].GetUInt32());
1468 std::string name
= fields
[1].GetCppString();
1469 uint8 gender
= fields
[4].GetUInt8();
1470 uint32 playerBytes
= fields
[5].GetUInt32();
1471 uint8 level
= fields
[7].GetUInt8();
1472 uint32 playerFlags
= fields
[14].GetUInt32();
1473 uint32 atLoginFlags
= fields
[15].GetUInt32();
1474 uint32 zone
= fields
[8].GetUInt32();
1475 uint32 petDisplayId
= 0;
1476 uint32 petLevel
= 0;
1477 uint32 petFamily
= 0;
1478 uint32 char_flags
= 0;
1480 data
->WriteGuidMask
<3>(guid
);
1481 data
->WriteGuidMask
<1, 7, 2>(guildGuid
);
1482 data
->WriteBits(name
.length(), 7);
1483 data
->WriteGuidMask
<4, 7>(guid
);
1484 data
->WriteGuidMask
<3>(guildGuid
);
1485 data
->WriteGuidMask
<5>(guid
);
1486 data
->WriteGuidMask
<6>(guildGuid
);
1487 data
->WriteGuidMask
<1>(guid
);
1488 data
->WriteGuidMask
<5, 4>(guildGuid
);
1489 data
->WriteBit(atLoginFlags
& AT_LOGIN_FIRST
);
1490 data
->WriteGuidMask
<0, 2, 6>(guid
);
1491 data
->WriteGuidMask
<0>(guildGuid
);
1493 // show pet at selection character in character list only for non-ghost character
1494 if (result
&& !(playerFlags
& PLAYER_FLAGS_GHOST
) && (pClass
== CLASS_WARLOCK
|| pClass
== CLASS_HUNTER
|| pClass
== CLASS_DEATH_KNIGHT
))
1496 uint32 entry
= fields
[16].GetUInt32();
1497 CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(entry
);
1500 petDisplayId
= fields
[17].GetUInt32();
1501 petLevel
= fields
[18].GetUInt32();
1502 petFamily
= cInfo
->family
;
1506 if(playerFlags
& PLAYER_FLAGS_HIDE_HELM
)
1507 char_flags
|= CHARACTER_FLAG_HIDE_HELM
;
1508 if(playerFlags
& PLAYER_FLAGS_HIDE_CLOAK
)
1509 char_flags
|= CHARACTER_FLAG_HIDE_CLOAK
;
1510 if(playerFlags
& PLAYER_FLAGS_GHOST
)
1511 char_flags
|= CHARACTER_FLAG_GHOST
;
1512 if(atLoginFlags
& AT_LOGIN_RENAME
)
1513 char_flags
|= CHARACTER_FLAG_RENAME
;
1514 if(sWorld
.getConfig(CONFIG_BOOL_DECLINED_NAMES_USED
))
1516 if(!fields
[21].GetCppString().empty())
1517 char_flags
|= CHARACTER_FLAG_DECLINED
;
1520 char_flags
|= CHARACTER_FLAG_DECLINED
;
1522 *buffer
<< uint8(pClass
); // class
1524 Tokens tdata
= StrSplit(fields
[19].GetCppString(), " ");
1525 for (uint8 slot
= 0; slot
< EQUIPMENT_SLOT_END
; ++slot
)
1527 uint32 visualbase
= slot
* 2;
1528 uint32 item_id
= GetUInt32ValueFromArray(tdata
, visualbase
);
1529 const ItemPrototype
* proto
= ObjectMgr::GetItemPrototype(item_id
);
1532 *buffer
<< uint8(0);
1533 *buffer
<< uint32(0);
1534 *buffer
<< uint32(0);
1538 SpellItemEnchantmentEntry
const *enchant
= NULL
;
1540 uint32 enchants
= GetUInt32ValueFromArray(tdata
, visualbase
+ 1);
1541 for(uint8 enchantSlot
= PERM_ENCHANTMENT_SLOT
; enchantSlot
<= TEMP_ENCHANTMENT_SLOT
; ++enchantSlot
)
1543 // values stored in 2 uint16
1544 uint32 enchantId
= 0x0000FFFF & (enchants
>> enchantSlot
*16);
1548 if ((enchant
= sSpellItemEnchantmentStore
.LookupEntry(enchantId
)))
1552 *buffer
<< uint8(proto
->InventoryType
);
1553 *buffer
<< uint32(proto
->DisplayInfoID
);
1554 *buffer
<< uint32(enchant
? enchant
->aura_id
: 0);
1557 for (int32 i
= 0; i
< 4; i
++)
1559 *buffer
<< uint8(0);
1560 *buffer
<< uint32(0);
1561 *buffer
<< uint32(0);
1564 *buffer
<< uint32(petFamily
); // Pet DisplayID
1565 buffer
->WriteGuidBytes
<2>(guildGuid
);
1566 *buffer
<< uint8(fields
[20].GetUInt8()); // char order id
1567 *buffer
<< uint8((playerBytes
>> 16) & 0xFF); // Hair style
1568 buffer
->WriteGuidBytes
<3>(guildGuid
);
1569 *buffer
<< uint32(petDisplayId
); // Pet DisplayID
1570 *buffer
<< uint32(char_flags
); // character flags
1571 *buffer
<< uint8((playerBytes
>> 24) & 0xFF); // Hair color
1572 buffer
->WriteGuidBytes
<4>(guid
);
1573 *buffer
<< uint32(fields
[9].GetUInt32()); // map
1574 buffer
->WriteGuidBytes
<5>(guildGuid
);
1575 *buffer
<< fields
[12].GetFloat(); // z
1576 buffer
->WriteGuidBytes
<6>(guildGuid
);
1577 *buffer
<< uint32(petLevel
); // pet level
1578 buffer
->WriteGuidBytes
<3>(guid
);
1579 *buffer
<< fields
[11].GetFloat(); // y
1580 // character customize flags
1581 *buffer
<< uint32(atLoginFlags
& AT_LOGIN_CUSTOMIZE
? CHAR_CUSTOMIZE_FLAG_CUSTOMIZE
: CHAR_CUSTOMIZE_FLAG_NONE
);
1583 uint32 playerBytes2
= fields
[6].GetUInt32();
1584 *buffer
<< uint8(playerBytes2
& 0xFF); // facial hair
1585 buffer
->WriteGuidBytes
<7>(guid
);
1586 *buffer
<< uint8(gender
); // Gender
1587 buffer
->append(name
.c_str(), name
.length());
1588 *buffer
<< uint8((playerBytes
>> 8) & 0xFF); // face
1590 buffer
->WriteGuidBytes
<0, 2>(guid
);
1591 buffer
->WriteGuidBytes
<1, 7>(guildGuid
);
1593 *buffer
<< fields
[10].GetFloat(); // x
1594 *buffer
<< uint8(playerBytes
& 0xFF); // skin
1595 *buffer
<< uint8(pRace
); // Race
1596 *buffer
<< uint8(level
); // Level
1597 buffer
->WriteGuidBytes
<6>(guid
);
1598 buffer
->WriteGuidBytes
<4, 0>(guildGuid
);
1599 buffer
->WriteGuidBytes
<5, 1>(guid
);
1601 *buffer
<< uint32(zone
); // Zone id
1606 void Player::ToggleAFK()
1608 ToggleFlag(PLAYER_FLAGS
, PLAYER_FLAGS_AFK
);
1610 // afk player not allowed in battleground
1611 if (isAFK() && InBattleGround() && !InArena())
1612 LeaveBattleground();
1615 void Player::ToggleDND()
1617 ToggleFlag(PLAYER_FLAGS
, PLAYER_FLAGS_DND
);
1620 uint8
Player::GetChatTag() const
1622 uint8 tag
= CHAT_TAG_NONE
;
1625 tag
|= CHAT_TAG_AFK
;
1627 tag
|= CHAT_TAG_DND
;
1630 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_COMMENTATOR
))
1631 tag
|= CHAT_TAG_COM
;
1632 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_DEVELOPER
))
1633 tag
|= CHAT_TAG_DEV
;
1638 void Player::SendTeleportPacket(float oldX
, float oldY
, float oldZ
, float oldO
)
1640 ObjectGuid guid
= GetObjectGuid();
1641 ObjectGuid transportGuid
= m_movementInfo
.GetTransportGuid();
1643 WorldPacket
data(SMSG_MOVE_TELEPORT
, 38);
1644 data
.WriteGuidMask
<6, 0, 3, 2>(guid
);
1645 data
.WriteBit(0); // unknown
1646 data
.WriteBit(!transportGuid
.IsEmpty());
1647 data
.WriteGuidMask
<1>(guid
);
1649 data
.WriteGuidMask
<1, 3, 2, 5, 0, 7, 6, 4>(transportGuid
);
1651 data
.WriteGuidMask
<4, 7, 5>(guid
);
1654 data
.WriteGuidBytes
<5, 6, 1, 7, 0, 2, 4, 3>(transportGuid
);
1656 data
<< uint32(0); // counter
1657 data
.WriteGuidBytes
<1, 2, 3, 5>(guid
);
1658 data
<< float(GetPositionX());
1659 data
.WriteGuidBytes
<4>(guid
);
1660 data
<< float(GetOrientation());
1661 data
.WriteGuidBytes
<7>(guid
);
1662 data
<< float(GetPositionZ());
1663 data
.WriteGuidBytes
<0, 6>(guid
);
1664 data
<< float(GetPositionY());
1666 Relocate(oldX
, oldY
, oldZ
, oldO
);
1667 SendDirectMessage(&data
);
1670 bool Player::TeleportTo(uint32 mapid
, float x
, float y
, float z
, float orientation
, uint32 options
/*=0*/, AreaTrigger
const* at
/*=NULL*/)
1672 orientation
= NormalizeOrientation(orientation
);
1674 if (!MapManager::IsValidMapCoord(mapid
, x
, y
, z
, orientation
))
1676 sLog
.outError("TeleportTo: invalid map %d or absent instance template.", mapid
);
1680 MapEntry
const* mEntry
= sMapStore
.LookupEntry(mapid
); // Validity checked in IsValidMapCoord
1682 // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
1683 Pet
* pet
= GetPet();
1685 // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)...
1686 // don't let gm level > 1 either
1687 if (!InBattleGround() && mEntry
->IsBattleGroundOrArena())
1690 // Get MapEntrance trigger if teleport to other -nonBG- map
1691 bool assignedAreaTrigger
= false;
1692 if (GetMapId() != mapid
&& !mEntry
->IsBattleGroundOrArena() && !at
)
1694 at
= sObjectMgr
.GetMapEntranceTrigger(mapid
);
1695 assignedAreaTrigger
= true;
1698 // Check requirements for teleport
1701 uint32 miscRequirement
= 0;
1702 AreaLockStatus lockStatus
= GetAreaTriggerLockStatus(at
, GetDifficulty(mEntry
->IsRaid()), miscRequirement
);
1703 if (lockStatus
!= AREA_LOCKSTATUS_OK
)
1705 // Teleport not requested by area-trigger
1706 // TODO - Assume a player with expansion 0 travels from BootyBay to Ratched, and he is attempted to be teleported to outlands
1707 // then he will repop near BootyBay instead of normally continuing his journey
1708 // This code is probably added to catch passengers on ships to northrend who shouldn't go there
1709 if (lockStatus
== AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION
&& !assignedAreaTrigger
&& GetTransport())
1710 RepopAtGraveyard(); // Teleport to near graveyard if on transport, looks blizz like :)
1712 SendTransferAbortedByLockStatus(mEntry
, lockStatus
, miscRequirement
);
1717 if (Group
* grp
= GetGroup()) // TODO: Verify that this is correct place
1718 grp
->SetPlayerMap(GetObjectGuid(), mapid
);
1720 // if we were on a transport, leave
1721 if (!(options
& TELE_TO_NOT_LEAVE_TRANSPORT
) && m_transport
)
1723 m_transport
->RemovePassenger(this);
1725 m_movementInfo
.ClearTransportData();
1728 // The player was ported to another map and looses the duel immediately.
1729 // We have to perform this check before the teleport, otherwise the
1730 // ObjectAccessor won't find the flag.
1731 if (duel
&& GetMapId() != mapid
)
1732 if (GetMap()->GetGameObject(GetGuidValue(PLAYER_DUEL_ARBITER
)))
1733 DuelComplete(DUEL_FLED
);
1735 // reset movement flags at teleport, because player will continue move with these flags after teleport
1736 m_movementInfo
.SetMovementFlags(MOVEFLAG_NONE
);
1739 if ((GetMapId() == mapid
) && (!m_transport
)) // TODO the !m_transport might have unexpected effects when teleporting from transport to other place on same map
1741 // lets reset far teleport flag if it wasn't reset during chained teleports
1742 SetSemaphoreTeleportFar(false);
1743 // setup delayed teleport flag
1744 // if teleport spell is casted in Unit::Update() func
1745 // then we need to delay it until update process will be finished
1746 if (SetDelayedTeleportFlagIfCan())
1748 SetSemaphoreTeleportNear(true);
1749 // lets save teleport destination for player
1750 m_teleport_dest
= WorldLocation(mapid
, x
, y
, z
, orientation
);
1751 m_teleport_options
= options
;
1755 if (!(options
& TELE_TO_NOT_UNSUMMON_PET
))
1757 // same map, only remove pet if out of range for new position
1758 if (pet
&& !pet
->IsWithinDist3d(x
, y
, z
, GetMap()->GetVisibilityDistance()))
1759 UnsummonPetTemporaryIfAny();
1762 if (!(options
& TELE_TO_NOT_LEAVE_COMBAT
))
1765 // this will be used instead of the current location in SaveToDB
1766 m_teleport_dest
= WorldLocation(mapid
, x
, y
, z
, orientation
);
1767 SetFallInformation(0, z
);
1769 // code for finish transfer called in WorldSession::HandleMovementOpcodes()
1770 // at client packet CMSG_MOVE_TELEPORT_ACK
1771 SetSemaphoreTeleportNear(true);
1772 // near teleport, triggering send CMSG_MOVE_TELEPORT_ACK from client at landing
1773 if (!GetSession()->PlayerLogout())
1775 float oldX
, oldY
, oldZ
;
1776 float oldO
= GetOrientation();
1777 GetPosition(oldX
, oldY
, oldZ
);;
1778 Relocate(x
, y
, z
, orientation
);
1779 SendTeleportPacket(oldX
, oldY
, oldZ
, oldO
);
1784 // far teleport to another map
1785 Map
* oldmap
= IsInWorld() ? GetMap() : NULL
;
1786 // check if we can enter before stopping combat / removing pet / totems / interrupting spells
1788 // If the map is not created, assume it is possible to enter it.
1789 // It will be created in the WorldPortAck.
1790 DungeonPersistentState
* state
= GetBoundInstanceSaveForSelfOrGroup(mapid
);
1791 Map
* map
= sMapMgr
.FindMap(mapid
, state
? state
->GetInstanceId() : 0);
1792 if (!map
|| map
->CanEnter(this))
1794 // lets reset near teleport flag if it wasn't reset during chained teleports
1795 SetSemaphoreTeleportNear(false);
1796 // setup delayed teleport flag
1797 // if teleport spell is casted in Unit::Update() func
1798 // then we need to delay it until update process will be finished
1799 if (SetDelayedTeleportFlagIfCan())
1801 SetSemaphoreTeleportFar(true);
1802 // lets save teleport destination for player
1803 m_teleport_dest
= WorldLocation(mapid
, x
, y
, z
, orientation
);
1804 m_teleport_options
= options
;
1808 SetSelectionGuid(ObjectGuid());
1812 ResetContestedPvP();
1814 // remove player from battleground on far teleport (when changing maps)
1815 if (BattleGround
const* bg
= GetBattleGround())
1817 // Note: at battleground join battleground id set before teleport
1818 // and we already will found "current" battleground
1819 // just need check that this is targeted map or leave
1820 if (bg
->GetMapId() != mapid
)
1821 LeaveBattleground(false); // don't teleport to entry point
1824 // remove pet on map change
1826 UnsummonPetTemporaryIfAny();
1828 // remove vehicle accessories on map change
1830 GetVehicleInfo()->RemoveAccessoriesFromMap();
1832 // remove all dyn objects
1833 RemoveAllDynObjects();
1835 // stop spellcasting
1836 // not attempt interrupt teleportation spell at caster teleport
1837 if (!(options
& TELE_TO_SPELL
))
1838 if (IsNonMeleeSpellCasted(true))
1839 InterruptNonMeleeSpells(true);
1841 // remove auras before removing from map...
1842 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP
| AURA_INTERRUPT_FLAG_MOVE
| AURA_INTERRUPT_FLAG_TURNING
);
1844 if (!GetSession()->PlayerLogout())
1846 // send transfer packet to display load screen
1847 WorldPacket
data(SMSG_TRANSFER_PENDING
, (4 + 4 + 4));
1848 data
.WriteBit(0); // unknown
1851 data
.WriteBit(1); // has transport
1852 data
<< uint32(GetMapId());
1853 data
<< uint32(m_transport
->GetEntry());
1856 data
.WriteBit(0); // has transport
1858 data
<< uint32(mapid
);
1859 GetSession()->SendPacket(&data
);
1862 // remove from old map now
1864 oldmap
->Remove(this, false);
1866 // new final coordinates
1870 float final_o
= orientation
;
1874 final_x
+= m_movementInfo
.GetTransportPos()->x
;
1875 final_y
+= m_movementInfo
.GetTransportPos()->y
;
1876 final_z
+= m_movementInfo
.GetTransportPos()->z
;
1877 final_o
+= m_movementInfo
.GetTransportPos()->o
;
1880 m_teleport_dest
= WorldLocation(mapid
, final_x
, final_y
, final_z
, final_o
);
1881 SetFallInformation(0, final_z
);
1882 // if the player is saved before worldport ack (at logout for example)
1883 // this will be used instead of the current location in SaveToDB
1885 // move packet sent by client always after far teleport
1886 // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
1887 SetSemaphoreTeleportFar(true);
1889 if (!GetSession()->PlayerLogout())
1891 // transfer finished, inform client to start load
1892 WorldPacket
data(SMSG_NEW_WORLD
, 20);
1895 data
<< float(m_movementInfo
.GetTransportPos()->x
);
1896 data
<< float(m_movementInfo
.GetTransportPos()->o
);
1897 data
<< float(m_movementInfo
.GetTransportPos()->z
);
1901 data
<< float(final_x
);
1902 data
<< float(final_o
);
1903 data
<< float(final_z
);
1906 data
<< uint32(mapid
);
1909 data
<< float(m_movementInfo
.GetTransportPos()->y
);
1911 data
<< float(final_y
);
1913 GetSession()->SendPacket(&data
);
1914 SendSavedInstances();
1917 else // !map->CanEnter(this)
1923 bool Player::TeleportToBGEntryPoint()
1925 ScheduleDelayedOperation(DELAYED_BG_MOUNT_RESTORE
);
1926 ScheduleDelayedOperation(DELAYED_BG_TAXI_RESTORE
);
1927 return TeleportTo(m_bgData
.joinPos
);
1930 void Player::ProcessDelayedOperations()
1932 if (m_DelayedOperations
== 0)
1935 if (m_DelayedOperations
& DELAYED_RESURRECT_PLAYER
)
1937 ResurrectPlayer(0.0f
, false);
1939 if (GetMaxHealth() > m_resurrectHealth
)
1940 SetHealth(m_resurrectHealth
);
1942 SetHealth(GetMaxHealth());
1944 if (GetMaxPower(POWER_MANA
) > m_resurrectMana
)
1945 SetPower(POWER_MANA
, m_resurrectMana
);
1947 SetPower(POWER_MANA
, GetMaxPower(POWER_MANA
));
1949 SetPower(POWER_RAGE
, 0);
1950 SetPower(POWER_ENERGY
, GetMaxPower(POWER_ENERGY
));
1955 if (m_DelayedOperations
& DELAYED_SAVE_PLAYER
)
1960 if (m_DelayedOperations
& DELAYED_SPELL_CAST_DESERTER
)
1962 CastSpell(this, 26013, true); // Deserter
1965 if (m_DelayedOperations
& DELAYED_BG_MOUNT_RESTORE
)
1967 if (m_bgData
.mountSpell
)
1969 CastSpell(this, m_bgData
.mountSpell
, true);
1970 m_bgData
.mountSpell
= 0;
1974 if (m_DelayedOperations
& DELAYED_BG_TAXI_RESTORE
)
1976 if (m_bgData
.HasTaxiPath())
1978 m_taxi
.AddTaxiDestination(m_bgData
.taxiPath
[0]);
1979 m_taxi
.AddTaxiDestination(m_bgData
.taxiPath
[1]);
1980 m_bgData
.ClearTaxiPath();
1982 ContinueTaxiFlight();
1986 // we have executed ALL delayed ops, so clear the flag
1987 m_DelayedOperations
= 0;
1990 void Player::AddToWorld()
1992 ///- Do not add/remove the player from the object storage
1993 ///- It will crash when updating the ObjectAccessor
1994 ///- The player should only be added when logging in
1997 for (int i
= PLAYER_SLOT_START
; i
< PLAYER_SLOT_END
; ++i
)
2000 m_items
[i
]->AddToWorld();
2004 void Player::RemoveFromWorld()
2006 for (int i
= PLAYER_SLOT_START
; i
< PLAYER_SLOT_END
; ++i
)
2009 m_items
[i
]->RemoveFromWorld();
2012 ///- Do not add/remove the player from the object storage
2013 ///- It will crash when updating the ObjectAccessor
2014 ///- The player should only be removed when logging out
2016 GetCamera().ResetView();
2018 Unit::RemoveFromWorld();
2021 void Player::RewardRage(uint32 damage
, uint32 weaponSpeedHitFactor
, bool attacker
)
2025 float rageconversion
= float((0.0091107836 * getLevel() * getLevel()) + 3.225598133 * getLevel()) + 4.2652911f
;
2029 addRage
= ((damage
/ rageconversion
* 7.5f
+ weaponSpeedHitFactor
) / 2.0f
);
2031 // talent who gave more rage on attack
2032 addRage
*= 1.0f
+ GetTotalAuraModifier(SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT
) / 100.0f
;
2036 addRage
= damage
/ rageconversion
* 2.5f
;
2038 // Berserker Rage effect
2039 if (HasAura(18499, EFFECT_INDEX_0
))
2043 addRage
*= sWorld
.getConfig(CONFIG_FLOAT_RATE_POWER_RAGE_INCOME
);
2045 ModifyPower(POWER_RAGE
, uint32(addRage
* 10));
2048 void Player::RegenerateAll(uint32 diff
)
2050 // Not in combat or they have regeneration
2051 if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT
) ||
2052 HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT
) || IsPolymorphed() || m_baseHealthRegen
)
2054 RegenerateHealth(diff
);
2055 if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN
))
2057 Regenerate(POWER_RAGE
, diff
);
2058 if (getClass() == CLASS_DEATH_KNIGHT
)
2059 Regenerate(POWER_RUNIC_POWER
, diff
);
2063 Regenerate(POWER_ENERGY
, diff
);
2065 Regenerate(POWER_MANA
, diff
);
2067 if (getClass() == CLASS_DEATH_KNIGHT
)
2068 Regenerate(POWER_RUNE
, diff
);
2070 if (getClass() == CLASS_HUNTER
)
2071 Regenerate(POWER_FOCUS
, diff
);
2073 if (getClass() == CLASS_PALADIN
)
2076 ResetHolyPowerRegenTimer();
2077 else if (m_holyPowerRegenTimer
<= diff
)
2078 m_holyPowerRegenTimer
= 0;
2080 m_holyPowerRegenTimer
-= diff
;
2082 if (!m_holyPowerRegenTimer
)
2084 Regenerate(POWER_HOLY_POWER
, diff
);
2085 ResetHolyPowerRegenTimer();
2089 m_regenTimer
= REGEN_TIME_FULL
;
2092 // diff contains the time in milliseconds since last regen.
2093 void Player::Regenerate(Powers power
, uint32 diff
)
2095 uint32 powerIndex
= GetPowerIndex(power
);
2096 if (powerIndex
== INVALID_POWER_INDEX
)
2099 uint32 maxValue
= GetMaxPowerByIndex(powerIndex
);
2103 uint32 curValue
= GetPowerByIndex(powerIndex
);
2105 float addvalue
= 0.0f
;
2111 if (HasAuraType(SPELL_AURA_STOP_NATURAL_MANA_REGEN
))
2113 float ManaIncreaseRate
= sWorld
.getConfig(CONFIG_FLOAT_RATE_POWER_MANA
);
2117 // Mangos Updates Mana in intervals of 2s, which is correct
2118 addvalue
= GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER
) * ManaIncreaseRate
* 2.00f
;
2122 addvalue
= GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER
) * ManaIncreaseRate
* 2.00f
;
2126 case POWER_RAGE
: // Regenerate rage
2128 float RageDecreaseRate
= sWorld
.getConfig(CONFIG_FLOAT_RATE_POWER_RAGE_LOSS
);
2129 addvalue
= 20 * RageDecreaseRate
; // 2 rage by tick (= 2 seconds => 1 rage/sec)
2135 case POWER_HOLY_POWER
:
2136 if (!m_holyPowerRegenTimer
)
2141 case POWER_ENERGY
: // Regenerate energy
2143 float EnergyRate
= sWorld
.getConfig(CONFIG_FLOAT_RATE_POWER_ENERGY
);
2144 addvalue
= 20 * EnergyRate
;
2147 case POWER_RUNIC_POWER
:
2149 float RunicPowerDecreaseRate
= sWorld
.getConfig(CONFIG_FLOAT_RATE_POWER_RUNICPOWER_LOSS
);
2150 addvalue
= 30 * RunicPowerDecreaseRate
; // 3 RunicPower by tick
2155 if (getClass() != CLASS_DEATH_KNIGHT
)
2158 for (uint32 rune
= 0; rune
< MAX_RUNES
; ++rune
)
2160 if (uint16 cd
= GetRuneCooldown(rune
)) // if we have cooldown, reduce it...
2162 uint32 cd_diff
= diff
;
2163 AuraList
const& ModPowerRegenPCTAuras
= GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT
);
2164 for (AuraList::const_iterator i
= ModPowerRegenPCTAuras
.begin(); i
!= ModPowerRegenPCTAuras
.end(); ++i
)
2165 if ((*i
)->GetModifier()->m_miscvalue
== int32(power
) && (*i
)->GetMiscBValue() == GetCurrentRune(rune
))
2166 cd_diff
= cd_diff
* ((*i
)->GetModifier()->m_amount
+ 100) / 100;
2168 SetRuneCooldown(rune
, (cd
< cd_diff
) ? 0 : cd
- cd_diff
);
2177 // Mana regen calculated in Player::UpdateManaRegen()
2178 // Exist only for POWER_MANA, POWER_ENERGY, POWER_FOCUS auras
2179 if (power
!= POWER_MANA
)
2181 AuraList
const& ModPowerRegenPCTAuras
= GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT
);
2182 for (AuraList::const_iterator i
= ModPowerRegenPCTAuras
.begin(); i
!= ModPowerRegenPCTAuras
.end(); ++i
)
2183 if ((*i
)->GetModifier()->m_miscvalue
== int32(power
))
2184 addvalue
*= ((*i
)->GetModifier()->m_amount
+ 100) / 100.0f
;
2187 // addvalue computed on a 2sec basis. => update to diff time
2188 addvalue
*= float(diff
) / REGEN_TIME_FULL
;
2190 if (power
!= POWER_RAGE
&& power
!= POWER_RUNIC_POWER
&& power
!= POWER_HOLY_POWER
)
2192 curValue
+= uint32(addvalue
);
2193 if (curValue
> maxValue
)
2194 curValue
= maxValue
;
2198 if (curValue
<= uint32(addvalue
))
2201 curValue
-= uint32(addvalue
);
2203 SetPower(power
, curValue
);
2206 void Player::RegenerateHealth(uint32 diff
)
2208 uint32 curValue
= GetHealth();
2209 uint32 maxValue
= GetMaxHealth();
2211 if (curValue
>= maxValue
) return;
2213 float HealthIncreaseRate
= sWorld
.getConfig(CONFIG_FLOAT_RATE_HEALTH
);
2215 float addvalue
= 0.0f
;
2218 if (IsPolymorphed())
2219 addvalue
= (float)GetMaxHealth() / 3;
2220 // normal regen case (maybe partly in combat case)
2221 else if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT
))
2223 addvalue
= HealthIncreaseRate
;
2226 if (getLevel() < 15)
2227 addvalue
= 0.20f
* GetMaxHealth() * addvalue
/ getLevel();
2229 addvalue
= 0.015f
* GetMaxHealth() * addvalue
;
2231 AuraList
const& mModHealthRegenPct
= GetAurasByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT
);
2232 for (AuraList::const_iterator i
= mModHealthRegenPct
.begin(); i
!= mModHealthRegenPct
.end(); ++i
)
2233 addvalue
*= (100.0f
+ (*i
)->GetModifier()->m_amount
) / 100.0f
;
2235 addvalue
+= GetTotalAuraModifier(SPELL_AURA_MOD_REGEN
) * 2.0f
/ 5.0f
;
2237 else if (HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT
))
2238 addvalue
*= GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT
) / 100.0f
;
2240 if (!IsStandState())
2244 // always regeneration bonus (including combat)
2245 addvalue
+= GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT
);
2247 addvalue
+= m_baseHealthRegen
/ 2.5f
;
2252 addvalue
*= (float)diff
/ REGEN_TIME_FULL
;
2254 ModifyHealth(int32(addvalue
));
2257 Creature
* Player::GetNPCIfCanInteractWith(ObjectGuid guid
, uint32 npcflagmask
)
2259 // some basic checks
2260 if (!guid
|| !IsInWorld() || IsTaxiFlying())
2263 // not in interactive state
2264 if (hasUnitState(UNIT_STAT_CAN_NOT_REACT_OR_LOST_CONTROL
))
2267 // exist (we need look pets also for some interaction (quest/etc)
2268 Creature
* unit
= GetMap()->GetAnyTypeCreature(guid
);
2272 // appropriate npc type
2273 if (npcflagmask
&& !unit
->HasFlag(UNIT_NPC_FLAGS
, npcflagmask
))
2276 if (npcflagmask
== UNIT_NPC_FLAG_STABLEMASTER
)
2278 if (getClass() != CLASS_HUNTER
)
2282 // if a dead unit should be able to talk - the creature must be alive and have special flags
2283 if (!unit
->isAlive())
2286 if (isAlive() && unit
->isInvisibleForAlive())
2289 // not allow interaction under control, but allow with own pets
2290 if (unit
->GetCharmerGuid())
2294 if (unit
->IsHostileTo(this))
2298 if (!unit
->IsWithinDistInMap(this, INTERACTION_DISTANCE
))
2304 GameObject
* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid
, uint32 gameobject_type
) const
2306 // some basic checks
2307 if (!guid
|| !IsInWorld() || IsTaxiFlying())
2310 // not in interactive state
2311 if (hasUnitState(UNIT_STAT_CAN_NOT_REACT_OR_LOST_CONTROL
))
2314 if (GameObject
* go
= GetMap()->GetGameObject(guid
))
2316 if (uint32(go
->GetGoType()) == gameobject_type
|| gameobject_type
== MAX_GAMEOBJECT_TYPE
)
2319 switch (go
->GetGoType())
2321 // TODO: find out how the client calculates the maximal usage distance to spellless working
2322 // gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary choosen number
2323 case GAMEOBJECT_TYPE_GUILD_BANK
:
2324 case GAMEOBJECT_TYPE_MAILBOX
:
2327 case GAMEOBJECT_TYPE_FISHINGHOLE
:
2328 maxdist
= 20.0f
+ CONTACT_DISTANCE
; // max spell range
2331 maxdist
= INTERACTION_DISTANCE
;
2335 if (go
->IsWithinDistInMap(this, maxdist
) && go
->isSpawned())
2338 sLog
.outError("GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal %f is allowed)",
2339 go
->GetGOInfo()->name
, go
->GetGUIDLow(), GetName(), GetGUIDLow(), go
->GetDistance(this), maxdist
);
2345 bool Player::IsUnderWater() const
2347 return GetTerrain()->IsUnderWater(GetPositionX(), GetPositionY(), GetPositionZ() + 2);
2350 void Player::SetInWater(bool apply
)
2352 if (m_isInWater
== apply
)
2355 // define player in water by opcodes
2356 // move player's guid into HateOfflineList of those mobs
2357 // which can't swim and move guid back into ThreatList when
2359 // TODO: exist also swimming mobs, and function must be symmetric to enter/leave water
2360 m_isInWater
= apply
;
2362 // remove auras that need water/land
2363 RemoveAurasWithInterruptFlags(apply
? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER
: AURA_INTERRUPT_FLAG_NOT_UNDERWATER
);
2365 getHostileRefManager().updateThreatTables();
2368 struct SetGameMasterOnHelper
2370 explicit SetGameMasterOnHelper() {}
2371 void operator()(Unit
* unit
) const
2373 unit
->setFaction(35);
2374 unit
->getHostileRefManager().setOnlineOfflineState(false);
2378 struct SetGameMasterOffHelper
2380 explicit SetGameMasterOffHelper(uint32 _faction
) : faction(_faction
) {}
2381 void operator()(Unit
* unit
) const
2383 unit
->setFaction(faction
);
2384 unit
->getHostileRefManager().setOnlineOfflineState(true);
2389 void Player::SetGameMaster(bool on
)
2393 m_ExtraFlags
|= PLAYER_EXTRA_GM_ON
;
2395 SetFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GM
);
2397 CallForAllControlledUnits(SetGameMasterOnHelper(), CONTROLLED_PET
| CONTROLLED_TOTEMS
| CONTROLLED_GUARDIANS
| CONTROLLED_CHARM
);
2400 ResetContestedPvP();
2402 getHostileRefManager().setOnlineOfflineState(false);
2403 CombatStopWithPets();
2405 SetPhaseMask(PHASEMASK_ANYWHERE
, false); // see and visible in all phases
2409 m_ExtraFlags
&= ~ PLAYER_EXTRA_GM_ON
;
2410 setFactionForRace(getRace());
2411 RemoveFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GM
);
2414 AuraList
const& phases
= GetAurasByType(SPELL_AURA_PHASE
);
2415 SetPhaseMask(!phases
.empty() ? phases
.front()->GetMiscValue() : uint32(PHASEMASK_NORMAL
), false);
2417 CallForAllControlledUnits(SetGameMasterOffHelper(getFaction()), CONTROLLED_PET
| CONTROLLED_TOTEMS
| CONTROLLED_GUARDIANS
| CONTROLLED_CHARM
);
2419 // restore FFA PvP Server state
2420 if (sWorld
.IsFFAPvPRealm())
2423 // restore FFA PvP area state, remove not allowed for GM mounts
2424 UpdateArea(m_areaUpdateId
);
2426 getHostileRefManager().setOnlineOfflineState(true);
2429 m_camera
.UpdateVisibilityForOwner();
2430 UpdateObjectVisibility();
2431 UpdateForQuestWorldObjects();
2434 void Player::SetGMVisible(bool on
)
2438 m_ExtraFlags
&= ~PLAYER_EXTRA_GM_INVISIBLE
; // remove flag
2440 // Reapply stealth/invisibility if active or show if not any
2441 if (HasAuraType(SPELL_AURA_MOD_STEALTH
))
2442 SetVisibility(VISIBILITY_GROUP_STEALTH
);
2443 else if (HasAuraType(SPELL_AURA_MOD_INVISIBILITY
))
2444 SetVisibility(VISIBILITY_GROUP_INVISIBILITY
);
2446 SetVisibility(VISIBILITY_ON
);
2450 m_ExtraFlags
|= PLAYER_EXTRA_GM_INVISIBLE
; // add flag
2452 SetAcceptWhispers(false);
2453 SetGameMaster(true);
2455 SetVisibility(VISIBILITY_OFF
);
2459 bool Player::IsGroupVisibleFor(Player
* p
) const
2461 switch (sWorld
.getConfig(CONFIG_UINT32_GROUP_VISIBILITY
))
2463 default: return IsInSameGroupWith(p
);
2464 case 1: return IsInSameRaidWith(p
);
2465 case 2: return GetTeam() == p
->GetTeam();
2469 bool Player::IsInSameGroupWith(Player
const* p
) const
2471 return (p
== this || (GetGroup() != NULL
&&
2472 GetGroup()->SameSubGroup(this, p
)));
2475 ///- If the player is invited, remove him. If the group if then only 1 person, disband the group.
2476 /// \todo Shouldn't we also check if there is no other invitees before disbanding the group?
2477 void Player::UninviteFromGroup()
2479 Group
* group
= GetGroupInvite();
2483 group
->RemoveInvite(this);
2485 if (group
->GetMembersCount() <= 1) // group has just 1 member => disband
2487 if (group
->IsCreated())
2489 group
->Disband(true);
2490 sObjectMgr
.RemoveGroup(group
);
2493 group
->RemoveAllInvites();
2499 void Player::RemoveFromGroup(Group
* group
, ObjectGuid guid
)
2503 if (group
->RemoveMember(guid
, 0) <= 1)
2505 // group->Disband(); already disbanded in RemoveMember
2506 sObjectMgr
.RemoveGroup(group
);
2508 // removemember sets the player's group pointer to NULL
2513 void Player::SendLogXPGain(uint32 GivenXP
, Unit
* victim
, uint32 RestXP
)
2515 WorldPacket
data(SMSG_LOG_XPGAIN
, 21);
2516 data
<< (victim
? victim
->GetObjectGuid() : ObjectGuid());// guid
2517 data
<< uint32(GivenXP
+ RestXP
); // given experience
2518 data
<< uint8(victim
? 0 : 1); // 00-kill_xp type, 01-non_kill_xp type
2521 data
<< uint32(GivenXP
); // experience without rested bonus
2522 data
<< float(1); // 1 - none 0 - 100% group bonus output
2524 data
<< uint8(0); // new 2.4.0
2525 GetSession()->SendPacket(&data
);
2528 void Player::GiveXP(uint32 xp
, Unit
* victim
)
2536 uint32 level
= getLevel();
2538 // XP to money conversion processed in Player::RewardQuest
2539 if (level
>= sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
2544 // handle SPELL_AURA_MOD_KILL_XP_PCT auras
2545 Unit::AuraList
const& ModXPPctAuras
= GetAurasByType(SPELL_AURA_MOD_KILL_XP_PCT
);
2546 for (Unit::AuraList::const_iterator i
= ModXPPctAuras
.begin(); i
!= ModXPPctAuras
.end(); ++i
)
2547 xp
= uint32(xp
* (1.0f
+ (*i
)->GetModifier()->m_amount
/ 100.0f
));
2551 // handle SPELL_AURA_MOD_QUEST_XP_PCT auras
2552 Unit::AuraList
const& ModXPPctAuras
= GetAurasByType(SPELL_AURA_MOD_QUEST_XP_PCT
);
2553 for (Unit::AuraList::const_iterator i
= ModXPPctAuras
.begin(); i
!= ModXPPctAuras
.end(); ++i
)
2554 xp
= uint32(xp
* (1.0f
+ (*i
)->GetModifier()->m_amount
/ 100.0f
));
2557 // XP resting bonus for kill
2558 uint32 rested_bonus_xp
= victim
? GetXPRestBonus(xp
) : 0;
2560 SendLogXPGain(xp
, victim
, rested_bonus_xp
);
2562 uint32 curXP
= GetUInt32Value(PLAYER_XP
);
2563 uint32 nextLvlXP
= GetUInt32Value(PLAYER_NEXT_LEVEL_XP
);
2564 uint32 newXP
= curXP
+ xp
+ rested_bonus_xp
;
2566 while (newXP
>= nextLvlXP
&& level
< sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
2570 if (level
< sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
2571 GiveLevel(level
+ 1);
2574 nextLvlXP
= GetUInt32Value(PLAYER_NEXT_LEVEL_XP
);
2577 SetUInt32Value(PLAYER_XP
, newXP
);
2580 // Update player to next level
2581 // Current player experience not update (must be update by caller)
2582 void Player::GiveLevel(uint32 level
)
2584 if (level
== getLevel())
2587 PlayerLevelInfo info
;
2588 sObjectMgr
.GetPlayerLevelInfo(getRace(), getClass(), level
, &info
);
2590 uint32 basehp
= 0, basemana
= 0;
2591 sObjectMgr
.GetPlayerClassLevelInfo(getClass(), level
, basehp
, basemana
);
2593 // send levelup info to client
2594 WorldPacket
data(SMSG_LEVELUP_INFO
, (4 + 4 + MAX_STORED_POWERS
* 4 + MAX_STATS
* 4));
2595 data
<< uint32(level
);
2596 data
<< uint32(int32(basehp
) - int32(GetCreateHealth()));
2597 // for(int i = 0; i < MAX_POWERS; ++i) // Powers loop (0-4)
2598 data
<< uint32(int32(basemana
) - int32(GetCreateMana()));
2604 for (int i
= STAT_STRENGTH
; i
< MAX_STATS
; ++i
) // Stats loop (0-4)
2605 data
<< uint32(int32(info
.stats
[i
]) - GetCreateStat(Stats(i
)));
2607 GetSession()->SendPacket(&data
);
2609 SetUInt32Value(PLAYER_NEXT_LEVEL_XP
, sObjectMgr
.GetXPForLevel(level
));
2611 // update level, max level of skills
2612 m_Played_time
[PLAYED_TIME_LEVEL
] = 0; // Level Played Time reset
2614 _ApplyAllLevelScaleItemMods(false);
2618 UpdateSkillsForLevel();
2620 // save base values (bonuses already included in stored stats
2621 for (int i
= STAT_STRENGTH
; i
< MAX_STATS
; ++i
)
2622 SetCreateStat(Stats(i
), info
.stats
[i
]);
2624 SetCreateHealth(basehp
);
2625 SetCreateMana(basemana
);
2627 InitTalentForLevel();
2628 InitTaxiNodesForLevel();
2629 InitGlyphsForLevel();
2633 // set current level health and mana/energy to maximum after applying all mods.
2635 SetHealth(GetMaxHealth());
2636 SetPower(POWER_MANA
, GetMaxPower(POWER_MANA
));
2637 SetPower(POWER_ENERGY
, GetMaxPower(POWER_ENERGY
));
2638 if (GetPower(POWER_RAGE
) > GetMaxPower(POWER_RAGE
))
2639 SetPower(POWER_RAGE
, GetMaxPower(POWER_RAGE
));
2640 SetPower(POWER_FOCUS
, 0);
2642 _ApplyAllLevelScaleItemMods(true);
2644 // update level to hunter/summon pet
2645 if (Pet
* pet
= GetPet())
2646 pet
->SynchronizeLevelWithOwner();
2648 if (MailLevelReward
const* mailReward
= sObjectMgr
.GetMailLevelReward(level
, getRaceMask()))
2649 MailDraft(mailReward
->mailTemplateId
).SendMailTo(this, MailSender(MAIL_CREATURE
, mailReward
->senderEntry
));
2651 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
);
2654 void Player::UpdateFreeTalentPoints(bool resetIfNeed
)
2656 uint32 level
= getLevel();
2657 // talents base at level diff ( talents = level - 9 but some can be used already)
2660 // Remove all talent points
2661 if (m_usedTalentCount
> 0) // Free any used talents
2665 SetFreeTalentPoints(0);
2670 if (m_specsCount
== 0)
2676 uint32 talentPointsForLevel
= CalculateTalentsPoints();
2678 // if used more that have then reset
2679 if (m_usedTalentCount
> talentPointsForLevel
)
2681 if (resetIfNeed
&& GetSession()->GetSecurity() < SEC_ADMINISTRATOR
)
2684 SetFreeTalentPoints(0);
2686 // else update amount of free points
2688 SetFreeTalentPoints(talentPointsForLevel
- m_usedTalentCount
);
2692 void Player::InitTalentForLevel()
2694 UpdateFreeTalentPoints();
2696 if (!GetSession()->PlayerLoading())
2697 SendTalentsInfoData(false); // update at client
2700 void Player::InitStatsForLevel(bool reapplyMods
)
2702 if (reapplyMods
) // reapply stats values only on .reset stats (level) command
2703 _RemoveAllStatBonuses();
2705 uint32 basehp
= 0, basemana
= 0;
2706 sObjectMgr
.GetPlayerClassLevelInfo(getClass(), getLevel(), basehp
, basemana
);
2708 PlayerLevelInfo info
;
2709 sObjectMgr
.GetPlayerLevelInfo(getRace(), getClass(), getLevel(), &info
);
2711 SetUInt32Value(PLAYER_FIELD_MAX_LEVEL
, sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
));
2712 SetUInt32Value(PLAYER_NEXT_LEVEL_XP
, sObjectMgr
.GetXPForLevel(getLevel()));
2714 // reset before any aura state sources (health set/aura apply)
2715 SetUInt32Value(UNIT_FIELD_AURASTATE
, 0);
2717 UpdateSkillsForLevel();
2719 // set default cast time multiplier
2720 SetFloatValue(UNIT_MOD_CAST_SPEED
, 1.0f
);
2722 // save base values (bonuses already included in stored stats
2723 for (int i
= STAT_STRENGTH
; i
< MAX_STATS
; ++i
)
2724 SetCreateStat(Stats(i
), info
.stats
[i
]);
2726 for (int i
= STAT_STRENGTH
; i
< MAX_STATS
; ++i
)
2727 SetStat(Stats(i
), info
.stats
[i
]);
2729 SetCreateHealth(basehp
);
2731 // set create powers
2732 SetCreateMana(basemana
);
2734 SetArmor(int32(m_createStats
[STAT_AGILITY
] * 2));
2738 // reset rating fields values
2739 for (uint16 index
= PLAYER_FIELD_COMBAT_RATING_1
; index
< PLAYER_FIELD_COMBAT_RATING_1
+ MAX_COMBAT_RATING
; ++index
)
2740 SetUInt32Value(index
, 0);
2742 SetUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS
, 0);
2743 SetFloatValue(PLAYER_FIELD_MOD_HEALING_PCT
, 1.0f
);
2744 SetFloatValue(PLAYER_FIELD_MOD_HEALING_DONE_PCT
, 1.0f
);
2745 for (int i
= 0; i
< MAX_SPELL_SCHOOL
; ++i
)
2747 SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG
+ i
, 0);
2748 SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS
+ i
, 0);
2749 SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT
+ i
, 1.00f
);
2752 SetFloatValue(PLAYER_FIELD_MOD_SPELL_POWER_PCT
, 1.0f
);
2754 // reset attack power, damage and attack speed fields
2755 SetFloatValue(UNIT_FIELD_BASEATTACKTIME
, 2000.0f
);
2756 SetFloatValue(UNIT_FIELD_BASEATTACKTIME
+ 1, 2000.0f
); // offhand attack time
2757 SetFloatValue(UNIT_FIELD_RANGEDATTACKTIME
, 2000.0f
);
2759 SetFloatValue(UNIT_FIELD_MINDAMAGE
, 0.0f
);
2760 SetFloatValue(UNIT_FIELD_MAXDAMAGE
, 0.0f
);
2761 SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE
, 0.0f
);
2762 SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE
, 0.0f
);
2763 SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE
, 0.0f
);
2764 SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE
, 0.0f
);
2765 SetFloatValue(PLAYER_FIELD_WEAPON_DMG_MULTIPLIERS
, 1.0f
);
2767 SetInt32Value(UNIT_FIELD_ATTACK_POWER
, 0 );
2768 SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_POS
, 0 );
2769 SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_NEG
, 0 );
2770 SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER
,0.0f
);
2771 SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER
, 0 );
2772 SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MOD_POS
,0 );
2773 SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MOD_NEG
,0 );
2774 SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER
,0.0f
);
2776 // Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
2777 SetFloatValue(PLAYER_CRIT_PERCENTAGE
, 0.0f
);
2778 SetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE
, 0.0f
);
2779 SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE
, 0.0f
);
2781 // Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
2782 for (uint8 i
= 0; i
< MAX_SPELL_SCHOOL
; ++i
)
2783 SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1
+ i
, 0.0f
);
2785 SetFloatValue(PLAYER_PARRY_PERCENTAGE
, 0.0f
);
2786 SetFloatValue(PLAYER_BLOCK_PERCENTAGE
, 0.0f
);
2787 SetUInt32Value(PLAYER_SHIELD_BLOCK
, uint32(BASE_BLOCK_DAMAGE_PERCENT
));
2790 SetFloatValue(PLAYER_DODGE_PERCENTAGE
, 0.0f
);
2792 // set armor (resistance 0) to original value (create_agility*2)
2793 SetArmor(int32(m_createStats
[STAT_AGILITY
] * 2));
2794 SetResistanceBuffMods(SpellSchools(0), true, 0.0f
);
2795 SetResistanceBuffMods(SpellSchools(0), false, 0.0f
);
2796 // set other resistance to original value (0)
2797 for (int i
= 1; i
< MAX_SPELL_SCHOOL
; ++i
)
2799 SetResistance(SpellSchools(i
), 0);
2800 SetResistanceBuffMods(SpellSchools(i
), true, 0.0f
);
2801 SetResistanceBuffMods(SpellSchools(i
), false, 0.0f
);
2804 SetUInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE
, 0);
2805 SetUInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE
, 0);
2806 for (int i
= 0; i
< MAX_SPELL_SCHOOL
; ++i
)
2808 SetUInt32Value(UNIT_FIELD_POWER_COST_MODIFIER
+ i
, 0);
2809 SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER
+ i
, 0.0f
);
2811 // Reset no reagent cost field
2812 for (int i
= 0; i
< 3; ++i
)
2813 SetUInt32Value(PLAYER_NO_REAGENT_COST_1
+ i
, 0);
2814 // Init data for form but skip reapply item mods for form
2815 InitDataForForm(reapplyMods
);
2818 for (int i
= POWER_MANA
; i
< MAX_POWERS
; ++i
)
2819 SetMaxPower(Powers(i
), GetCreateMaxPowers(Powers(i
)));
2821 SetMaxHealth(basehp
); // stamina bonus will applied later
2823 // cleanup mounted state (it will set correctly at aura loading if player saved at mount.
2824 SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID
, 0);
2826 // cleanup unit flags (will be re-applied if need at aura load).
2827 RemoveFlag(UNIT_FIELD_FLAGS
,
2828 UNIT_FLAG_NON_ATTACKABLE
| UNIT_FLAG_DISABLE_MOVE
| UNIT_FLAG_NOT_ATTACKABLE_1
|
2829 UNIT_FLAG_OOC_NOT_ATTACKABLE
| UNIT_FLAG_PASSIVE
| UNIT_FLAG_LOOTING
|
2830 UNIT_FLAG_PET_IN_COMBAT
| UNIT_FLAG_SILENCED
| UNIT_FLAG_PACIFIED
|
2831 UNIT_FLAG_STUNNED
| UNIT_FLAG_IN_COMBAT
| UNIT_FLAG_DISARMED
|
2832 UNIT_FLAG_CONFUSED
| UNIT_FLAG_FLEEING
| UNIT_FLAG_NOT_SELECTABLE
|
2833 UNIT_FLAG_SKINNABLE
| UNIT_FLAG_MOUNT
| UNIT_FLAG_TAXI_FLIGHT
);
2834 SetFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_PVP_ATTACKABLE
); // must be set
2836 SetFlag(UNIT_FIELD_FLAGS_2
, UNIT_FLAG2_REGENERATE_POWER
); // must be set
2838 // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example.
2839 RemoveFlag(PLAYER_FLAGS
, PLAYER_FLAGS_AFK
| PLAYER_FLAGS_DND
| PLAYER_FLAGS_GM
| PLAYER_FLAGS_GHOST
);
2841 RemoveStandFlags(UNIT_STAND_FLAGS_ALL
); // one form stealth modified bytes
2842 RemoveByteFlag(UNIT_FIELD_BYTES_2
, 1, UNIT_BYTE2_FLAG_FFA_PVP
| UNIT_BYTE2_FLAG_SANCTUARY
);
2844 // restore if need some important flags
2845 SetUInt32Value(PLAYER_FIELD_BYTES2
, 0); // flags empty by default
2847 if (reapplyMods
) // reapply stats values only on .reset stats (level) command
2848 _ApplyAllStatBonuses();
2850 // set current level health and mana/energy to maximum after applying all mods.
2851 SetHealth(GetMaxHealth());
2852 SetPower(POWER_MANA
, GetMaxPower(POWER_MANA
));
2853 SetPower(POWER_ENERGY
, GetMaxPower(POWER_ENERGY
));
2854 if (GetPower(POWER_RAGE
) > GetMaxPower(POWER_RAGE
))
2855 SetPower(POWER_RAGE
, GetMaxPower(POWER_RAGE
));
2856 SetPower(POWER_FOCUS
, 0);
2857 SetPower(POWER_RUNIC_POWER
, 0);
2859 // update level to hunter/summon pet
2860 if (Pet
* pet
= GetPet())
2861 pet
->SynchronizeLevelWithOwner();
2864 void Player::SendInitialSpells()
2866 time_t curTime
= time(NULL
);
2867 time_t infTime
= curTime
+ infinityCooldownDelayCheck
;
2869 uint16 spellCount
= 0;
2871 WorldPacket
data(SMSG_INITIAL_SPELLS
, (1 + 2 + 4 * m_spells
.size() + 2 + m_spellCooldowns
.size() * (2 + 2 + 2 + 4 + 4)));
2874 size_t countPos
= data
.wpos();
2875 data
<< uint16(spellCount
); // spell count placeholder
2877 for (PlayerSpellMap::const_iterator itr
= m_spells
.begin(); itr
!= m_spells
.end(); ++itr
)
2879 if (itr
->second
.state
== PLAYERSPELL_REMOVED
)
2882 if (!itr
->second
.active
|| itr
->second
.disabled
)
2885 data
<< uint32(itr
->first
);
2886 data
<< uint16(0); // it's not slot id
2891 data
.put
<uint16
>(countPos
, spellCount
); // write real count value
2893 uint16 spellCooldowns
= m_spellCooldowns
.size();
2894 data
<< uint16(spellCooldowns
);
2895 for (SpellCooldowns::const_iterator itr
= m_spellCooldowns
.begin(); itr
!= m_spellCooldowns
.end(); ++itr
)
2897 SpellEntry
const* sEntry
= sSpellStore
.LookupEntry(itr
->first
);
2901 data
<< uint32(itr
->first
);
2903 data
<< uint16(itr
->second
.itemid
); // cast item id
2904 data
<< uint16(sEntry
->GetCategory()); // spell category
2906 // send infinity cooldown in special format
2907 if (itr
->second
.end
>= infTime
)
2909 data
<< uint32(1); // cooldown
2910 data
<< uint32(0x80000000); // category cooldown
2914 time_t cooldown
= itr
->second
.end
> curTime
? (itr
->second
.end
- curTime
) * IN_MILLISECONDS
: 0;
2916 if(sEntry
->GetCategory()) // may be wrong, but anyway better than nothing...
2918 data
<< uint32(0); // cooldown
2919 data
<< uint32(cooldown
); // category cooldown
2923 data
<< uint32(cooldown
); // cooldown
2924 data
<< uint32(0); // category cooldown
2928 GetSession()->SendPacket(&data
);
2930 DETAIL_LOG("CHARACTER: Sent Initial Spells");
2933 void Player::RemoveMail(uint32 id
)
2935 for (PlayerMails::iterator itr
= m_mail
.begin(); itr
!= m_mail
.end(); ++itr
)
2937 if ((*itr
)->messageID
== id
)
2939 // do not delete item, because Player::removeMail() is called when returning mail to sender.
2946 void Player::SendMailResult(uint32 mailId
, MailResponseType mailAction
, MailResponseResult mailError
, uint32 equipError
, uint32 item_guid
, uint32 item_count
)
2948 WorldPacket
data(SMSG_SEND_MAIL_RESULT
, (4 + 4 + 4 + (mailError
== MAIL_ERR_EQUIP_ERROR
? 4 : (mailAction
== MAIL_ITEM_TAKEN
? 4 + 4 : 0))));
2949 data
<< (uint32
) mailId
;
2950 data
<< (uint32
) mailAction
;
2951 data
<< (uint32
) mailError
;
2952 if (mailError
== MAIL_ERR_EQUIP_ERROR
)
2953 data
<< (uint32
) equipError
;
2954 else if (mailAction
== MAIL_ITEM_TAKEN
)
2956 data
<< (uint32
) item_guid
; // item guid low?
2957 data
<< (uint32
) item_count
; // item count?
2959 GetSession()->SendPacket(&data
);
2962 void Player::SendNewMail()
2964 // deliver undelivered mail
2965 WorldPacket
data(SMSG_RECEIVED_MAIL
, 4);
2966 data
<< float(0.0f
);
2967 GetSession()->SendPacket(&data
);
2970 void Player::UpdateNextMailTimeAndUnreads()
2972 // calculate next delivery time (min. from non-delivered mails
2973 // and recalculate unReadMail
2974 time_t cTime
= time(NULL
);
2975 m_nextMailDelivereTime
= 0;
2977 for (PlayerMails::iterator itr
= m_mail
.begin(); itr
!= m_mail
.end(); ++itr
)
2979 if ((*itr
)->deliver_time
> cTime
)
2981 if (!m_nextMailDelivereTime
|| m_nextMailDelivereTime
> (*itr
)->deliver_time
)
2982 m_nextMailDelivereTime
= (*itr
)->deliver_time
;
2984 else if (((*itr
)->checked
& MAIL_CHECK_MASK_READ
) == 0)
2989 void Player::AddNewMailDeliverTime(time_t deliver_time
)
2991 if (deliver_time
<= time(NULL
)) // ready now
2996 else // not ready and no have ready mails
2998 if (!m_nextMailDelivereTime
|| m_nextMailDelivereTime
> deliver_time
)
2999 m_nextMailDelivereTime
= deliver_time
;
3003 bool Player::addSpell(uint32 spell_id
, bool active
, bool learning
, bool dependent
, bool disabled
)
3005 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(spell_id
);
3008 // do character spell book cleanup (all characters)
3009 if (!IsInWorld() && !learning
) // spell load case
3011 sLog
.outError("Player::addSpell: nonexistent in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spell_id
);
3012 CharacterDatabase
.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id
);
3015 sLog
.outError("Player::addSpell: nonexistent in SpellStore spell #%u request.", spell_id
);
3020 if (!SpellMgr::IsSpellValid(spellInfo
, this, false))
3022 // do character spell book cleanup (all characters)
3023 if (!IsInWorld() && !learning
) // spell load case
3025 sLog
.outError("Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.", spell_id
);
3026 CharacterDatabase
.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id
);
3029 sLog
.outError("Player::addSpell: Broken spell #%u learning not allowed.", spell_id
);
3034 PlayerSpellState state
= learning
? PLAYERSPELL_NEW
: PLAYERSPELL_UNCHANGED
;
3036 bool dependent_set
= false;
3037 bool disabled_case
= false;
3038 bool superceded_old
= false;
3040 PlayerSpellMap::iterator itr
= m_spells
.find(spell_id
);
3041 if (itr
!= m_spells
.end())
3043 uint32 next_active_spell_id
= 0;
3044 // fix activate state for non-stackable low rank (and find next spell for !active case)
3045 if (sSpellMgr
.IsRankedSpellNonStackableInSpellBook(spellInfo
))
3047 SpellChainMapNext
const& nextMap
= sSpellMgr
.GetSpellChainNext();
3048 for (SpellChainMapNext::const_iterator next_itr
= nextMap
.lower_bound(spell_id
); next_itr
!= nextMap
.upper_bound(spell_id
); ++next_itr
)
3050 if (HasSpell(next_itr
->second
))
3052 // high rank already known so this must !active
3054 next_active_spell_id
= next_itr
->second
;
3060 // not do anything if already known in expected state
3061 if (itr
->second
.state
!= PLAYERSPELL_REMOVED
&& itr
->second
.active
== active
&&
3062 itr
->second
.dependent
== dependent
&& itr
->second
.disabled
== disabled
)
3064 if (!IsInWorld() && !learning
) // explicitly load from DB and then exist in it already and set correctly
3065 itr
->second
.state
= PLAYERSPELL_UNCHANGED
;
3070 // dependent spell known as not dependent, overwrite state
3071 if (itr
->second
.state
!= PLAYERSPELL_REMOVED
&& !itr
->second
.dependent
&& dependent
)
3073 itr
->second
.dependent
= dependent
;
3074 if (itr
->second
.state
!= PLAYERSPELL_NEW
)
3075 itr
->second
.state
= PLAYERSPELL_CHANGED
;
3076 dependent_set
= true;
3079 // update active state for known spell
3080 if (itr
->second
.active
!= active
&& itr
->second
.state
!= PLAYERSPELL_REMOVED
&& !itr
->second
.disabled
)
3082 itr
->second
.active
= active
;
3084 if (!IsInWorld() && !learning
&& !dependent_set
)// explicitly load from DB and then exist in it already and set correctly
3085 itr
->second
.state
= PLAYERSPELL_UNCHANGED
;
3086 else if (itr
->second
.state
!= PLAYERSPELL_NEW
)
3087 itr
->second
.state
= PLAYERSPELL_CHANGED
;
3091 if (IsNeedCastPassiveLikeSpellAtLearn(spellInfo
))
3092 CastSpell(this, spell_id
, true);
3094 else if (IsInWorld())
3096 if (next_active_spell_id
)
3098 // update spell ranks in spellbook and action bar
3099 WorldPacket
data(SMSG_SUPERCEDED_SPELL
, 4 + 4);
3100 data
<< uint32(spell_id
);
3101 data
<< uint32(next_active_spell_id
);
3102 GetSession()->SendPacket(&data
);
3106 WorldPacket
data(SMSG_REMOVED_SPELL
, 4);
3107 data
<< uint32(spell_id
);
3108 GetSession()->SendPacket(&data
);
3112 return active
; // learn (show in spell book if active now)
3115 if (itr
->second
.disabled
!= disabled
&& itr
->second
.state
!= PLAYERSPELL_REMOVED
)
3117 if (itr
->second
.state
!= PLAYERSPELL_NEW
)
3118 itr
->second
.state
= PLAYERSPELL_CHANGED
;
3119 itr
->second
.disabled
= disabled
;
3124 disabled_case
= true;
3126 else switch (itr
->second
.state
)
3128 case PLAYERSPELL_UNCHANGED
: // known saved spell
3130 case PLAYERSPELL_REMOVED
: // re-learning removed not saved spell
3132 m_spells
.erase(itr
);
3133 state
= PLAYERSPELL_CHANGED
;
3134 break; // need re-add
3136 default: // known not saved yet spell (new or modified)
3138 // can be in case spell loading but learned at some previous spell loading
3139 if (!IsInWorld() && !learning
&& !dependent_set
)
3140 itr
->second
.state
= PLAYERSPELL_UNCHANGED
;
3147 TalentSpellPos
const* talentPos
= GetTalentSpellPos(spell_id
);
3149 if (!disabled_case
) // skip new spell adding if spell already known (disabled spells case)
3151 // talent: unlearn all other talent ranks (high and low)
3154 if (TalentEntry
const* talentInfo
= sTalentStore
.LookupEntry(talentPos
->talent_id
))
3156 for (int i
= 0; i
< MAX_TALENT_RANK
; ++i
)
3158 // skip learning spell and no rank spell case
3159 uint32 rankSpellId
= talentInfo
->RankID
[i
];
3160 if (!rankSpellId
|| rankSpellId
== spell_id
)
3163 removeSpell(rankSpellId
, false, false);
3167 // non talent spell: learn low ranks (recursive call)
3168 else if (uint32 prev_spell
= sSpellMgr
.GetPrevSpellInChain(spell_id
))
3170 if (!IsInWorld() || disabled
) // at spells loading, no output, but allow save
3171 addSpell(prev_spell
, active
, true, true, disabled
);
3172 else // at normal learning
3173 learnSpell(prev_spell
, true);
3176 PlayerSpell newspell
;
3177 newspell
.state
= state
;
3178 newspell
.active
= active
;
3179 newspell
.dependent
= dependent
;
3180 newspell
.disabled
= disabled
;
3182 // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
3183 if (newspell
.active
&& !newspell
.disabled
&& sSpellMgr
.IsRankedSpellNonStackableInSpellBook(spellInfo
))
3185 for (PlayerSpellMap::iterator itr2
= m_spells
.begin(); itr2
!= m_spells
.end(); ++itr2
)
3187 if (itr2
->second
.state
== PLAYERSPELL_REMOVED
) continue;
3188 SpellEntry
const* i_spellInfo
= sSpellStore
.LookupEntry(itr2
->first
);
3189 if (!i_spellInfo
) continue;
3191 if (sSpellMgr
.IsRankSpellDueToSpell(spellInfo
, itr2
->first
))
3193 if (itr2
->second
.active
)
3195 if (sSpellMgr
.IsHighRankOfSpell(spell_id
, itr2
->first
))
3197 if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
3199 WorldPacket
data(SMSG_SUPERCEDED_SPELL
, 4 + 4);
3200 data
<< uint32(itr2
->first
);
3201 data
<< uint32(spell_id
);
3202 GetSession()->SendPacket(&data
);
3205 // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
3206 itr2
->second
.active
= false;
3207 if (itr2
->second
.state
!= PLAYERSPELL_NEW
)
3208 itr2
->second
.state
= PLAYERSPELL_CHANGED
;
3209 superceded_old
= true; // new spell replace old in action bars and spell book.
3211 else if (sSpellMgr
.IsHighRankOfSpell(itr2
->first
, spell_id
))
3213 if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
3215 WorldPacket
data(SMSG_SUPERCEDED_SPELL
, 4 + 4);
3216 data
<< uint32(spell_id
);
3217 data
<< uint32(itr2
->first
);
3218 GetSession()->SendPacket(&data
);
3221 // mark new spell as disable (not learned yet for client and will not learned)
3222 newspell
.active
= false;
3223 if (newspell
.state
!= PLAYERSPELL_NEW
)
3224 newspell
.state
= PLAYERSPELL_CHANGED
;
3231 m_spells
[spell_id
] = newspell
;
3233 // return false if spell disabled
3234 if (newspell
.disabled
)
3240 // update talent map
3241 PlayerTalentMap::iterator iter
= m_talents
[m_activeSpec
].find(talentPos
->talent_id
);
3242 if (iter
!= m_talents
[m_activeSpec
].end())
3244 // check if ranks different or removed
3245 if ((*iter
).second
.state
== PLAYERSPELL_REMOVED
|| talentPos
->rank
!= (*iter
).second
.currentRank
)
3247 (*iter
).second
.currentRank
= talentPos
->rank
;
3249 if ((*iter
).second
.state
!= PLAYERSPELL_NEW
)
3250 (*iter
).second
.state
= PLAYERSPELL_CHANGED
;
3255 PlayerTalent talent
;
3256 talent
.currentRank
= talentPos
->rank
;
3257 talent
.talentEntry
= sTalentStore
.LookupEntry(talentPos
->talent_id
);
3258 talent
.state
= IsInWorld() ? PLAYERSPELL_NEW
: PLAYERSPELL_UNCHANGED
;
3259 m_talents
[m_activeSpec
][talentPos
->talent_id
] = talent
;
3262 // update used talent points count
3263 m_usedTalentCount
+= GetTalentSpellCost(talentPos
);
3264 UpdateFreeTalentPoints(false);
3267 // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
3268 if (uint32 freeProfs
= GetFreePrimaryProfessionPoints())
3270 if (sSpellMgr
.IsPrimaryProfessionFirstRankSpell(spell_id
))
3271 SetFreePrimaryProfessions(freeProfs
- 1);
3274 // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
3275 // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
3276 if (talentPos
&& IsSpellHaveEffect(spellInfo
, SPELL_EFFECT_LEARN_SPELL
))
3278 // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
3279 CastSpell(this, spell_id
, true);
3281 // also cast passive (and passive like) spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
3282 else if (IsNeedCastPassiveLikeSpellAtLearn(spellInfo
))
3284 CastSpell(this, spell_id
, true);
3286 else if (IsSpellHaveEffect(spellInfo
, SPELL_EFFECT_SKILL_STEP
))
3288 CastSpell(this, spell_id
, true);
3292 // add dependent skills
3293 uint16 maxskill
= GetMaxSkillValueForLevel();
3295 SpellLearnSkillNode
const* spellLearnSkill
= sSpellMgr
.GetSpellLearnSkill(spell_id
);
3297 SkillLineAbilityMapBounds skill_bounds
= sSpellMgr
.GetSkillLineAbilityMapBounds(spell_id
);
3299 if (spellLearnSkill
)
3301 uint32 skill_value
= GetPureSkillValue(spellLearnSkill
->skill
);
3302 uint32 skill_max_value
= GetPureMaxSkillValue(spellLearnSkill
->skill
);
3304 if (skill_value
< spellLearnSkill
->value
)
3305 skill_value
= spellLearnSkill
->value
;
3307 uint32 new_skill_max_value
= spellLearnSkill
->maxvalue
== 0 ? maxskill
: spellLearnSkill
->maxvalue
;
3309 if (skill_max_value
< new_skill_max_value
)
3310 skill_max_value
= new_skill_max_value
;
3312 SetSkill(spellLearnSkill
->skill
, skill_value
, skill_max_value
, spellLearnSkill
->step
);
3316 // not ranked skills
3317 for (SkillLineAbilityMap::const_iterator _spell_idx
= skill_bounds
.first
; _spell_idx
!= skill_bounds
.second
; ++_spell_idx
)
3319 SkillLineEntry
const* pSkill
= sSkillLineStore
.LookupEntry(_spell_idx
->second
->skillId
);
3323 if (HasSkill(pSkill
->id
))
3326 if (_spell_idx
->second
->learnOnGetSkill
== ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
||
3327 // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
3328 (pSkill
->id
== SKILL_RUNEFORGING
&& _spell_idx
->second
->max_value
== 0))
3330 switch (GetSkillRangeType(pSkill
, _spell_idx
->second
->racemask
!= 0))
3332 case SKILL_RANGE_LANGUAGE
:
3333 SetSkill(pSkill
->id
, 300, 300, GetSkillStep(pSkill
->id
));
3335 case SKILL_RANGE_LEVEL
:
3336 SetSkill(pSkill
->id
, 1, GetMaxSkillValueForLevel(), GetSkillStep(pSkill
->id
));
3338 case SKILL_RANGE_MONO
:
3339 SetSkill(pSkill
->id
, 1, 1, GetSkillStep(pSkill
->id
));
3348 // learn dependent spells
3349 SpellLearnSpellMapBounds spell_bounds
= sSpellMgr
.GetSpellLearnSpellMapBounds(spell_id
);
3351 for (SpellLearnSpellMap::const_iterator itr2
= spell_bounds
.first
; itr2
!= spell_bounds
.second
; ++itr2
)
3353 if (!itr2
->second
.autoLearned
)
3355 if (!IsInWorld() || !itr2
->second
.active
) // at spells loading, no output, but allow save
3356 addSpell(itr2
->second
.spell
, itr2
->second
.active
, true, true, false);
3357 else // at normal learning
3358 learnSpell(itr2
->second
.spell
, true);
3362 if (!GetSession()->PlayerLoading())
3364 // not ranked skills
3365 for (SkillLineAbilityMap::const_iterator _spell_idx
= skill_bounds
.first
; _spell_idx
!= skill_bounds
.second
; ++_spell_idx
)
3367 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE
, _spell_idx
->second
->skillId
);
3368 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
, _spell_idx
->second
->skillId
);
3371 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
, spell_id
);
3374 // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell
3375 return active
&& !disabled
&& !superceded_old
;
3378 bool Player::IsNeedCastPassiveLikeSpellAtLearn(SpellEntry
const* spellInfo
) const
3380 ShapeshiftForm form
= GetShapeshiftForm();
3382 if (IsNeedCastSpellAtFormApply(spellInfo
, form
)) // SPELL_ATTR_PASSIVE | SPELL_ATTR_UNK7 spells
3383 return true; // all stance req. cases, not have auarastate cases
3385 if (!spellInfo
->HasAttribute(SPELL_ATTR_PASSIVE
))
3388 // note: form passives activated with shapeshift spells be implemented by HandleShapeshiftBoosts instead of spell_learn_spell
3389 // talent dependent passives activated at form apply have proper stance data
3390 SpellShapeshiftEntry
const* shapeShift
= spellInfo
->GetSpellShapeshift();
3391 bool need_cast
= (!shapeShift
|| !shapeShift
->Stances
|| (!form
&& spellInfo
->HasAttribute(SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT
)));
3393 // Check CasterAuraStates
3394 SpellAuraRestrictionsEntry
const* auraRestrictions
= spellInfo
->GetSpellAuraRestrictions();
3395 return need_cast
&& (!auraRestrictions
|| !auraRestrictions
->CasterAuraState
|| HasAuraState(AuraState(auraRestrictions
->CasterAuraState
)));
3398 void Player::learnSpell(uint32 spell_id
, bool dependent
)
3400 PlayerSpellMap::iterator itr
= m_spells
.find(spell_id
);
3402 bool disabled
= (itr
!= m_spells
.end()) ? itr
->second
.disabled
: false;
3403 bool active
= disabled
? itr
->second
.active
: true;
3405 bool learning
= addSpell(spell_id
, active
, true, dependent
, false);
3407 // prevent duplicated entires in spell book, also not send if not in world (loading)
3408 if (learning
&& IsInWorld())
3410 WorldPacket
data(SMSG_LEARNED_SPELL
, 6);
3411 data
<< uint32(spell_id
);
3412 data
<< uint32(0); // 3.3.3 unk
3413 GetSession()->SendPacket(&data
);
3416 // learn all disabled higher ranks (recursive)
3419 SpellChainMapNext
const& nextMap
= sSpellMgr
.GetSpellChainNext();
3420 for (SpellChainMapNext::const_iterator i
= nextMap
.lower_bound(spell_id
); i
!= nextMap
.upper_bound(spell_id
); ++i
)
3422 PlayerSpellMap::iterator iter
= m_spells
.find(i
->second
);
3423 if (iter
!= m_spells
.end() && iter
->second
.disabled
)
3424 learnSpell(i
->second
, false);
3429 SpellAddedQuestCheck(spell_id
);
3432 void Player::removeSpell(uint32 spell_id
, bool disabled
, bool learn_low_rank
, bool sendUpdate
)
3434 PlayerSpellMap::iterator itr
= m_spells
.find(spell_id
);
3435 if (itr
== m_spells
.end())
3438 if (itr
->second
.state
== PLAYERSPELL_REMOVED
|| (disabled
&& itr
->second
.disabled
))
3441 // unlearn non talent higher ranks (recursive)
3442 SpellChainMapNext
const& nextMap
= sSpellMgr
.GetSpellChainNext();
3443 for (SpellChainMapNext::const_iterator itr2
= nextMap
.lower_bound(spell_id
); itr2
!= nextMap
.upper_bound(spell_id
); ++itr2
)
3444 if (HasSpell(itr2
->second
) && !GetTalentSpellPos(itr2
->second
))
3445 removeSpell(itr2
->second
, disabled
, false);
3447 // re-search, it can be corrupted in prev loop
3448 itr
= m_spells
.find(spell_id
);
3449 if (itr
== m_spells
.end() || itr
->second
.state
== PLAYERSPELL_REMOVED
)
3450 return; // already unleared
3452 bool cur_active
= itr
->second
.active
;
3453 bool cur_dependent
= itr
->second
.dependent
;
3457 itr
->second
.disabled
= disabled
;
3458 if (itr
->second
.state
!= PLAYERSPELL_NEW
)
3459 itr
->second
.state
= PLAYERSPELL_CHANGED
;
3463 if (itr
->second
.state
== PLAYERSPELL_NEW
)
3464 m_spells
.erase(itr
);
3466 itr
->second
.state
= PLAYERSPELL_REMOVED
;
3469 RemoveAurasDueToSpell(spell_id
);
3472 for (int i
= 0; i
< MAX_EFFECT_INDEX
; ++i
)
3473 if (PetAura
const* petSpell
= sSpellMgr
.GetPetAura(spell_id
, SpellEffectIndex(i
)))
3474 RemovePetAura(petSpell
);
3476 TalentSpellPos
const* talentPos
= GetTalentSpellPos(spell_id
);
3479 // update talent map
3480 PlayerTalentMap::iterator iter
= m_talents
[m_activeSpec
].find(talentPos
->talent_id
);
3481 if (iter
!= m_talents
[m_activeSpec
].end())
3483 if ((*iter
).second
.state
!= PLAYERSPELL_NEW
)
3484 (*iter
).second
.state
= PLAYERSPELL_REMOVED
;
3486 m_talents
[m_activeSpec
].erase(iter
);
3489 sLog
.outError("removeSpell: Player (GUID: %u) has talent spell (id: %u) but doesn't have talent", GetGUIDLow(), spell_id
);
3491 // free talent points
3492 uint32 talentCosts
= GetTalentSpellCost(talentPos
);
3494 if (talentCosts
< m_usedTalentCount
)
3495 m_usedTalentCount
-= talentCosts
;
3497 m_usedTalentCount
= 0;
3499 UpdateFreeTalentPoints(false);
3502 // update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
3503 if (sSpellMgr
.IsPrimaryProfessionFirstRankSpell(spell_id
))
3505 uint32 freeProfs
= GetFreePrimaryProfessionPoints() + 1;
3506 uint32 maxProfs
= GetSession()->GetSecurity() < AccountTypes(sWorld
.getConfig(CONFIG_UINT32_TRADE_SKILL_GMIGNORE_MAX_PRIMARY_COUNT
)) ? sWorld
.getConfig(CONFIG_UINT32_MAX_PRIMARY_TRADE_SKILL
) : 10;
3507 if (freeProfs
<= maxProfs
)
3508 SetFreePrimaryProfessions(freeProfs
);
3511 // remove dependent skill
3512 SpellLearnSkillNode
const* spellLearnSkill
= sSpellMgr
.GetSpellLearnSkill(spell_id
);
3513 if (spellLearnSkill
)
3515 uint32 prev_spell
= sSpellMgr
.GetPrevSpellInChain(spell_id
);
3516 if (!prev_spell
) // first rank, remove skill
3517 SetSkill(spellLearnSkill
->skill
, 0, 0);
3520 // search prev. skill setting by spell ranks chain
3521 SpellLearnSkillNode
const* prevSkill
= sSpellMgr
.GetSpellLearnSkill(prev_spell
);
3522 while (!prevSkill
&& prev_spell
)
3524 prev_spell
= sSpellMgr
.GetPrevSpellInChain(prev_spell
);
3525 prevSkill
= sSpellMgr
.GetSpellLearnSkill(sSpellMgr
.GetFirstSpellInChain(prev_spell
));
3528 if (!prevSkill
) // not found prev skill setting, remove skill
3529 SetSkill(spellLearnSkill
->skill
, 0, 0);
3530 else // set to prev. skill setting values
3532 uint32 skill_value
= GetPureSkillValue(prevSkill
->skill
);
3533 uint32 skill_max_value
= GetPureMaxSkillValue(prevSkill
->skill
);
3535 if (skill_value
> prevSkill
->value
)
3536 skill_value
= prevSkill
->value
;
3538 uint32 new_skill_max_value
= prevSkill
->maxvalue
== 0 ? GetMaxSkillValueForLevel() : prevSkill
->maxvalue
;
3540 if (skill_max_value
> new_skill_max_value
)
3541 skill_max_value
= new_skill_max_value
;
3543 SetSkill(prevSkill
->skill
, skill_value
, skill_max_value
, prevSkill
->step
);
3550 // not ranked skills
3551 SkillLineAbilityMapBounds bounds
= sSpellMgr
.GetSkillLineAbilityMapBounds(spell_id
);
3553 for (SkillLineAbilityMap::const_iterator _spell_idx
= bounds
.first
; _spell_idx
!= bounds
.second
; ++_spell_idx
)
3555 SkillLineEntry
const* pSkill
= sSkillLineStore
.LookupEntry(_spell_idx
->second
->skillId
);
3559 if ((_spell_idx
->second
->learnOnGetSkill
== ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
&&
3560 pSkill
->categoryId
!= SKILL_CATEGORY_CLASS
) ||// not unlearn class skills (spellbook/talent pages)
3561 // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
3562 (pSkill
->id
== SKILL_RUNEFORGING
&& _spell_idx
->second
->max_value
== 0))
3564 // not reset skills for professions and racial abilities
3565 if ((pSkill
->categoryId
== SKILL_CATEGORY_SECONDARY
|| pSkill
->categoryId
== SKILL_CATEGORY_PROFESSION
) &&
3566 (IsProfessionSkill(pSkill
->id
) || _spell_idx
->second
->racemask
!= 0))
3569 SetSkill(pSkill
->id
, 0, 0, GetSkillStep(pSkill
->id
));
3574 // remove dependent spells
3575 SpellLearnSpellMapBounds spell_bounds
= sSpellMgr
.GetSpellLearnSpellMapBounds(spell_id
);
3577 for (SpellLearnSpellMap::const_iterator itr2
= spell_bounds
.first
; itr2
!= spell_bounds
.second
; ++itr2
)
3578 removeSpell(itr2
->second
.spell
, disabled
);
3580 // activate lesser rank in spellbook/action bar, and cast it if need
3581 bool prev_activate
= false;
3583 if (uint32 prev_id
= sSpellMgr
.GetPrevSpellInChain(spell_id
))
3585 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(spell_id
);
3587 // if talent then lesser rank also talent and need learn
3591 learnSpell(prev_id
, false);
3593 // if ranked non-stackable spell: need activate lesser rank and update dependence state
3594 else if (cur_active
&& sSpellMgr
.IsRankedSpellNonStackableInSpellBook(spellInfo
))
3596 // need manually update dependence state (learn spell ignore like attempts)
3597 PlayerSpellMap::iterator prev_itr
= m_spells
.find(prev_id
);
3598 if (prev_itr
!= m_spells
.end())
3600 if (prev_itr
->second
.dependent
!= cur_dependent
)
3602 prev_itr
->second
.dependent
= cur_dependent
;
3603 if (prev_itr
->second
.state
!= PLAYERSPELL_NEW
)
3604 prev_itr
->second
.state
= PLAYERSPELL_CHANGED
;
3607 // now re-learn if need re-activate
3608 if (cur_active
&& !prev_itr
->second
.active
&& learn_low_rank
)
3610 if (addSpell(prev_id
, true, false, prev_itr
->second
.dependent
, prev_itr
->second
.disabled
))
3612 // downgrade spell ranks in spellbook and action bar
3613 WorldPacket
data(SMSG_SUPERCEDED_SPELL
, 4 + 4);
3614 data
<< uint32(spell_id
);
3615 data
<< uint32(prev_id
);
3616 GetSession()->SendPacket(&data
);
3617 prev_activate
= true;
3624 // for Titan's Grip and shaman Dual-wield
3625 if (CanDualWield() || CanTitanGrip())
3627 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(spell_id
);
3629 if (CanDualWield() && IsSpellHaveEffect(spellInfo
, SPELL_EFFECT_DUAL_WIELD
))
3630 SetCanDualWield(false);
3632 if (CanTitanGrip() && IsSpellHaveEffect(spellInfo
, SPELL_EFFECT_TITAN_GRIP
))
3634 SetCanTitanGrip(false);
3635 // Remove Titan's Grip damage penalty now
3636 RemoveAurasDueToSpell(49152);
3640 // for talents and normal spell unlearn that allow offhand use for some weapons
3641 if (sWorld
.getConfig(CONFIG_BOOL_OFFHAND_CHECK_AT_TALENTS_RESET
))
3642 AutoUnequipOffhandIfNeed();
3644 // remove from spell book if not replaced by lesser rank
3645 if (!prev_activate
&& sendUpdate
)
3647 WorldPacket
data(SMSG_REMOVED_SPELL
, 4);
3648 data
<< uint32(spell_id
);
3649 GetSession()->SendPacket(&data
);
3653 SpellRemovedQuestCheck(spell_id
);
3656 void Player::RemoveSpellCooldown(uint32 spell_id
, bool update
/* = false */)
3658 m_spellCooldowns
.erase(spell_id
);
3661 SendClearCooldown(spell_id
, this);
3664 void Player::RemoveSpellCategoryCooldown(uint32 cat
, bool update
/* = false */)
3666 SpellCategoryStore::const_iterator ct
= sSpellCategoryStore
.find(cat
);
3667 if (ct
== sSpellCategoryStore
.end())
3670 const SpellCategorySet
& ct_set
= ct
->second
;
3671 for (SpellCooldowns::const_iterator i
= m_spellCooldowns
.begin(); i
!= m_spellCooldowns
.end();)
3673 if (ct_set
.find(i
->first
) != ct_set
.end())
3674 RemoveSpellCooldown((i
++)->first
, update
);
3680 void Player::RemoveArenaSpellCooldowns()
3682 // remove cooldowns on spells that has < 15 min CD
3683 SpellCooldowns::iterator itr
, next
;
3684 // iterate spell cooldowns
3685 for (itr
= m_spellCooldowns
.begin(); itr
!= m_spellCooldowns
.end(); itr
= next
)
3689 SpellEntry
const* entry
= sSpellStore
.LookupEntry(itr
->first
);
3690 // check if spellentry is present and if the cooldown is less than 15 mins
3692 entry
->GetRecoveryTime() <= 15 * MINUTE
* IN_MILLISECONDS
&&
3693 entry
->GetCategoryRecoveryTime() <= 15 * MINUTE
* IN_MILLISECONDS
)
3696 RemoveSpellCooldown(itr
->first
, true);
3701 void Player::RemoveAllSpellCooldown()
3703 if (!m_spellCooldowns
.empty())
3705 ObjectGuid guid
= GetObjectGuid();
3707 WorldPacket
data(SMSG_CLEAR_COOLDOWNS
, 1 + 8 + m_spellCooldowns
.size() * 4);
3708 data
.WriteGuidMask
<1, 3, 6>(guid
);
3709 data
.WriteBits(m_spellCooldowns
.size(), 24); // cooldown count
3710 data
.WriteGuidMask
<7, 5, 2, 4, 0>(guid
);
3712 data
.WriteGuidBytes
<7, 2, 4, 5, 1, 3>(guid
);
3714 for (SpellCooldowns::const_iterator itr
= m_spellCooldowns
.begin(); itr
!= m_spellCooldowns
.end(); ++itr
)
3715 data
<< uint32(itr
->first
);
3717 data
.WriteGuidBytes
<0, 6>(guid
);
3719 SendDirectMessage(&data
);
3721 m_spellCooldowns
.clear();
3725 void Player::_LoadSpellCooldowns(QueryResult
* result
)
3727 // some cooldowns can be already set at aura loading...
3729 // QueryResult *result = CharacterDatabase.PQuery("SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'",GetGUIDLow());
3733 time_t curTime
= time(NULL
);
3737 Field
* fields
= result
->Fetch();
3739 uint32 spell_id
= fields
[0].GetUInt32();
3740 uint32 item_id
= fields
[1].GetUInt32();
3741 time_t db_time
= (time_t)fields
[2].GetUInt64();
3743 if (!sSpellStore
.LookupEntry(spell_id
))
3745 sLog
.outError("Player %u has unknown spell %u in `character_spell_cooldown`, skipping.", GetGUIDLow(), spell_id
);
3749 // skip outdated cooldown
3750 if (db_time
<= curTime
)
3753 AddSpellCooldown(spell_id
, item_id
, db_time
);
3755 DEBUG_LOG("Player (GUID: %u) spell %u, item %u cooldown loaded (%u secs).", GetGUIDLow(), spell_id
, item_id
, uint32(db_time
- curTime
));
3757 while (result
->NextRow());
3763 void Player::_SaveSpellCooldowns()
3765 static SqlStatementID deleteSpellCooldown
;
3766 static SqlStatementID insertSpellCooldown
;
3768 SqlStatement stmt
= CharacterDatabase
.CreateStatement(deleteSpellCooldown
, "DELETE FROM character_spell_cooldown WHERE guid = ?");
3769 stmt
.PExecute(GetGUIDLow());
3771 time_t curTime
= time(NULL
);
3772 time_t infTime
= curTime
+ infinityCooldownDelayCheck
;
3774 // remove outdated and save active
3775 for (SpellCooldowns::iterator itr
= m_spellCooldowns
.begin(); itr
!= m_spellCooldowns
.end();)
3777 if (itr
->second
.end
<= curTime
)
3778 m_spellCooldowns
.erase(itr
++);
3779 else if (itr
->second
.end
<= infTime
) // not save locked cooldowns, it will be reset or set at reload
3781 stmt
= CharacterDatabase
.CreateStatement(insertSpellCooldown
, "INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES( ?, ?, ?, ?)");
3782 stmt
.PExecute(GetGUIDLow(), itr
->first
, itr
->second
.itemid
, uint64(itr
->second
.end
));
3790 uint32
Player::resetTalentsCost() const
3792 // The first time reset costs 1 gold
3793 if (m_resetTalentsCost
< 1 * GOLD
)
3796 else if (m_resetTalentsCost
< 5 * GOLD
)
3798 // After that it increases in increments of 5 gold
3799 else if (m_resetTalentsCost
< 10 * GOLD
)
3803 time_t months
= (sWorld
.GetGameTime() - m_resetTalentsTime
) / MONTH
;
3806 // This cost will be reduced by a rate of 5 gold per month
3807 int32 new_cost
= int32((m_resetTalentsCost
) - 5 * GOLD
* months
);
3808 // to a minimum of 10 gold.
3809 return uint32(new_cost
< 10 * GOLD
? 10 * GOLD
: new_cost
);
3813 // After that it increases in increments of 5 gold
3814 int32 new_cost
= m_resetTalentsCost
+ 5 * GOLD
;
3815 // until it hits a cap of 50 gold.
3816 if (new_cost
> 50 * GOLD
)
3817 new_cost
= 50 * GOLD
;
3823 bool Player::resetTalents(bool no_cost
, bool all_specs
)
3825 // not need after this call
3826 if (HasAtLoginFlag(AT_LOGIN_RESET_TALENTS
) && all_specs
)
3827 RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS
, true);
3829 if (m_usedTalentCount
== 0 && !all_specs
)
3831 UpdateFreeTalentPoints(false); // for fix if need counter
3839 cost
= resetTalentsCost();
3841 if (GetMoney() < cost
)
3843 SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY
, 0, 0, 0);
3848 for (PlayerTalentMap::iterator iter
= m_talents
[m_activeSpec
].begin(); iter
!= m_talents
[m_activeSpec
].end();)
3850 if (iter
->second
.state
== PLAYERSPELL_REMOVED
)
3856 TalentEntry
const* talentInfo
= iter
->second
.talentEntry
;
3859 m_talents
[m_activeSpec
].erase(iter
++);
3863 TalentTabEntry
const* talentTabInfo
= sTalentTabStore
.LookupEntry(talentInfo
->TalentTab
);
3867 m_talents
[m_activeSpec
].erase(iter
++);
3871 // unlearn only talents for character class
3872 // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
3873 // to prevent unexpected lost normal learned spell skip another class talents
3874 if ((getClassMask() & talentTabInfo
->ClassMask
) == 0)
3880 for (int j
= 0; j
< MAX_TALENT_RANK
; ++j
)
3881 if (talentInfo
->RankID
[j
])
3882 removeSpell(talentInfo
->RankID
[j
], !IsPassiveSpell(talentInfo
->RankID
[j
]), false);
3884 iter
= m_talents
[m_activeSpec
].begin();
3887 // Remove spec specific spells
3888 for (uint32 i
= 0; i
< MAX_TALENT_TABS
; ++i
)
3890 if (std::vector
<uint32
> const* specSpells
= GetTalentTreeMasterySpells(GetTalentTabPages(getClass())[i
]))
3891 for (size_t i
= 0; i
< specSpells
->size(); ++i
)
3892 removeSpell(specSpells
->at(i
), true);
3894 if (std::vector
<uint32
> const* specSpells
= GetTalentTreePrimarySpells(GetTalentTabPages(getClass())[i
]))
3895 for (size_t i
= 0; i
< specSpells
->size(); ++i
)
3896 removeSpell(specSpells
->at(i
), true);
3899 for (uint8 spec
= 0; spec
< MAX_TALENT_SPEC_COUNT
; ++spec
)
3901 if (!all_specs
&& spec
!= m_activeSpec
)
3904 m_talentsPrimaryTree
[spec
] = 0;
3907 // for not current spec just mark removed all saved to DB case and drop not saved
3910 for (uint8 spec
= 0; spec
< MAX_TALENT_SPEC_COUNT
; ++spec
)
3912 if (spec
== m_activeSpec
)
3915 for (PlayerTalentMap::iterator iter
= m_talents
[spec
].begin(); iter
!= m_talents
[spec
].end();)
3917 switch (iter
->second
.state
)
3919 case PLAYERSPELL_REMOVED
:
3922 case PLAYERSPELL_NEW
:
3923 m_talents
[spec
].erase(iter
++);
3926 iter
->second
.state
= PLAYERSPELL_REMOVED
;
3934 UpdateFreeTalentPoints(false);
3938 ModifyMoney(-(int64
)cost
);
3939 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS
, cost
);
3940 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS
, 1);
3942 m_resetTalentsCost
= cost
;
3943 m_resetTalentsTime
= time(NULL
);
3946 // Update talent tree role-dependent mana regen
3949 UpdateArmorSpecializations();
3951 // FIXME: remove pet before or after unlearn spells? for now after unlearn to allow removing of talent related, pet affecting auras
3952 RemovePet(PET_SAVE_REAGENTS
);
3953 /* when prev line will dropped use next line
3954 if(Pet* pet = GetPet())
3956 if(pet->getPetType()==HUNTER_PET && !pet->GetCreatureInfo()->isTameable(CanTameExoticPets()))
3957 pet->Unsummon(PET_SAVE_REAGENTS, this);
3963 Mail
* Player::GetMail(uint32 id
)
3965 for (PlayerMails::iterator itr
= m_mail
.begin(); itr
!= m_mail
.end(); ++itr
)
3967 if ((*itr
)->messageID
== id
)
3975 void Player::_SetCreateBits(UpdateMask
* updateMask
, Player
* target
) const
3979 Object::_SetCreateBits(updateMask
, target
);
3983 for (uint16 index
= 0; index
< m_valuesCount
; ++index
)
3985 if (GetUInt32Value(index
) != 0 && updateVisualBits
.GetBit(index
))
3986 updateMask
->SetBit(index
);
3991 void Player::_SetUpdateBits(UpdateMask
* updateMask
, Player
* target
) const
3995 Object::_SetUpdateBits(updateMask
, target
);
3999 Object::_SetUpdateBits(updateMask
, target
);
4000 *updateMask
&= updateVisualBits
;
4004 void Player::InitVisibleBits()
4006 updateVisualBits
.SetCount(PLAYER_END
);
4008 updateVisualBits
.SetBit(OBJECT_FIELD_GUID
+ 0);
4009 updateVisualBits
.SetBit(OBJECT_FIELD_GUID
+ 1);
4010 updateVisualBits
.SetBit(OBJECT_FIELD_TYPE
);
4011 updateVisualBits
.SetBit(OBJECT_FIELD_ENTRY
);
4012 updateVisualBits
.SetBit(OBJECT_FIELD_DATA
+ 0);
4013 updateVisualBits
.SetBit(OBJECT_FIELD_DATA
+ 1);
4014 updateVisualBits
.SetBit(OBJECT_FIELD_SCALE_X
);
4015 updateVisualBits
.SetBit(UNIT_FIELD_CHARM
+ 0);
4016 updateVisualBits
.SetBit(UNIT_FIELD_CHARM
+ 1);
4017 updateVisualBits
.SetBit(UNIT_FIELD_SUMMON
+ 0);
4018 updateVisualBits
.SetBit(UNIT_FIELD_SUMMON
+ 1);
4019 updateVisualBits
.SetBit(UNIT_FIELD_CHARMEDBY
+ 0);
4020 updateVisualBits
.SetBit(UNIT_FIELD_CHARMEDBY
+ 1);
4021 updateVisualBits
.SetBit(UNIT_FIELD_TARGET
+ 0);
4022 updateVisualBits
.SetBit(UNIT_FIELD_TARGET
+ 1);
4023 updateVisualBits
.SetBit(UNIT_FIELD_CHANNEL_OBJECT
+ 0);
4024 updateVisualBits
.SetBit(UNIT_FIELD_CHANNEL_OBJECT
+ 1);
4025 updateVisualBits
.SetBit(UNIT_FIELD_BYTES_0
);
4026 updateVisualBits
.SetBit(UNIT_FIELD_HEALTH
);
4027 updateVisualBits
.SetBit(UNIT_FIELD_POWER1
);
4028 updateVisualBits
.SetBit(UNIT_FIELD_POWER2
);
4029 updateVisualBits
.SetBit(UNIT_FIELD_POWER3
);
4030 updateVisualBits
.SetBit(UNIT_FIELD_POWER4
);
4031 updateVisualBits
.SetBit(UNIT_FIELD_POWER5
);
4032 updateVisualBits
.SetBit(UNIT_FIELD_MAXHEALTH
);
4033 updateVisualBits
.SetBit(UNIT_FIELD_MAXPOWER1
);
4034 updateVisualBits
.SetBit(UNIT_FIELD_MAXPOWER2
);
4035 updateVisualBits
.SetBit(UNIT_FIELD_MAXPOWER3
);
4036 updateVisualBits
.SetBit(UNIT_FIELD_MAXPOWER4
);
4037 updateVisualBits
.SetBit(UNIT_FIELD_MAXPOWER5
);
4038 updateVisualBits
.SetBit(UNIT_FIELD_LEVEL
);
4039 updateVisualBits
.SetBit(UNIT_FIELD_FACTIONTEMPLATE
);
4040 updateVisualBits
.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID
+ 0);
4041 updateVisualBits
.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID
+ 1);
4042 updateVisualBits
.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID
+ 2);
4043 updateVisualBits
.SetBit(UNIT_FIELD_FLAGS
);
4044 updateVisualBits
.SetBit(UNIT_FIELD_FLAGS_2
);
4045 updateVisualBits
.SetBit(UNIT_FIELD_AURASTATE
);
4046 updateVisualBits
.SetBit(UNIT_FIELD_BASEATTACKTIME
+ 0);
4047 updateVisualBits
.SetBit(UNIT_FIELD_BASEATTACKTIME
+ 1);
4048 updateVisualBits
.SetBit(UNIT_FIELD_BOUNDINGRADIUS
);
4049 updateVisualBits
.SetBit(UNIT_FIELD_COMBATREACH
);
4050 updateVisualBits
.SetBit(UNIT_FIELD_DISPLAYID
);
4051 updateVisualBits
.SetBit(UNIT_FIELD_NATIVEDISPLAYID
);
4052 updateVisualBits
.SetBit(UNIT_FIELD_MOUNTDISPLAYID
);
4053 updateVisualBits
.SetBit(UNIT_FIELD_BYTES_1
);
4054 updateVisualBits
.SetBit(UNIT_FIELD_PETNUMBER
);
4055 updateVisualBits
.SetBit(UNIT_FIELD_PET_NAME_TIMESTAMP
);
4056 updateVisualBits
.SetBit(UNIT_DYNAMIC_FLAGS
);
4057 updateVisualBits
.SetBit(UNIT_CHANNEL_SPELL
);
4058 updateVisualBits
.SetBit(UNIT_MOD_CAST_SPEED
);
4059 updateVisualBits
.SetBit(UNIT_NPC_FLAGS
);
4060 updateVisualBits
.SetBit(UNIT_FIELD_BASE_MANA
);
4061 updateVisualBits
.SetBit(UNIT_FIELD_BYTES_2
);
4062 updateVisualBits
.SetBit(UNIT_FIELD_HOVERHEIGHT
);
4064 updateVisualBits
.SetBit(PLAYER_DUEL_ARBITER
+ 0);
4065 updateVisualBits
.SetBit(PLAYER_DUEL_ARBITER
+ 1);
4066 updateVisualBits
.SetBit(PLAYER_FLAGS
);
4067 //updateVisualBits.SetBit(PLAYER_GUILDID);
4068 updateVisualBits
.SetBit(PLAYER_GUILDRANK
);
4069 updateVisualBits
.SetBit(PLAYER_GUILDLEVEL
);
4070 updateVisualBits
.SetBit(PLAYER_BYTES
);
4071 updateVisualBits
.SetBit(PLAYER_BYTES_2
);
4072 updateVisualBits
.SetBit(PLAYER_BYTES_3
);
4073 updateVisualBits
.SetBit(PLAYER_DUEL_TEAM
);
4074 updateVisualBits
.SetBit(PLAYER_GUILD_TIMESTAMP
);
4075 updateVisualBits
.SetBit(UNIT_NPC_FLAGS
);
4077 // PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)...
4078 for (uint16 i
= PLAYER_QUEST_LOG_1_1
; i
< PLAYER_QUEST_LOG_25_2
; i
+= MAX_QUEST_OFFSET
)
4079 updateVisualBits
.SetBit(i
);
4081 // Players visible items are not inventory stuff
4082 for (uint16 i
= 0; i
< EQUIPMENT_SLOT_END
; ++i
)
4084 uint32 offset
= i
* 2;
4087 updateVisualBits
.SetBit(PLAYER_VISIBLE_ITEM_1_ENTRYID
+ offset
);
4089 updateVisualBits
.SetBit(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT
+ offset
);
4092 updateVisualBits
.SetBit(PLAYER_CHOSEN_TITLE
);
4095 void Player::BuildCreateUpdateBlockForPlayer(UpdateData
* data
, Player
* target
) const
4099 for (int i
= 0; i
< EQUIPMENT_SLOT_END
; ++i
)
4101 if (m_items
[i
] == NULL
)
4104 m_items
[i
]->BuildCreateUpdateBlockForPlayer(data
, target
);
4106 for (int i
= INVENTORY_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
4108 if (m_items
[i
] == NULL
)
4111 m_items
[i
]->BuildCreateUpdateBlockForPlayer(data
, target
);
4115 SetPhaseAndMap(target
);
4116 Unit::BuildCreateUpdateBlockForPlayer(data
, target
);
4119 void Player::SetPhaseAndMap(Player
* target
) const
4121 if (QueryResult
*result
= CharacterDatabase
.PQuery("SELECT map, phase FROM character_phase_data WHERE guid = '%u'", target
->GetGUIDLow()))
4123 Field
*fields
= result
->Fetch();
4125 uint16 mapId
= fields
[0].GetUInt16();
4126 uint32 phase
= fields
[1].GetUInt32();
4128 target
->GetSession()->SendSetPhaseShift(phase
, mapId
);
4134 void Player::DestroyForPlayer(Player
* target
, bool anim
) const
4136 Unit::DestroyForPlayer(target
, anim
);
4138 for (int i
= 0; i
< INVENTORY_SLOT_BAG_END
; ++i
)
4140 if (m_items
[i
] == NULL
)
4143 m_items
[i
]->DestroyForPlayer(target
);
4148 for (int i
= INVENTORY_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
4150 if (m_items
[i
] == NULL
)
4153 m_items
[i
]->DestroyForPlayer(target
);
4158 bool Player::HasSpell(uint32 spell
) const
4160 PlayerSpellMap::const_iterator itr
= m_spells
.find(spell
);
4161 return (itr
!= m_spells
.end() && itr
->second
.state
!= PLAYERSPELL_REMOVED
&&
4162 !itr
->second
.disabled
);
4165 bool Player::HasActiveSpell(uint32 spell
) const
4167 PlayerSpellMap::const_iterator itr
= m_spells
.find(spell
);
4168 return (itr
!= m_spells
.end() && itr
->second
.state
!= PLAYERSPELL_REMOVED
&&
4169 itr
->second
.active
&& !itr
->second
.disabled
);
4172 TrainerSpellState
Player::GetTrainerSpellState(TrainerSpell
const* trainer_spell
, uint32 reqLevel
) const
4175 return TRAINER_SPELL_RED
;
4177 if (!trainer_spell
->learnedSpell
)
4178 return TRAINER_SPELL_RED
;
4181 if (HasSpell(trainer_spell
->learnedSpell
))
4182 return TRAINER_SPELL_GRAY
;
4184 // check race/class requirement
4185 if (!IsSpellFitByClassAndRace(trainer_spell
->learnedSpell
))
4186 return TRAINER_SPELL_RED
;
4188 bool prof
= SpellMgr::IsProfessionSpell(trainer_spell
->learnedSpell
);
4190 // check level requirement
4191 if (!prof
|| GetSession()->GetSecurity() < AccountTypes(sWorld
.getConfig(CONFIG_UINT32_TRADE_SKILL_GMIGNORE_LEVEL
)))
4192 if (getLevel() < reqLevel
)
4193 return TRAINER_SPELL_RED
;
4195 if (SpellChainNode
const* spell_chain
= sSpellMgr
.GetSpellChainNode(trainer_spell
->learnedSpell
))
4197 // check prev.rank requirement
4198 if (spell_chain
->prev
&& !HasSpell(spell_chain
->prev
))
4199 return TRAINER_SPELL_RED
;
4201 // check additional spell requirement
4202 if (spell_chain
->req
&& !HasSpell(spell_chain
->req
))
4203 return TRAINER_SPELL_RED
;
4206 // check skill requirement
4207 if (!prof
|| GetSession()->GetSecurity() < AccountTypes(sWorld
.getConfig(CONFIG_UINT32_TRADE_SKILL_GMIGNORE_SKILL
)))
4208 if (trainer_spell
->reqSkill
&& GetBaseSkillValue(trainer_spell
->reqSkill
) < trainer_spell
->reqSkillValue
)
4209 return TRAINER_SPELL_RED
;
4211 // exist, already checked at loading
4212 SpellEntry
const* spell
= sSpellStore
.LookupEntry(trainer_spell
->learnedSpell
);
4214 // secondary prof. or not prof. spell
4215 SpellEffectEntry
const* spellEffect
= spell
->GetSpellEffect(EFFECT_INDEX_1
);
4216 uint32 skill
= spellEffect
? spellEffect
->EffectMiscValue
: 0;
4218 if(spellEffect
&& (spellEffect
->Effect
!= SPELL_EFFECT_SKILL
|| !IsPrimaryProfessionSkill(skill
)))
4219 return TRAINER_SPELL_GREEN
;
4221 // check primary prof. limit
4222 if (sSpellMgr
.IsPrimaryProfessionFirstRankSpell(spell
->Id
) && GetFreePrimaryProfessionPoints() == 0)
4223 return TRAINER_SPELL_GREEN_DISABLED
;
4225 return TRAINER_SPELL_GREEN
;
4229 * Deletes a character from the database
4231 * The way, how the characters will be deleted is decided based on the config option.
4233 * @see Player::DeleteOldCharacters
4235 * @param playerguid the low-GUID from the player which should be deleted
4236 * @param accountId the account id from the player
4237 * @param updateRealmChars when this flag is set, the amount of characters on that realm will be updated in the realmlist
4238 * @param deleteFinally if this flag is set, the config option will be ignored and the character will be permanently removed from the database
4240 void Player::DeleteFromDB(ObjectGuid playerguid
, uint32 accountId
, bool updateRealmChars
, bool deleteFinally
)
4242 // for nonexistent account avoid update realm
4244 updateRealmChars
= false;
4246 uint32 charDelete_method
= sWorld
.getConfig(CONFIG_UINT32_CHARDELETE_METHOD
);
4247 uint32 charDelete_minLvl
= sWorld
.getConfig(CONFIG_UINT32_CHARDELETE_MIN_LEVEL
);
4249 // if we want to finally delete the character or the character does not meet the level requirement, we set it to mode 0
4250 if (deleteFinally
|| Player::GetLevelFromDB(playerguid
) < charDelete_minLvl
)
4251 charDelete_method
= 0;
4253 uint32 lowguid
= playerguid
.GetCounter();
4255 // convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry)
4256 // bones will be deleted by corpse/bones deleting thread shortly
4257 sObjectAccessor
.ConvertCorpseForPlayer(playerguid
);
4259 // remove from guild
4260 if (uint32 guildId
= GetGuildIdFromDB(playerguid
))
4262 if (Guild
* guild
= sGuildMgr
.GetGuildById(guildId
))
4264 if (guild
->DelMember(playerguid
))
4272 // remove from arena teams
4273 LeaveAllArenaTeams(playerguid
);
4275 // the player was uninvited already on logout so just remove from group
4276 QueryResult
* resultGroup
= CharacterDatabase
.PQuery("SELECT groupId FROM group_member WHERE memberGuid='%u'", lowguid
);
4279 uint32 groupId
= (*resultGroup
)[0].GetUInt32();
4281 if (Group
* group
= sObjectMgr
.GetGroupById(groupId
))
4282 RemoveFromGroup(group
, playerguid
);
4285 // remove signs from petitions (also remove petitions if owner);
4286 RemovePetitionsAndSigns(playerguid
);
4288 switch (charDelete_method
)
4290 // completely remove from the database
4293 // return back all mails with COD and Item 0 1 2 3 4 5 6 7
4294 QueryResult
* resultMail
= CharacterDatabase
.PQuery("SELECT id,messageType,mailTemplateId,sender,subject,body,money,has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0", lowguid
);
4299 Field
* fields
= resultMail
->Fetch();
4301 uint32 mail_id
= fields
[0].GetUInt32();
4302 uint16 mailType
= fields
[1].GetUInt16();
4303 uint16 mailTemplateId
= fields
[2].GetUInt16();
4304 uint32 sender
= fields
[3].GetUInt32();
4305 std::string subject
= fields
[4].GetCppString();
4306 std::string body
= fields
[5].GetCppString();
4307 uint64 money
= fields
[6].GetUInt32();
4308 bool has_items
= fields
[7].GetBool();
4310 // we can return mail now
4311 // so firstly delete the old one
4312 CharacterDatabase
.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id
);
4314 // mail not from player
4315 if (mailType
!= MAIL_NORMAL
)
4318 CharacterDatabase
.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id
);
4324 draft
.SetMailTemplate(mailTemplateId
, false);// items already included
4326 draft
.SetSubjectAndBody(subject
, body
);
4330 // data needs to be at first place for Item::LoadFromDB
4332 QueryResult
* resultItems
= CharacterDatabase
.PQuery("SELECT data,text,item_guid,item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE mail_id='%u'", mail_id
);
4337 Field
* fields2
= resultItems
->Fetch();
4339 uint32 item_guidlow
= fields2
[2].GetUInt32();
4340 uint32 item_template
= fields2
[3].GetUInt32();
4342 ItemPrototype
const* itemProto
= ObjectMgr::GetItemPrototype(item_template
);
4345 CharacterDatabase
.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guidlow
);
4349 Item
* pItem
= NewItemOrBag(itemProto
);
4350 if (!pItem
->LoadFromDB(item_guidlow
, fields2
, playerguid
))
4352 pItem
->FSetState(ITEM_REMOVED
);
4353 pItem
->SaveToDB(); // it also deletes item object !
4357 draft
.AddItem(pItem
);
4359 while (resultItems
->NextRow());
4365 CharacterDatabase
.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id
);
4367 uint32 pl_account
= sObjectMgr
.GetPlayerAccountIdByGUID(playerguid
);
4369 draft
.SetMoney(money
).SendReturnToSender(pl_account
, playerguid
, ObjectGuid(HIGHGUID_PLAYER
, sender
));
4371 while (resultMail
->NextRow());
4376 // unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
4377 // Get guids of character's pets, will deleted in transaction
4378 QueryResult
* resultPets
= CharacterDatabase
.PQuery("SELECT id FROM character_pet WHERE owner = '%u'", lowguid
);
4380 // delete char from friends list when selected chars is online (non existing - error)
4381 QueryResult
* resultFriend
= CharacterDatabase
.PQuery("SELECT DISTINCT guid FROM character_social WHERE friend = '%u'", lowguid
);
4383 // NOW we can finally clear other DB data related to character
4384 CharacterDatabase
.BeginTransaction();
4389 Field
* fields3
= resultPets
->Fetch();
4390 uint32 petguidlow
= fields3
[0].GetUInt32();
4391 // do not create separate transaction for pet delete otherwise we will get fatal error!
4392 Pet::DeleteFromDB(petguidlow
, false);
4394 while (resultPets
->NextRow());
4398 // cleanup friends for online players, offline case will cleanup later in code
4403 Field
* fieldsFriend
= resultFriend
->Fetch();
4404 if (Player
* sFriend
= sObjectAccessor
.FindPlayer(ObjectGuid(HIGHGUID_PLAYER
, fieldsFriend
[0].GetUInt32())))
4406 if (sFriend
->IsInWorld())
4408 sFriend
->GetSocial()->RemoveFromSocialList(playerguid
, false);
4409 sSocialMgr
.SendFriendStatus(sFriend
, FRIEND_REMOVED
, playerguid
, false);
4413 while (resultFriend
->NextRow());
4414 delete resultFriend
;
4417 CharacterDatabase
.PExecute("DELETE FROM characters WHERE guid = '%u'", lowguid
);
4418 CharacterDatabase
.PExecute("DELETE FROM character_account_data WHERE guid = '%u'", lowguid
);
4419 CharacterDatabase
.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", lowguid
);
4420 CharacterDatabase
.PExecute("DELETE FROM character_action WHERE guid = '%u'", lowguid
);
4421 CharacterDatabase
.PExecute("DELETE FROM character_aura WHERE guid = '%u'", lowguid
);
4422 CharacterDatabase
.PExecute("DELETE FROM character_battleground_data WHERE guid = '%u'", lowguid
);
4423 CharacterDatabase
.PExecute("DELETE FROM character_gifts WHERE guid = '%u'", lowguid
);
4424 CharacterDatabase
.PExecute("DELETE FROM character_glyphs WHERE guid = '%u'", lowguid
);
4425 CharacterDatabase
.PExecute("DELETE FROM character_homebind WHERE guid = '%u'", lowguid
);
4426 CharacterDatabase
.PExecute("DELETE FROM character_instance WHERE guid = '%u'", lowguid
);
4427 CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u'", lowguid
);
4428 CharacterDatabase
.PExecute("DELETE FROM character_inventory WHERE guid = '%u'", lowguid
);
4429 CharacterDatabase
.PExecute("DELETE FROM character_queststatus WHERE guid = '%u'", lowguid
);
4430 CharacterDatabase
.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'", lowguid
);
4431 CharacterDatabase
.PExecute("DELETE FROM character_queststatus_weekly WHERE guid = '%u'", lowguid
);
4432 CharacterDatabase
.PExecute("DELETE FROM character_reputation WHERE guid = '%u'", lowguid
);
4433 CharacterDatabase
.PExecute("DELETE FROM character_skills WHERE guid = '%u'", lowguid
);
4434 CharacterDatabase
.PExecute("DELETE FROM character_spell WHERE guid = '%u'", lowguid
);
4435 CharacterDatabase
.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", lowguid
);
4436 CharacterDatabase
.PExecute("DELETE FROM character_talent WHERE guid = '%u'", lowguid
);
4437 CharacterDatabase
.PExecute("DELETE FROM character_ticket WHERE guid = '%u'", lowguid
);
4438 CharacterDatabase
.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'", lowguid
);
4439 CharacterDatabase
.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'", lowguid
, lowguid
);
4440 CharacterDatabase
.PExecute("DELETE FROM mail WHERE receiver = '%u'", lowguid
);
4441 CharacterDatabase
.PExecute("DELETE FROM mail_items WHERE receiver = '%u'", lowguid
);
4442 CharacterDatabase
.PExecute("DELETE FROM character_pet WHERE owner = '%u'", lowguid
);
4443 CharacterDatabase
.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'", lowguid
);
4444 CharacterDatabase
.PExecute("DELETE FROM character_achievement WHERE guid = '%u'", lowguid
);
4445 CharacterDatabase
.PExecute("DELETE FROM character_achievement_progress WHERE guid = '%u'", lowguid
);
4446 CharacterDatabase
.PExecute("DELETE FROM character_equipmentsets WHERE guid = '%u'", lowguid
);
4447 CharacterDatabase
.PExecute("DELETE FROM guild_eventlog WHERE PlayerGuid1 = '%u' OR PlayerGuid2 = '%u'", lowguid
, lowguid
);
4448 CharacterDatabase
.PExecute("DELETE FROM guild_bank_eventlog WHERE PlayerGuid = '%u'", lowguid
);
4449 CharacterDatabase
.PExecute("DELETE FROM character_currencies WHERE guid = '%u'", lowguid
);
4450 CharacterDatabase
.CommitTransaction();
4453 // The character gets unlinked from the account, the name gets freed up and appears as deleted ingame
4455 CharacterDatabase
.PExecute("UPDATE characters SET deleteInfos_Name=name, deleteInfos_Account=account, deleteDate='" UI64FMTD
"', name='', account=0 WHERE guid=%u", uint64(time(NULL
)), lowguid
);
4458 sLog
.outError("Player::DeleteFromDB: Unsupported delete method: %u.", charDelete_method
);
4461 if (updateRealmChars
)
4462 sWorld
.UpdateRealmCharCount(accountId
);
4466 * Characters which were kept back in the database after being deleted and are now too old (see config option "CharDelete.KeepDays"), will be completely deleted.
4468 * @see Player::DeleteFromDB
4470 void Player::DeleteOldCharacters()
4472 uint32 keepDays
= sWorld
.getConfig(CONFIG_UINT32_CHARDELETE_KEEP_DAYS
);
4476 Player::DeleteOldCharacters(keepDays
);
4480 * Characters which were kept back in the database after being deleted and are older than the specified amount of days, will be completely deleted.
4482 * @see Player::DeleteFromDB
4484 * @param keepDays overrite the config option by another amount of days
4486 void Player::DeleteOldCharacters(uint32 keepDays
)
4488 sLog
.outString("Player::DeleteOldChars: Deleting all characters which have been deleted %u days before...", keepDays
);
4490 QueryResult
* resultChars
= CharacterDatabase
.PQuery("SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < '" UI64FMTD
"'", uint64(time(NULL
) - time_t(keepDays
* DAY
)));
4493 sLog
.outString("Player::DeleteOldChars: Found %u character(s) to delete", uint32(resultChars
->GetRowCount()));
4496 Field
* charFields
= resultChars
->Fetch();
4497 ObjectGuid guid
= ObjectGuid(HIGHGUID_PLAYER
, charFields
[0].GetUInt32());
4498 Player::DeleteFromDB(guid
, charFields
[1].GetUInt32(), true, true);
4500 while (resultChars
->NextRow());
4505 void Player::SetRoot(bool enable
)
4508 BuildForceMoveRootPacket(&data
, enable
, 0);
4509 GetSession()->SendPacket(&data
);
4512 void Player::SetWaterWalk(bool enable
)
4515 BuildMoveWaterWalkPacket(&data
, enable
, 0);
4516 GetSession()->SendPacket(&data
);
4520 - a resurrectable corpse must not be loaded for the player (only bones)
4521 - the player must be in world
4523 void Player::BuildPlayerRepop()
4525 WorldPacket
data(SMSG_PRE_RESURRECT
, GetPackGUID().size());
4526 data
<< GetPackGUID();
4527 GetSession()->SendPacket(&data
);
4529 if (getRace() == RACE_NIGHTELF
)
4530 CastSpell(this, 20584, true); // auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form)
4531 CastSpell(this, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
4533 // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK
4534 // there must be SMSG.STOP_MIRROR_TIMER
4535 // there we must send 888 opcode
4537 // the player cannot have a corpse already, only bones which are not returned by GetCorpse
4540 sLog
.outError("BuildPlayerRepop: player %s(%d) already has a corpse", GetName(), GetGUIDLow());
4541 MANGOS_ASSERT(false);
4544 // create a corpse and place it at the player's location
4545 Corpse
* corpse
= CreateCorpse();
4548 sLog
.outError("Error creating corpse for Player %s [%u]", GetName(), GetGUIDLow());
4551 GetMap()->Add(corpse
);
4553 // convert player body to ghost
4557 if (!GetSession()->isLogingOut())
4560 // BG - remove insignia related
4561 RemoveFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_SKINNABLE
);
4563 SendCorpseReclaimDelay();
4565 // to prevent cheating
4566 corpse
->ResetGhostTime();
4568 StopMirrorTimers(); // disable timers(bars)
4570 // set and clear other
4571 SetByteValue(UNIT_FIELD_BYTES_1
, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND
);
4574 void Player::ResurrectPlayer(float restore_percent
, bool applySickness
)
4576 WorldPacket
data(SMSG_DEATH_RELEASE_LOC
, 4 * 4); // remove spirit healer position
4581 GetSession()->SendPacket(&data
);
4583 // speed change, land walk
4585 // remove death flag + set aura
4586 SetByteValue(UNIT_FIELD_BYTES_1
, 3, 0x00);
4588 SetDeathState(ALIVE
);
4590 if (getRace() == RACE_NIGHTELF
)
4591 RemoveAurasDueToSpell(20584); // speed bonuses
4592 RemoveAurasDueToSpell(8326); // SPELL_AURA_GHOST
4594 SetWaterWalk(false);
4599 // set health/powers (0- will be set in caller)
4600 if (restore_percent
> 0.0f
)
4602 SetHealth(uint32(GetMaxHealth()*restore_percent
));
4603 SetPower(POWER_MANA
, uint32(GetMaxPower(POWER_MANA
)*restore_percent
));
4604 SetPower(POWER_RAGE
, 0);
4605 SetPower(POWER_ENERGY
, uint32(GetMaxPower(POWER_ENERGY
)*restore_percent
));
4608 // trigger update zone for alive state zone updates
4609 uint32 newzone
, newarea
;
4610 GetZoneAndAreaId(newzone
, newarea
);
4611 UpdateZone(newzone
, newarea
);
4613 // update visibility of world around viewpoint
4614 m_camera
.UpdateVisibilityForOwner();
4615 // update visibility of player for nearby cameras
4616 UpdateObjectVisibility();
4621 // Characters from level 1-10 are not affected by resurrection sickness.
4622 // Characters from level 11-19 will suffer from one minute of sickness
4623 // for each level they are above 10.
4624 // Characters level 20 and up suffer from ten minutes of sickness.
4625 int32 startLevel
= sWorld
.getConfig(CONFIG_INT32_DEATH_SICKNESS_LEVEL
);
4627 if (int32(getLevel()) >= startLevel
)
4629 // set resurrection sickness
4630 CastSpell(this, SPELL_ID_PASSIVE_RESURRECTION_SICKNESS
, true);
4632 // not full duration
4633 if (int32(getLevel()) < startLevel
+ 9)
4635 int32 delta
= (int32(getLevel()) - startLevel
+ 1) * MINUTE
;
4637 if (SpellAuraHolder
* holder
= GetSpellAuraHolder(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS
))
4639 holder
->SetAuraDuration(delta
* IN_MILLISECONDS
);
4640 holder
->SendAuraUpdate(false);
4646 void Player::KillPlayer()
4650 StopMirrorTimers(); // disable timers(bars)
4652 SetDeathState(CORPSE
);
4653 // SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_IN_PVP );
4655 SetUInt32Value(UNIT_DYNAMIC_FLAGS
, UNIT_DYNFLAG_NONE
);
4656 ApplyModByteFlag(PLAYER_FIELD_BYTES
, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER
, !sMapStore
.LookupEntry(GetMapId())->Instanceable());
4658 // 6 minutes until repop at graveyard
4659 m_deathTimer
= 6 * MINUTE
* IN_MILLISECONDS
;
4661 UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill
4663 // don't create corpse at this moment, player might be falling
4665 // update visibility
4666 UpdateObjectVisibility();
4669 Corpse
* Player::CreateCorpse()
4671 // prevent existence 2 corpse for player
4674 Corpse
* corpse
= new Corpse((m_ExtraFlags
& PLAYER_EXTRA_PVP_DEATH
) ? CORPSE_RESURRECTABLE_PVP
: CORPSE_RESURRECTABLE_PVE
);
4677 if (!corpse
->Create(sObjectMgr
.GenerateCorpseLowGuid(), this))
4683 uint8 skin
= GetByteValue(PLAYER_BYTES
, 0);
4684 uint8 face
= GetByteValue(PLAYER_BYTES
, 1);
4685 uint8 hairstyle
= GetByteValue(PLAYER_BYTES
, 2);
4686 uint8 haircolor
= GetByteValue(PLAYER_BYTES
, 3);
4687 uint8 facialhair
= GetByteValue(PLAYER_BYTES_2
, 0);
4689 corpse
->SetByteValue(CORPSE_FIELD_BYTES_1
, 1, getRace());
4690 corpse
->SetByteValue(CORPSE_FIELD_BYTES_1
, 2, getGender());
4691 corpse
->SetByteValue(CORPSE_FIELD_BYTES_1
, 3, skin
);
4693 corpse
->SetByteValue(CORPSE_FIELD_BYTES_2
, 0, face
);
4694 corpse
->SetByteValue(CORPSE_FIELD_BYTES_2
, 1, hairstyle
);
4695 corpse
->SetByteValue(CORPSE_FIELD_BYTES_2
, 2, haircolor
);
4696 corpse
->SetByteValue(CORPSE_FIELD_BYTES_2
, 3, facialhair
);
4698 uint32 flags
= CORPSE_FLAG_UNK2
;
4699 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_HIDE_HELM
))
4700 flags
|= CORPSE_FLAG_HIDE_HELM
;
4701 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_HIDE_CLOAK
))
4702 flags
|= CORPSE_FLAG_HIDE_CLOAK
;
4703 if (InBattleGround() && !InArena())
4704 flags
|= CORPSE_FLAG_LOOTABLE
; // to be able to remove insignia
4705 corpse
->SetUInt32Value(CORPSE_FIELD_FLAGS
, flags
);
4707 corpse
->SetUInt32Value(CORPSE_FIELD_DISPLAY_ID
, GetNativeDisplayId());
4710 uint32 iIventoryType
;
4712 for (int i
= 0; i
< EQUIPMENT_SLOT_END
; ++i
)
4716 iDisplayID
= m_items
[i
]->GetProto()->DisplayInfoID
;
4717 iIventoryType
= m_items
[i
]->GetProto()->InventoryType
;
4719 _cfi
= iDisplayID
| (iIventoryType
<< 24);
4720 corpse
->SetUInt32Value(CORPSE_FIELD_ITEM
+ i
, _cfi
);
4724 // we not need saved corpses for BG/arenas
4725 if (!GetMap()->IsBattleGroundOrArena())
4728 // register for player, but not show
4729 sObjectAccessor
.AddCorpse(corpse
);
4733 void Player::SpawnCorpseBones()
4735 if (sObjectAccessor
.ConvertCorpseForPlayer(GetObjectGuid()))
4736 if (!GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player
4737 SaveToDB(); // prevent loading as ghost without corpse
4740 Corpse
* Player::GetCorpse() const
4742 return sObjectAccessor
.GetCorpseForPlayerGUID(GetObjectGuid());
4745 void Player::DurabilityLossAll(double percent
, bool inventory
)
4747 for (int i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_END
; ++i
)
4748 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
4749 DurabilityLoss(pItem
, percent
);
4753 // bags not have durability
4754 // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
4756 for (int i
= INVENTORY_SLOT_ITEM_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
4757 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
4758 DurabilityLoss(pItem
, percent
);
4760 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
4761 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
4762 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
4763 if (Item
* pItem
= GetItemByPos(i
, j
))
4764 DurabilityLoss(pItem
, percent
);
4768 void Player::DurabilityLoss(Item
* item
, double percent
)
4773 uint32 pMaxDurability
= item
->GetUInt32Value(ITEM_FIELD_MAXDURABILITY
);
4775 if (!pMaxDurability
)
4778 uint32 pDurabilityLoss
= uint32(pMaxDurability
* percent
);
4780 if (pDurabilityLoss
< 1)
4781 pDurabilityLoss
= 1;
4783 DurabilityPointsLoss(item
, pDurabilityLoss
);
4786 void Player::DurabilityPointsLossAll(int32 points
, bool inventory
)
4788 for (int i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_END
; ++i
)
4789 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
4790 DurabilityPointsLoss(pItem
, points
);
4794 // bags not have durability
4795 // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
4797 for (int i
= INVENTORY_SLOT_ITEM_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
4798 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
4799 DurabilityPointsLoss(pItem
, points
);
4801 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
4802 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
4803 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
4804 if (Item
* pItem
= GetItemByPos(i
, j
))
4805 DurabilityPointsLoss(pItem
, points
);
4809 void Player::DurabilityPointsLoss(Item
* item
, int32 points
)
4811 int32 pMaxDurability
= item
->GetUInt32Value(ITEM_FIELD_MAXDURABILITY
);
4812 int32 pOldDurability
= item
->GetUInt32Value(ITEM_FIELD_DURABILITY
);
4813 int32 pNewDurability
= pOldDurability
- points
;
4815 if (pNewDurability
< 0)
4817 else if (pNewDurability
> pMaxDurability
)
4818 pNewDurability
= pMaxDurability
;
4820 if (pOldDurability
!= pNewDurability
)
4822 // modify item stats _before_ Durability set to 0 to pass _ApplyItemMods internal check
4823 if (pNewDurability
== 0 && pOldDurability
> 0 && item
->IsEquipped())
4824 _ApplyItemMods(item
, item
->GetSlot(), false);
4826 item
->SetUInt32Value(ITEM_FIELD_DURABILITY
, pNewDurability
);
4828 // modify item stats _after_ restore durability to pass _ApplyItemMods internal check
4829 if (pNewDurability
> 0 && pOldDurability
== 0 && item
->IsEquipped())
4830 _ApplyItemMods(item
, item
->GetSlot(), true);
4832 item
->SetState(ITEM_CHANGED
, this);
4836 void Player::DurabilityPointLossForEquipSlot(EquipmentSlots slot
)
4838 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, slot
))
4839 DurabilityPointsLoss(pItem
, 1);
4842 uint32
Player::DurabilityRepairAll(bool cost
, float discountMod
, bool guildBank
)
4844 uint32 TotalCost
= 0;
4845 // equipped, backpack, bags itself
4846 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
4847 TotalCost
+= DurabilityRepair(((INVENTORY_SLOT_BAG_0
<< 8) | i
), cost
, discountMod
, guildBank
);
4849 // bank, buyback and keys not repaired
4851 // items in inventory bags
4852 for (int j
= INVENTORY_SLOT_BAG_START
; j
< INVENTORY_SLOT_BAG_END
; ++j
)
4853 for (int i
= 0; i
< MAX_BAG_SIZE
; ++i
)
4854 TotalCost
+= DurabilityRepair(((j
<< 8) | i
), cost
, discountMod
, guildBank
);
4858 uint32
Player::DurabilityRepair(uint16 pos
, bool cost
, float discountMod
, bool guildBank
)
4860 Item
* item
= GetItemByPos(pos
);
4862 uint32 TotalCost
= 0;
4866 uint32 maxDurability
= item
->GetUInt32Value(ITEM_FIELD_MAXDURABILITY
);
4870 uint32 curDurability
= item
->GetUInt32Value(ITEM_FIELD_DURABILITY
);
4874 uint32 LostDurability
= maxDurability
- curDurability
;
4875 if (LostDurability
> 0)
4877 ItemPrototype
const* ditemProto
= item
->GetProto();
4879 DurabilityCostsEntry
const* dcost
= sDurabilityCostsStore
.LookupEntry(ditemProto
->ItemLevel
);
4882 sLog
.outError("RepairDurability: Wrong item lvl %u", ditemProto
->ItemLevel
);
4886 uint32 dQualitymodEntryId
= (ditemProto
->Quality
+ 1) * 2;
4887 DurabilityQualityEntry
const* dQualitymodEntry
= sDurabilityQualityStore
.LookupEntry(dQualitymodEntryId
);
4888 if (!dQualitymodEntry
)
4890 sLog
.outError("RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntryId
);
4894 uint32 dmultiplier
= dcost
->multiplier
[ItemSubClassToDurabilityMultiplierId(ditemProto
->Class
, ditemProto
->SubClass
)];
4895 uint32 costs
= uint32(LostDurability
* dmultiplier
* double(dQualitymodEntry
->quality_mod
));
4897 costs
= uint32(costs
* discountMod
);
4899 if (costs
== 0) // fix for ITEM_QUALITY_ARTIFACT
4904 if (GetGuildId() == 0)
4906 DEBUG_LOG("You are not member of a guild");
4910 Guild
* pGuild
= sGuildMgr
.GetGuildById(GetGuildId());
4914 if (!pGuild
->HasRankRight(GetRank(), GR_RIGHT_WITHDRAW_REPAIR
))
4916 DEBUG_LOG("You do not have rights to withdraw for repairs");
4920 if (pGuild
->GetMemberMoneyWithdrawRem(GetGUIDLow()) < costs
)
4922 DEBUG_LOG("You do not have enough money withdraw amount remaining");
4926 if (pGuild
->GetGuildBankMoney() < costs
)
4928 DEBUG_LOG("There is not enough money in bank");
4932 pGuild
->MemberMoneyWithdraw(costs
, GetGUIDLow());
4935 else if (GetMoney() < costs
)
4937 DEBUG_LOG("You do not have enough money");
4941 ModifyMoney(-int64(costs
));
4945 item
->SetUInt32Value(ITEM_FIELD_DURABILITY
, maxDurability
);
4946 item
->SetState(ITEM_CHANGED
, this);
4948 // reapply mods for total broken and repaired item if equipped
4949 if (IsEquipmentPos(pos
) && !curDurability
)
4950 _ApplyItemMods(item
, pos
& 255, true);
4954 void Player::RepopAtGraveyard()
4956 // note: this can be called also when the player is alive
4957 // for example from WorldSession::HandleMovementOpcodes
4959 AreaTableEntry
const* zone
= GetAreaEntryByAreaID(GetAreaId());
4961 // Such zones are considered unreachable as a ghost and the player must be automatically revived
4962 if ((!isAlive() && zone
&& zone
->flags
& AREA_FLAG_NEED_FLY
) || GetTransport())
4964 ResurrectPlayer(0.5f
);
4968 WorldSafeLocsEntry
const* ClosestGrave
= NULL
;
4970 // Special handle for battleground maps
4971 if (BattleGround
* bg
= GetBattleGround())
4972 ClosestGrave
= bg
->GetClosestGraveYard(this);
4974 ClosestGrave
= sObjectMgr
.GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam());
4976 // stop countdown until repop
4979 // if no grave found, stay at the current location
4980 // and don't show spirit healer location
4983 TeleportTo(ClosestGrave
->map_id
, ClosestGrave
->x
, ClosestGrave
->y
, ClosestGrave
->z
, GetOrientation());
4984 if (isDead()) // not send if alive, because it used in TeleportTo()
4986 WorldPacket
data(SMSG_DEATH_RELEASE_LOC
, 4 * 4);// show spirit healer position on minimap
4987 data
<< ClosestGrave
->map_id
;
4988 data
<< ClosestGrave
->x
;
4989 data
<< ClosestGrave
->y
;
4990 data
<< ClosestGrave
->z
;
4991 GetSession()->SendPacket(&data
);
4996 void Player::JoinedChannel(Channel
* c
)
4998 m_channels
.push_back(c
);
5001 void Player::LeftChannel(Channel
* c
)
5003 m_channels
.remove(c
);
5006 void Player::CleanupChannels()
5008 while (!m_channels
.empty())
5010 Channel
* ch
= *m_channels
.begin();
5011 m_channels
.erase(m_channels
.begin()); // remove from player's channel list
5012 ch
->Leave(GetObjectGuid(), false); // not send to client, not remove from player's channel list
5013 if (ChannelMgr
* cMgr
= channelMgr(GetTeam()))
5014 cMgr
->LeftChannel(ch
->GetName()); // deleted channel if empty
5017 DEBUG_LOG("Player: channels cleaned up!");
5020 void Player::UpdateLocalChannels(uint32 newZone
)
5022 if (m_channels
.empty())
5025 AreaTableEntry
const* current_zone
= GetAreaEntryByAreaID(newZone
);
5029 ChannelMgr
* cMgr
= channelMgr(GetTeam());
5033 std::string current_zone_name
= current_zone
->area_name
[GetSession()->GetSessionDbcLocale()];
5035 for (JoinedChannelsList::iterator i
= m_channels
.begin(), next
; i
!= m_channels
.end(); i
= next
)
5039 // skip non built-in channels
5040 if (!(*i
)->IsConstant())
5043 ChatChannelsEntry
const* ch
= GetChannelEntryFor((*i
)->GetChannelId());
5047 if ((ch
->flags
& 4) == 4) // global channel without zone name in pattern
5051 char new_channel_name_buf
[100];
5052 snprintf(new_channel_name_buf
, 100, ch
->pattern
[m_session
->GetSessionDbcLocale()], current_zone_name
.c_str());
5053 Channel
* new_channel
= cMgr
->GetJoinChannel(new_channel_name_buf
, ch
->ChannelID
);
5055 if ((*i
) != new_channel
)
5057 new_channel
->Join(GetObjectGuid(), ""); // will output Changed Channel: N. Name
5059 // leave old channel
5060 (*i
)->Leave(GetObjectGuid(), false); // not send leave channel, it already replaced at client
5061 std::string name
= (*i
)->GetName(); // store name, (*i)erase in LeftChannel
5062 LeftChannel(*i
); // remove from player's channel list
5063 cMgr
->LeftChannel(name
); // delete if empty
5066 DEBUG_LOG("Player: channels cleaned up!");
5069 void Player::LeaveLFGChannel()
5071 for (JoinedChannelsList::iterator i
= m_channels
.begin(); i
!= m_channels
.end(); ++i
)
5075 (*i
)->Leave(GetObjectGuid());
5081 void Player::HandleBaseModValue(BaseModGroup modGroup
, BaseModType modType
, float amount
, bool apply
)
5083 if (modGroup
>= BASEMOD_END
|| modType
>= MOD_END
)
5085 sLog
.outError("ERROR in HandleBaseModValue(): nonexistent BaseModGroup of wrong BaseModType!");
5094 m_auraBaseMod
[modGroup
][modType
] += apply
? amount
: -amount
;
5097 if (amount
<= -100.0f
)
5100 val
= (100.0f
+ amount
) / 100.0f
;
5101 m_auraBaseMod
[modGroup
][modType
] *= apply
? val
: (1.0f
/ val
);
5105 if (!CanModifyStats())
5110 case CRIT_PERCENTAGE
: UpdateCritPercentage(BASE_ATTACK
); break;
5111 case RANGED_CRIT_PERCENTAGE
: UpdateCritPercentage(RANGED_ATTACK
); break;
5112 case OFFHAND_CRIT_PERCENTAGE
: UpdateCritPercentage(OFF_ATTACK
); break;
5113 case SHIELD_BLOCK_DAMAGE_VALUE
: UpdateShieldBlockDamageValue(); break;
5118 float Player::GetBaseModValue(BaseModGroup modGroup
, BaseModType modType
) const
5120 if (modGroup
>= BASEMOD_END
|| modType
> MOD_END
)
5122 sLog
.outError("trial to access nonexistent BaseModGroup or wrong BaseModType!");
5126 if (modType
== PCT_MOD
&& m_auraBaseMod
[modGroup
][PCT_MOD
] <= 0.0f
)
5129 return m_auraBaseMod
[modGroup
][modType
];
5132 float Player::GetTotalBaseModValue(BaseModGroup modGroup
) const
5134 if (modGroup
>= BASEMOD_END
)
5136 sLog
.outError("wrong BaseModGroup in GetTotalBaseModValue()!");
5140 if (m_auraBaseMod
[modGroup
][PCT_MOD
] <= 0.0f
)
5143 return m_auraBaseMod
[modGroup
][FLAT_MOD
] * m_auraBaseMod
[modGroup
][PCT_MOD
];
5146 uint32
Player::GetShieldBlockDamageValue() const
5148 float value
= m_canBlock
? BASE_BLOCK_DAMAGE_PERCENT
: 0;
5149 value
= (value
+ m_auraBaseMod
[SHIELD_BLOCK_DAMAGE_VALUE
][FLAT_MOD
]) * m_auraBaseMod
[SHIELD_BLOCK_DAMAGE_VALUE
][PCT_MOD
];
5151 value
= (value
< 0) ? 0 : value
;
5153 return uint32(value
);
5156 float Player::GetMeleeCritFromAgility()
5158 uint32 level
= getLevel();
5159 uint32 pclass
= getClass();
5161 if (level
> GT_MAX_LEVEL
) level
= GT_MAX_LEVEL
;
5163 GtChanceToMeleeCritBaseEntry
const* critBase
= sGtChanceToMeleeCritBaseStore
.LookupEntry(pclass
- 1);
5164 GtChanceToMeleeCritEntry
const* critRatio
= sGtChanceToMeleeCritStore
.LookupEntry((pclass
- 1) * GT_MAX_LEVEL
+ level
- 1);
5165 if (critBase
== NULL
|| critRatio
== NULL
)
5168 float crit
= critBase
->base
+ GetStat(STAT_AGILITY
) * critRatio
->ratio
;
5169 return crit
* 100.0f
;
5172 void Player::GetDodgeFromAgility(float& diminishing
, float& nondiminishing
)
5174 // 4.2.0: these classes no longer receive dodge from agility and have 5% base
5175 if (getClass() == CLASS_WARRIOR
|| getClass() == CLASS_PALADIN
|| getClass() == CLASS_DEATH_KNIGHT
)
5177 nondiminishing
+= 5.0f
;
5181 // Table for base dodge values
5182 const float dodge_base
[MAX_CLASSES
] =
5184 0.036640f
, // Warrior
5185 0.034943f
, // Paladin
5186 -0.040873f
, // Hunter
5188 0.034178f
, // Priest
5190 0.021080f
, // Shaman
5192 0.024211f
, // Warlock
5196 // Crit/agility to dodge/agility coefficient multipliers; 3.2.0 increased required agility by 15%
5197 const float crit_to_dodge
[MAX_CLASSES
] =
5199 0.85f
/ 1.15f
, // Warrior
5200 1.00f
/ 1.15f
, // Paladin
5201 1.11f
/ 1.15f
, // Hunter
5202 2.00f
/ 1.15f
, // Rogue
5203 1.00f
/ 1.15f
, // Priest
5204 0.85f
/ 1.15f
, // DK
5205 1.60f
/ 1.15f
, // Shaman
5206 1.00f
/ 1.15f
, // Mage
5207 0.97f
/ 1.15f
, // Warlock (?)
5209 2.00f
/ 1.15f
// Druid
5212 uint32 level
= getLevel();
5213 uint32 pclass
= getClass();
5215 if (level
> GT_MAX_LEVEL
) level
= GT_MAX_LEVEL
;
5217 // Dodge per agility is proportional to crit per agility, which is available from DBC files
5218 GtChanceToMeleeCritEntry
const* dodgeRatio
= sGtChanceToMeleeCritStore
.LookupEntry((pclass
- 1) * GT_MAX_LEVEL
+ level
- 1);
5219 if (dodgeRatio
== NULL
|| pclass
> MAX_CLASSES
)
5222 // TODO: research if talents/effects that increase total agility by x% should increase non-diminishing part
5223 float base_agility
= GetCreateStat(STAT_AGILITY
) * m_auraModifiersGroup
[UNIT_MOD_STAT_START
+ STAT_AGILITY
][BASE_PCT
];
5224 float bonus_agility
= GetStat(STAT_AGILITY
) - base_agility
;
5225 // calculate diminishing (green in char screen) and non-diminishing (white) contribution
5226 diminishing
+= 100.0f
* bonus_agility
* dodgeRatio
->ratio
* crit_to_dodge
[pclass
- 1];
5227 nondiminishing
+= 100.0f
* (dodge_base
[pclass
- 1] + base_agility
* dodgeRatio
->ratio
* crit_to_dodge
[pclass
- 1]);
5230 void Player::GetParryFromStrength(float& diminishing
, float& nondiminishing
)
5232 // other classes than these do not receive parry bonus from strength
5233 if (getClass() != CLASS_WARRIOR
&& getClass() != CLASS_PALADIN
&& getClass() != CLASS_DEATH_KNIGHT
)
5236 float base_strength
= GetCreateStat(STAT_STRENGTH
) * m_auraModifiersGroup
[UNIT_MOD_STAT_START
+ STAT_STRENGTH
][BASE_PCT
];
5237 float bonus_strength
= GetStat(STAT_STRENGTH
) - base_strength
;
5238 // calculate diminishing (green in char screen) and non-diminishing (white) contribution
5239 diminishing
+= bonus_strength
* GetRatingMultiplier(CR_PARRY
) * 0.27f
;
5240 nondiminishing
+= base_strength
* GetRatingMultiplier(CR_PARRY
) * 0.27f
;
5243 float Player::GetSpellCritFromIntellect()
5245 uint32 level
= getLevel();
5246 uint32 pclass
= getClass();
5248 if (level
> GT_MAX_LEVEL
) level
= GT_MAX_LEVEL
;
5250 GtChanceToSpellCritBaseEntry
const* critBase
= sGtChanceToSpellCritBaseStore
.LookupEntry(pclass
- 1);
5251 GtChanceToSpellCritEntry
const* critRatio
= sGtChanceToSpellCritStore
.LookupEntry((pclass
- 1) * GT_MAX_LEVEL
+ level
- 1);
5252 if (critBase
== NULL
|| critRatio
== NULL
)
5255 float crit
= critBase
->base
+ GetStat(STAT_INTELLECT
) * critRatio
->ratio
;
5256 return crit
* 100.0f
;
5259 float Player::GetRatingMultiplier(CombatRating cr
) const
5261 uint32 level
= getLevel();
5263 if (level
> GT_MAX_LEVEL
) level
= GT_MAX_LEVEL
;
5265 GtCombatRatingsEntry
const* Rating
= sGtCombatRatingsStore
.LookupEntry(cr
* GT_MAX_LEVEL
+ level
- 1);
5266 // gtOCTClassCombatRatingScalarStore.dbc starts with 1, CombatRating with zero, so cr+1
5267 GtOCTClassCombatRatingScalarEntry
const* classRating
= sGtOCTClassCombatRatingScalarStore
.LookupEntry((getClass() - 1) * GT_MAX_RATING
+ cr
+ 1);
5268 if (!Rating
|| !classRating
)
5269 return 1.0f
; // By default use minimum coefficient (not must be called)
5271 return classRating
->ratio
/ Rating
->ratio
;
5274 float Player::GetRatingBonusValue(CombatRating cr
) const
5276 return float(GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1
+ cr
)) * GetRatingMultiplier(cr
);
5279 float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType
) const
5284 return GetUInt32Value(PLAYER_EXPERTISE
) / 4.0f
;
5286 return GetUInt32Value(PLAYER_OFFHAND_EXPERTISE
) / 4.0f
;
5293 float Player::OCTRegenMPPerSpirit()
5295 // Only healers have regen bonus from spirit. Others regenerate by combat regen.
5296 uint32 rolesMask
= GetTalentTreeRolesMask(m_talentsPrimaryTree
[m_activeSpec
]);
5297 if ((rolesMask
& TALENT_ROLE_HEALER
) == 0)
5300 uint32 level
= getLevel();
5301 uint32 pclass
= getClass();
5303 if (level
> GT_MAX_LEVEL
) level
= GT_MAX_LEVEL
;
5305 // GtOCTRegenMPEntry const *baseRatio = sGtOCTRegenMPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
5306 GtRegenMPPerSptEntry
const* moreRatio
= sGtRegenMPPerSptStore
.LookupEntry((pclass
- 1) * GT_MAX_LEVEL
+ level
- 1);
5307 if (moreRatio
== NULL
)
5310 // Formula get from PaperDollFrame script
5311 float spirit
= GetStat(STAT_SPIRIT
);
5312 float regen
= spirit
* moreRatio
->ratio
;
5316 void Player::ApplyRatingMod(CombatRating cr
, int32 value
, bool apply
)
5318 m_baseRatingValue
[cr
] += (apply
? value
: -value
);
5320 // explicit affected values
5323 case CR_HASTE_MELEE
:
5325 float RatingChange
= value
* GetRatingMultiplier(cr
);
5326 ApplyAttackTimePercentMod(BASE_ATTACK
, RatingChange
, apply
);
5327 ApplyAttackTimePercentMod(OFF_ATTACK
, RatingChange
, apply
);
5330 case CR_HASTE_RANGED
:
5332 float RatingChange
= value
* GetRatingMultiplier(cr
);
5333 ApplyAttackTimePercentMod(RANGED_ATTACK
, RatingChange
, apply
);
5336 case CR_HASTE_SPELL
:
5338 float RatingChange
= value
* GetRatingMultiplier(cr
);
5339 ApplyCastTimePercentMod(RatingChange
, apply
);
5349 void Player::UpdateRating(CombatRating cr
)
5351 int32 amount
= m_baseRatingValue
[cr
];
5352 // Apply bonus from SPELL_AURA_MOD_RATING_FROM_STAT
5353 // stat used stored in miscValueB for this aura
5354 AuraList
const& modRatingFromStat
= GetAurasByType(SPELL_AURA_MOD_RATING_FROM_STAT
);
5355 for (AuraList::const_iterator i
= modRatingFromStat
.begin(); i
!= modRatingFromStat
.end(); ++i
)
5356 if ((*i
)->GetMiscValue() & (1 << cr
))
5357 amount
+= int32(GetStat(Stats((*i
)->GetMiscBValue())) * (*i
)->GetModifier()->m_amount
/ 100.0f
);
5360 SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1
+ cr
, uint32(amount
));
5362 bool affectStats
= CanModifyStats();
5366 case CR_WEAPON_SKILL
:
5367 case CR_DEFENSE_SKILL
:
5370 UpdateDodgePercentage();
5373 UpdateParryPercentage();
5376 UpdateBlockPercentage();
5379 UpdateMeleeHitChances();
5382 UpdateRangedHitChances();
5385 UpdateSpellHitChances();
5390 UpdateCritPercentage(BASE_ATTACK
);
5391 UpdateCritPercentage(OFF_ATTACK
);
5394 case CR_CRIT_RANGED
:
5396 UpdateCritPercentage(RANGED_ATTACK
);
5400 UpdateAllSpellCritChances();
5402 case CR_RESILIENCE_DAMAGE_TAKEN
:
5404 case CR_HASTE_MELEE
: // Implemented in Player::ApplyRatingMod
5405 case CR_HASTE_RANGED
:
5406 case CR_HASTE_SPELL
:
5411 UpdateExpertise(BASE_ATTACK
);
5412 UpdateExpertise(OFF_ATTACK
);
5415 case CR_ARMOR_PENETRATION
:
5417 UpdateArmorPenetration();
5420 UpdateMasteryAuras();
5423 case CR_HIT_TAKEN_MELEE
:
5424 case CR_HIT_TAKEN_RANGED
:
5425 case CR_HIT_TAKEN_SPELL
:
5426 case CR_CRIT_TAKEN_SPELL
:
5427 case CR_CRIT_TAKEN_MELEE
:
5428 case CR_WEAPON_SKILL_MAINHAND
:
5429 case CR_WEAPON_SKILL_OFFHAND
:
5430 case CR_WEAPON_SKILL_RANGED
:
5435 void Player::UpdateAllRatings()
5437 for (int cr
= 0; cr
< MAX_COMBAT_RATING
; ++cr
)
5438 UpdateRating(CombatRating(cr
));
5441 void Player::SetRegularAttackTime()
5443 for (int i
= 0; i
< MAX_ATTACK
; ++i
)
5445 Item
* tmpitem
= GetWeaponForAttack(WeaponAttackType(i
), true, false);
5448 ItemPrototype
const* proto
= tmpitem
->GetProto();
5450 SetAttackTime(WeaponAttackType(i
), proto
->Delay
);
5452 SetAttackTime(WeaponAttackType(i
), BASE_ATTACK_TIME
);
5457 // skill+step, checking for max value
5458 bool Player::UpdateSkill(uint32 skill_id
, uint32 step
)
5463 if (skill_id
== SKILL_FIST_WEAPONS
)
5464 skill_id
= SKILL_UNARMED
;
5466 SkillStatusMap::iterator itr
= mSkillStatus
.find(skill_id
);
5467 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5470 uint16 field
= itr
->second
.pos
/ 2;
5471 uint8 offset
= itr
->second
.pos
& 1; // itr->second.pos % 2
5473 uint16 value
= GetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
);
5474 uint16 max
= GetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
);
5476 if (!max
|| !value
|| value
>= max
)
5479 uint32 new_value
= value
+ step
;
5480 if (new_value
> max
)
5483 SetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
, new_value
);
5484 if (itr
->second
.uState
!= SKILL_NEW
)
5485 itr
->second
.uState
= SKILL_CHANGED
;
5486 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
, skill_id
);
5491 inline int SkillGainChance(uint32 SkillValue
, uint32 GrayLevel
, uint32 GreenLevel
, uint32 YellowLevel
)
5493 if (SkillValue
>= GrayLevel
)
5494 return sWorld
.getConfig(CONFIG_UINT32_SKILL_CHANCE_GREY
) * 10;
5495 if (SkillValue
>= GreenLevel
)
5496 return sWorld
.getConfig(CONFIG_UINT32_SKILL_CHANCE_GREEN
) * 10;
5497 if (SkillValue
>= YellowLevel
)
5498 return sWorld
.getConfig(CONFIG_UINT32_SKILL_CHANCE_YELLOW
) * 10;
5499 return sWorld
.getConfig(CONFIG_UINT32_SKILL_CHANCE_ORANGE
) * 10;
5502 bool Player::UpdateCraftSkill(uint32 spellid
)
5504 DEBUG_LOG("UpdateCraftSkill spellid %d", spellid
);
5506 SkillLineAbilityMapBounds bounds
= sSpellMgr
.GetSkillLineAbilityMapBounds(spellid
);
5508 for (SkillLineAbilityMap::const_iterator _spell_idx
= bounds
.first
; _spell_idx
!= bounds
.second
; ++_spell_idx
)
5510 if (_spell_idx
->second
->skillId
)
5512 uint32 SkillValue
= GetPureSkillValue(_spell_idx
->second
->skillId
);
5514 // Alchemy Discoveries here
5515 SpellEntry
const* spellEntry
= sSpellStore
.LookupEntry(spellid
);
5516 if (spellEntry
&& spellEntry
->GetMechanic() == MECHANIC_DISCOVERY
)
5518 if (uint32 discoveredSpell
= GetSkillDiscoverySpell(_spell_idx
->second
->skillId
, spellid
, this))
5519 learnSpell(discoveredSpell
, false);
5522 uint32 craft_skill_gain
= sWorld
.getConfig(CONFIG_UINT32_SKILL_GAIN_CRAFTING
);
5524 return UpdateSkillPro(_spell_idx
->second
->skillId
, SkillGainChance(SkillValue
,
5525 _spell_idx
->second
->max_value
,
5526 (_spell_idx
->second
->max_value
+ _spell_idx
->second
->min_value
) / 2,
5527 _spell_idx
->second
->min_value
),
5534 bool Player::UpdateGatherSkill(uint32 SkillId
, uint32 SkillValue
, uint32 RedLevel
, uint32 Multiplicator
)
5536 DEBUG_LOG("UpdateGatherSkill(SkillId %d SkillLevel %d RedLevel %d)", SkillId
, SkillValue
, RedLevel
);
5538 uint32 gathering_skill_gain
= sWorld
.getConfig(CONFIG_UINT32_SKILL_GAIN_GATHERING
);
5540 // For skinning and Mining chance decrease with level. 1-74 - no decrease, 75-149 - 2 times, 225-299 - 8 times
5543 case SKILL_HERBALISM
:
5544 case SKILL_JEWELCRAFTING
:
5545 case SKILL_INSCRIPTION
:
5546 return UpdateSkillPro(SkillId
, SkillGainChance(SkillValue
, RedLevel
+ 100, RedLevel
+ 50, RedLevel
+ 25) * Multiplicator
, gathering_skill_gain
);
5547 case SKILL_SKINNING
:
5548 if (sWorld
.getConfig(CONFIG_UINT32_SKILL_CHANCE_SKINNING_STEPS
) == 0)
5549 return UpdateSkillPro(SkillId
, SkillGainChance(SkillValue
, RedLevel
+ 100, RedLevel
+ 50, RedLevel
+ 25) * Multiplicator
, gathering_skill_gain
);
5551 return UpdateSkillPro(SkillId
, (SkillGainChance(SkillValue
, RedLevel
+ 100, RedLevel
+ 50, RedLevel
+ 25) * Multiplicator
) >> (SkillValue
/ sWorld
.getConfig(CONFIG_UINT32_SKILL_CHANCE_SKINNING_STEPS
)), gathering_skill_gain
);
5553 if (sWorld
.getConfig(CONFIG_UINT32_SKILL_CHANCE_MINING_STEPS
) == 0)
5554 return UpdateSkillPro(SkillId
, SkillGainChance(SkillValue
, RedLevel
+ 100, RedLevel
+ 50, RedLevel
+ 25) * Multiplicator
, gathering_skill_gain
);
5556 return UpdateSkillPro(SkillId
, (SkillGainChance(SkillValue
, RedLevel
+ 100, RedLevel
+ 50, RedLevel
+ 25) * Multiplicator
) >> (SkillValue
/ sWorld
.getConfig(CONFIG_UINT32_SKILL_CHANCE_MINING_STEPS
)), gathering_skill_gain
);
5561 bool Player::UpdateFishingSkill()
5563 DEBUG_LOG("UpdateFishingSkill");
5565 uint32 SkillValue
= GetPureSkillValue(SKILL_FISHING
);
5567 int32 chance
= SkillValue
< 75 ? 100 : 2500 / (SkillValue
- 50);
5569 uint32 gathering_skill_gain
= sWorld
.getConfig(CONFIG_UINT32_SKILL_GAIN_GATHERING
);
5571 return UpdateSkillPro(SKILL_FISHING
, chance
* 10, gathering_skill_gain
);
5574 // levels sync. with spell requirement for skill levels to learn
5575 // bonus abilities in sSkillLineAbilityStore
5576 // Used only to avoid scan DBC at each skill grow
5577 static uint32 bonusSkillLevels
[] = {75, 150, 225, 300, 375, 450};
5579 bool Player::UpdateSkillPro(uint16 SkillId
, int32 Chance
, uint32 step
)
5581 DEBUG_LOG("UpdateSkillPro(SkillId %d, Chance %3.1f%%)", SkillId
, Chance
/ 10.0);
5585 if (Chance
<= 0) // speedup in 0 chance case
5587 DEBUG_LOG("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance
/ 10.0);
5591 SkillStatusMap::iterator itr
= mSkillStatus
.find(SkillId
);
5592 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5595 uint16 field
= itr
->second
.pos
/ 2;
5596 uint8 offset
= itr
->second
.pos
& 1; // itr->second.pos % 2
5598 uint16 SkillValue
= GetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
);
5599 uint16 MaxValue
= GetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
);
5601 if (!MaxValue
|| !SkillValue
|| SkillValue
>= MaxValue
)
5604 int32 Roll
= irand(1, 1000);
5608 uint16 new_value
= SkillValue
+ step
;
5609 if (new_value
> MaxValue
)
5610 new_value
= MaxValue
;
5612 SetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
, new_value
);
5613 if (itr
->second
.uState
!= SKILL_NEW
)
5614 itr
->second
.uState
= SKILL_CHANGED
;
5615 for (uint32
* bsl
= &bonusSkillLevels
[0]; *bsl
; ++bsl
)
5617 if (SkillValue
< *bsl
&& new_value
>= *bsl
)
5619 learnSkillRewardedSpells(SkillId
, new_value
);
5623 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
, SkillId
);
5624 DEBUG_LOG("Player::UpdateSkillPro Chance=%3.1f%% taken", Chance
/ 10.0);
5628 DEBUG_LOG("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance
/ 10.0);
5632 void Player::ModifySkillBonus(uint32 skillid
, int32 val
, bool talent
)
5634 SkillStatusMap::const_iterator itr
= mSkillStatus
.find(skillid
);
5635 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5638 uint16 field
= itr
->second
.pos
/ 2 + (talent
? PLAYER_SKILL_TALENT_0
: PLAYER_SKILL_MODIFIER_0
);
5639 uint8 offset
= itr
->second
.pos
& 1; // itr->second.pos % 2
5641 uint16 bonus
= GetUInt16Value(field
, offset
);
5643 SetUInt16Value(field
, offset
, bonus
+ val
);
5646 void Player::UpdateSkillsForLevel()
5648 uint32 maxSkill
= GetMaxSkillValueForLevel();
5650 for (SkillStatusMap::iterator itr
= mSkillStatus
.begin(); itr
!= mSkillStatus
.end(); ++itr
)
5652 if (itr
->second
.uState
== SKILL_DELETED
)
5655 uint32 pskill
= itr
->first
;
5657 SkillLineEntry
const* pSkill
= sSkillLineStore
.LookupEntry(pskill
);
5661 if (GetSkillRangeType(pSkill
, false) != SKILL_RANGE_LEVEL
)
5663 // only weapons skills curently have SKILL_RANGE_LEVEL
5665 uint16 field
= itr
->second
.pos
/ 2;
5666 uint8 offset
= itr
->second
.pos
& 1; // itr->second.pos % 2
5668 uint16 max
= GetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
);
5670 /// update only level dependent max skill values
5673 SetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
, maxSkill
);
5674 if (itr
->second
.uState
!= SKILL_NEW
)
5675 itr
->second
.uState
= SKILL_CHANGED
;
5680 void Player::UpdateSkillsToMaxSkillsForLevel()
5682 for (SkillStatusMap::iterator itr
= mSkillStatus
.begin(); itr
!= mSkillStatus
.end(); ++itr
)
5684 if (itr
->second
.uState
== SKILL_DELETED
)
5687 uint32 pskill
= itr
->first
;
5688 if (IsProfessionOrRidingSkill(pskill
))
5690 uint16 field
= itr
->second
.pos
/ 2;
5691 uint8 offset
= itr
->second
.pos
& 1; // itr->second.pos % 2
5693 uint16 max
= GetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
);
5697 SetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
, max
);
5698 if (itr
->second
.uState
!= SKILL_NEW
)
5699 itr
->second
.uState
= SKILL_CHANGED
;
5700 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
, pskill
);
5705 // This functions sets a skill line value (and adds if doesn't exist yet)
5706 // To "remove" a skill line, set it's values to zero
5707 void Player::SetSkill(uint16 id
, uint16 currVal
, uint16 maxVal
, uint16 step
/*=0*/)
5713 SkillStatusMap::iterator itr
= mSkillStatus
.find(id
);
5716 if (itr
!= mSkillStatus
.end() && itr
->second
.uState
!= SKILL_DELETED
)
5718 uint16 field
= itr
->second
.pos
/ 2;
5719 uint8 offset
= itr
->second
.pos
& 1; // itr->second.pos % 2
5720 oldVal
= GetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
);
5723 SetUInt16Value(PLAYER_SKILL_STEP_0
+ field
, offset
, step
);
5725 SetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
, currVal
);
5726 SetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
, maxVal
);
5727 if (itr
->second
.uState
!= SKILL_NEW
)
5728 itr
->second
.uState
= SKILL_CHANGED
;
5729 learnSkillRewardedSpells(id
, oldVal
);
5730 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
, id
);
5731 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL
, id
);
5735 // clear skill fields
5736 SetUInt16Value(PLAYER_SKILL_LINEID_0
+ field
, offset
, 0);
5737 SetUInt16Value(PLAYER_SKILL_STEP_0
+ field
, offset
, 0);
5738 SetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
, 0);
5739 SetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
, 0);
5740 SetUInt16Value(PLAYER_SKILL_MODIFIER_0
+ field
, offset
, 0);
5741 SetUInt16Value(PLAYER_SKILL_TALENT_0
+ field
, offset
, 0);
5743 // mark as deleted or simply remove from map if not saved yet
5744 if (itr
->second
.uState
!= SKILL_NEW
)
5745 itr
->second
.uState
= SKILL_DELETED
;
5747 mSkillStatus
.erase(itr
);
5749 // remove all spells that related to this skill
5750 for (uint32 j
= 0; j
< sSkillLineAbilityStore
.GetNumRows(); ++j
)
5751 if (SkillLineAbilityEntry
const* pAbility
= sSkillLineAbilityStore
.LookupEntry(j
))
5752 if (pAbility
->skillId
== id
)
5753 removeSpell(sSpellMgr
.GetFirstSpellInChain(pAbility
->spellId
));
5756 else if (currVal
) // add
5758 for (int i
= 0; i
< PLAYER_MAX_SKILLS
; ++i
)
5760 uint16 field
= i
/ 2;
5761 uint8 offset
= i
& 1; // i % 2
5763 if (!GetUInt16Value(PLAYER_SKILL_LINEID_0
+ field
, offset
))
5765 SkillLineEntry
const* pSkill
= sSkillLineStore
.LookupEntry(id
);
5768 sLog
.outError("Skill not found in SkillLineStore: skill #%u", id
);
5772 SetUInt16Value(PLAYER_SKILL_LINEID_0
+ field
, offset
, id
);
5773 SetUInt16Value(PLAYER_SKILL_STEP_0
+ field
, offset
, step
);
5774 SetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
, currVal
);
5775 SetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
, maxVal
);
5776 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
, id
);
5777 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL
, id
);
5779 // insert new entry or update if not deleted old entry yet
5780 if (itr
!= mSkillStatus
.end())
5782 itr
->second
.pos
= i
;
5783 itr
->second
.uState
= SKILL_CHANGED
;
5786 mSkillStatus
.insert(SkillStatusMap::value_type(id
, SkillStatusData(i
, SKILL_NEW
)));
5788 // apply skill bonuses
5789 SetUInt16Value(PLAYER_SKILL_MODIFIER_0
+ field
, offset
, 0);
5790 SetUInt16Value(PLAYER_SKILL_TALENT_0
+ field
, offset
, 0);
5792 // temporary bonuses
5793 AuraList
const& mModSkill
= GetAurasByType(SPELL_AURA_MOD_SKILL
);
5794 for (AuraList::const_iterator j
= mModSkill
.begin(); j
!= mModSkill
.end(); ++j
)
5795 if ((*j
)->GetModifier()->m_miscvalue
== int32(id
))
5796 (*j
)->ApplyModifier(true);
5798 // permanent bonuses
5799 AuraList
const& mModSkillTalent
= GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT
);
5800 for (AuraList::const_iterator j
= mModSkillTalent
.begin(); j
!= mModSkillTalent
.end(); ++j
)
5801 if ((*j
)->GetModifier()->m_miscvalue
== int32(id
))
5802 (*j
)->ApplyModifier(true);
5804 // Learn all spells for skill
5805 learnSkillRewardedSpells(id
, currVal
);
5812 bool Player::HasSkill(uint32 skill
) const
5817 SkillStatusMap::const_iterator itr
= mSkillStatus
.find(skill
);
5818 return (itr
!= mSkillStatus
.end() && itr
->second
.uState
!= SKILL_DELETED
);
5821 uint16
Player::GetSkillStep(uint16 skill
) const
5826 SkillStatusMap::const_iterator itr
= mSkillStatus
.find(skill
);
5827 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5830 return GetUInt16Value(PLAYER_SKILL_STEP_0
+ itr
->second
.pos
/ 2, itr
->second
.pos
& 1);
5833 uint16
Player::GetSkillValue(uint32 skill
) const
5838 SkillStatusMap::const_iterator itr
= mSkillStatus
.find(skill
);
5839 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5842 uint16 field
= itr
->second
.pos
/ 2;
5843 uint8 offset
= itr
->second
.pos
& 1;
5845 int32 result
= int32(GetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
));
5846 result
+= int32(GetUInt16Value(PLAYER_SKILL_MODIFIER_0
+ field
, offset
));
5847 result
+= int32(GetUInt16Value(PLAYER_SKILL_TALENT_0
+ field
, offset
));
5848 return result
< 0 ? 0 : result
;
5851 uint16
Player::GetMaxSkillValue(uint32 skill
) const
5856 SkillStatusMap::const_iterator itr
= mSkillStatus
.find(skill
);
5857 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5860 uint16 field
= itr
->second
.pos
/ 2;
5861 uint8 offset
= itr
->second
.pos
& 1;
5863 int32 result
= int32(GetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
));
5864 result
+= int32(GetUInt16Value(PLAYER_SKILL_MODIFIER_0
+ field
, offset
));
5865 result
+= int32(GetUInt16Value(PLAYER_SKILL_TALENT_0
+ field
, offset
));
5866 return result
< 0 ? 0 : result
;
5869 uint16
Player::GetPureMaxSkillValue(uint32 skill
) const
5874 SkillStatusMap::const_iterator itr
= mSkillStatus
.find(skill
);
5875 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5878 uint16 field
= itr
->second
.pos
/ 2;
5879 uint8 offset
= itr
->second
.pos
& 1;
5881 return GetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
);
5884 uint16
Player::GetBaseSkillValue(uint32 skill
) const
5889 SkillStatusMap::const_iterator itr
= mSkillStatus
.find(skill
);
5890 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5893 uint16 field
= itr
->second
.pos
/ 2;
5894 uint8 offset
= itr
->second
.pos
& 1;
5896 int32 result
= int32(GetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
));
5897 result
+= int32(GetUInt16Value(PLAYER_SKILL_TALENT_0
+ field
, offset
));
5898 return result
< 0 ? 0 : result
;
5901 uint16
Player::GetPureSkillValue(uint32 skill
) const
5906 SkillStatusMap::const_iterator itr
= mSkillStatus
.find(skill
);
5907 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5910 uint16 field
= itr
->second
.pos
/ 2;
5911 uint8 offset
= itr
->second
.pos
& 1;
5913 return GetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
);
5916 int16
Player::GetSkillPermBonusValue(uint32 skill
) const
5921 SkillStatusMap::const_iterator itr
= mSkillStatus
.find(skill
);
5922 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5925 uint16 field
= itr
->second
.pos
/ 2;
5926 uint8 offset
= itr
->second
.pos
& 1;
5928 return GetUInt16Value(PLAYER_SKILL_TALENT_0
+ field
, offset
);
5931 int16
Player::GetSkillTempBonusValue(uint32 skill
) const
5936 SkillStatusMap::const_iterator itr
= mSkillStatus
.find(skill
);
5937 if (itr
== mSkillStatus
.end() || itr
->second
.uState
== SKILL_DELETED
)
5940 uint16 field
= itr
->second
.pos
/ 2;
5941 uint8 offset
= itr
->second
.pos
& 1;
5943 return GetUInt16Value(PLAYER_SKILL_MODIFIER_0
+ field
, offset
);
5946 void Player::SendInitialActionButtons() const
5948 DETAIL_LOG("Initializing Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec
);
5950 WorldPacket
data(SMSG_ACTION_BUTTONS
, 1 + (MAX_ACTION_BUTTONS
* 4));
5951 ActionButtonList
const& currentActionButtonList
= m_actionButtons
[m_activeSpec
];
5952 for (uint8 button
= 0; button
< MAX_ACTION_BUTTONS
; ++button
)
5954 ActionButtonList::const_iterator itr
= currentActionButtonList
.find(button
);
5955 if (itr
!= currentActionButtonList
.end() && itr
->second
.uState
!= ACTIONBUTTON_DELETED
)
5956 data
<< uint32(itr
->second
.packedData
);
5960 data
<< uint8(1); // talent spec amount (in packet)
5961 GetSession()->SendPacket(&data
);
5962 DETAIL_LOG("Action Buttons for '%u' spec '%u' Initialized", GetGUIDLow(), m_activeSpec
);
5965 void Player::SendLockActionButtons() const
5967 DETAIL_LOG("Locking Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec
);
5968 WorldPacket
data(SMSG_ACTION_BUTTONS
, 1);
5969 // sending 2 locks actions bars, neither user can remove buttons, nor client removes buttons at spell unlearn
5970 // they remain locked until server sends new action buttons
5972 GetSession()->SendPacket(&data
);
5975 bool Player::IsActionButtonDataValid(uint8 button
, uint32 action
, uint8 type
, Player
* player
, bool msg
)
5977 if (button
>= MAX_ACTION_BUTTONS
)
5982 sLog
.outError("Action %u not added into button %u for player %s: button must be < %u", action
, button
, player
->GetName(), MAX_ACTION_BUTTONS
);
5984 sLog
.outError("Table `playercreateinfo_action` have action %u into button %u : button must be < %u", action
, button
, MAX_ACTION_BUTTONS
);
5990 if (action
>= MAX_ACTION_BUTTON_ACTION_VALUE
)
5995 sLog
.outError("Action %u not added into button %u for player %s: action must be < %u", action
, button
, player
->GetName(), MAX_ACTION_BUTTON_ACTION_VALUE
);
5997 sLog
.outError("Table `playercreateinfo_action` have action %u into button %u : action must be < %u", action
, button
, MAX_ACTION_BUTTON_ACTION_VALUE
);
6004 case ACTION_BUTTON_SPELL
:
6006 SpellEntry
const* spellProto
= sSpellStore
.LookupEntry(action
);
6012 sLog
.outError("Spell action %u not added into button %u for player %s: spell not exist", action
, button
, player
->GetName());
6014 sLog
.outError("Table `playercreateinfo_action` have spell action %u into button %u: spell not exist", action
, button
);
6021 if (!player
->HasSpell(spellProto
->Id
))
6024 sLog
.outError("Spell action %u not added into button %u for player %s: player don't known this spell", action
, button
, player
->GetName());
6027 else if (IsPassiveSpell(spellProto
))
6030 sLog
.outError("Spell action %u not added into button %u for player %s: spell is passive", action
, button
, player
->GetName());
6033 // current range for button of totem bar is from ACTION_BUTTON_SHAMAN_TOTEMS_BAR to (but not including) ACTION_BUTTON_SHAMAN_TOTEMS_BAR + 12
6034 else if (button
>= ACTION_BUTTON_SHAMAN_TOTEMS_BAR
&& button
< (ACTION_BUTTON_SHAMAN_TOTEMS_BAR
+ 12)
6035 && !spellProto
->HasAttribute(SPELL_ATTR_EX7_TOTEM_SPELL
))
6038 sLog
.outError("Spell action %u not added into button %u for player %s: attempt to add non totem spell to totem bar", action
, button
, player
->GetName());
6044 case ACTION_BUTTON_ITEM
:
6046 if (!ObjectMgr::GetItemPrototype(action
))
6051 sLog
.outError("Item action %u not added into button %u for player %s: item not exist", action
, button
, player
->GetName());
6053 sLog
.outError("Table `playercreateinfo_action` have item action %u into button %u: item not exist", action
, button
);
6060 break; // other cases not checked at this moment
6066 ActionButton
* Player::addActionButton(uint8 spec
, uint8 button
, uint32 action
, uint8 type
)
6068 // check action only for active spec (so not check at copy/load passive spec)
6069 if (spec
== GetActiveSpec() && !IsActionButtonDataValid(button
, action
, type
, this))
6072 // it create new button (NEW state) if need or return existing
6073 ActionButton
& ab
= m_actionButtons
[spec
][button
];
6075 // set data and update to CHANGED if not NEW
6076 ab
.SetActionAndType(action
, ActionButtonType(type
));
6078 DETAIL_LOG("Player '%u' Added Action '%u' (type %u) to Button '%u' for spec %u", GetGUIDLow(), action
, uint32(type
), button
, spec
);
6082 void Player::removeActionButton(uint8 spec
, uint8 button
)
6084 ActionButtonList
& currentActionButtonList
= m_actionButtons
[spec
];
6085 ActionButtonList::iterator buttonItr
= currentActionButtonList
.find(button
);
6086 if (buttonItr
== currentActionButtonList
.end() || buttonItr
->second
.uState
== ACTIONBUTTON_DELETED
)
6089 if (buttonItr
->second
.uState
== ACTIONBUTTON_NEW
)
6090 currentActionButtonList
.erase(buttonItr
); // new and not saved
6092 buttonItr
->second
.uState
= ACTIONBUTTON_DELETED
; // saved, will deleted at next save
6094 DETAIL_LOG("Action Button '%u' Removed from Player '%u' for spec %u", button
, GetGUIDLow(), spec
);
6097 ActionButton
const* Player::GetActionButton(uint8 button
)
6099 ActionButtonList
& currentActionButtonList
= m_actionButtons
[m_activeSpec
];
6100 ActionButtonList::iterator buttonItr
= currentActionButtonList
.find(button
);
6101 if (buttonItr
== currentActionButtonList
.end() || buttonItr
->second
.uState
== ACTIONBUTTON_DELETED
)
6104 return &buttonItr
->second
;
6107 bool Player::SetPosition(float x
, float y
, float z
, float orientation
, bool teleport
)
6109 // prevent crash when a bad coord is sent by the client
6110 if (!MaNGOS::IsValidMapCoord(x
, y
, z
, orientation
))
6112 DEBUG_LOG("Player::SetPosition(%f, %f, %f, %f, %d) .. bad coordinates for player %d!", x
, y
, z
, orientation
, teleport
, GetGUIDLow());
6118 const float old_x
= GetPositionX();
6119 const float old_y
= GetPositionY();
6120 const float old_z
= GetPositionZ();
6121 const float old_r
= GetOrientation();
6123 if (teleport
|| old_x
!= x
|| old_y
!= y
|| old_z
!= z
|| old_r
!= orientation
)
6125 if (teleport
|| old_x
!= x
|| old_y
!= y
|| old_z
!= z
)
6126 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE
| AURA_INTERRUPT_FLAG_TURNING
);
6128 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING
);
6130 RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH
);
6132 // move and update visible state if need
6133 m
->PlayerRelocation(this, x
, y
, z
, orientation
);
6135 // reread after Map::Relocation
6142 if (GetGroup() && (old_x
!= x
|| old_y
!= y
))
6143 SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION
);
6145 if (GetTrader() && !IsWithinDistInMap(GetTrader(), INTERACTION_DISTANCE
))
6146 GetSession()->SendCancelTrade(); // will close both side trade windows
6149 // code block for underwater state update
6150 UpdateUnderwaterState(m
, x
, y
, z
);
6152 CheckAreaExploreAndOutdoor();
6157 void Player::SaveRecallPosition()
6159 m_recallMap
= GetMapId();
6160 m_recallX
= GetPositionX();
6161 m_recallY
= GetPositionY();
6162 m_recallZ
= GetPositionZ();
6163 m_recallO
= GetOrientation();
6166 void Player::SendMessageToSet(WorldPacket
* data
, bool self
)
6169 GetMap()->MessageBroadcast(this, data
, false);
6171 // if player is not in world and map in not created/already destroyed
6172 // no need to create one, just send packet for itself!
6174 GetSession()->SendPacket(data
);
6177 void Player::SendMessageToSetInRange(WorldPacket
* data
, float dist
, bool self
)
6180 GetMap()->MessageDistBroadcast(this, data
, dist
, false);
6183 GetSession()->SendPacket(data
);
6186 void Player::SendMessageToSetInRange(WorldPacket
* data
, float dist
, bool self
, bool own_team_only
)
6189 GetMap()->MessageDistBroadcast(this, data
, dist
, false, own_team_only
);
6192 GetSession()->SendPacket(data
);
6195 void Player::SendDirectMessage(WorldPacket
* data
)
6197 GetSession()->SendPacket(data
);
6200 void Player::SendCinematicStart(uint32 CinematicSequenceId
)
6202 WorldPacket
data(SMSG_TRIGGER_CINEMATIC
, 4);
6203 data
<< uint32(CinematicSequenceId
);
6204 SendDirectMessage(&data
);
6207 void Player::SendMovieStart(uint32 MovieId
)
6209 WorldPacket
data(SMSG_TRIGGER_MOVIE
, 4);
6210 data
<< uint32(MovieId
);
6211 SendDirectMessage(&data
);
6214 void Player::CheckAreaExploreAndOutdoor()
6223 uint16 areaFlag
= GetTerrain()->GetAreaFlag(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor
);
6227 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_RESTING
) && GetRestType() == REST_TYPE_IN_TAVERN
)
6229 AreaTriggerEntry
const* at
= sAreaTriggerStore
.LookupEntry(inn_trigger_id
);
6230 if (!at
|| !IsPointInAreaTriggerZone(at
, GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ()))
6232 // Player left inn (REST_TYPE_IN_CITY overrides REST_TYPE_IN_TAVERN, so just clear rest)
6233 SetRestType(REST_TYPE_NO
);
6237 else if (sWorld
.getConfig(CONFIG_BOOL_VMAP_INDOOR_CHECK
) && !isGameMaster())
6238 RemoveAurasWithAttribute(SPELL_ATTR_OUTDOORS_ONLY
);
6240 if (areaFlag
== 0xffff)
6242 int offset
= areaFlag
/ 32;
6244 if (offset
>= PLAYER_EXPLORED_ZONES_SIZE
)
6246 sLog
.outError("Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).", areaFlag
, GetPositionX(), GetPositionY(), offset
, offset
, PLAYER_EXPLORED_ZONES_SIZE
);
6250 uint32 val
= (uint32
)(1 << (areaFlag
% 32));
6251 uint32 currFields
= GetUInt32Value(PLAYER_EXPLORED_ZONES_1
+ offset
);
6253 if (!(currFields
& val
))
6255 SetUInt32Value(PLAYER_EXPLORED_ZONES_1
+ offset
, (uint32
)(currFields
| val
));
6257 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA
);
6259 AreaTableEntry
const* p
= GetAreaEntryByAreaFlagAndMap(areaFlag
, GetMapId());
6262 sLog
.outError("PLAYER: Player %u discovered unknown area (x: %f y: %f map: %u", GetGUIDLow(), GetPositionX(), GetPositionY(), GetMapId());
6264 else if (p
->area_level
> 0)
6266 uint32 area
= p
->ID
;
6267 if (getLevel() >= sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
6269 SendExplorationExperience(area
, 0);
6273 int32 diff
= int32(getLevel()) - p
->area_level
;
6277 XP
= uint32(sObjectMgr
.GetBaseXP(getLevel() + 5) * sWorld
.getConfig(CONFIG_FLOAT_RATE_XP_EXPLORE
));
6281 int32 exploration_percent
= (100 - ((diff
- 5) * 5));
6282 if (exploration_percent
> 100)
6283 exploration_percent
= 100;
6284 else if (exploration_percent
< 0)
6285 exploration_percent
= 0;
6287 XP
= uint32(sObjectMgr
.GetBaseXP(p
->area_level
) * exploration_percent
/ 100 * sWorld
.getConfig(CONFIG_FLOAT_RATE_XP_EXPLORE
));
6291 XP
= uint32(sObjectMgr
.GetBaseXP(p
->area_level
) * sWorld
.getConfig(CONFIG_FLOAT_RATE_XP_EXPLORE
));
6295 SendExplorationExperience(area
, XP
);
6297 DETAIL_LOG("PLAYER: Player %u discovered a new area: %u", GetGUIDLow(), area
);
6302 Team
Player::TeamForRace(uint8 race
)
6304 ChrRacesEntry
const* rEntry
= sChrRacesStore
.LookupEntry(race
);
6307 sLog
.outError("Race %u not found in DBC: wrong DBC files?", uint32(race
));
6311 switch (rEntry
->TeamID
)
6313 case 7: return ALLIANCE
;
6314 case 1: return HORDE
;
6317 sLog
.outError("Race %u have wrong teamid %u in DBC: wrong DBC files?", uint32(race
), rEntry
->TeamID
);
6321 uint32
Player::getFactionForRace(uint8 race
)
6323 ChrRacesEntry
const* rEntry
= sChrRacesStore
.LookupEntry(race
);
6326 sLog
.outError("Race %u not found in DBC: wrong DBC files?", uint32(race
));
6330 return rEntry
->FactionID
;
6333 void Player::setFactionForRace(uint8 race
)
6335 m_team
= TeamForRace(race
);
6336 setFaction(getFactionForRace(race
));
6339 ReputationRank
Player::GetReputationRank(uint32 faction
) const
6341 FactionEntry
const* factionEntry
= sFactionStore
.LookupEntry(faction
);
6342 return GetReputationMgr().GetRank(factionEntry
);
6345 // Calculate total reputation percent player gain with quest/creature level
6346 int32
Player::CalculateReputationGain(ReputationSource source
, int32 rep
, int32 faction
, uint32 creatureOrQuestLevel
, bool noAuraBonus
)
6348 float percent
= 100.0f
;
6350 float repMod
= noAuraBonus
? 0.0f
: (float)GetTotalAuraModifier(SPELL_AURA_MOD_REPUTATION_GAIN
);
6352 // faction specific auras only seem to apply to kills
6353 if (source
== REPUTATION_SOURCE_KILL
)
6354 repMod
+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_FACTION_REPUTATION_GAIN
, faction
);
6356 percent
+= rep
> 0 ? repMod
: -repMod
;
6361 case REPUTATION_SOURCE_KILL
:
6362 rate
= sWorld
.getConfig(CONFIG_FLOAT_RATE_REPUTATION_LOWLEVEL_KILL
);
6364 case REPUTATION_SOURCE_QUEST
:
6365 rate
= sWorld
.getConfig(CONFIG_FLOAT_RATE_REPUTATION_LOWLEVEL_QUEST
);
6367 case REPUTATION_SOURCE_SPELL
:
6373 if (rate
!= 1.0f
&& creatureOrQuestLevel
<= MaNGOS::XP::GetGrayLevel(getLevel()))
6376 if (percent
<= 0.0f
)
6379 // Multiply result with the faction specific rate
6380 if (const RepRewardRate
* repData
= sObjectMgr
.GetRepRewardRate(faction
))
6382 float repRate
= 0.0f
;
6385 case REPUTATION_SOURCE_KILL
:
6386 repRate
= repData
->creature_rate
;
6388 case REPUTATION_SOURCE_QUEST
:
6389 repRate
= repData
->quest_rate
;
6391 case REPUTATION_SOURCE_SPELL
:
6392 repRate
= repData
->spell_rate
;
6396 // for custom, a rate of 0.0 will totally disable reputation gain for this faction/type
6397 if (repRate
<= 0.0f
)
6403 return int32(sWorld
.getConfig(CONFIG_FLOAT_RATE_REPUTATION_GAIN
) * rep
* percent
/ 100.0f
);
6406 // Calculates how many reputation points player gains in victim's enemy factions
6407 void Player::RewardReputation(Unit
* pVictim
, float rate
)
6409 if (!pVictim
|| pVictim
->GetTypeId() == TYPEID_PLAYER
)
6412 // used current difficulty creature entry instead normal version (GetEntry())
6413 ReputationOnKillEntry
const* Rep
= sObjectMgr
.GetReputationOnKillEntry(((Creature
*)pVictim
)->GetCreatureInfo()->Entry
);
6418 uint32 repFaction1
= Rep
->repfaction1
;
6419 uint32 repFaction2
= Rep
->repfaction2
;
6421 // Championning tabard reputation system
6422 // Aura 57818 is a hidden aura common to tabards allowing championning.
6423 if (GetMap()->IsNonRaidDungeon() && HasAura(57818))
6425 MapEntry
const* storedMap
= sMapStore
.LookupEntry(GetMapId());
6426 InstanceTemplate
const* instance
= ObjectMgr::GetInstanceTemplate(GetMapId());
6427 Item
const* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_TABARD
);
6428 if (storedMap
&& instance
&& pItem
)
6430 ItemPrototype
const* pProto
= pItem
->GetProto();// Checked on load
6431 // The required MinLevel for the tabard to work is related to the item level of the tabard
6432 if ((instance
->levelMin
+ 1 >= pProto
->ItemLevel
|| !GetMap()->IsRegularDifficulty())
6433 // For ItemLevel == 75 (or 85) need to check expansion
6434 && (pProto
->ItemLevel
== 75 && storedMap
->Expansion() == 2))
6436 if (uint32 tabardFactionID
= pItem
->GetProto()->RequiredReputationFaction
)
6438 repFaction1
= tabardFactionID
;
6439 repFaction2
= tabardFactionID
;
6445 if (repFaction1
&& (!Rep
->team_dependent
|| GetTeam() == ALLIANCE
))
6447 int32 donerep1
= CalculateReputationGain(REPUTATION_SOURCE_KILL
, Rep
->repvalue1
, repFaction1
, pVictim
->getLevel());
6448 donerep1
= int32(donerep1
* rate
);
6449 FactionEntry
const* factionEntry1
= sFactionStore
.LookupEntry(repFaction1
);
6450 uint32 current_reputation_rank1
= GetReputationMgr().GetRank(factionEntry1
);
6451 if (factionEntry1
&& current_reputation_rank1
<= Rep
->reputation_max_cap1
)
6452 GetReputationMgr().ModifyReputation(factionEntry1
, donerep1
);
6454 // Wiki: Team factions value divided by 2
6455 if (factionEntry1
&& Rep
->is_teamaward1
)
6457 FactionEntry
const* team1_factionEntry
= sFactionStore
.LookupEntry(factionEntry1
->team
);
6458 if (team1_factionEntry
)
6459 GetReputationMgr().ModifyReputation(team1_factionEntry
, donerep1
/ 2);
6463 if (repFaction2
&& (!Rep
->team_dependent
|| GetTeam() == HORDE
))
6465 int32 donerep2
= CalculateReputationGain(REPUTATION_SOURCE_KILL
, Rep
->repvalue2
, repFaction2
, pVictim
->getLevel());
6466 donerep2
= int32(donerep2
* rate
);
6467 FactionEntry
const* factionEntry2
= sFactionStore
.LookupEntry(repFaction2
);
6468 uint32 current_reputation_rank2
= GetReputationMgr().GetRank(factionEntry2
);
6469 if (factionEntry2
&& current_reputation_rank2
<= Rep
->reputation_max_cap2
)
6470 GetReputationMgr().ModifyReputation(factionEntry2
, donerep2
);
6472 // Wiki: Team factions value divided by 2
6473 if (factionEntry2
&& Rep
->is_teamaward2
)
6475 FactionEntry
const* team2_factionEntry
= sFactionStore
.LookupEntry(factionEntry2
->team
);
6476 if (team2_factionEntry
)
6477 GetReputationMgr().ModifyReputation(team2_factionEntry
, donerep2
/ 2);
6482 // Calculate how many reputation points player gain with the quest
6483 void Player::RewardReputation(Quest
const* pQuest
)
6485 // quest reputation reward/loss
6486 for (int i
= 0; i
< QUEST_REPUTATIONS_COUNT
; ++i
)
6488 if (!pQuest
->RewRepFaction
[i
])
6491 // No diplomacy mod are applied to the final value (flat). Note the formula (finalValue = DBvalue/100)
6492 if (pQuest
->RewRepValue
[i
])
6494 int32 rep
= CalculateReputationGain(REPUTATION_SOURCE_QUEST
, pQuest
->RewRepValue
[i
] / 100, pQuest
->RewRepFaction
[i
], GetQuestLevelForPlayer(pQuest
), true);
6496 if (FactionEntry
const* factionEntry
= sFactionStore
.LookupEntry(pQuest
->RewRepFaction
[i
]))
6497 GetReputationMgr().ModifyReputation(factionEntry
, rep
);
6501 uint32 row
= ((pQuest
->RewRepValueId
[i
] < 0) ? 1 : 0) + 1;
6502 uint32 field
= abs(pQuest
->RewRepValueId
[i
]);
6504 if (const QuestFactionRewardEntry
* pRow
= sQuestFactionRewardStore
.LookupEntry(row
))
6506 int32 repPoints
= pRow
->rewardValue
[field
];
6511 repPoints
= CalculateReputationGain(REPUTATION_SOURCE_QUEST
, repPoints
, pQuest
->RewRepFaction
[i
], GetQuestLevelForPlayer(pQuest
));
6513 if (const FactionEntry
* factionEntry
= sFactionStore
.LookupEntry(pQuest
->RewRepFaction
[i
]))
6514 GetReputationMgr().ModifyReputation(factionEntry
, repPoints
);
6519 // TODO: implement reputation spillover
6522 void Player::UpdateHonorKills()
6524 /// called when rewarding honor and at each save
6525 time_t now
= time(NULL
);
6526 time_t today
= (time(NULL
) / DAY
) * DAY
;
6528 if (m_lastHonorKillsUpdateTime
< today
)
6530 time_t yesterday
= today
- DAY
;
6532 uint16 kills_today
= GetUInt16Value(PLAYER_FIELD_KILLS
, 0);
6534 // update yesterday's contribution
6535 if (m_lastHonorKillsUpdateTime
>= yesterday
)
6537 // this is the first update today, reset today's contribution
6538 SetUInt16Value(PLAYER_FIELD_KILLS
, 0, 0);
6539 SetUInt16Value(PLAYER_FIELD_KILLS
, 1, kills_today
);
6543 // no honor/kills yesterday or today, reset
6544 SetUInt32Value(PLAYER_FIELD_KILLS
, 0);
6548 m_lastHonorKillsUpdateTime
= now
;
6551 /// Calculate the amount of honor gained based on the victim
6552 /// and the size of the group for which the honor is divided
6553 /// An exact honor value can also be given (overriding the calcs)
6554 bool Player::RewardHonor(Unit
* uVictim
, uint32 groupsize
, float honor
)
6556 // do not reward honor in arenas, but enable onkill spellproc
6559 if (!uVictim
|| uVictim
== this || uVictim
->GetTypeId() != TYPEID_PLAYER
)
6562 if (GetBGTeam() == ((Player
*)uVictim
)->GetBGTeam())
6568 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
6569 if (GetDummyAura(SPELL_AURA_PLAYER_INACTIVE
))
6572 ObjectGuid victim_guid
;
6573 uint32 victim_rank
= 0;
6575 // need call before fields update to have chance move yesterday data to appropriate fields before today data change.
6580 if (!uVictim
|| uVictim
== this || uVictim
->HasAuraType(SPELL_AURA_NO_PVP_CREDIT
))
6583 victim_guid
= uVictim
->GetObjectGuid();
6585 if (uVictim
->GetTypeId() == TYPEID_PLAYER
)
6587 Player
* pVictim
= (Player
*)uVictim
;
6589 if (GetTeam() == pVictim
->GetTeam() && !sWorld
.IsFFAPvPRealm())
6592 float f
= 1; // need for total kills (?? need more info)
6594 uint32 k_level
= getLevel();
6595 uint32 v_level
= pVictim
->getLevel();
6598 // PLAYER_CHOSEN_TITLE VALUES DESCRIPTION
6600 // [1..14] Alliance honor titles and player name
6601 // [15..28] Horde honor titles and player name
6602 // [29..38] Other title and player name
6604 uint32 victim_title
= pVictim
->GetUInt32Value(PLAYER_CHOSEN_TITLE
);
6605 // Get Killer titles, CharTitlesEntry::bit_index
6607 // title[1..14] -> rank[5..18]
6608 // title[15..28] -> rank[5..18]
6609 // title[other] -> 0
6610 if (victim_title
== 0)
6611 victim_guid
.Clear(); // Don't show HK: <rank> message, only log.
6612 else if (victim_title
< 15)
6613 victim_rank
= victim_title
+ 4;
6614 else if (victim_title
< 29)
6615 victim_rank
= victim_title
- 14 + 4;
6617 victim_guid
.Clear(); // Don't show HK: <rank> message, only log.
6620 k_grey
= MaNGOS::XP::GetGrayLevel(k_level
);
6622 if (v_level
<= k_grey
)
6625 float diff_level
= (k_level
== k_grey
) ? 1 : ((float(v_level
) - float(k_grey
)) / (float(k_level
) - float(k_grey
)));
6627 int32 v_rank
= 1; // need more info
6629 honor
= ((f
* diff_level
* (190 + v_rank
* 10)) / 6);
6630 honor
*= float(k_level
) / 70.0f
; // factor of dependence on levels of the killer
6632 // count the number of playerkills in one day
6633 ApplyModUInt32Value(PLAYER_FIELD_KILLS
, 1, true);
6634 // and those in a lifetime
6635 ApplyModUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS
, 1, true);
6636 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL
);
6637 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS
, pVictim
->getClass());
6638 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_RACE
, pVictim
->getRace());
6642 Creature
* cVictim
= (Creature
*)uVictim
;
6644 if (!cVictim
->IsRacialLeader())
6647 honor
= 100; // ??? need more info
6648 victim_rank
= 19; // HK: Leader
6652 if (uVictim
!= NULL
)
6654 honor
*= sWorld
.getConfig(CONFIG_FLOAT_RATE_HONOR
);
6655 honor
*= (GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HONOR_GAIN
) + 100.0f
) / 100.0f
;
6660 honor
*= (((float)urand(8, 12)) / 10); // approx honor: 80% - 120% of real honor
6663 // honor - for show honor points in log
6664 // victim_guid - for show victim name in log
6665 // victim_rank [1..4] HK: <dishonored rank>
6666 // victim_rank [5..19] HK: <alliance\horde rank>
6667 // victim_rank [0,20+] HK: <>
6668 WorldPacket
data(SMSG_PVP_CREDIT
, 4 + 8 + 4);
6669 data
<< uint32(honor
);
6670 data
<< ObjectGuid(victim_guid
);
6671 data
<< uint32(victim_rank
);
6672 GetSession()->SendPacket(&data
);
6675 ModifyCurrencyCount(CURRENCY_HONOR_POINTS
, int32(honor
));
6680 void Player::SetInGuild(uint32 GuildId
)
6683 SetGuidValue(OBJECT_FIELD_DATA
, ObjectGuid(HIGHGUID_GUILD
, 0, GuildId
));
6685 SetGuidValue(OBJECT_FIELD_DATA
, ObjectGuid());
6687 ApplyModFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GUILD_LEVELING_ENABLED
, GuildId
!= 0 && sWorld
.getConfig(CONFIG_BOOL_GUILD_LEVELING_ENABLED
));
6688 SetUInt16Value(OBJECT_FIELD_TYPE
, 1, GuildId
!= 0);
6691 std::string
Player::GetGuildName() const
6693 if (Guild
* guild
= sGuildMgr
.GetGuildById(GetGuildId()))
6694 return guild
->GetName();
6699 uint32
Player::GetGuildIdFromDB(ObjectGuid guid
)
6701 uint32 lowguid
= guid
.GetCounter();
6703 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT guildid FROM guild_member WHERE guid='%u'", lowguid
);
6707 uint32 id
= result
->Fetch()[0].GetUInt32();
6712 ObjectGuid
Player::GetGuildGuidFromDB(ObjectGuid guid
)
6714 if (uint32 guildId
= GetGuildIdFromDB(guid
))
6715 return ObjectGuid(HIGHGUID_GUILD
, GetGuildIdFromDB(guid
));
6717 return ObjectGuid();
6720 uint32
Player::GetRankFromDB(ObjectGuid guid
)
6722 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT rank FROM guild_member WHERE guid='%u'", guid
.GetCounter());
6725 uint32 v
= result
->Fetch()[0].GetUInt32();
6733 void Player::SendGuildDeclined(std::string name
, bool autodecline
)
6735 WorldPacket
data(SMSG_GUILD_DECLINE
, 10);
6737 data
<< uint8(autodecline
);
6738 GetSession()->SendPacket(&data
);
6742 uint32
Player::GetArenaTeamIdFromDB(ObjectGuid guid
, ArenaType type
)
6744 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid='%u' AND type='%u' LIMIT 1", guid
.GetCounter(), type
);
6748 uint32 id
= (*result
)[0].GetUInt32();
6753 uint32
Player::GetZoneIdFromDB(ObjectGuid guid
)
6755 uint32 lowguid
= guid
.GetCounter();
6756 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT zone FROM characters WHERE guid='%u'", lowguid
);
6759 Field
* fields
= result
->Fetch();
6760 uint32 zone
= fields
[0].GetUInt32();
6765 // stored zone is zero, use generic and slow zone detection
6766 result
= CharacterDatabase
.PQuery("SELECT map,position_x,position_y,position_z FROM characters WHERE guid='%u'", lowguid
);
6769 fields
= result
->Fetch();
6770 uint32 map
= fields
[0].GetUInt32();
6771 float posx
= fields
[1].GetFloat();
6772 float posy
= fields
[2].GetFloat();
6773 float posz
= fields
[3].GetFloat();
6776 zone
= sTerrainMgr
.GetZoneId(map
, posx
, posy
, posz
);
6779 CharacterDatabase
.PExecute("UPDATE characters SET zone='%u' WHERE guid='%u'", zone
, lowguid
);
6785 uint32
Player::GetLevelFromDB(ObjectGuid guid
)
6787 uint32 lowguid
= guid
.GetCounter();
6789 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT level FROM characters WHERE guid='%u'", lowguid
);
6793 Field
* fields
= result
->Fetch();
6794 uint32 level
= fields
[0].GetUInt32();
6800 void Player::UpdateArea(uint32 newArea
)
6802 m_areaUpdateId
= newArea
;
6804 AreaTableEntry
const* area
= GetAreaEntryByAreaID(newArea
);
6806 // FFA_PVP flags are area and not zone id dependent
6807 // so apply them accordingly
6808 if (area
&& (area
->flags
& AREA_FLAG_ARENA
))
6810 if (!isGameMaster())
6815 // remove ffa flag only if not ffapvp realm
6816 // removal in sanctuaries and capitals is handled in zone update
6817 if (IsFFAPvP() && !sWorld
.IsFFAPvPRealm())
6823 // Dalaran restricted flight zone
6824 if ((area
->flags
& AREA_FLAG_CANNOT_FLY
) && IsFreeFlying() && !isGameMaster() && !HasAura(58600))
6825 CastSpell(this, 58600, true); // Restricted Flight Area
6827 // TODO: implement wintergrasp parachute when battle in progress
6828 /* if ((area->flags & AREA_FLAG_OUTDOOR_PVP) && IsFreeFlying() && <WINTERGRASP_BATTLE_IN_PROGRESS> && !isGameMaster())
6829 CastSpell(this, 58730, true); */
6832 UpdateAreaDependentAuras();
6835 bool Player::CanUseCapturePoint()
6837 return isAlive() && // living
6838 !HasStealthAura() && // not stealthed
6839 !HasInvisibilityAura() && // visible
6840 (IsPvP() || sWorld
.IsPvPRealm()) &&
6841 !HasMovementFlag(MOVEFLAG_FLYING
) &&
6846 void Player::UpdateZone(uint32 newZone
, uint32 newArea
)
6848 AreaTableEntry
const* zone
= GetAreaEntryByAreaID(newZone
);
6852 if (m_zoneUpdateId
!= newZone
)
6854 // handle outdoor pvp zones
6855 sOutdoorPvPMgr
.HandlePlayerLeaveZone(this, m_zoneUpdateId
);
6856 sOutdoorPvPMgr
.HandlePlayerEnterZone(this, newZone
);
6858 SendInitWorldStates(newZone
, newArea
); // only if really enters to new zone, not just area change, works strange...
6860 if (sWorld
.getConfig(CONFIG_BOOL_WEATHER
))
6862 if (Weather
* wth
= sWorld
.FindWeather(zone
->ID
))
6863 wth
->SendWeatherUpdateToPlayer(this);
6864 else if (!sWorld
.AddWeather(zone
->ID
))
6866 // send fine weather packet to remove old zone's weather
6867 Weather::SendFineWeatherUpdateToPlayer(this);
6872 m_zoneUpdateId
= newZone
;
6873 m_zoneUpdateTimer
= ZONE_UPDATE_INTERVAL
;
6875 // zone changed, so area changed as well, update it
6876 UpdateArea(newArea
);
6878 // in PvP, any not controlled zone (except zone->team == 6, default case)
6879 // in PvE, only opposition team capital
6883 pvpInfo
.inHostileArea
= GetTeam() != ALLIANCE
&& (sWorld
.IsPvPRealm() || zone
->flags
& AREA_FLAG_CAPITAL
);
6885 case AREATEAM_HORDE
:
6886 pvpInfo
.inHostileArea
= GetTeam() != HORDE
&& (sWorld
.IsPvPRealm() || zone
->flags
& AREA_FLAG_CAPITAL
);
6889 // overwrite for battlegrounds, maybe batter some zone flags but current known not 100% fit to this
6890 pvpInfo
.inHostileArea
= sWorld
.IsPvPRealm() || InBattleGround();
6892 default: // 6 in fact
6893 pvpInfo
.inHostileArea
= false;
6897 if (pvpInfo
.inHostileArea
) // in hostile area
6899 if (!IsPvP() || pvpInfo
.endTimer
!= 0)
6900 UpdatePvP(true, true);
6902 else // in friendly area
6904 if (IsPvP() && !HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_IN_PVP
) && pvpInfo
.endTimer
== 0)
6905 pvpInfo
.endTimer
= time(0); // start toggle-off
6908 if (zone
->flags
& AREA_FLAG_SANCTUARY
) // in sanctuary
6910 SetByteFlag(UNIT_FIELD_BYTES_2
, 1, UNIT_BYTE2_FLAG_SANCTUARY
);
6911 if (sWorld
.IsFFAPvPRealm())
6916 RemoveByteFlag(UNIT_FIELD_BYTES_2
, 1, UNIT_BYTE2_FLAG_SANCTUARY
);
6919 if (zone
->flags
& AREA_FLAG_CAPITAL
) // in capital city
6920 SetRestType(REST_TYPE_IN_CITY
);
6921 else if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_RESTING
) && GetRestType() != REST_TYPE_IN_TAVERN
)
6922 // resting and not in tavern (leave city then); tavern leave handled in CheckAreaExploreAndOutdoor
6923 SetRestType(REST_TYPE_NO
);
6925 // remove items with area/map limitations (delete only for alive player to allow back in ghost mode)
6926 // if player resurrected at teleport this will be applied in resurrect code
6928 DestroyZoneLimitedItem(true, newZone
);
6930 // check some item equip limitations (in result lost CanTitanGrip at talent reset, for example)
6931 AutoUnequipOffhandIfNeed();
6933 // recent client version not send leave/join channel packets for built-in local channels
6934 UpdateLocalChannels(newZone
);
6938 SetGroupUpdateFlag(GROUP_UPDATE_FLAG_ZONE
);
6940 UpdateZoneDependentAuras();
6941 UpdateZoneDependentPets();
6944 // If players are too far way of duel flag... then player loose the duel
6945 void Player::CheckDuelDistance(time_t currTime
)
6950 GameObject
* obj
= GetMap()->GetGameObject(GetGuidValue(PLAYER_DUEL_ARBITER
));
6953 // player not at duel start map
6954 DuelComplete(DUEL_FLED
);
6958 if (duel
->outOfBound
== 0)
6960 if (!IsWithinDistInMap(obj
, 50))
6962 duel
->outOfBound
= currTime
;
6964 WorldPacket
data(SMSG_DUEL_OUTOFBOUNDS
, 0);
6965 GetSession()->SendPacket(&data
);
6970 if (IsWithinDistInMap(obj
, 40))
6972 duel
->outOfBound
= 0;
6974 WorldPacket
data(SMSG_DUEL_INBOUNDS
, 0);
6975 GetSession()->SendPacket(&data
);
6977 else if (currTime
>= (duel
->outOfBound
+ 10))
6979 DuelComplete(DUEL_FLED
);
6984 void Player::DuelComplete(DuelCompleteType type
)
6986 // duel not requested
6990 WorldPacket
data(SMSG_DUEL_COMPLETE
, (1));
6991 data
<< (uint8
)((type
!= DUEL_INTERRUPTED
) ? 1 : 0);
6992 GetSession()->SendPacket(&data
);
6993 duel
->opponent
->GetSession()->SendPacket(&data
);
6995 if (type
!= DUEL_INTERRUPTED
)
6997 data
.Initialize(SMSG_DUEL_WINNER
, (1 + 20)); // we guess size
6998 data
<< (uint8
)((type
== DUEL_WON
) ? 0 : 1); // 0 = just won; 1 = fled
6999 data
<< duel
->opponent
->GetName();
7001 SendMessageToSet(&data
, true);
7004 if (type
== DUEL_WON
)
7006 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL
, 1);
7008 duel
->opponent
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL
, 1);
7011 // Remove Duel Flag object
7012 if (GameObject
* obj
= GetMap()->GetGameObject(GetGuidValue(PLAYER_DUEL_ARBITER
)))
7013 duel
->initiator
->RemoveGameObject(obj
, true);
7016 std::vector
<uint32
> auras2remove
;
7017 SpellAuraHolderMap
const& vAuras
= duel
->opponent
->GetSpellAuraHolderMap();
7018 for (SpellAuraHolderMap::const_iterator i
= vAuras
.begin(); i
!= vAuras
.end(); ++i
)
7020 if (!i
->second
->IsPositive() && i
->second
->GetCasterGuid() == GetObjectGuid() && i
->second
->GetAuraApplyTime() >= duel
->startTime
)
7021 auras2remove
.push_back(i
->second
->GetId());
7024 for (size_t i
= 0; i
< auras2remove
.size(); ++i
)
7025 duel
->opponent
->RemoveAurasDueToSpell(auras2remove
[i
]);
7027 auras2remove
.clear();
7028 SpellAuraHolderMap
const& auras
= GetSpellAuraHolderMap();
7029 for (SpellAuraHolderMap::const_iterator i
= auras
.begin(); i
!= auras
.end(); ++i
)
7031 if (!i
->second
->IsPositive() && i
->second
->GetCasterGuid() == duel
->opponent
->GetObjectGuid() && i
->second
->GetAuraApplyTime() >= duel
->startTime
)
7032 auras2remove
.push_back(i
->second
->GetId());
7034 for (size_t i
= 0; i
< auras2remove
.size(); ++i
)
7035 RemoveAurasDueToSpell(auras2remove
[i
]);
7037 // cleanup combo points
7038 if (GetComboTargetGuid() == duel
->opponent
->GetObjectGuid())
7040 else if (GetComboTargetGuid() == duel
->opponent
->GetPetGuid())
7043 if (duel
->opponent
->GetComboTargetGuid() == GetObjectGuid())
7044 duel
->opponent
->ClearComboPoints();
7045 else if (duel
->opponent
->GetComboTargetGuid() == GetPetGuid())
7046 duel
->opponent
->ClearComboPoints();
7049 SetGuidValue(PLAYER_DUEL_ARBITER
, ObjectGuid());
7050 SetUInt32Value(PLAYER_DUEL_TEAM
, 0);
7051 duel
->opponent
->SetGuidValue(PLAYER_DUEL_ARBITER
, ObjectGuid());
7052 duel
->opponent
->SetUInt32Value(PLAYER_DUEL_TEAM
, 0);
7054 delete duel
->opponent
->duel
;
7055 duel
->opponent
->duel
= NULL
;
7060 //---------------------------------------------------------//
7062 void Player::_ApplyItemMods(Item
* item
, uint8 slot
, bool apply
)
7064 if (slot
>= INVENTORY_SLOT_BAG_END
|| !item
)
7067 // not apply/remove mods for broken item
7068 if (item
->IsBroken())
7071 ItemPrototype
const* proto
= item
->GetProto();
7076 DETAIL_LOG("applying mods for item %u ", item
->GetGUIDLow());
7078 uint32 attacktype
= Player::GetAttackBySlot(slot
);
7079 if (attacktype
< MAX_ATTACK
)
7080 _ApplyWeaponDependentAuraMods(item
, WeaponAttackType(attacktype
), apply
);
7082 _ApplyItemBonuses(proto
, slot
, apply
);
7084 if (slot
== EQUIPMENT_SLOT_RANGED
)
7085 _ApplyAmmoBonuses();
7087 ApplyItemEquipSpell(item
, apply
);
7088 ApplyEnchantment(item
, apply
);
7090 if (proto
->Socket
[0].Color
) // only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items
7091 CorrectMetaGemEnchants(slot
, apply
);
7093 DEBUG_LOG("_ApplyItemMods complete.");
7096 void Player::_ApplyItemBonuses(ItemPrototype
const* proto
, uint8 slot
, bool apply
, bool only_level_scale
/*= false*/)
7098 if (slot
>= INVENTORY_SLOT_BAG_END
|| !proto
)
7101 ScalingStatDistributionEntry
const* ssd
= proto
->ScalingStatDistribution
? sScalingStatDistributionStore
.LookupEntry(proto
->ScalingStatDistribution
) : NULL
;
7102 if (only_level_scale
&& !ssd
)
7105 // req. check at equip, but allow use for extended range if range limit max level, set proper level
7106 uint32 ssd_level
= getLevel();
7107 if (ssd
&& ssd_level
> ssd
->MaxLevel
)
7108 ssd_level
= ssd
->MaxLevel
;
7110 ScalingStatValuesEntry
const* ssv
= proto
->StatScalingFactor
? sScalingStatValuesStore
.LookupEntry(ssd_level
) : NULL
;
7111 if (only_level_scale
&& !ssv
)
7114 for (uint32 i
= 0; i
< MAX_ITEM_PROTO_STATS
; ++i
)
7116 uint32 statType
= 0;
7118 // If set ScalingStatDistribution need get stats and values from it
7121 if (ssd
->StatMod
[i
] < 0)
7123 statType
= ssd
->StatMod
[i
];
7124 val
= (ssv
->getssdMultiplier(proto
->StatScalingFactor
) * ssd
->Modifier
[i
]) / 10000;
7128 statType
= proto
->ItemStat
[i
].ItemStatType
;
7129 val
= proto
->ItemStat
[i
].ItemStatValue
;
7137 /*case ITEM_MOD_MANA:
7138 HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply);
7140 case ITEM_MOD_AGILITY
: // modify agility
7141 HandleStatModifier(UNIT_MOD_STAT_AGILITY
, BASE_VALUE
, float(val
), apply
);
7142 ApplyStatBuffMod(STAT_AGILITY
, float(val
), apply
);
7144 case ITEM_MOD_STRENGTH
: // modify strength
7145 HandleStatModifier(UNIT_MOD_STAT_STRENGTH
, BASE_VALUE
, float(val
), apply
);
7146 ApplyStatBuffMod(STAT_STRENGTH
, float(val
), apply
);
7148 case ITEM_MOD_INTELLECT
: // modify intellect
7149 HandleStatModifier(UNIT_MOD_STAT_INTELLECT
, BASE_VALUE
, float(val
), apply
);
7150 ApplyStatBuffMod(STAT_INTELLECT
, float(val
), apply
);
7152 case ITEM_MOD_SPIRIT
: // modify spirit
7153 HandleStatModifier(UNIT_MOD_STAT_SPIRIT
, BASE_VALUE
, float(val
), apply
);
7154 ApplyStatBuffMod(STAT_SPIRIT
, float(val
), apply
);
7156 case ITEM_MOD_STAMINA
: // modify stamina
7157 HandleStatModifier(UNIT_MOD_STAT_STAMINA
, BASE_VALUE
, float(val
), apply
);
7158 ApplyStatBuffMod(STAT_STAMINA
, float(val
), apply
);
7160 case ITEM_MOD_DODGE_RATING
:
7161 ApplyRatingMod(CR_DODGE
, int32(val
), apply
);
7163 case ITEM_MOD_PARRY_RATING
:
7164 ApplyRatingMod(CR_PARRY
, int32(val
), apply
);
7166 case ITEM_MOD_CRIT_RANGED_RATING
:
7167 ApplyRatingMod(CR_CRIT_RANGED
, int32(val
), apply
);
7169 case ITEM_MOD_HIT_RATING
:
7170 ApplyRatingMod(CR_HIT_MELEE
, int32(val
), apply
);
7171 ApplyRatingMod(CR_HIT_RANGED
, int32(val
), apply
);
7172 ApplyRatingMod(CR_HIT_SPELL
, int32(val
), apply
);
7174 case ITEM_MOD_CRIT_RATING
:
7175 ApplyRatingMod(CR_CRIT_MELEE
, int32(val
), apply
);
7176 ApplyRatingMod(CR_CRIT_RANGED
, int32(val
), apply
);
7177 ApplyRatingMod(CR_CRIT_SPELL
, int32(val
), apply
);
7179 case ITEM_MOD_RESILIENCE_RATING
:
7180 ApplyRatingMod(CR_RESILIENCE_DAMAGE_TAKEN
, int32(val
), apply
);
7182 case ITEM_MOD_HASTE_RATING
:
7183 ApplyRatingMod(CR_HASTE_MELEE
, int32(val
), apply
);
7184 ApplyRatingMod(CR_HASTE_RANGED
, int32(val
), apply
);
7185 ApplyRatingMod(CR_HASTE_SPELL
, int32(val
), apply
);
7187 case ITEM_MOD_EXPERTISE_RATING
:
7188 ApplyRatingMod(CR_EXPERTISE
, int32(val
), apply
);
7190 case ITEM_MOD_ATTACK_POWER
:
7191 HandleStatModifier(UNIT_MOD_ATTACK_POWER
, TOTAL_VALUE
, float(val
), apply
);
7192 HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED
, TOTAL_VALUE
, float(val
), apply
);
7194 case ITEM_MOD_RANGED_ATTACK_POWER
:
7195 HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED
, TOTAL_VALUE
, float(val
), apply
);
7197 case ITEM_MOD_SPELL_POWER
:
7198 ApplySpellPowerBonus(int32(val
), apply
);
7200 case ITEM_MOD_HEALTH_REGEN
:
7201 ApplyHealthRegenBonus(int32(val
), apply
);
7203 case ITEM_MOD_SPELL_PENETRATION
:
7204 ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE
, -int32(val
), apply
);
7205 m_spellPenetrationItemMod
+= apply
? val
: -val
;
7207 case ITEM_MOD_MASTERY_RATING
:
7208 ApplyRatingMod(CR_MASTERY
, int32(val
), apply
);
7210 // deprecated item mods
7211 case ITEM_MOD_HEALTH
:
7212 case ITEM_MOD_DEFENSE_SKILL_RATING
:
7213 case ITEM_MOD_BLOCK_RATING
:
7214 case ITEM_MOD_HIT_MELEE_RATING
:
7215 case ITEM_MOD_HIT_RANGED_RATING
:
7216 case ITEM_MOD_HIT_SPELL_RATING
:
7217 case ITEM_MOD_CRIT_MELEE_RATING
:
7218 case ITEM_MOD_CRIT_SPELL_RATING
:
7219 case ITEM_MOD_HIT_TAKEN_MELEE_RATING
:
7220 case ITEM_MOD_HIT_TAKEN_RANGED_RATING
:
7221 case ITEM_MOD_HIT_TAKEN_SPELL_RATING
:
7222 case ITEM_MOD_CRIT_TAKEN_MELEE_RATING
:
7223 case ITEM_MOD_CRIT_TAKEN_RANGED_RATING
:
7224 case ITEM_MOD_CRIT_TAKEN_SPELL_RATING
:
7225 case ITEM_MOD_HASTE_MELEE_RATING
:
7226 case ITEM_MOD_HASTE_RANGED_RATING
:
7227 case ITEM_MOD_HASTE_SPELL_RATING
:
7228 case ITEM_MOD_HIT_TAKEN_RATING
:
7229 case ITEM_MOD_CRIT_TAKEN_RATING
:
7230 case ITEM_MOD_FERAL_ATTACK_POWER
:
7231 case ITEM_MOD_SPELL_HEALING_DONE
:
7232 case ITEM_MOD_SPELL_DAMAGE_DONE
:
7233 case ITEM_MOD_MANA_REGENERATION
:
7234 case ITEM_MOD_ARMOR_PENETRATION_RATING
:
7235 case ITEM_MOD_BLOCK_VALUE
:
7240 // Apply Spell Power from ScalingStatValue if set
7243 if (int32 spellbonus
= ssv
->getSpellBonus(proto
->StatScalingFactor
))
7244 ApplySpellPowerBonus(spellbonus
, apply
);
7247 // If set ScalingStatValue armor get it or use item armor
7248 uint32 armor
= proto
->GetArmor();
7251 if (uint32 ssvarmor
= ssv
->getArmorMod(proto
->StatScalingFactor
))
7254 // Add armor bonus from ArmorDamageModifier if > 0
7255 if (proto
->ArmorDamageModifier
> 0)
7256 armor
+= uint32(proto
->ArmorDamageModifier
);
7260 switch (proto
->InventoryType
)
7262 case INVTYPE_TRINKET
:
7265 case INVTYPE_FINGER
:
7266 HandleStatModifier(UNIT_MOD_ARMOR
, TOTAL_VALUE
, float(armor
), apply
);
7269 HandleStatModifier(UNIT_MOD_ARMOR
, BASE_VALUE
, float(armor
), apply
);
7274 WeaponAttackType attType
= BASE_ATTACK
;
7275 float damage
= 0.0f
;
7277 if (slot
== EQUIPMENT_SLOT_RANGED
&& (
7278 proto
->InventoryType
== INVTYPE_RANGED
|| proto
->InventoryType
== INVTYPE_THROWN
||
7279 proto
->InventoryType
== INVTYPE_RANGEDRIGHT
))
7281 attType
= RANGED_ATTACK
;
7283 else if (slot
== EQUIPMENT_SLOT_OFFHAND
)
7285 attType
= OFF_ATTACK
;
7288 float minDamage
= proto
->GetMinDamage();
7289 float maxDamage
= proto
->GetMaxDamage();
7291 // If set dpsMod in ScalingStatValue use it for min (70% from average), max (130% from average) damage
7294 if ((extraDPS
= ssv
->getDPSMod(proto
->StatScalingFactor
)))
7296 float average
= extraDPS
* proto
->Delay
/ 1000.0f
;
7297 minDamage
= 0.7f
* average
;
7298 maxDamage
= 1.3f
* average
;
7303 damage
= apply
? minDamage
: BASE_MINDAMAGE
;
7304 SetBaseWeaponDamage(attType
, MINDAMAGE
, damage
);
7305 // sLog.outError("applying mindam: assigning %f to weapon mindamage, now is: %f", damage, GetWeaponDamageRange(attType, MINDAMAGE));
7310 damage
= apply
? maxDamage
: BASE_MAXDAMAGE
;
7311 SetBaseWeaponDamage(attType
, MAXDAMAGE
, damage
);
7314 if (!CanUseEquippedWeapon(attType
))
7319 if (slot
== EQUIPMENT_SLOT_RANGED
)
7320 SetAttackTime(RANGED_ATTACK
, apply
? proto
->Delay
: BASE_ATTACK_TIME
);
7321 else if (slot
== EQUIPMENT_SLOT_MAINHAND
)
7322 SetAttackTime(BASE_ATTACK
, apply
? proto
->Delay
: BASE_ATTACK_TIME
);
7323 else if (slot
== EQUIPMENT_SLOT_OFFHAND
)
7324 SetAttackTime(OFF_ATTACK
, apply
? proto
->Delay
: BASE_ATTACK_TIME
);
7327 if (CanModifyStats() && (damage
|| proto
->Delay
))
7328 UpdateDamagePhysical(attType
);
7331 void Player::_ApplyWeaponDependentAuraMods(Item
* item
, WeaponAttackType attackType
, bool apply
)
7333 AuraList
const& auraCritList
= GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT
);
7334 for (AuraList::const_iterator itr
= auraCritList
.begin(); itr
!= auraCritList
.end(); ++itr
)
7335 _ApplyWeaponDependentAuraCritMod(item
, attackType
, *itr
, apply
);
7337 AuraList
const& auraDamageFlatList
= GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE
);
7338 for (AuraList::const_iterator itr
= auraDamageFlatList
.begin(); itr
!= auraDamageFlatList
.end(); ++itr
)
7339 _ApplyWeaponDependentAuraDamageMod(item
, attackType
, *itr
, apply
);
7341 AuraList
const& auraDamagePCTList
= GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE
);
7342 for (AuraList::const_iterator itr
= auraDamagePCTList
.begin(); itr
!= auraDamagePCTList
.end(); ++itr
)
7343 _ApplyWeaponDependentAuraDamageMod(item
, attackType
, *itr
, apply
);
7346 void Player::_ApplyWeaponDependentAuraCritMod(Item
* item
, WeaponAttackType attackType
, Aura
* aura
, bool apply
)
7348 // generic not weapon specific case processes in aura code
7349 if(aura
->GetSpellProto()->GetEquippedItemClass() == -1)
7352 BaseModGroup mod
= BASEMOD_END
;
7355 case BASE_ATTACK
: mod
= CRIT_PERCENTAGE
; break;
7356 case OFF_ATTACK
: mod
= OFFHAND_CRIT_PERCENTAGE
; break;
7357 case RANGED_ATTACK
: mod
= RANGED_CRIT_PERCENTAGE
; break;
7361 if (item
->IsFitToSpellRequirements(aura
->GetSpellProto()))
7363 HandleBaseModValue(mod
, FLAT_MOD
, float(aura
->GetModifier()->m_amount
), apply
);
7367 void Player::_ApplyWeaponDependentAuraDamageMod(Item
* item
, WeaponAttackType attackType
, Aura
* aura
, bool apply
)
7369 // ignore spell mods for not wands
7370 Modifier
const* modifier
= aura
->GetModifier();
7371 if ((modifier
->m_miscvalue
& SPELL_SCHOOL_MASK_NORMAL
) == 0 && (getClassMask() & CLASSMASK_WAND_USERS
) == 0)
7374 // generic not weapon specific case processes in aura code
7375 if(aura
->GetSpellProto()->GetEquippedItemClass() == -1)
7378 UnitMods unitMod
= UNIT_MOD_END
;
7381 case BASE_ATTACK
: unitMod
= UNIT_MOD_DAMAGE_MAINHAND
; break;
7382 case OFF_ATTACK
: unitMod
= UNIT_MOD_DAMAGE_OFFHAND
; break;
7383 case RANGED_ATTACK
: unitMod
= UNIT_MOD_DAMAGE_RANGED
; break;
7387 UnitModifierType unitModType
= TOTAL_VALUE
;
7388 switch (modifier
->m_auraname
)
7390 case SPELL_AURA_MOD_DAMAGE_DONE
: unitModType
= TOTAL_VALUE
; break;
7391 case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE
: unitModType
= TOTAL_PCT
; break;
7395 if (item
->IsFitToSpellRequirements(aura
->GetSpellProto()))
7397 HandleStatModifier(unitMod
, unitModType
, float(modifier
->m_amount
), apply
);
7401 void Player::ApplyItemEquipSpell(Item
* item
, bool apply
, bool form_change
)
7406 ItemPrototype
const* proto
= item
->GetProto();
7410 for (int i
= 0; i
< MAX_ITEM_PROTO_SPELLS
; ++i
)
7412 _Spell
const& spellData
= proto
->Spells
[i
];
7415 if (!spellData
.SpellId
)
7420 // apply only at-equip spells
7421 if (spellData
.SpellTrigger
!= ITEM_SPELLTRIGGER_ON_EQUIP
)
7426 // at un-apply remove all spells (not only at-apply, so any at-use active affects from item and etc)
7427 // except with at-use with negative charges, so allow consuming item spells (including with extra flag that prevent consume really)
7428 // applied to player after item remove from equip slot
7429 if (spellData
.SpellTrigger
== ITEM_SPELLTRIGGER_ON_USE
&& spellData
.SpellCharges
< 0)
7433 // check if it is valid spell
7434 SpellEntry
const* spellproto
= sSpellStore
.LookupEntry(spellData
.SpellId
);
7438 ApplyEquipSpell(spellproto
, item
, apply
, form_change
);
7442 void Player::ApplyEquipSpell(SpellEntry
const* spellInfo
, Item
* item
, bool apply
, bool form_change
)
7446 // Cannot be used in this stance/form
7447 if (GetErrorAtShapeshiftedCast(spellInfo
, GetShapeshiftForm()) != SPELL_CAST_OK
)
7450 if (form_change
) // check aura active state from other form
7453 for (int k
= 0; k
< MAX_EFFECT_INDEX
; ++k
)
7455 SpellAuraHolderBounds spair
= GetSpellAuraHolderBounds(spellInfo
->Id
);
7456 for (SpellAuraHolderMap::const_iterator iter
= spair
.first
; iter
!= spair
.second
; ++iter
)
7458 if (!item
|| iter
->second
->GetCastItemGuid() == item
->GetObjectGuid())
7468 if (found
) // and skip re-cast already active aura at form change
7472 DEBUG_LOG("WORLD: cast %s Equip spellId - %i", (item
? "item" : "itemset"), spellInfo
->Id
);
7474 CastSpell(this, spellInfo
, true, item
);
7478 if (form_change
) // check aura compatibility
7480 // Cannot be used in this stance/form
7481 if (GetErrorAtShapeshiftedCast(spellInfo
, GetShapeshiftForm()) == SPELL_CAST_OK
)
7482 return; // and remove only not compatible at form change
7486 RemoveAurasDueToItemSpell(item
, spellInfo
->Id
); // un-apply all spells , not only at-equipped
7488 RemoveAurasDueToSpell(spellInfo
->Id
); // un-apply spell (item set case)
7492 void Player::UpdateEquipSpellsAtFormChange()
7494 for (int i
= 0; i
< INVENTORY_SLOT_BAG_END
; ++i
)
7496 if (m_items
[i
] && !m_items
[i
]->IsBroken())
7498 ApplyItemEquipSpell(m_items
[i
], false, true); // remove spells that not fit to form
7499 ApplyItemEquipSpell(m_items
[i
], true, true); // add spells that fit form but not active
7503 // item set bonuses not dependent from item broken state
7504 for (size_t setindex
= 0; setindex
< ItemSetEff
.size(); ++setindex
)
7506 ItemSetEffect
* eff
= ItemSetEff
[setindex
];
7510 for (uint32 y
= 0; y
< 8; ++y
)
7512 SpellEntry
const* spellInfo
= eff
->spells
[y
];
7516 ApplyEquipSpell(spellInfo
, NULL
, false, true); // remove spells that not fit to form
7517 ApplyEquipSpell(spellInfo
, NULL
, true, true); // add spells that fit form but not active
7523 * (un-)Apply item spells triggered at adding item to inventory ITEM_SPELLTRIGGER_ON_STORE
7525 * @param item added/removed item to/from inventory
7526 * @param apply (un-)apply spell affects.
7528 * Note: item moved from slot to slot in 2 steps RemoveItem and StoreItem/EquipItem
7529 * In result function not called in RemoveItem for prevent unexpected re-apply auras from related spells
7530 * with duration reset and etc. Instead unapply done in StoreItem/EquipItem and in specialized
7531 * functions for item final remove/destroy from inventory. If new RemoveItem calls added need be sure that
7532 * function will call after it in some way if need.
7535 void Player::ApplyItemOnStoreSpell(Item
* item
, bool apply
)
7540 ItemPrototype
const* proto
= item
->GetProto();
7544 for (int i
= 0; i
< MAX_ITEM_PROTO_SPELLS
; ++i
)
7546 _Spell
const& spellData
= proto
->Spells
[i
];
7549 if (!spellData
.SpellId
)
7552 // apply/unapply only at-store spells
7553 if (spellData
.SpellTrigger
!= ITEM_SPELLTRIGGER_ON_STORE
)
7558 // can be attempt re-applied at move in inventory slots
7559 if (!HasAura(spellData
.SpellId
))
7560 CastSpell(this, spellData
.SpellId
, true, item
);
7563 RemoveAurasDueToItemSpell(item
, spellData
.SpellId
);
7567 void Player::DestroyItemWithOnStoreSpell(Item
* item
, uint32 spellId
)
7572 ItemPrototype
const* proto
= item
->GetProto();
7576 for (int i
= 0; i
< MAX_ITEM_PROTO_SPELLS
; ++i
)
7578 _Spell
const& spellData
= proto
->Spells
[i
];
7580 if (spellData
.SpellId
!= spellId
)
7583 // apply/unapply only at-store spells
7584 if (spellData
.SpellTrigger
!= ITEM_SPELLTRIGGER_ON_STORE
)
7587 DestroyItem(item
->GetBagSlot(), item
->GetSlot(), true);
7593 /// handles unique effect of Deadly Poison: apply poison of the other weapon when already at max. stack
7594 void Player::_HandleDeadlyPoison(Unit
* Target
, WeaponAttackType attType
, SpellEntry
const* spellInfo
)
7596 SpellAuraHolder
const* dPoison
= NULL
;
7597 SpellAuraHolderConstBounds holders
= Target
->GetSpellAuraHolderBounds(spellInfo
->Id
);
7598 for (SpellAuraHolderMap::const_iterator iter
= holders
.first
; iter
!= holders
.second
; ++iter
)
7600 if (iter
->second
->GetCaster() == this)
7602 dPoison
= iter
->second
;
7606 if (dPoison
&& dPoison
->GetStackAmount() == spellInfo
->GetStackAmount())
7608 Item
* otherWeapon
= GetWeaponForAttack(attType
== BASE_ATTACK
? OFF_ATTACK
: BASE_ATTACK
);
7612 // all poison enchantments are temporary
7613 uint32 enchant_id
= otherWeapon
->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT
);
7617 SpellItemEnchantmentEntry
const* pSecondEnchant
= sSpellItemEnchantmentStore
.LookupEntry(enchant_id
);
7618 if (!pSecondEnchant
)
7621 for (int s
= 0; s
< 3; ++s
)
7623 if (pSecondEnchant
->type
[s
] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL
)
7626 if (SpellEntry
const* combatEntry
= sSpellStore
.LookupEntry(pSecondEnchant
->spellid
[s
]))
7627 if (combatEntry
->GetDispel() == DISPEL_POISON
)
7628 CastSpell(Target
, combatEntry
, true, otherWeapon
);
7633 void Player::CastItemCombatSpell(Unit
* Target
, WeaponAttackType attType
)
7635 Item
* item
= GetWeaponForAttack(attType
, true, false);
7639 ItemPrototype
const* proto
= item
->GetProto();
7643 if (!Target
|| Target
== this)
7646 for (int i
= 0; i
< MAX_ITEM_PROTO_SPELLS
; ++i
)
7648 _Spell
const& spellData
= proto
->Spells
[i
];
7651 if (!spellData
.SpellId
)
7654 // wrong triggering type
7655 if (spellData
.SpellTrigger
!= ITEM_SPELLTRIGGER_CHANCE_ON_HIT
)
7658 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(spellData
.SpellId
);
7661 sLog
.outError("WORLD: unknown Item spellid %i", spellData
.SpellId
);
7665 // not allow proc extra attack spell at extra attack
7666 if (m_extraAttacks
&& IsSpellHaveEffect(spellInfo
, SPELL_EFFECT_ADD_EXTRA_ATTACKS
))
7669 float chance
= (float)spellInfo
->GetProcChance();
7671 if (spellData
.SpellPPMRate
)
7673 uint32 WeaponSpeed
= proto
->Delay
;
7674 chance
= GetPPMProcChance(WeaponSpeed
, spellData
.SpellPPMRate
);
7676 else if (chance
> 100.0f
)
7678 chance
= GetWeaponProcChance();
7681 if (roll_chance_f(chance
))
7682 CastSpell(Target
, spellInfo
->Id
, true, item
);
7685 // item combat enchantments
7686 for (int e_slot
= 0; e_slot
< MAX_ENCHANTMENT_SLOT
; ++e_slot
)
7688 if (e_slot
> PRISMATIC_ENCHANTMENT_SLOT
&& e_slot
< PROP_ENCHANTMENT_SLOT_0
)
7691 uint32 enchant_id
= item
->GetEnchantmentId(EnchantmentSlot(e_slot
));
7692 SpellItemEnchantmentEntry
const* pEnchant
= sSpellItemEnchantmentStore
.LookupEntry(enchant_id
);
7693 if (!pEnchant
) continue;
7694 for (int s
= 0; s
< 3; ++s
)
7696 if (pEnchant
->type
[s
] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL
)
7699 uint32 proc_spell_id
= pEnchant
->spellid
[s
];
7700 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(proc_spell_id
);
7703 sLog
.outError("Player::CastItemCombatSpell Enchant %i, cast unknown spell %i", pEnchant
->ID
, proc_spell_id
);
7707 // Use first rank to access spell item enchant procs
7708 float ppmRate
= sSpellMgr
.GetItemEnchantProcChance(spellInfo
->Id
);
7710 float chance
= ppmRate
7711 ? GetPPMProcChance(proto
->Delay
, ppmRate
)
7712 : pEnchant
->amount
[s
] != 0 ? float(pEnchant
->amount
[s
]) : GetWeaponProcChance();
7715 ApplySpellMod(spellInfo
->Id
, SPELLMOD_CHANCE_OF_SUCCESS
, chance
);
7716 ApplySpellMod(spellInfo
->Id
, SPELLMOD_FREQUENCY_OF_SUCCESS
, chance
);
7718 if (roll_chance_f(chance
))
7720 if (IsPositiveSpell(spellInfo
->Id
))
7721 CastSpell(this, spellInfo
->Id
, true, item
);
7724 // Deadly Poison, unique effect needs to be handled before casting triggered spell
7725 if (spellInfo
->IsFitToFamily(SPELLFAMILY_ROGUE
, UI64LIT(0x0000000000010000)))
7726 _HandleDeadlyPoison(Target
, attType
, spellInfo
);
7728 CastSpell(Target
, spellInfo
->Id
, true, item
);
7735 void Player::CastItemUseSpell(Item
* item
, SpellCastTargets
const& targets
, uint8 cast_count
, uint32 glyphIndex
)
7737 ItemPrototype
const* proto
= item
->GetProto();
7738 // special learning case
7739 if (proto
->Spells
[0].SpellId
== SPELL_ID_GENERIC_LEARN
|| proto
->Spells
[0].SpellId
== SPELL_ID_GENERIC_LEARN_PET
)
7741 uint32 learn_spell_id
= proto
->Spells
[0].SpellId
;
7742 uint32 learning_spell_id
= proto
->Spells
[1].SpellId
;
7744 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(learn_spell_id
);
7747 sLog
.outError("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring ", proto
->ItemId
, learn_spell_id
);
7748 SendEquipError(EQUIP_ERR_NONE
, item
);
7752 Spell
* spell
= new Spell(this, spellInfo
, false);
7753 spell
->m_CastItem
= item
;
7754 spell
->m_cast_count
= cast_count
; // set count of casts
7755 spell
->m_currentBasePoints
[EFFECT_INDEX_0
] = learning_spell_id
;
7756 spell
->prepare(&targets
);
7760 // use triggered flag only for items with many spell casts and for not first cast
7763 // item spells casted at use
7764 for (int i
= 0; i
< MAX_ITEM_PROTO_SPELLS
; ++i
)
7766 _Spell
const& spellData
= proto
->Spells
[i
];
7769 if (!spellData
.SpellId
)
7772 // wrong triggering type
7773 if (spellData
.SpellTrigger
!= ITEM_SPELLTRIGGER_ON_USE
)
7776 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(spellData
.SpellId
);
7779 sLog
.outError("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring", proto
->ItemId
, spellData
.SpellId
);
7783 Spell
* spell
= new Spell(this, spellInfo
, (count
> 0));
7784 spell
->m_CastItem
= item
;
7785 spell
->m_cast_count
= cast_count
; // set count of casts
7786 spell
->m_glyphIndex
= glyphIndex
; // glyph index
7787 spell
->prepare(&targets
);
7792 // Item enchantments spells casted at use
7793 for (int e_slot
= 0; e_slot
< MAX_ENCHANTMENT_SLOT
; ++e_slot
)
7795 if (e_slot
> PRISMATIC_ENCHANTMENT_SLOT
&& e_slot
< PROP_ENCHANTMENT_SLOT_0
)
7798 uint32 enchant_id
= item
->GetEnchantmentId(EnchantmentSlot(e_slot
));
7799 SpellItemEnchantmentEntry
const* pEnchant
= sSpellItemEnchantmentStore
.LookupEntry(enchant_id
);
7803 for (int s
= 0; s
< 3; ++s
)
7805 if (pEnchant
->type
[s
] != ITEM_ENCHANTMENT_TYPE_USE_SPELL
)
7808 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(pEnchant
->spellid
[s
]);
7811 sLog
.outError("Player::CastItemUseSpell Enchant %i, cast unknown spell %i", pEnchant
->ID
, pEnchant
->spellid
[s
]);
7815 Spell
* spell
= new Spell(this, spellInfo
, (count
> 0));
7816 spell
->m_CastItem
= item
;
7817 spell
->m_cast_count
= cast_count
; // set count of casts
7818 spell
->m_glyphIndex
= glyphIndex
; // glyph index
7819 spell
->prepare(&targets
);
7826 void Player::_RemoveAllItemMods()
7828 DEBUG_LOG("_RemoveAllItemMods start.");
7830 for (int i
= 0; i
< INVENTORY_SLOT_BAG_END
; ++i
)
7834 ItemPrototype
const* proto
= m_items
[i
]->GetProto();
7838 // item set bonuses not dependent from item broken state
7840 RemoveItemsSetItem(this, proto
);
7842 if (m_items
[i
]->IsBroken())
7845 ApplyItemEquipSpell(m_items
[i
], false);
7846 ApplyEnchantment(m_items
[i
], false);
7850 for (int i
= 0; i
< INVENTORY_SLOT_BAG_END
; ++i
)
7854 if (m_items
[i
]->IsBroken())
7856 ItemPrototype
const* proto
= m_items
[i
]->GetProto();
7860 uint32 attacktype
= Player::GetAttackBySlot(i
);
7861 if (attacktype
< MAX_ATTACK
)
7862 _ApplyWeaponDependentAuraMods(m_items
[i
], WeaponAttackType(attacktype
), false);
7864 _ApplyItemBonuses(proto
, i
, false);
7866 if (i
== EQUIPMENT_SLOT_RANGED
)
7867 _ApplyAmmoBonuses();
7871 DEBUG_LOG("_RemoveAllItemMods complete.");
7874 void Player::_ApplyAllItemMods()
7876 DEBUG_LOG("_ApplyAllItemMods start.");
7878 for (int i
= 0; i
< INVENTORY_SLOT_BAG_END
; ++i
)
7882 if (m_items
[i
]->IsBroken())
7885 ItemPrototype
const* proto
= m_items
[i
]->GetProto();
7889 uint32 attacktype
= Player::GetAttackBySlot(i
);
7890 if (attacktype
< MAX_ATTACK
)
7891 _ApplyWeaponDependentAuraMods(m_items
[i
], WeaponAttackType(attacktype
), true);
7893 _ApplyItemBonuses(proto
, i
, true);
7895 if (i
== EQUIPMENT_SLOT_RANGED
)
7896 _ApplyAmmoBonuses();
7900 for (int i
= 0; i
< INVENTORY_SLOT_BAG_END
; ++i
)
7904 ItemPrototype
const* proto
= m_items
[i
]->GetProto();
7908 // item set bonuses not dependent from item broken state
7910 AddItemsSetItem(this, m_items
[i
]);
7912 if (m_items
[i
]->IsBroken())
7915 ApplyItemEquipSpell(m_items
[i
], true);
7916 ApplyEnchantment(m_items
[i
], true);
7920 DEBUG_LOG("_ApplyAllItemMods complete.");
7923 void Player::_ApplyAllLevelScaleItemMods(bool apply
)
7925 for (int i
= 0; i
< INVENTORY_SLOT_BAG_END
; ++i
)
7929 if (m_items
[i
]->IsBroken())
7932 ItemPrototype
const* proto
= m_items
[i
]->GetProto();
7936 _ApplyItemBonuses(proto
, i
, apply
, true);
7941 void Player::_ApplyAmmoBonuses()
7944 //uint32 ammo_id = GetUInt32Value(PLAYER_AMMO_ID);
7948 //float currentAmmoDPS;
7950 //ItemPrototype const *ammo_proto = ObjectMgr::GetItemPrototype( ammo_id );
7951 //if( !ammo_proto || ammo_proto->Class!=ITEM_CLASS_PROJECTILE || !CheckAmmoCompatibility(ammo_proto))
7952 // currentAmmoDPS = 0.0f;
7954 // currentAmmoDPS = ammo_proto->Damage[0].DamageMin;
7956 //if(currentAmmoDPS == GetAmmoDPS())
7959 //m_ammoDPS = currentAmmoDPS;
7961 //if(CanModifyStats())
7962 // UpdateDamagePhysical(RANGED_ATTACK);
7965 bool Player::CheckAmmoCompatibility(const ItemPrototype
* ammo_proto
) const
7970 // check ranged weapon
7971 Item
* weapon
= GetWeaponForAttack(RANGED_ATTACK
, true, false);
7975 ItemPrototype
const* weapon_proto
= weapon
->GetProto();
7976 if (!weapon_proto
|| weapon_proto
->Class
!= ITEM_CLASS_WEAPON
)
7979 // check ammo ws. weapon compatibility
7980 switch (weapon_proto
->SubClass
)
7982 case ITEM_SUBCLASS_WEAPON_BOW
:
7983 case ITEM_SUBCLASS_WEAPON_CROSSBOW
:
7984 if (ammo_proto
->SubClass
!= ITEM_SUBCLASS_ARROW
)
7987 case ITEM_SUBCLASS_WEAPON_GUN
:
7988 if (ammo_proto
->SubClass
!= ITEM_SUBCLASS_BULLET
)
7998 /* If in a battleground a player dies, and an enemy removes the insignia, the player's bones is lootable
7999 Called by remove insignia spell effect */
8000 void Player::RemovedInsignia(Player
* looterPlr
)
8002 if (!GetBattleGroundId())
8005 // If not released spirit, do it !
8006 if (m_deathTimer
> 0)
8013 Corpse
* corpse
= GetCorpse();
8017 // We have to convert player corpse to bones, not to be able to resurrect there
8018 // SpawnCorpseBones isn't handy, 'cos it saves player while he in BG
8019 Corpse
* bones
= sObjectAccessor
.ConvertCorpseForPlayer(GetObjectGuid(), true);
8023 // Now we must make bones lootable, and send player loot
8024 bones
->SetFlag(CORPSE_FIELD_DYNAMIC_FLAGS
, CORPSE_DYNFLAG_LOOTABLE
);
8026 // We store the level of our player in the gold field
8027 // We retrieve this information at Player::SendLoot()
8028 bones
->loot
.gold
= getLevel();
8029 bones
->lootRecipient
= looterPlr
;
8030 looterPlr
->SendLoot(bones
->GetObjectGuid(), LOOT_INSIGNIA
);
8033 void Player::SendLootRelease(ObjectGuid guid
)
8035 WorldPacket
data(SMSG_LOOT_RELEASE_RESPONSE
, (8 + 1));
8038 SendDirectMessage(&data
);
8041 void Player::SendLoot(ObjectGuid guid
, LootType loot_type
)
8043 if (ObjectGuid lootGuid
= GetLootGuid())
8044 m_session
->DoLootRelease(lootGuid
);
8047 PermissionTypes permission
= ALL_PERMISSION
;
8049 DEBUG_LOG("Player::SendLoot");
8050 switch (guid
.GetHigh())
8052 case HIGHGUID_GAMEOBJECT
:
8054 DEBUG_LOG(" IS_GAMEOBJECT_GUID(guid)");
8055 GameObject
* go
= GetMap()->GetGameObject(guid
);
8057 // not check distance for GO in case owned GO (fishing bobber case, for example)
8058 // And permit out of range GO with no owner in case fishing hole
8059 if (!go
|| (loot_type
!= LOOT_FISHINGHOLE
&& (loot_type
!= LOOT_FISHING
&& loot_type
!= LOOT_FISHING_FAIL
|| go
->GetOwnerGuid() != GetObjectGuid()) && !go
->IsWithinDistInMap(this, INTERACTION_DISTANCE
)))
8061 SendLootRelease(guid
);
8067 Player
* recipient
= go
->GetLootRecipient();
8070 go
->SetLootRecipient(this);
8074 // generate loot only if ready for open and spawned in world
8075 if (go
->getLootState() == GO_READY
&& go
->isSpawned())
8077 uint32 lootid
= go
->GetGOInfo()->GetLootId();
8078 if ((go
->GetEntry() == BG_AV_OBJECTID_MINE_N
|| go
->GetEntry() == BG_AV_OBJECTID_MINE_S
))
8079 if (BattleGround
* bg
= GetBattleGround())
8080 if (bg
->GetTypeID() == BATTLEGROUND_AV
)
8081 if (!(((BattleGroundAV
*)bg
)->PlayerCanDoMineQuest(go
->GetEntry(), GetTeam())))
8083 SendLootRelease(guid
);
8087 // Entry 0 in fishing loot template used for store junk fish loot at fishing fail it junk allowed by config option
8088 // this is overwrite fishinghole loot for example
8089 if (loot_type
== LOOT_FISHING_FAIL
)
8090 loot
->FillLoot(0, LootTemplates_Fishing
, this, true);
8093 DEBUG_LOG(" if(lootid)");
8095 loot
->FillLoot(lootid
, LootTemplates_Gameobject
, this, false);
8096 loot
->generateMoneyLoot(go
->GetGOInfo()->MinMoneyLoot
, go
->GetGOInfo()->MaxMoneyLoot
);
8098 if (go
->GetGoType() == GAMEOBJECT_TYPE_CHEST
&& go
->GetGOInfo()->chest
.groupLootRules
)
8100 if (Group
* group
= go
->GetGroupLootRecipient())
8102 group
->UpdateLooterGuid(go
, true);
8104 switch (group
->GetLootMethod())
8107 // GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with quality<threshold, round robin
8108 group
->GroupLoot(go
, loot
);
8109 permission
= GROUP_PERMISSION
;
8111 case NEED_BEFORE_GREED
:
8112 group
->NeedBeforeGreed(go
, loot
);
8113 permission
= GROUP_PERMISSION
;
8116 group
->MasterLoot(go
, loot
);
8117 permission
= MASTER_PERMISSION
;
8125 else if (loot_type
== LOOT_FISHING
)
8126 go
->getFishLoot(loot
, this);
8128 go
->SetLootState(GO_ACTIVATED
);
8130 if (go
->getLootState() == GO_ACTIVATED
&& go
->GetGoType() == GAMEOBJECT_TYPE_CHEST
&& go
->GetGOInfo()->chest
.groupLootRules
)
8132 if (Group
* group
= go
->GetGroupLootRecipient())
8134 if (group
== GetGroup())
8136 if (group
->GetLootMethod() == FREE_FOR_ALL
)
8137 permission
= ALL_PERMISSION
;
8138 else if (group
->GetLooterGuid() == GetObjectGuid())
8140 if (group
->GetLootMethod() == MASTER_LOOT
)
8141 permission
= MASTER_PERMISSION
;
8143 permission
= ALL_PERMISSION
;
8146 permission
= GROUP_PERMISSION
;
8149 permission
= NONE_PERMISSION
;
8151 else if (recipient
== this)
8152 permission
= ALL_PERMISSION
;
8154 permission
= NONE_PERMISSION
;
8160 Item
* item
= GetItemByGuid(guid
);
8164 SendLootRelease(guid
);
8168 permission
= OWNER_PERMISSION
;
8172 if (!item
->HasGeneratedLoot())
8178 case LOOT_DISENCHANTING
:
8179 loot
->FillLoot(item
->GetProto()->DisenchantID
, LootTemplates_Disenchant
, this, true);
8180 item
->SetLootState(ITEM_LOOT_TEMPORARY
);
8182 case LOOT_PROSPECTING
:
8183 loot
->FillLoot(item
->GetEntry(), LootTemplates_Prospecting
, this, true);
8184 item
->SetLootState(ITEM_LOOT_TEMPORARY
);
8187 loot
->FillLoot(item
->GetEntry(), LootTemplates_Milling
, this, true);
8188 item
->SetLootState(ITEM_LOOT_TEMPORARY
);
8191 loot
->FillLoot(item
->GetEntry(), LootTemplates_Item
, this, true, item
->GetProto()->MaxMoneyLoot
== 0);
8192 loot
->generateMoneyLoot(item
->GetProto()->MinMoneyLoot
, item
->GetProto()->MaxMoneyLoot
);
8193 item
->SetLootState(ITEM_LOOT_CHANGED
);
8199 case HIGHGUID_CORPSE
: // remove insignia
8201 Corpse
* bones
= GetMap()->GetCorpse(guid
);
8203 if (!bones
|| !((loot_type
== LOOT_CORPSE
) || (loot_type
== LOOT_INSIGNIA
)) || (bones
->GetType() != CORPSE_BONES
))
8205 SendLootRelease(guid
);
8209 loot
= &bones
->loot
;
8211 if (!bones
->lootForBody
)
8213 bones
->lootForBody
= true;
8214 uint32 pLevel
= bones
->loot
.gold
;
8215 bones
->loot
.clear();
8216 if (GetBattleGround()->GetTypeID() == BATTLEGROUND_AV
)
8217 loot
->FillLoot(0, LootTemplates_Creature
, this, false);
8218 // It may need a better formula
8219 // Now it works like this: lvl10: ~6copper, lvl70: ~9silver
8220 bones
->loot
.gold
= (uint32
)(urand(50, 150) * 0.016f
* pow(((float)pLevel
) / 5.76f
, 2.5f
) * sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY
));
8223 if (bones
->lootRecipient
!= this)
8224 permission
= NONE_PERMISSION
;
8226 permission
= OWNER_PERMISSION
;
8230 case HIGHGUID_VEHICLE
:
8232 Creature
* creature
= GetMap()->GetCreature(guid
);
8234 // must be in range and creature must be alive for pickpocket and must be dead for another loot
8235 if (!creature
|| creature
->isAlive() != (loot_type
== LOOT_PICKPOCKETING
) || !creature
->IsWithinDistInMap(this, INTERACTION_DISTANCE
))
8237 SendLootRelease(guid
);
8241 if (loot_type
== LOOT_PICKPOCKETING
&& IsFriendlyTo(creature
))
8243 SendLootRelease(guid
);
8247 loot
= &creature
->loot
;
8249 if (loot_type
== LOOT_PICKPOCKETING
)
8251 if (!creature
->lootForPickPocketed
)
8253 creature
->lootForPickPocketed
= true;
8256 if (uint32 lootid
= creature
->GetCreatureInfo()->pickpocketLootId
)
8257 loot
->FillLoot(lootid
, LootTemplates_Pickpocketing
, this, false);
8259 // Generate extra money for pick pocket loot
8260 const uint32 a
= urand(0, creature
->getLevel() / 2);
8261 const uint32 b
= urand(0, getLevel() / 2);
8262 loot
->gold
= uint32(10 * (a
+ b
) * sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY
));
8263 permission
= OWNER_PERMISSION
;
8268 // the player whose group may loot the corpse
8269 Player
* recipient
= creature
->GetLootRecipient();
8272 creature
->SetLootRecipient(this);
8276 if (creature
->lootForPickPocketed
)
8278 creature
->lootForPickPocketed
= false;
8282 if (!creature
->lootForBody
)
8284 creature
->lootForBody
= true;
8287 if (uint32 lootid
= creature
->GetCreatureInfo()->lootid
)
8288 loot
->FillLoot(lootid
, LootTemplates_Creature
, recipient
, false);
8290 loot
->generateMoneyLoot(creature
->GetCreatureInfo()->mingold
, creature
->GetCreatureInfo()->maxgold
);
8292 if (Group
* group
= creature
->GetGroupLootRecipient())
8294 group
->UpdateLooterGuid(creature
, true);
8296 switch (group
->GetLootMethod())
8299 // GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with quality<threshold, round robin
8300 group
->GroupLoot(creature
, loot
);
8302 case NEED_BEFORE_GREED
:
8303 group
->NeedBeforeGreed(creature
, loot
);
8306 group
->MasterLoot(creature
, loot
);
8314 // possible only if creature->lootForBody && loot->empty() at spell cast check
8315 if (loot_type
== LOOT_SKINNING
)
8317 if (!creature
->lootForSkin
)
8319 creature
->lootForSkin
= true;
8321 loot
->FillLoot(creature
->GetCreatureInfo()->SkinLootId
, LootTemplates_Skinning
, this, false);
8323 // let reopen skinning loot if will closed.
8325 creature
->SetUInt32Value(UNIT_DYNAMIC_FLAGS
, UNIT_DYNFLAG_LOOTABLE
);
8327 permission
= OWNER_PERMISSION
;
8330 // set group rights only for loot_type != LOOT_SKINNING
8333 if (Group
* group
= creature
->GetGroupLootRecipient())
8335 if (group
== GetGroup())
8337 if (group
->GetLootMethod() == FREE_FOR_ALL
)
8338 permission
= ALL_PERMISSION
;
8339 else if (group
->GetLooterGuid() == GetObjectGuid())
8341 if (group
->GetLootMethod() == MASTER_LOOT
)
8342 permission
= MASTER_PERMISSION
;
8344 permission
= ALL_PERMISSION
;
8347 permission
= GROUP_PERMISSION
;
8350 permission
= NONE_PERMISSION
;
8352 else if (recipient
== this)
8353 permission
= OWNER_PERMISSION
;
8355 permission
= NONE_PERMISSION
;
8362 sLog
.outError("%s is unsupported for looting.", guid
.GetString().c_str());
8369 // LOOT_INSIGNIA and LOOT_FISHINGHOLE unsupported by client
8372 case LOOT_INSIGNIA
: loot_type
= LOOT_SKINNING
; break;
8373 case LOOT_FISHING_FAIL
: loot_type
= LOOT_FISHING
; break;
8374 case LOOT_FISHINGHOLE
: loot_type
= LOOT_FISHING
; break;
8378 // need know merged fishing/corpse loot type for achievements
8379 loot
->loot_type
= loot_type
;
8381 WorldPacket
data(SMSG_LOOT_RESPONSE
, (9 + 50)); // we guess size
8382 data
<< ObjectGuid(guid
);
8383 data
<< uint8(loot_type
);
8384 data
<< LootView(*loot
, this, permission
);
8385 SendDirectMessage(&data
);
8387 // add 'this' player as one of the players that are looting 'loot'
8388 if (permission
!= NONE_PERMISSION
)
8389 loot
->AddLooter(GetObjectGuid());
8391 if (loot_type
== LOOT_CORPSE
&& !guid
.IsItem())
8392 SetFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_LOOTING
);
8395 void Player::SendNotifyLootMoneyRemoved()
8397 WorldPacket
data(SMSG_LOOT_CLEAR_MONEY
, 0);
8398 GetSession()->SendPacket(&data
);
8401 void Player::SendNotifyLootItemRemoved(uint8 lootSlot
, bool currency
)
8403 WorldPacket
data(currency
? SMSG_LOOT_CURRENCY_REMOVED
: SMSG_LOOT_REMOVED
, 1);
8404 data
<< uint8(lootSlot
);
8405 GetSession()->SendPacket(&data
);
8408 void Player::SendUpdateWorldState(uint32 Field
, uint32 Value
)
8410 WorldPacket
data(SMSG_UPDATE_WORLD_STATE
, 8 + 1);
8414 GetSession()->SendPacket(&data
);
8417 static WorldStatePair AV_world_states
[] =
8419 { 0x7ae, 0x1 }, // 1966 7 snowfall n
8420 { 0x532, 0x1 }, // 1330 8 frostwolfhut hc
8421 { 0x531, 0x0 }, // 1329 9 frostwolfhut ac
8422 { 0x52e, 0x0 }, // 1326 10 stormpike firstaid a_a
8423 { 0x571, 0x0 }, // 1393 11 east frostwolf tower horde assaulted -unused
8424 { 0x570, 0x0 }, // 1392 12 west frostwolf tower horde assaulted - unused
8425 { 0x567, 0x1 }, // 1383 13 frostwolfe c
8426 { 0x566, 0x1 }, // 1382 14 frostwolfw c
8427 { 0x550, 0x1 }, // 1360 15 irondeep (N) ally
8428 { 0x544, 0x0 }, // 1348 16 ice grave a_a
8429 { 0x536, 0x0 }, // 1334 17 stormpike grave h_c
8430 { 0x535, 0x1 }, // 1333 18 stormpike grave a_c
8431 { 0x518, 0x0 }, // 1304 19 stoneheart grave a_a
8432 { 0x517, 0x0 }, // 1303 20 stoneheart grave h_a
8433 { 0x574, 0x0 }, // 1396 21 unk
8434 { 0x573, 0x0 }, // 1395 22 iceblood tower horde assaulted -unused
8435 { 0x572, 0x0 }, // 1394 23 towerpoint horde assaulted - unused
8436 { 0x56f, 0x0 }, // 1391 24 unk
8437 { 0x56e, 0x0 }, // 1390 25 iceblood a
8438 { 0x56d, 0x0 }, // 1389 26 towerp a
8439 { 0x56c, 0x0 }, // 1388 27 frostwolfe a
8440 { 0x56b, 0x0 }, // 1387 28 froswolfw a
8441 { 0x56a, 0x1 }, // 1386 29 unk
8442 { 0x569, 0x1 }, // 1385 30 iceblood c
8443 { 0x568, 0x1 }, // 1384 31 towerp c
8444 { 0x565, 0x0 }, // 1381 32 stoneh tower a
8445 { 0x564, 0x0 }, // 1380 33 icewing tower a
8446 { 0x563, 0x0 }, // 1379 34 dunn a
8447 { 0x562, 0x0 }, // 1378 35 duns a
8448 { 0x561, 0x0 }, // 1377 36 stoneheart bunker alliance assaulted - unused
8449 { 0x560, 0x0 }, // 1376 37 icewing bunker alliance assaulted - unused
8450 { 0x55f, 0x0 }, // 1375 38 dunbaldar south alliance assaulted - unused
8451 { 0x55e, 0x0 }, // 1374 39 dunbaldar north alliance assaulted - unused
8452 { 0x55d, 0x0 }, // 1373 40 stone tower d
8453 { 0x3c6, 0x0 }, // 966 41 unk
8454 { 0x3c4, 0x0 }, // 964 42 unk
8455 { 0x3c2, 0x0 }, // 962 43 unk
8456 { 0x516, 0x1 }, // 1302 44 stoneheart grave a_c
8457 { 0x515, 0x0 }, // 1301 45 stonheart grave h_c
8458 { 0x3b6, 0x0 }, // 950 46 unk
8459 { 0x55c, 0x0 }, // 1372 47 icewing tower d
8460 { 0x55b, 0x0 }, // 1371 48 dunn d
8461 { 0x55a, 0x0 }, // 1370 49 duns d
8462 { 0x559, 0x0 }, // 1369 50 unk
8463 { 0x558, 0x0 }, // 1368 51 iceblood d
8464 { 0x557, 0x0 }, // 1367 52 towerp d
8465 { 0x556, 0x0 }, // 1366 53 frostwolfe d
8466 { 0x555, 0x0 }, // 1365 54 frostwolfw d
8467 { 0x554, 0x1 }, // 1364 55 stoneh tower c
8468 { 0x553, 0x1 }, // 1363 56 icewing tower c
8469 { 0x552, 0x1 }, // 1362 57 dunn c
8470 { 0x551, 0x1 }, // 1361 58 duns c
8471 { 0x54f, 0x0 }, // 1359 59 irondeep (N) horde
8472 { 0x54e, 0x0 }, // 1358 60 irondeep (N) ally
8473 { 0x54d, 0x1 }, // 1357 61 mine (S) neutral
8474 { 0x54c, 0x0 }, // 1356 62 mine (S) horde
8475 { 0x54b, 0x0 }, // 1355 63 mine (S) ally
8476 { 0x545, 0x0 }, // 1349 64 iceblood h_a
8477 { 0x543, 0x1 }, // 1347 65 iceblod h_c
8478 { 0x542, 0x0 }, // 1346 66 iceblood a_c
8479 { 0x540, 0x0 }, // 1344 67 snowfall h_a
8480 { 0x53f, 0x0 }, // 1343 68 snowfall a_a
8481 { 0x53e, 0x0 }, // 1342 69 snowfall h_c
8482 { 0x53d, 0x0 }, // 1341 70 snowfall a_c
8483 { 0x53c, 0x0 }, // 1340 71 frostwolf g h_a
8484 { 0x53b, 0x0 }, // 1339 72 frostwolf g a_a
8485 { 0x53a, 0x1 }, // 1338 73 frostwolf g h_c
8486 { 0x539, 0x0 }, // l33t 74 frostwolf g a_c
8487 { 0x538, 0x0 }, // 1336 75 stormpike grave h_a
8488 { 0x537, 0x0 }, // 1335 76 stormpike grave a_a
8489 { 0x534, 0x0 }, // 1332 77 frostwolf hut h_a
8490 { 0x533, 0x0 }, // 1331 78 frostwolf hut a_a
8491 { 0x530, 0x0 }, // 1328 79 stormpike first aid h_a
8492 { 0x52f, 0x0 }, // 1327 80 stormpike first aid h_c
8493 { 0x52d, 0x1 }, // 1325 81 stormpike first aid a_c
8497 static WorldStatePair WS_world_states
[] =
8499 { 0x62d, 0x0 }, // 1581 7 alliance flag captures
8500 { 0x62e, 0x0 }, // 1582 8 horde flag captures
8501 { 0x609, 0x0 }, // 1545 9 unk, set to 1 on alliance flag pickup...
8502 { 0x60a, 0x0 }, // 1546 10 unk, set to 1 on horde flag pickup, after drop it's -1
8503 { 0x60b, 0x2 }, // 1547 11 unk
8504 { 0x641, 0x3 }, // 1601 12 unk (max flag captures?)
8505 { 0x922, 0x1 }, // 2338 13 horde (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
8506 { 0x923, 0x1 }, // 2339 14 alliance (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
8507 { 0x1097, 0x1 }, // 4247 15 show time limit?
8508 { 0x1098, 0x19 }, // 4248 16 time remaining in minutes
8512 static WorldStatePair AB_world_states
[] =
8514 { 0x6e7, 0x0 }, // 1767 7 stables alliance
8515 { 0x6e8, 0x0 }, // 1768 8 stables horde
8516 { 0x6e9, 0x0 }, // 1769 9 unk, ST?
8517 { 0x6ea, 0x0 }, // 1770 10 stables (show/hide)
8518 { 0x6ec, 0x0 }, // 1772 11 farm (0 - horde controlled, 1 - alliance controlled)
8519 { 0x6ed, 0x0 }, // 1773 12 farm (show/hide)
8520 { 0x6ee, 0x0 }, // 1774 13 farm color
8521 { 0x6ef, 0x0 }, // 1775 14 gold mine color, may be FM?
8522 { 0x6f0, 0x0 }, // 1776 15 alliance resources
8523 { 0x6f1, 0x0 }, // 1777 16 horde resources
8524 { 0x6f2, 0x0 }, // 1778 17 horde bases
8525 { 0x6f3, 0x0 }, // 1779 18 alliance bases
8526 { 0x6f4, 0x7d0 }, // 1780 19 max resources (2000)
8527 { 0x6f6, 0x0 }, // 1782 20 blacksmith color
8528 { 0x6f7, 0x0 }, // 1783 21 blacksmith (show/hide)
8529 { 0x6f8, 0x0 }, // 1784 22 unk, bs?
8530 { 0x6f9, 0x0 }, // 1785 23 unk, bs?
8531 { 0x6fb, 0x0 }, // 1787 24 gold mine (0 - horde contr, 1 - alliance contr)
8532 { 0x6fc, 0x0 }, // 1788 25 gold mine (0 - conflict, 1 - horde)
8533 { 0x6fd, 0x0 }, // 1789 26 gold mine (1 - show/0 - hide)
8534 { 0x6fe, 0x0 }, // 1790 27 gold mine color
8535 { 0x700, 0x0 }, // 1792 28 gold mine color, wtf?, may be LM?
8536 { 0x701, 0x0 }, // 1793 29 lumber mill color (0 - conflict, 1 - horde contr)
8537 { 0x702, 0x0 }, // 1794 30 lumber mill (show/hide)
8538 { 0x703, 0x0 }, // 1795 31 lumber mill color color
8539 { 0x732, 0x1 }, // 1842 32 stables (1 - uncontrolled)
8540 { 0x733, 0x1 }, // 1843 33 gold mine (1 - uncontrolled)
8541 { 0x734, 0x1 }, // 1844 34 lumber mill (1 - uncontrolled)
8542 { 0x735, 0x1 }, // 1845 35 farm (1 - uncontrolled)
8543 { 0x736, 0x1 }, // 1846 36 blacksmith (1 - uncontrolled)
8544 { 0x745, 0x2 }, // 1861 37 unk
8545 { 0x7a3, 0x708 }, // 1955 38 warning limit (1800)
8549 static WorldStatePair EY_world_states
[] =
8551 { 2753, 0 }, // WORLD_STATE_EY_TOWER_COUNT_HORDE
8552 { 2752, 0 }, // WORLD_STATE_EY_TOWER_COUNT_ALLIANCE
8553 { 2733, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_DRAENEI_RUINS_HORDE
8554 { 2732, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_DRAENEI_RUINS_ALLIANCE
8555 { 2731, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_DRAENEI_RUINS_NEUTRAL
8556 { 2730, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_MAGE_TOWER_ALLIANCE
8557 { 2729, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_MAGE_TOWER_HORDE
8558 { 2728, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_MAGE_TOWER_NEUTRAL
8559 { 2727, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_FEL_REAVER_HORDE
8560 { 2726, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_FEL_REAVER_ALLIANCE
8561 { 2725, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_FEL_REAVER_NEUTRAL
8562 { 2724, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_BLOOD_ELF_HORDE
8563 { 2723, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_BLOOD_ELF_ALLIANCE
8564 { 2722, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_BLOOD_ELF_NEUTRAL
8565 { 2757, WORLD_STATE_REMOVE
}, // WORLD_STATE_EY_NETHERSTORM_FLAG_READY
8566 { 2770, 1 }, // WORLD_STATE_EY_NETHERSTORM_FLAG_STATE_HORDE
8567 { 2769, 1 }, // WORLD_STATE_EY_NETHERSTORM_FLAG_STATE_ALLIANCE
8568 { 2750, 0 }, // WORLD_STATE_EY_RESOURCES_HORDE
8569 { 2749, 0 }, // WORLD_STATE_EY_RESOURCES_ALLIANCE
8570 { 2565, 0x8e }, // global unk -- TODO: move to global world state
8571 { 3085, 0x17b } // global unk -- TODO: move to global world state
8574 static WorldStatePair SI_world_states
[] = // Silithus
8576 { 2313, 0 }, // WORLD_STATE_SI_GATHERED_A
8577 { 2314, 0 }, // WORLD_STATE_SI_GATHERED_H
8578 { 2317, 0 } // WORLD_STATE_SI_SILITHYST_MAX
8581 static WorldStatePair EP_world_states
[] = // Eastern Plaguelands
8583 { 2327, 0 }, // WORLD_STATE_EP_TOWER_COUNT_ALLIANCE
8584 { 2328, 0 }, // WORLD_STATE_EP_TOWER_COUNT_HORDE
8585 { 2355, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_CROWNGUARD_NEUTRAL
8586 { 2374, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_CROWNGUARD_CONTEST_ALLIANCE
8587 { 2375, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_CROWNGUARD_CONTEST_HORDE
8588 { 2376, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_CROWNGUARD_PROGRESS_ALLIANCE
8589 { 2377, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_CROWNGUARD_PROGRESS_HORDE
8590 { 2378, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_CROWNGUARD_ALLIANCE
8591 { 2379, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_CROWNGUARD_HORDE
8592 { 2354, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_EASTWALL_ALLIANCE
8593 { 2356, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_EASTWALL_HORDE
8594 { 2357, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_EASTWALL_PROGRESS_ALLIANCE
8595 { 2358, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_EASTWALL_PROGRESS_HORDE
8596 { 2359, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_EASTWALL_CONTEST_ALLIANCE
8597 { 2360, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_EASTWALL_CONTEST_HORDE
8598 { 2361, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_EASTWALL_NEUTRAL
8599 { 2352, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_NORTHPASS_NEUTRAL
8600 { 2362, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_NORTHPASS_CONTEST_ALLIANCE
8601 { 2363, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_NORTHPASS_CONTEST_HORDE
8602 { 2364, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_NORTHPASS_PROGRESS_ALLIANCE
8603 { 2365, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_NORTHPASS_PROGRESS_HORDE
8604 { 2372, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_NORTHPASS_ALLIANCE
8605 { 2373, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_NORTHPASS_HORDE
8606 { 2353, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_PLAGUEWOOD_NEUTRAL
8607 { 2366, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_PLAGUEWOOD_CONTEST_ALLIANCE
8608 { 2367, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_PLAGUEWOOD_CONTEST_HORDE - not in dbc! sent for consistency's sake, and to match field count
8609 { 2368, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_PLAGUEWOOD_PROGRESS_ALLIANCE
8610 { 2369, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_PLAGUEWOOD_PROGRESS_HORDE
8611 { 2370, WORLD_STATE_REMOVE
}, // WORLD_STATE_EP_PLAGUEWOOD_ALLIANCE
8612 { 2371, WORLD_STATE_REMOVE
} // WORLD_STATE_EP_PLAGUEWOOD_HORDE
8615 static WorldStatePair HP_world_states
[] = // Hellfire Peninsula
8617 { 2490, WORLD_STATE_REMOVE
}, // WORLD_STATE_HP_TOWER_DISPLAY_A
8618 { 2489, WORLD_STATE_REMOVE
}, // WORLD_STATE_HP_TOWER_DISPLAY_H
8619 { 2485, WORLD_STATE_REMOVE
}, // WORLD_STATE_HP_BROKEN_HILL_NEUTRAL
8620 { 2484, WORLD_STATE_REMOVE
}, // WORLD_STATE_HP_BROKEN_HILL_HORDE
8621 { 2483, WORLD_STATE_REMOVE
}, // WORLD_STATE_HP_BROKEN_HILL_ALLIANCE
8622 { 2482, WORLD_STATE_REMOVE
}, // WORLD_STATE_HP_OVERLOOK_NEUTRAL
8623 { 2481, WORLD_STATE_REMOVE
}, // WORLD_STATE_HP_OVERLOOK_HORDE
8624 { 2480, WORLD_STATE_REMOVE
}, // WORLD_STATE_HP_OVERLOOK_ALLIANCE
8625 { 2478, 0 }, // WORLD_STATE_HP_TOWER_COUNT_HORDE
8626 { 2476, 0 }, // WORLD_STATE_HP_TOWER_COUNT_ALLIANCE
8627 { 2472, WORLD_STATE_REMOVE
}, // WORLD_STATE_HP_STADIUM_NEUTRAL
8628 { 2471, WORLD_STATE_REMOVE
}, // WORLD_STATE_HP_STADIUM_ALLIANCE
8629 { 2470, WORLD_STATE_REMOVE
} // WORLD_STATE_HP_STADIUM_HORDE
8632 static WorldStatePair TF_world_states
[] = // Terokkar Forest
8634 { 2622, 0 }, // WORLD_STATE_TF_TOWER_COUNT_H
8635 { 2621, 0 }, // WORLD_STATE_TF_TOWER_COUNT_A
8636 { 2620, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_TOWERS_CONTROLLED
8637 { 2695, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_SOUTH_EAST_TOWER_HORDE
8638 { 2694, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_SOUTH_EAST_TOWER_ALLIANCE
8639 { 2693, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_SOUTH_TOWER_NEUTRAL
8640 { 2692, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_SOUTH_TOWER_HORDE
8641 { 2691, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_SOUTH_TOWER_ALLIANCE
8642 { 2690, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_EAST_TOWER_NEUTRAL
8643 { 2689, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_EAST_TOWER_HORDE
8644 { 2688, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_EAST_TOWER_ALLIANCE
8645 { 2686, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_NORTH_TOWER_NEUTRAL
8646 { 2685, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_NORTH_TOWER_HORDE
8647 { 2684, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_NORTH_TOWER_ALLIANCE
8648 { 2683, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_WEST_TOWER_ALLIANCE
8649 { 2682, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_WEST_TOWER_HORDE
8650 { 2681, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_WEST_TOWER_NEUTRAL
8651 { 2512, 0 }, // WORLD_STATE_TF_TIME_MIN_FIRST_DIGIT
8652 { 2510, 0 }, // WORLD_STATE_TF_TIME_MIN_SECOND_DIGIT
8653 { 2509, 0 }, // WORLD_STATE_TF_TIME_HOURS
8654 { 2508, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_LOCKED_NEUTRAL
8655 { 2696, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_SOUTH_EAST_TOWER_NEUTRAL
8656 { 2768, WORLD_STATE_REMOVE
}, // WORLD_STATE_TF_LOCKED_HORDE
8657 { 2767, WORLD_STATE_REMOVE
} // WORLD_STATE_TF_LOCKED_ALLIANCE
8660 static WorldStatePair ZM_world_states
[] = // Zangarmarsh
8662 { 2653, 0x1 }, // WORLD_STATE_ZM_UNK
8663 { 2652, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_EAST_NEUTRAL
8664 { 2651, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_EAST_HORDE
8665 { 2650, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_EAST_ALLIANCE
8666 { 2649, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_GRAVEYARD_HORDE
8667 { 2648, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_GRAVEYARD_ALLIANCE
8668 { 2647, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_GRAVEYARD_NEUTRAL
8669 { 2646, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_WEST_NEUTRAL
8670 { 2645, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_WEST_HORDE
8671 { 2644, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_WEST_ALLIANCE
8672 { 2560, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_EAST_UI_NEUTRAL
8673 { 2559, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_EAST_UI_HORDE
8674 { 2558, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_EAST_UI_ALLIANCE
8675 { 2557, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_WEST_UI_NEUTRAL
8676 { 2556, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_WEST_UI_HORDE
8677 { 2555, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_BEACON_WEST_UI_ALLIANCE
8678 { 2658, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_FLAG_READY_HORDE
8679 { 2657, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_FLAG_NOT_READY_HORDE
8680 { 2656, WORLD_STATE_REMOVE
}, // WORLD_STATE_ZM_FLAG_NOT_READY_ALLIANCE
8681 { 2655, WORLD_STATE_REMOVE
} // WORLD_STATE_ZM_FLAG_READY_ALLIANCE
8684 static WorldStatePair NA_world_states
[] =
8686 { 2503, 0 }, // WORLD_STATE_NA_GUARDS_HORDE
8687 { 2502, 0 }, // WORLD_STATE_NA_GUARDS_ALLIANCE
8688 { 2493, 0 }, // WORLD_STATE_NA_GUARDS_MAX
8689 { 2491, 0 }, // WORLD_STATE_NA_GUARDS_LEFT
8690 { 2762, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_NORTH_NEUTRAL_H
8691 { 2662, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_NORTH_NEUTRAL_A
8692 { 2663, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_NORTH_H
8693 { 2664, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_NORTH_A
8694 { 2760, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_SOUTH_NEUTRAL_H
8695 { 2670, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_SOUTH_NEUTRAL_A
8696 { 2668, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_SOUTH_H
8697 { 2669, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_SOUTH_A
8698 { 2761, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_WEST_NEUTRAL_H
8699 { 2667, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_WEST_NEUTRAL_A
8700 { 2665, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_WEST_H
8701 { 2666, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_WEST_A
8702 { 2763, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_EAST_NEUTRAL_H
8703 { 2659, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_EAST_NEUTRAL_A
8704 { 2660, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_EAST_H
8705 { 2661, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_WYVERN_EAST_A
8706 { 2671, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_HALAA_NEUTRAL
8707 { 2676, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_HALAA_NEUTRAL_A
8708 { 2677, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_HALAA_NEUTRAL_H
8709 { 2672, WORLD_STATE_REMOVE
}, // WORLD_STATE_NA_HALAA_HORDE
8710 { 2673, WORLD_STATE_REMOVE
} // WORLD_STATE_NA_HALAA_ALLIANCE
8713 void Player::SendInitWorldStates(uint32 zoneid
, uint32 areaid
)
8715 // data depends on zoneid/mapid...
8716 BattleGround
* bg
= GetBattleGround();
8717 uint32 mapid
= GetMapId();
8719 DEBUG_LOG("Sending SMSG_INIT_WORLD_STATES to Map:%u, Zone: %u", mapid
, zoneid
);
8721 uint32 count
= 0; // count of world states in packet
8723 WorldPacket
data(SMSG_INIT_WORLD_STATES
, (4 + 4 + 4 + 2 + 8 * 8)); // guess
8724 data
<< uint32(mapid
); // mapid
8725 data
<< uint32(zoneid
); // zone id
8726 data
<< uint32(areaid
); // area id, new 2.1.0
8727 size_t count_pos
= data
.wpos();
8728 data
<< uint16(0); // count of uint64 blocks, placeholder
8731 FillInitialWorldState(data
, count
, 0x8d8, 0x0); // 2264 1
8732 FillInitialWorldState(data
, count
, 0x8d7, 0x0); // 2263 2
8733 FillInitialWorldState(data
, count
, 0x8d6, 0x0); // 2262 3
8734 FillInitialWorldState(data
, count
, 0x8d5, 0x0); // 2261 4
8735 FillInitialWorldState(data
, count
, 0x8d4, 0x0); // 2260 5
8736 FillInitialWorldState(data
, count
, 0x8d3, 0x0); // 2259 6
8737 // 3191 7 Current arena season
8738 FillInitialWorldState(data
, count
, 0xC77, sWorld
.getConfig(CONFIG_UINT32_ARENA_SEASON_ID
));
8739 // 3901 8 Previous arena season
8740 FillInitialWorldState(data
, count
, 0xF3D, sWorld
.getConfig(CONFIG_UINT32_ARENA_SEASON_PREVIOUS_ID
));
8741 FillInitialWorldState(data
, count
, 0xED9, 1); // 3801 9 0 - Battle for Wintergrasp in progress, 1 - otherwise
8742 // 4354 10 Time when next Battle for Wintergrasp starts
8743 FillInitialWorldState(data
, count
, 0x1102, uint32(time(NULL
) + 9000));
8745 if (mapid
== 530) // Outland
8747 FillInitialWorldState(data
, count
, 0x9bf, 0x0); // 2495
8748 FillInitialWorldState(data
, count
, 0x9bd, 0xF); // 2493
8749 FillInitialWorldState(data
, count
, 0x9bb, 0xF); // 2491
8754 case 1: // Dun Morogh
8755 case 11: // Wetlands
8756 case 12: // Elwynn Forest
8757 case 38: // Loch Modan
8758 case 40: // Westfall
8759 case 51: // Searing Gorge
8760 case 1519: // Stormwind City
8761 case 1537: // Ironforge
8762 case 2257: // Deeprun Tram
8763 case 3703: // Shattrath City
8765 case 139: // Eastern Plaguelands
8766 if (OutdoorPvP
* outdoorPvP
= sOutdoorPvPMgr
.GetScript(zoneid
))
8767 outdoorPvP
->FillInitialWorldStates(data
, count
);
8769 FillInitialWorldState(data
, count
, EP_world_states
);
8771 case 1377: // Silithus
8772 if (OutdoorPvP
* outdoorPvP
= sOutdoorPvPMgr
.GetScript(zoneid
))
8773 outdoorPvP
->FillInitialWorldStates(data
, count
);
8775 FillInitialWorldState(data
, count
, SI_world_states
);
8778 if (bg
&& bg
->GetTypeID() == BATTLEGROUND_AV
)
8779 bg
->FillInitialWorldStates(data
, count
);
8781 FillInitialWorldState(data
, count
, AV_world_states
);
8784 if (bg
&& bg
->GetTypeID() == BATTLEGROUND_WS
)
8785 bg
->FillInitialWorldStates(data
, count
);
8787 FillInitialWorldState(data
, count
, WS_world_states
);
8790 if (bg
&& bg
->GetTypeID() == BATTLEGROUND_AB
)
8791 bg
->FillInitialWorldStates(data
, count
);
8793 FillInitialWorldState(data
, count
, AB_world_states
);
8796 if (bg
&& bg
->GetTypeID() == BATTLEGROUND_EY
)
8797 bg
->FillInitialWorldStates(data
, count
);
8799 FillInitialWorldState(data
, count
, EY_world_states
);
8801 case 3483: // Hellfire Peninsula
8802 if (OutdoorPvP
* outdoorPvP
= sOutdoorPvPMgr
.GetScript(zoneid
))
8803 outdoorPvP
->FillInitialWorldStates(data
, count
);
8805 FillInitialWorldState(data
, count
, HP_world_states
);
8807 case 3518: // Nagrand
8808 if (OutdoorPvP
* outdoorPvP
= sOutdoorPvPMgr
.GetScript(zoneid
))
8809 outdoorPvP
->FillInitialWorldStates(data
, count
);
8811 FillInitialWorldState(data
, count
, NA_world_states
);
8813 case 3519: // Terokkar Forest
8814 if (OutdoorPvP
* outdoorPvP
= sOutdoorPvPMgr
.GetScript(zoneid
))
8815 outdoorPvP
->FillInitialWorldStates(data
, count
);
8817 FillInitialWorldState(data
, count
, TF_world_states
);
8819 case 3521: // Zangarmarsh
8820 if (OutdoorPvP
* outdoorPvP
= sOutdoorPvPMgr
.GetScript(zoneid
))
8821 outdoorPvP
->FillInitialWorldStates(data
, count
);
8823 FillInitialWorldState(data
, count
, ZM_world_states
);
8825 case 3698: // Nagrand Arena
8826 if (bg
&& bg
->GetTypeID() == BATTLEGROUND_NA
)
8827 bg
->FillInitialWorldStates(data
, count
);
8830 FillInitialWorldState(data
, count
, 0xa0f, 0x0); // 2575 7
8831 FillInitialWorldState(data
, count
, 0xa10, 0x0); // 2576 8
8832 FillInitialWorldState(data
, count
, 0xa11, 0x0); // 2577 9 show
8835 case 3702: // Blade's Edge Arena
8836 if (bg
&& bg
->GetTypeID() == BATTLEGROUND_BE
)
8837 bg
->FillInitialWorldStates(data
, count
);
8840 FillInitialWorldState(data
, count
, 0x9f0, 0x0); // 2544 7 gold
8841 FillInitialWorldState(data
, count
, 0x9f1, 0x0); // 2545 8 green
8842 FillInitialWorldState(data
, count
, 0x9f3, 0x0); // 2547 9 show
8845 case 3968: // Ruins of Lordaeron
8846 if (bg
&& bg
->GetTypeID() == BATTLEGROUND_RL
)
8847 bg
->FillInitialWorldStates(data
, count
);
8850 FillInitialWorldState(data
, count
, 0xbb8, 0x0); // 3000 7 gold
8851 FillInitialWorldState(data
, count
, 0xbb9, 0x0); // 3001 8 green
8852 FillInitialWorldState(data
, count
, 0xbba, 0x0); // 3002 9 show
8856 FillInitialWorldState(data
, count
, 0x914, 0x0); // 2324 7
8857 FillInitialWorldState(data
, count
, 0x913, 0x0); // 2323 8
8858 FillInitialWorldState(data
, count
, 0x912, 0x0); // 2322 9
8859 FillInitialWorldState(data
, count
, 0x915, 0x0); // 2325 10
8863 FillBGWeekendWorldStates(data
, count
);
8865 data
.put
<uint16
>(count_pos
, count
); // set actual world state amount
8867 GetSession()->SendPacket(&data
);
8870 void Player::FillBGWeekendWorldStates(WorldPacket
& data
, uint32
& count
)
8872 for (uint32 i
= 1; i
< sBattlemasterListStore
.GetNumRows(); ++i
)
8874 BattlemasterListEntry
const* bl
= sBattlemasterListStore
.LookupEntry(i
);
8875 if (bl
&& bl
->HolidayWorldStateId
)
8877 if (BattleGroundMgr::IsBGWeekend(BattleGroundTypeId(bl
->id
)))
8878 FillInitialWorldState(data
, count
, bl
->HolidayWorldStateId
, 1);
8880 FillInitialWorldState(data
, count
, bl
->HolidayWorldStateId
, 0);
8885 uint32
Player::GetXPRestBonus(uint32 xp
)
8887 uint32 rested_bonus
= (uint32
)GetRestBonus(); // xp for each rested bonus
8889 if (rested_bonus
> xp
) // max rested_bonus == xp or (r+x) = 200% xp
8892 SetRestBonus(GetRestBonus() - rested_bonus
);
8894 DETAIL_LOG("Player gain %u xp (+ %u Rested Bonus). Rested points=%f", xp
+ rested_bonus
, rested_bonus
, GetRestBonus());
8895 return rested_bonus
;
8898 void Player::SetBindPoint(ObjectGuid guid
)
8900 WorldPacket
data(SMSG_BINDER_CONFIRM
, 8);
8901 data
<< ObjectGuid(guid
);
8902 GetSession()->SendPacket(&data
);
8905 void Player::SendTalentWipeConfirm(ObjectGuid guid
)
8907 WorldPacket
data(MSG_TALENT_WIPE_CONFIRM
, (8 + 4));
8908 data
<< ObjectGuid(guid
);
8909 data
<< uint32(resetTalentsCost());
8910 GetSession()->SendPacket(&data
);
8913 void Player::SendPetSkillWipeConfirm()
8915 Pet
* pet
= GetPet();
8918 WorldPacket
data(SMSG_PET_UNLEARN_CONFIRM
, (8 + 4));
8919 data
<< ObjectGuid(pet
->GetObjectGuid());
8920 data
<< uint32(pet
->resetTalentsCost());
8921 GetSession()->SendPacket(&data
);
8924 /*********************************************************/
8925 /*** STORAGE SYSTEM ***/
8926 /*********************************************************/
8928 void Player::SetVirtualItemSlot(uint8 i
, Item
* item
)
8930 MANGOS_ASSERT(i
< 3);
8933 if (!item
->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT
))
8935 uint32 charges
= item
->GetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT
);
8939 item
->SetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT
, charges
- 1);
8940 else if (charges
<= 1)
8942 ApplyEnchantment(item
, TEMP_ENCHANTMENT_SLOT
, false);
8943 item
->ClearEnchantment(TEMP_ENCHANTMENT_SLOT
);
8948 void Player::SetSheath(SheathState sheathed
)
8952 case SHEATH_STATE_UNARMED
: // no prepared weapon
8953 SetVirtualItemSlot(0, NULL
);
8954 SetVirtualItemSlot(1, NULL
);
8955 SetVirtualItemSlot(2, NULL
);
8957 case SHEATH_STATE_MELEE
: // prepared melee weapon
8959 SetVirtualItemSlot(0, GetWeaponForAttack(BASE_ATTACK
, true, true));
8960 SetVirtualItemSlot(1, GetWeaponForAttack(OFF_ATTACK
, true, true));
8961 SetVirtualItemSlot(2, NULL
);
8963 case SHEATH_STATE_RANGED
: // prepared ranged weapon
8964 SetVirtualItemSlot(0, NULL
);
8965 SetVirtualItemSlot(1, NULL
);
8966 SetVirtualItemSlot(2, GetWeaponForAttack(RANGED_ATTACK
, true, true));
8969 SetVirtualItemSlot(0, NULL
);
8970 SetVirtualItemSlot(1, NULL
);
8971 SetVirtualItemSlot(2, NULL
);
8974 Unit::SetSheath(sheathed
); // this must visualize Sheath changing for other players...
8977 bool Player::GetSlotsForInventoryType(uint8 invType
, uint8
* slots
, uint32 subClass
) const
8979 uint8 pClass
= getClass();
8981 slots
[0] = NULL_SLOT
;
8982 slots
[1] = NULL_SLOT
;
8983 slots
[2] = NULL_SLOT
;
8984 slots
[3] = NULL_SLOT
;
8988 slots
[0] = EQUIPMENT_SLOT_HEAD
;
8991 slots
[0] = EQUIPMENT_SLOT_NECK
;
8993 case INVTYPE_SHOULDERS
:
8994 slots
[0] = EQUIPMENT_SLOT_SHOULDERS
;
8997 slots
[0] = EQUIPMENT_SLOT_BODY
;
9000 slots
[0] = EQUIPMENT_SLOT_CHEST
;
9003 slots
[0] = EQUIPMENT_SLOT_CHEST
;
9006 slots
[0] = EQUIPMENT_SLOT_WAIST
;
9009 slots
[0] = EQUIPMENT_SLOT_LEGS
;
9012 slots
[0] = EQUIPMENT_SLOT_FEET
;
9014 case INVTYPE_WRISTS
:
9015 slots
[0] = EQUIPMENT_SLOT_WRISTS
;
9018 slots
[0] = EQUIPMENT_SLOT_HANDS
;
9020 case INVTYPE_FINGER
:
9021 slots
[0] = EQUIPMENT_SLOT_FINGER1
;
9022 slots
[1] = EQUIPMENT_SLOT_FINGER2
;
9024 case INVTYPE_TRINKET
:
9025 slots
[0] = EQUIPMENT_SLOT_TRINKET1
;
9026 slots
[1] = EQUIPMENT_SLOT_TRINKET2
;
9029 slots
[0] = EQUIPMENT_SLOT_BACK
;
9031 case INVTYPE_WEAPON
:
9033 slots
[0] = EQUIPMENT_SLOT_MAINHAND
;
9035 // suggest offhand slot only if know dual wielding
9036 // (this will be replace mainhand weapon at auto equip instead unwanted "you don't known dual wielding" ...
9038 slots
[1] = EQUIPMENT_SLOT_OFFHAND
;
9041 case INVTYPE_SHIELD
:
9042 slots
[0] = EQUIPMENT_SLOT_OFFHAND
;
9044 case INVTYPE_RANGED
:
9045 slots
[0] = EQUIPMENT_SLOT_RANGED
;
9047 case INVTYPE_2HWEAPON
:
9048 slots
[0] = EQUIPMENT_SLOT_MAINHAND
;
9049 if (CanDualWield() && CanTitanGrip())
9050 slots
[1] = EQUIPMENT_SLOT_OFFHAND
;
9052 case INVTYPE_TABARD
:
9053 slots
[0] = EQUIPMENT_SLOT_TABARD
;
9055 case INVTYPE_WEAPONMAINHAND
:
9056 slots
[0] = EQUIPMENT_SLOT_MAINHAND
;
9058 case INVTYPE_WEAPONOFFHAND
:
9059 slots
[0] = EQUIPMENT_SLOT_OFFHAND
;
9061 case INVTYPE_HOLDABLE
:
9062 slots
[0] = EQUIPMENT_SLOT_OFFHAND
;
9064 case INVTYPE_THROWN
:
9065 slots
[0] = EQUIPMENT_SLOT_RANGED
;
9067 case INVTYPE_RANGEDRIGHT
:
9068 slots
[0] = EQUIPMENT_SLOT_RANGED
;
9071 slots
[0] = INVENTORY_SLOT_BAG_START
+ 0;
9072 slots
[1] = INVENTORY_SLOT_BAG_START
+ 1;
9073 slots
[2] = INVENTORY_SLOT_BAG_START
+ 2;
9074 slots
[3] = INVENTORY_SLOT_BAG_START
+ 3;
9080 case ITEM_SUBCLASS_ARMOR_LIBRAM
:
9081 if (pClass
== CLASS_PALADIN
)
9082 slots
[0] = EQUIPMENT_SLOT_RANGED
;
9084 case ITEM_SUBCLASS_ARMOR_IDOL
:
9085 if (pClass
== CLASS_DRUID
)
9086 slots
[0] = EQUIPMENT_SLOT_RANGED
;
9088 case ITEM_SUBCLASS_ARMOR_TOTEM
:
9089 if (pClass
== CLASS_SHAMAN
)
9090 slots
[0] = EQUIPMENT_SLOT_RANGED
;
9092 case ITEM_SUBCLASS_ARMOR_MISC
:
9093 if (pClass
== CLASS_WARLOCK
)
9094 slots
[0] = EQUIPMENT_SLOT_RANGED
;
9096 case ITEM_SUBCLASS_ARMOR_SIGIL
:
9097 if (pClass
== CLASS_DEATH_KNIGHT
)
9098 slots
[0] = EQUIPMENT_SLOT_RANGED
;
9110 uint8
Player::FindEquipSlot(ItemPrototype
const* proto
, uint32 slot
, bool swap
) const
9113 if (!GetSlotsForInventoryType(proto
->InventoryType
, slots
, proto
->SubClass
))
9116 if (slot
!= NULL_SLOT
)
9118 if (swap
|| !GetItemByPos(INVENTORY_SLOT_BAG_0
, slot
))
9120 for (int i
= 0; i
< 4; ++i
)
9122 if (slots
[i
] == slot
)
9129 // search free slot at first
9130 for (int i
= 0; i
< 4; ++i
)
9132 if (slots
[i
] != NULL_SLOT
&& !GetItemByPos(INVENTORY_SLOT_BAG_0
, slots
[i
]))
9134 // in case 2hand equipped weapon (without titan grip) offhand slot empty but not free
9135 if (slots
[i
] != EQUIPMENT_SLOT_OFFHAND
|| !IsTwoHandUsed())
9140 // if not found free and can swap return first appropriate from used
9141 for (int i
= 0; i
< 4; ++i
)
9143 if (slots
[i
] != NULL_SLOT
&& swap
)
9152 InventoryResult
Player::CanUnequipItems(uint32 item
, uint32 count
) const
9155 uint32 tempcount
= 0;
9157 InventoryResult res
= EQUIP_ERR_OK
;
9159 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
9161 pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9162 if (pItem
&& pItem
->GetEntry() == item
)
9164 InventoryResult ires
= CanUnequipItem(INVENTORY_SLOT_BAG_0
<< 8 | i
, false);
9165 if (ires
== EQUIP_ERR_OK
)
9167 tempcount
+= pItem
->GetCount();
9168 if (tempcount
>= count
)
9169 return EQUIP_ERR_OK
;
9175 for (int i
= INVENTORY_SLOT_ITEM_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
9177 pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9178 if (pItem
&& pItem
->GetEntry() == item
)
9180 tempcount
+= pItem
->GetCount();
9181 if (tempcount
>= count
)
9182 return EQUIP_ERR_OK
;
9186 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
9188 pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9191 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
9193 pItem
= GetItemByPos(i
, j
);
9194 if (pItem
&& pItem
->GetEntry() == item
)
9196 tempcount
+= pItem
->GetCount();
9197 if (tempcount
>= count
)
9198 return EQUIP_ERR_OK
;
9204 // not found req. item count and have unequippable items
9208 uint32
Player::GetItemCount(uint32 item
, bool inBankAlso
, Item
* skipItem
) const
9211 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
9213 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9214 if (pItem
&& pItem
!= skipItem
&& pItem
->GetEntry() == item
)
9215 count
+= pItem
->GetCount();
9217 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
9219 Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9221 count
+= pBag
->GetItemCount(item
, skipItem
);
9224 if (skipItem
&& skipItem
->GetProto()->GemProperties
)
9226 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
9228 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9229 if (pItem
&& pItem
!= skipItem
&& pItem
->GetProto()->Socket
[0].Color
)
9230 count
+= pItem
->GetGemCountWithID(item
);
9236 for (int i
= BANK_SLOT_ITEM_START
; i
< BANK_SLOT_ITEM_END
; ++i
)
9238 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9239 if (pItem
&& pItem
!= skipItem
&& pItem
->GetEntry() == item
)
9240 count
+= pItem
->GetCount();
9242 for (int i
= BANK_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
9244 Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9246 count
+= pBag
->GetItemCount(item
, skipItem
);
9249 if (skipItem
&& skipItem
->GetProto()->GemProperties
)
9251 for (int i
= BANK_SLOT_ITEM_START
; i
< BANK_SLOT_ITEM_END
; ++i
)
9253 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9254 if (pItem
&& pItem
!= skipItem
&& pItem
->GetProto()->Socket
[0].Color
)
9255 count
+= pItem
->GetGemCountWithID(item
);
9263 uint32
Player::GetItemCountWithLimitCategory(uint32 limitCategory
, Item
* skipItem
) const
9266 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
9267 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9268 if (pItem
->GetProto()->ItemLimitCategory
== limitCategory
&& pItem
!= skipItem
)
9269 count
+= pItem
->GetCount();
9271 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
9272 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9273 count
+= pBag
->GetItemCountWithLimitCategory(limitCategory
, skipItem
);
9275 for (int i
= BANK_SLOT_ITEM_START
; i
< BANK_SLOT_ITEM_END
; ++i
)
9276 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9277 if (pItem
->GetProto()->ItemLimitCategory
== limitCategory
&& pItem
!= skipItem
)
9278 count
+= pItem
->GetCount();
9280 for (int i
= BANK_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
9281 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9282 count
+= pBag
->GetItemCountWithLimitCategory(limitCategory
, skipItem
);
9287 Item
* Player::GetItemByEntry(uint32 item
) const
9289 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
9290 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9291 if (pItem
->GetEntry() == item
)
9294 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
9295 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9296 if (Item
* itemPtr
= pBag
->GetItemByEntry(item
))
9302 Item
* Player::GetItemByLimitedCategory(uint32 limitedCategory
) const
9304 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
9305 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9306 if (pItem
->GetProto()->ItemLimitCategory
== limitedCategory
)
9309 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
9310 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9311 if (Item
* itemPtr
= pBag
->GetItemByLimitedCategory(limitedCategory
))
9317 Item
* Player::GetItemByGuid(ObjectGuid guid
) const
9319 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
9320 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9321 if (pItem
->GetObjectGuid() == guid
)
9324 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
9325 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9326 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
9327 if (Item
* pItem
= pBag
->GetItemByPos(j
))
9328 if (pItem
->GetObjectGuid() == guid
)
9331 for (int i
= BANK_SLOT_ITEM_START
; i
< BANK_SLOT_ITEM_END
; ++i
)
9332 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9333 if (pItem
->GetObjectGuid() == guid
)
9336 for (int i
= BANK_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
9337 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9338 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
9339 if (Item
* pItem
= pBag
->GetItemByPos(j
))
9340 if (pItem
->GetObjectGuid() == guid
)
9346 Item
* Player::GetItemByPos(uint16 pos
) const
9348 uint8 bag
= pos
>> 8;
9349 uint8 slot
= pos
& 255;
9350 return GetItemByPos(bag
, slot
);
9353 Item
* Player::GetItemByPos(uint8 bag
, uint8 slot
) const
9355 if (bag
== INVENTORY_SLOT_BAG_0
&& slot
< BANK_SLOT_BAG_END
)
9356 return m_items
[slot
];
9357 else if ((bag
>= INVENTORY_SLOT_BAG_START
&& bag
< INVENTORY_SLOT_BAG_END
)
9358 || (bag
>= BANK_SLOT_BAG_START
&& bag
< BANK_SLOT_BAG_END
))
9360 Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, bag
);
9362 return pBag
->GetItemByPos(slot
);
9367 uint32
Player::GetItemDisplayIdInSlot(uint8 bag
, uint8 slot
) const
9369 const Item
* pItem
= GetItemByPos(bag
, slot
);
9374 return pItem
->GetProto()->DisplayInfoID
;
9377 Item
* Player::GetWeaponForAttack(WeaponAttackType attackType
, bool nonbroken
, bool useable
) const
9382 case BASE_ATTACK
: slot
= EQUIPMENT_SLOT_MAINHAND
; break;
9383 case OFF_ATTACK
: slot
= EQUIPMENT_SLOT_OFFHAND
; break;
9384 case RANGED_ATTACK
: slot
= EQUIPMENT_SLOT_RANGED
; break;
9385 default: return NULL
;
9388 Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, slot
);
9389 if (!item
|| item
->GetProto()->Class
!= ITEM_CLASS_WEAPON
)
9392 if (useable
&& !CanUseEquippedWeapon(attackType
))
9395 if (nonbroken
&& item
->IsBroken())
9401 Item
* Player::GetShield(bool useable
) const
9403 Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
);
9404 if (!item
|| item
->GetProto()->Class
!= ITEM_CLASS_ARMOR
)
9410 if (item
->IsBroken() || !CanUseEquippedWeapon(OFF_ATTACK
))
9416 uint32
Player::GetAttackBySlot(uint8 slot
)
9420 case EQUIPMENT_SLOT_MAINHAND
: return BASE_ATTACK
;
9421 case EQUIPMENT_SLOT_OFFHAND
: return OFF_ATTACK
;
9422 case EQUIPMENT_SLOT_RANGED
: return RANGED_ATTACK
;
9423 default: return MAX_ATTACK
;
9427 bool Player::IsInventoryPos(uint8 bag
, uint8 slot
)
9429 if (bag
== INVENTORY_SLOT_BAG_0
&& slot
== NULL_SLOT
)
9431 if (bag
== INVENTORY_SLOT_BAG_0
&& (slot
>= INVENTORY_SLOT_ITEM_START
&& slot
< INVENTORY_SLOT_ITEM_END
))
9433 if (bag
>= INVENTORY_SLOT_BAG_START
&& bag
< INVENTORY_SLOT_BAG_END
)
9438 bool Player::IsEquipmentPos(uint8 bag
, uint8 slot
)
9440 if (bag
== INVENTORY_SLOT_BAG_0
&& (slot
< EQUIPMENT_SLOT_END
))
9442 if (bag
== INVENTORY_SLOT_BAG_0
&& (slot
>= INVENTORY_SLOT_BAG_START
&& slot
< INVENTORY_SLOT_BAG_END
))
9447 bool Player::IsBankPos(uint8 bag
, uint8 slot
)
9449 if (bag
== INVENTORY_SLOT_BAG_0
&& (slot
>= BANK_SLOT_ITEM_START
&& slot
< BANK_SLOT_ITEM_END
))
9451 if (bag
== INVENTORY_SLOT_BAG_0
&& (slot
>= BANK_SLOT_BAG_START
&& slot
< BANK_SLOT_BAG_END
))
9453 if (bag
>= BANK_SLOT_BAG_START
&& bag
< BANK_SLOT_BAG_END
)
9458 bool Player::IsBagPos(uint16 pos
)
9460 uint8 bag
= pos
>> 8;
9461 uint8 slot
= pos
& 255;
9462 if (bag
== INVENTORY_SLOT_BAG_0
&& (slot
>= INVENTORY_SLOT_BAG_START
&& slot
< INVENTORY_SLOT_BAG_END
))
9464 if (bag
== INVENTORY_SLOT_BAG_0
&& (slot
>= BANK_SLOT_BAG_START
&& slot
< BANK_SLOT_BAG_END
))
9469 bool Player::IsValidPos(uint8 bag
, uint8 slot
, bool explicit_pos
) const
9472 if (bag
== NULL_BAG
&& !explicit_pos
)
9475 if (bag
== INVENTORY_SLOT_BAG_0
)
9477 // any post selected
9478 if (slot
== NULL_SLOT
&& !explicit_pos
)
9482 if (slot
< EQUIPMENT_SLOT_END
)
9486 if (slot
>= INVENTORY_SLOT_BAG_START
&& slot
< INVENTORY_SLOT_BAG_END
)
9490 if (slot
>= INVENTORY_SLOT_ITEM_START
&& slot
< INVENTORY_SLOT_ITEM_END
)
9494 if (slot
>= BANK_SLOT_ITEM_START
&& slot
< BANK_SLOT_ITEM_END
)
9498 if (slot
>= BANK_SLOT_BAG_START
&& slot
< BANK_SLOT_BAG_END
)
9504 // bag content slots
9505 if (bag
>= INVENTORY_SLOT_BAG_START
&& bag
< INVENTORY_SLOT_BAG_END
)
9507 Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, bag
);
9511 // any post selected
9512 if (slot
== NULL_SLOT
&& !explicit_pos
)
9515 return slot
< pBag
->GetBagSize();
9518 // bank bag content slots
9519 if (bag
>= BANK_SLOT_BAG_START
&& bag
< BANK_SLOT_BAG_END
)
9521 Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, bag
);
9525 // any post selected
9526 if (slot
== NULL_SLOT
&& !explicit_pos
)
9529 return slot
< pBag
->GetBagSize();
9537 bool Player::HasItemCount(uint32 item
, uint32 count
, bool inBankAlso
) const
9539 uint32 tempcount
= 0;
9540 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
9542 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9543 if (pItem
&& pItem
->GetEntry() == item
&& !pItem
->IsInTrade())
9545 tempcount
+= pItem
->GetCount();
9546 if (tempcount
>= count
)
9550 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
9552 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9554 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
9556 Item
* pItem
= GetItemByPos(i
, j
);
9557 if (pItem
&& pItem
->GetEntry() == item
&& !pItem
->IsInTrade())
9559 tempcount
+= pItem
->GetCount();
9560 if (tempcount
>= count
)
9569 for (int i
= BANK_SLOT_ITEM_START
; i
< BANK_SLOT_ITEM_END
; ++i
)
9571 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9572 if (pItem
&& pItem
->GetEntry() == item
&& !pItem
->IsInTrade())
9574 tempcount
+= pItem
->GetCount();
9575 if (tempcount
>= count
)
9579 for (int i
= BANK_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
9581 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
9583 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
9585 Item
* pItem
= GetItemByPos(i
, j
);
9586 if (pItem
&& pItem
->GetEntry() == item
&& !pItem
->IsInTrade())
9588 tempcount
+= pItem
->GetCount();
9589 if (tempcount
>= count
)
9600 bool Player::HasItemOrGemWithIdEquipped(uint32 item
, uint32 count
, uint8 except_slot
) const
9602 uint32 tempcount
= 0;
9603 for (int i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_END
; ++i
)
9605 if (i
== int(except_slot
))
9608 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9609 if (pItem
&& pItem
->GetEntry() == item
)
9611 tempcount
+= pItem
->GetCount();
9612 if (tempcount
>= count
)
9617 ItemPrototype
const* pProto
= ObjectMgr::GetItemPrototype(item
);
9618 if (pProto
&& pProto
->GemProperties
)
9620 for (int i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_END
; ++i
)
9622 if (i
== int(except_slot
))
9625 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9626 if (pItem
&& pItem
->GetProto()->Socket
[0].Color
)
9628 tempcount
+= pItem
->GetGemCountWithID(item
);
9629 if (tempcount
>= count
)
9638 bool Player::HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory
, uint32 count
, uint8 except_slot
) const
9640 uint32 tempcount
= 0;
9641 for (int i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_END
; ++i
)
9643 if (i
== int(except_slot
))
9646 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
9650 ItemPrototype
const* pProto
= pItem
->GetProto();
9654 if (pProto
->ItemLimitCategory
== limitCategory
)
9656 tempcount
+= pItem
->GetCount();
9657 if (tempcount
>= count
)
9661 if (pProto
->Socket
[0].Color
)
9663 tempcount
+= pItem
->GetGemCountWithLimitCategory(limitCategory
);
9664 if (tempcount
>= count
)
9672 InventoryResult
Player::_CanTakeMoreSimilarItems(uint32 entry
, uint32 count
, Item
* pItem
, uint32
* no_space_count
) const
9674 ItemPrototype
const* pProto
= ObjectMgr::GetItemPrototype(entry
);
9678 *no_space_count
= count
;
9679 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
9683 if (pProto
->MaxCount
> 0)
9685 uint32 curcount
= GetItemCount(pProto
->ItemId
, true, pItem
);
9687 if (curcount
+ count
> uint32(pProto
->MaxCount
))
9690 *no_space_count
= count
+ curcount
- pProto
->MaxCount
;
9691 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
9695 // check unique-equipped limit
9696 if (pProto
->ItemLimitCategory
)
9698 ItemLimitCategoryEntry
const* limitEntry
= sItemLimitCategoryStore
.LookupEntry(pProto
->ItemLimitCategory
);
9702 *no_space_count
= count
;
9703 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED
;
9706 if (limitEntry
->mode
== ITEM_LIMIT_CATEGORY_MODE_HAVE
)
9708 uint32 curcount
= GetItemCountWithLimitCategory(pProto
->ItemLimitCategory
, pItem
);
9710 if (curcount
+ count
> uint32(limitEntry
->maxCount
))
9713 *no_space_count
= count
+ curcount
- limitEntry
->maxCount
;
9714 return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS
;
9719 return EQUIP_ERR_OK
;
9722 InventoryResult
Player::_CanStoreItem_InSpecificSlot(uint8 bag
, uint8 slot
, ItemPosCountVec
& dest
, ItemPrototype
const* pProto
, uint32
& count
, bool swap
, Item
* pSrcItem
) const
9724 Item
* pItem2
= GetItemByPos(bag
, slot
);
9726 // ignore move item (this slot will be empty at move)
9727 if (pItem2
== pSrcItem
)
9732 // empty specific slot - check item fit to slot
9733 if (!pItem2
|| swap
)
9735 if (bag
== INVENTORY_SLOT_BAG_0
)
9738 if ((slot
>= BUYBACK_SLOT_START
&& slot
< BUYBACK_SLOT_END
) || slot
>= PLAYER_SLOT_END
)
9739 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG
;
9743 Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, bag
);
9745 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG
;
9747 ItemPrototype
const* pBagProto
= pBag
->GetProto();
9749 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG
;
9751 if (slot
>= pBagProto
->ContainerSlots
)
9752 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG
;
9754 if (!ItemCanGoIntoBag(pProto
, pBagProto
))
9755 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG
;
9758 // non empty stack with space
9759 need_space
= pProto
->GetMaxStackSize();
9761 // non empty slot, check item type
9764 // can be merged at least partly
9765 InventoryResult res
= pItem2
->CanBeMergedPartlyWith(pProto
);
9766 if (res
!= EQUIP_ERR_OK
)
9769 // free stack space or infinity
9770 need_space
= pProto
->GetMaxStackSize() - pItem2
->GetCount();
9773 if (need_space
> count
)
9776 ItemPosCount newPosition
= ItemPosCount((bag
<< 8) | slot
, need_space
);
9777 if (!newPosition
.isContainedIn(dest
))
9779 dest
.push_back(newPosition
);
9780 count
-= need_space
;
9782 return EQUIP_ERR_OK
;
9785 InventoryResult
Player::_CanStoreItem_InBag(uint8 bag
, ItemPosCountVec
& dest
, ItemPrototype
const* pProto
, uint32
& count
, bool merge
, bool non_specialized
, Item
* pSrcItem
, uint8 skip_bag
, uint8 skip_slot
) const
9787 // skip specific bag already processed in first called _CanStoreItem_InBag
9788 if (bag
== skip_bag
)
9789 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG
;
9791 // skip nonexistent bag or self targeted bag
9792 Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, bag
);
9793 if (!pBag
|| pBag
== pSrcItem
)
9794 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG
;
9796 ItemPrototype
const* pBagProto
= pBag
->GetProto();
9798 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG
;
9800 // specialized bag mode or non-specilized
9801 if (non_specialized
!= (pBagProto
->Class
== ITEM_CLASS_CONTAINER
&& pBagProto
->SubClass
== ITEM_SUBCLASS_CONTAINER
))
9802 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG
;
9804 if (!ItemCanGoIntoBag(pProto
, pBagProto
))
9805 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG
;
9807 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
9809 // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
9813 Item
* pItem2
= GetItemByPos(bag
, j
);
9815 // ignore move item (this slot will be empty at move)
9816 if (pItem2
== pSrcItem
)
9819 // if merge skip empty, if !merge skip non-empty
9820 if ((pItem2
!= NULL
) != merge
)
9823 uint32 need_space
= pProto
->GetMaxStackSize();
9827 // can be merged at least partly
9828 uint8 res
= pItem2
->CanBeMergedPartlyWith(pProto
);
9829 if (res
!= EQUIP_ERR_OK
)
9832 // decrease at current stacksize
9833 need_space
-= pItem2
->GetCount();
9836 if (need_space
> count
)
9839 ItemPosCount newPosition
= ItemPosCount((bag
<< 8) | j
, need_space
);
9840 if (!newPosition
.isContainedIn(dest
))
9842 dest
.push_back(newPosition
);
9843 count
-= need_space
;
9846 return EQUIP_ERR_OK
;
9849 return EQUIP_ERR_OK
;
9852 InventoryResult
Player::_CanStoreItem_InInventorySlots(uint8 slot_begin
, uint8 slot_end
, ItemPosCountVec
& dest
, ItemPrototype
const* pProto
, uint32
& count
, bool merge
, Item
* pSrcItem
, uint8 skip_bag
, uint8 skip_slot
) const
9854 for (uint32 j
= slot_begin
; j
< slot_end
; ++j
)
9856 // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
9857 if (INVENTORY_SLOT_BAG_0
== skip_bag
&& j
== skip_slot
)
9860 Item
* pItem2
= GetItemByPos(INVENTORY_SLOT_BAG_0
, j
);
9862 // ignore move item (this slot will be empty at move)
9863 if (pItem2
== pSrcItem
)
9866 // if merge skip empty, if !merge skip non-empty
9867 if ((pItem2
!= NULL
) != merge
)
9870 uint32 need_space
= pProto
->GetMaxStackSize();
9874 // can be merged at least partly
9875 uint8 res
= pItem2
->CanBeMergedPartlyWith(pProto
);
9876 if (res
!= EQUIP_ERR_OK
)
9879 // descrease at current stacksize
9880 need_space
-= pItem2
->GetCount();
9883 if (need_space
> count
)
9886 ItemPosCount newPosition
= ItemPosCount((INVENTORY_SLOT_BAG_0
<< 8) | j
, need_space
);
9887 if (!newPosition
.isContainedIn(dest
))
9889 dest
.push_back(newPosition
);
9890 count
-= need_space
;
9893 return EQUIP_ERR_OK
;
9896 return EQUIP_ERR_OK
;
9899 InventoryResult
Player::_CanStoreItem(uint8 bag
, uint8 slot
, ItemPosCountVec
& dest
, uint32 entry
, uint32 count
, Item
* pItem
, bool swap
, uint32
* no_space_count
) const
9901 DEBUG_LOG("STORAGE: CanStoreItem bag = %u, slot = %u, item = %u, count = %u", bag
, slot
, entry
, count
);
9903 ItemPrototype
const* pProto
= ObjectMgr::GetItemPrototype(entry
);
9907 *no_space_count
= count
;
9908 return swap
? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED
: EQUIP_ERR_ITEM_NOT_FOUND
;
9914 if (pItem
->HasTemporaryLoot())
9917 *no_space_count
= count
;
9918 return EQUIP_ERR_ALREADY_LOOTED
;
9921 if (pItem
->IsBindedNotWith(this))
9924 *no_space_count
= count
;
9925 return EQUIP_ERR_DONT_OWN_THAT_ITEM
;
9929 // check count of items (skip for auto move for same player from bank)
9930 uint32 no_similar_count
= 0; // can't store this amount similar items
9931 InventoryResult res
= _CanTakeMoreSimilarItems(entry
, count
, pItem
, &no_similar_count
);
9932 if (res
!= EQUIP_ERR_OK
)
9934 if (count
== no_similar_count
)
9937 *no_space_count
= no_similar_count
;
9940 count
-= no_similar_count
;
9944 if (bag
!= NULL_BAG
&& slot
!= NULL_SLOT
)
9946 res
= _CanStoreItem_InSpecificSlot(bag
, slot
, dest
, pProto
, count
, swap
, pItem
);
9947 if (res
!= EQUIP_ERR_OK
)
9950 *no_space_count
= count
+ no_similar_count
;
9956 if (no_similar_count
== 0)
9957 return EQUIP_ERR_OK
;
9960 *no_space_count
= count
+ no_similar_count
;
9961 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
9965 // not specific slot or have space for partly store only in specific slot
9968 if (bag
!= NULL_BAG
)
9970 // search stack in bag for merge to
9971 if (pProto
->Stackable
!= 1)
9973 if (bag
== INVENTORY_SLOT_BAG_0
) // inventory
9975 res
= _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START
, INVENTORY_SLOT_ITEM_END
, dest
, pProto
, count
, true, pItem
, bag
, slot
);
9976 if (res
!= EQUIP_ERR_OK
)
9979 *no_space_count
= count
+ no_similar_count
;
9985 if (no_similar_count
== 0)
9986 return EQUIP_ERR_OK
;
9989 *no_space_count
= count
+ no_similar_count
;
9990 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
9993 else // equipped bag
9995 // we need check 2 time (specialized/non_specialized), use NULL_BAG to prevent skipping bag
9996 res
= _CanStoreItem_InBag(bag
, dest
, pProto
, count
, true, false, pItem
, NULL_BAG
, slot
);
9997 if (res
!= EQUIP_ERR_OK
)
9998 res
= _CanStoreItem_InBag(bag
, dest
, pProto
, count
, true, true, pItem
, NULL_BAG
, slot
);
10000 if (res
!= EQUIP_ERR_OK
)
10002 if (no_space_count
)
10003 *no_space_count
= count
+ no_similar_count
;
10009 if (no_similar_count
== 0)
10010 return EQUIP_ERR_OK
;
10012 if (no_space_count
)
10013 *no_space_count
= count
+ no_similar_count
;
10014 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
10019 // search free slot in bag for place to
10020 if (bag
== INVENTORY_SLOT_BAG_0
) // inventory
10022 res
= _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START
, INVENTORY_SLOT_ITEM_END
, dest
, pProto
, count
, false, pItem
, bag
, slot
);
10023 if (res
!= EQUIP_ERR_OK
)
10025 if (no_space_count
)
10026 *no_space_count
= count
+ no_similar_count
;
10032 if (no_similar_count
== 0)
10033 return EQUIP_ERR_OK
;
10035 if (no_space_count
)
10036 *no_space_count
= count
+ no_similar_count
;
10037 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
10040 else // equipped bag
10042 res
= _CanStoreItem_InBag(bag
, dest
, pProto
, count
, false, false, pItem
, NULL_BAG
, slot
);
10043 if (res
!= EQUIP_ERR_OK
)
10044 res
= _CanStoreItem_InBag(bag
, dest
, pProto
, count
, false, true, pItem
, NULL_BAG
, slot
);
10046 if (res
!= EQUIP_ERR_OK
)
10048 if (no_space_count
)
10049 *no_space_count
= count
+ no_similar_count
;
10055 if (no_similar_count
== 0)
10056 return EQUIP_ERR_OK
;
10058 if (no_space_count
)
10059 *no_space_count
= count
+ no_similar_count
;
10060 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
10065 // not specific bag or have space for partly store only in specific bag
10067 // search stack for merge to
10068 if (pProto
->Stackable
!= 1)
10070 res
= _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START
, INVENTORY_SLOT_ITEM_END
, dest
, pProto
, count
, true, pItem
, bag
, slot
);
10071 if (res
!= EQUIP_ERR_OK
)
10073 if (no_space_count
)
10074 *no_space_count
= count
+ no_similar_count
;
10080 if (no_similar_count
== 0)
10081 return EQUIP_ERR_OK
;
10083 if (no_space_count
)
10084 *no_space_count
= count
+ no_similar_count
;
10085 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
10088 if (pProto
->BagFamily
)
10090 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
10092 res
= _CanStoreItem_InBag(i
, dest
, pProto
, count
, true, false, pItem
, bag
, slot
);
10093 if (res
!= EQUIP_ERR_OK
)
10098 if (no_similar_count
== 0)
10099 return EQUIP_ERR_OK
;
10101 if (no_space_count
)
10102 *no_space_count
= count
+ no_similar_count
;
10103 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
10108 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
10110 res
= _CanStoreItem_InBag(i
, dest
, pProto
, count
, true, true, pItem
, bag
, slot
);
10111 if (res
!= EQUIP_ERR_OK
)
10116 if (no_similar_count
== 0)
10117 return EQUIP_ERR_OK
;
10119 if (no_space_count
)
10120 *no_space_count
= count
+ no_similar_count
;
10121 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
10126 // search free slot - special bag case
10127 if (pProto
->BagFamily
)
10129 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
10131 res
= _CanStoreItem_InBag(i
, dest
, pProto
, count
, false, false, pItem
, bag
, slot
);
10132 if (res
!= EQUIP_ERR_OK
)
10137 if (no_similar_count
== 0)
10138 return EQUIP_ERR_OK
;
10140 if (no_space_count
)
10141 *no_space_count
= count
+ no_similar_count
;
10142 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
10147 // Normally it would be impossible to autostore not empty bags
10148 if (pItem
&& pItem
->IsBag() && !((Bag
*)pItem
)->IsEmpty())
10149 return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG
;
10151 // search free slot
10152 res
= _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START
, INVENTORY_SLOT_ITEM_END
, dest
, pProto
, count
, false, pItem
, bag
, slot
);
10153 if (res
!= EQUIP_ERR_OK
)
10155 if (no_space_count
)
10156 *no_space_count
= count
+ no_similar_count
;
10162 if (no_similar_count
== 0)
10163 return EQUIP_ERR_OK
;
10165 if (no_space_count
)
10166 *no_space_count
= count
+ no_similar_count
;
10167 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
10170 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
10172 res
= _CanStoreItem_InBag(i
, dest
, pProto
, count
, false, true, pItem
, bag
, slot
);
10173 if (res
!= EQUIP_ERR_OK
)
10178 if (no_similar_count
== 0)
10179 return EQUIP_ERR_OK
;
10181 if (no_space_count
)
10182 *no_space_count
= count
+ no_similar_count
;
10183 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS
;
10187 if (no_space_count
)
10188 *no_space_count
= count
+ no_similar_count
;
10190 return EQUIP_ERR_INVENTORY_FULL
;
10193 //////////////////////////////////////////////////////////////////////////
10194 InventoryResult
Player::CanStoreItems(Item
** pItems
, int count
) const
10198 // fill space table
10199 int inv_slot_items
[INVENTORY_SLOT_ITEM_END
- INVENTORY_SLOT_ITEM_START
];
10200 int inv_bags
[INVENTORY_SLOT_BAG_END
- INVENTORY_SLOT_BAG_START
][MAX_BAG_SIZE
];
10202 memset(inv_slot_items
, 0, sizeof(int) * (INVENTORY_SLOT_ITEM_END
- INVENTORY_SLOT_ITEM_START
));
10203 memset(inv_bags
, 0, sizeof(int) * (INVENTORY_SLOT_BAG_END
- INVENTORY_SLOT_BAG_START
)*MAX_BAG_SIZE
);
10205 for (int i
= INVENTORY_SLOT_ITEM_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
10207 pItem2
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
10209 if (pItem2
&& !pItem2
->IsInTrade())
10211 inv_slot_items
[i
- INVENTORY_SLOT_ITEM_START
] = pItem2
->GetCount();
10215 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
10217 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
10219 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
10221 pItem2
= GetItemByPos(i
, j
);
10222 if (pItem2
&& !pItem2
->IsInTrade())
10224 inv_bags
[i
- INVENTORY_SLOT_BAG_START
][j
] = pItem2
->GetCount();
10230 // check free space for all items
10231 for (int k
= 0; k
< count
; ++k
)
10233 Item
* pItem
= pItems
[k
];
10236 if (!pItem
) continue;
10238 DEBUG_LOG("STORAGE: CanStoreItems %i. item = %u, count = %u", k
+ 1, pItem
->GetEntry(), pItem
->GetCount());
10239 ItemPrototype
const* pProto
= pItem
->GetProto();
10243 return EQUIP_ERR_ITEM_NOT_FOUND
;
10246 if (pItem
->HasTemporaryLoot())
10247 return EQUIP_ERR_ALREADY_LOOTED
;
10250 if (pItem
->IsBindedNotWith(this))
10251 return EQUIP_ERR_DONT_OWN_THAT_ITEM
;
10254 ItemPrototype
const* pBagProto
;
10256 // item is 'one item only'
10257 InventoryResult res
= CanTakeMoreSimilarItems(pItem
);
10258 if (res
!= EQUIP_ERR_OK
)
10261 // search stack for merge to
10262 if (pProto
->Stackable
!= 1)
10264 bool b_found
= false;
10266 for (int t
= INVENTORY_SLOT_ITEM_START
; t
< INVENTORY_SLOT_ITEM_END
; ++t
)
10268 pItem2
= GetItemByPos(INVENTORY_SLOT_BAG_0
, t
);
10269 if (pItem2
&& pItem2
->CanBeMergedPartlyWith(pProto
) == EQUIP_ERR_OK
&& inv_slot_items
[t
- INVENTORY_SLOT_ITEM_START
] + pItem
->GetCount() <= pProto
->GetMaxStackSize())
10271 inv_slot_items
[t
- INVENTORY_SLOT_ITEM_START
] += pItem
->GetCount();
10276 if (b_found
) continue;
10278 for (int t
= INVENTORY_SLOT_BAG_START
; !b_found
&& t
< INVENTORY_SLOT_BAG_END
; ++t
)
10280 pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, t
);
10283 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
10285 pItem2
= GetItemByPos(t
, j
);
10286 if (pItem2
&& pItem2
->CanBeMergedPartlyWith(pProto
) == EQUIP_ERR_OK
&& inv_bags
[t
- INVENTORY_SLOT_BAG_START
][j
] + pItem
->GetCount() <= pProto
->GetMaxStackSize())
10288 inv_bags
[t
- INVENTORY_SLOT_BAG_START
][j
] += pItem
->GetCount();
10295 if (b_found
) continue;
10298 // special bag case
10299 if (pProto
->BagFamily
)
10301 bool b_found
= false;
10303 for (int t
= INVENTORY_SLOT_BAG_START
; !b_found
&& t
< INVENTORY_SLOT_BAG_END
; ++t
)
10305 pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, t
);
10308 pBagProto
= pBag
->GetProto();
10310 // not plain container check
10311 if (pBagProto
&& (pBagProto
->Class
!= ITEM_CLASS_CONTAINER
|| pBagProto
->SubClass
!= ITEM_SUBCLASS_CONTAINER
) &&
10312 ItemCanGoIntoBag(pProto
, pBagProto
))
10314 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
10316 if (inv_bags
[t
- INVENTORY_SLOT_BAG_START
][j
] == 0)
10318 inv_bags
[t
- INVENTORY_SLOT_BAG_START
][j
] = 1;
10326 if (b_found
) continue;
10329 // search free slot
10330 bool b_found
= false;
10331 for (int t
= INVENTORY_SLOT_ITEM_START
; t
< INVENTORY_SLOT_ITEM_END
; ++t
)
10333 if (inv_slot_items
[t
- INVENTORY_SLOT_ITEM_START
] == 0)
10335 inv_slot_items
[t
- INVENTORY_SLOT_ITEM_START
] = 1;
10340 if (b_found
) continue;
10342 // search free slot in bags
10343 for (int t
= INVENTORY_SLOT_BAG_START
; !b_found
&& t
< INVENTORY_SLOT_BAG_END
; ++t
)
10345 pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, t
);
10348 pBagProto
= pBag
->GetProto();
10350 // special bag already checked
10351 if (pBagProto
&& (pBagProto
->Class
!= ITEM_CLASS_CONTAINER
|| pBagProto
->SubClass
!= ITEM_SUBCLASS_CONTAINER
))
10354 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
10356 if (inv_bags
[t
- INVENTORY_SLOT_BAG_START
][j
] == 0)
10358 inv_bags
[t
- INVENTORY_SLOT_BAG_START
][j
] = 1;
10366 // no free slot found?
10368 return EQUIP_ERR_INVENTORY_FULL
;
10371 return EQUIP_ERR_OK
;
10374 //////////////////////////////////////////////////////////////////////////
10375 InventoryResult
Player::CanEquipNewItem(uint8 slot
, uint16
& dest
, uint32 item
, bool swap
) const
10378 Item
* pItem
= Item::CreateItem(item
, 1, this);
10381 InventoryResult result
= CanEquipItem(slot
, dest
, pItem
, swap
);
10386 return EQUIP_ERR_ITEM_NOT_FOUND
;
10389 InventoryResult
Player::CanEquipItem(uint8 slot
, uint16
& dest
, Item
* pItem
, bool swap
, bool direct_action
) const
10394 DEBUG_LOG("STORAGE: CanEquipItem slot = %u, item = %u, count = %u", slot
, pItem
->GetEntry(), pItem
->GetCount());
10395 ItemPrototype
const* pProto
= pItem
->GetProto();
10399 if (pItem
->HasTemporaryLoot())
10400 return EQUIP_ERR_ALREADY_LOOTED
;
10402 if (pItem
->IsBindedNotWith(this))
10403 return EQUIP_ERR_DONT_OWN_THAT_ITEM
;
10405 // check count of items (skip for auto move for same player from bank)
10406 InventoryResult res
= CanTakeMoreSimilarItems(pItem
);
10407 if (res
!= EQUIP_ERR_OK
)
10410 // check this only in game
10413 // May be here should be more stronger checks; STUNNED checked
10414 // ROOT, CONFUSED, DISTRACTED, FLEEING this needs to be checked.
10415 if (hasUnitState(UNIT_STAT_STUNNED
))
10416 return EQUIP_ERR_YOU_ARE_STUNNED
;
10418 // do not allow equipping gear except weapons, offhands, projectiles, relics in
10420 // - in-progress arenas
10421 if (!pProto
->CanChangeEquipStateInCombat())
10424 return EQUIP_ERR_NOT_IN_COMBAT
;
10426 if (BattleGround
* bg
= GetBattleGround())
10427 if (bg
->isArena() && bg
->GetStatus() == STATUS_IN_PROGRESS
)
10428 return EQUIP_ERR_NOT_DURING_ARENA_MATCH
;
10431 // prevent equip item in process logout
10432 if (GetSession()->isLogingOut())
10433 return EQUIP_ERR_YOU_ARE_STUNNED
;
10435 if (isInCombat() && pProto
->Class
== ITEM_CLASS_WEAPON
&& m_weaponChangeTimer
!= 0)
10436 return EQUIP_ERR_CANT_DO_RIGHT_NOW
; // maybe exist better err
10438 if (IsNonMeleeSpellCasted(false))
10439 return EQUIP_ERR_CANT_DO_RIGHT_NOW
;
10442 ScalingStatDistributionEntry
const* ssd
= pProto
->ScalingStatDistribution
? sScalingStatDistributionStore
.LookupEntry(pProto
->ScalingStatDistribution
) : 0;
10443 // check allowed level (extend range to upper values if MaxLevel more or equal max player level, this let GM set high level with 1...max range items)
10444 if (ssd
&& ssd
->MaxLevel
< DEFAULT_MAX_LEVEL
&& ssd
->MaxLevel
< getLevel())
10445 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED
;
10447 uint8 eslot
= FindEquipSlot(pProto
, slot
, swap
);
10448 if (eslot
== NULL_SLOT
)
10449 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED
;
10451 InventoryResult msg
= CanUseItem(pItem
, direct_action
);
10452 if (msg
!= EQUIP_ERR_OK
)
10454 if (!swap
&& GetItemByPos(INVENTORY_SLOT_BAG_0
, eslot
))
10455 return EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE
;
10457 // if swap ignore item (equipped also)
10458 if (InventoryResult res2
= CanEquipUniqueItem(pItem
, swap
? eslot
: uint8(NULL_SLOT
)))
10461 // check unique-equipped special item classes
10462 if (pProto
->Class
== ITEM_CLASS_QUIVER
)
10464 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
10466 if (Item
* pBag
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
10470 if (ItemPrototype
const* pBagProto
= pBag
->GetProto())
10472 if (pBagProto
->Class
== pProto
->Class
&& (!swap
|| pBag
->GetSlot() != eslot
))
10473 return (pBagProto
->SubClass
== ITEM_SUBCLASS_AMMO_POUCH
)
10474 ? EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH
10475 : EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER
;
10482 uint32 type
= pProto
->InventoryType
;
10484 if (eslot
== EQUIPMENT_SLOT_OFFHAND
)
10486 if (type
== INVTYPE_WEAPON
|| type
== INVTYPE_WEAPONOFFHAND
)
10488 if (!CanDualWield())
10489 return EQUIP_ERR_CANT_DUAL_WIELD
;
10491 else if (type
== INVTYPE_2HWEAPON
)
10493 if (!CanDualWield() || !CanTitanGrip())
10494 return EQUIP_ERR_CANT_DUAL_WIELD
;
10497 if (IsTwoHandUsed())
10498 return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED
;
10501 // equip two-hand weapon case (with possible unequip 2 items)
10502 if (type
== INVTYPE_2HWEAPON
)
10504 if (eslot
== EQUIPMENT_SLOT_OFFHAND
)
10506 if (!CanTitanGrip())
10507 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED
;
10509 else if (eslot
!= EQUIPMENT_SLOT_MAINHAND
)
10510 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED
;
10512 if (!CanTitanGrip())
10514 // offhand item must can be stored in inventory for offhand item and it also must be unequipped
10515 Item
* offItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
);
10516 ItemPosCountVec off_dest
;
10517 if (offItem
&& (!direct_action
||
10518 CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0
) << 8 | EQUIPMENT_SLOT_OFFHAND
, false) != EQUIP_ERR_OK
||
10519 CanStoreItem(NULL_BAG
, NULL_SLOT
, off_dest
, offItem
, false) != EQUIP_ERR_OK
))
10520 return swap
? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED
: EQUIP_ERR_INVENTORY_FULL
;
10523 dest
= ((INVENTORY_SLOT_BAG_0
<< 8) | eslot
);
10524 return EQUIP_ERR_OK
;
10528 return !swap
? EQUIP_ERR_ITEM_NOT_FOUND
: EQUIP_ERR_ITEMS_CANT_BE_SWAPPED
;
10531 InventoryResult
Player::CanUnequipItem(uint16 pos
, bool swap
) const
10533 // Applied only to equipped items and bank bags
10534 if (!IsEquipmentPos(pos
) && !IsBagPos(pos
))
10535 return EQUIP_ERR_OK
;
10537 Item
* pItem
= GetItemByPos(pos
);
10539 // Applied only to existing equipped item
10541 return EQUIP_ERR_OK
;
10543 DEBUG_LOG("STORAGE: CanUnequipItem slot = %u, item = %u, count = %u", pos
, pItem
->GetEntry(), pItem
->GetCount());
10545 ItemPrototype
const* pProto
= pItem
->GetProto();
10547 return EQUIP_ERR_ITEM_NOT_FOUND
;
10550 if (pItem
->HasTemporaryLoot())
10551 return EQUIP_ERR_ALREADY_LOOTED
;
10553 // do not allow unequipping gear except weapons, offhands, projectiles, relics in
10555 // - in-progress arenas
10556 if (!pProto
->CanChangeEquipStateInCombat())
10559 return EQUIP_ERR_NOT_IN_COMBAT
;
10561 if (BattleGround
* bg
= GetBattleGround())
10562 if (bg
->isArena() && bg
->GetStatus() == STATUS_IN_PROGRESS
)
10563 return EQUIP_ERR_NOT_DURING_ARENA_MATCH
;
10566 // prevent unequip item in process logout
10567 if (GetSession()->isLogingOut())
10568 return EQUIP_ERR_YOU_ARE_STUNNED
;
10570 if (!swap
&& pItem
->IsBag() && !((Bag
*)pItem
)->IsEmpty())
10571 return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS
;
10573 return EQUIP_ERR_OK
;
10576 InventoryResult
Player::CanBankItem(uint8 bag
, uint8 slot
, ItemPosCountVec
& dest
, Item
* pItem
, bool swap
, bool not_loading
) const
10579 return swap
? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED
: EQUIP_ERR_ITEM_NOT_FOUND
;
10581 uint32 count
= pItem
->GetCount();
10583 DEBUG_LOG("STORAGE: CanBankItem bag = %u, slot = %u, item = %u, count = %u", bag
, slot
, pItem
->GetEntry(), pItem
->GetCount());
10584 ItemPrototype
const* pProto
= pItem
->GetProto();
10586 return swap
? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED
: EQUIP_ERR_ITEM_NOT_FOUND
;
10589 if (pItem
->HasTemporaryLoot())
10590 return EQUIP_ERR_ALREADY_LOOTED
;
10592 if (pItem
->IsBindedNotWith(this))
10593 return EQUIP_ERR_DONT_OWN_THAT_ITEM
;
10595 // check count of items (skip for auto move for same player from bank)
10596 InventoryResult res
= CanTakeMoreSimilarItems(pItem
);
10597 if (res
!= EQUIP_ERR_OK
)
10600 // in specific slot
10601 if (bag
!= NULL_BAG
&& slot
!= NULL_SLOT
)
10603 if (slot
>= BANK_SLOT_BAG_START
&& slot
< BANK_SLOT_BAG_END
)
10605 if (!pItem
->IsBag())
10606 return EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT
;
10608 if (slot
- BANK_SLOT_BAG_START
>= GetBankBagSlotCount())
10609 return EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT
;
10611 res
= CanUseItem(pItem
, not_loading
);
10612 if (res
!= EQUIP_ERR_OK
)
10616 res
= _CanStoreItem_InSpecificSlot(bag
, slot
, dest
, pProto
, count
, swap
, pItem
);
10617 if (res
!= EQUIP_ERR_OK
)
10621 return EQUIP_ERR_OK
;
10624 // not specific slot or have space for partly store only in specific slot
10627 if (bag
!= NULL_BAG
)
10629 if (pProto
->InventoryType
== INVTYPE_BAG
)
10631 Bag
* pBag
= (Bag
*)pItem
;
10632 if (pBag
&& !pBag
->IsEmpty())
10633 return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG
;
10636 // search stack in bag for merge to
10637 if (pProto
->Stackable
!= 1)
10639 if (bag
== INVENTORY_SLOT_BAG_0
)
10641 res
= _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START
, BANK_SLOT_ITEM_END
, dest
, pProto
, count
, true, pItem
, bag
, slot
);
10642 if (res
!= EQUIP_ERR_OK
)
10646 return EQUIP_ERR_OK
;
10650 res
= _CanStoreItem_InBag(bag
, dest
, pProto
, count
, true, false, pItem
, NULL_BAG
, slot
);
10651 if (res
!= EQUIP_ERR_OK
)
10652 res
= _CanStoreItem_InBag(bag
, dest
, pProto
, count
, true, true, pItem
, NULL_BAG
, slot
);
10654 if (res
!= EQUIP_ERR_OK
)
10658 return EQUIP_ERR_OK
;
10662 // search free slot in bag
10663 if (bag
== INVENTORY_SLOT_BAG_0
)
10665 res
= _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START
, BANK_SLOT_ITEM_END
, dest
, pProto
, count
, false, pItem
, bag
, slot
);
10666 if (res
!= EQUIP_ERR_OK
)
10670 return EQUIP_ERR_OK
;
10674 res
= _CanStoreItem_InBag(bag
, dest
, pProto
, count
, false, false, pItem
, NULL_BAG
, slot
);
10675 if (res
!= EQUIP_ERR_OK
)
10676 res
= _CanStoreItem_InBag(bag
, dest
, pProto
, count
, false, true, pItem
, NULL_BAG
, slot
);
10678 if (res
!= EQUIP_ERR_OK
)
10682 return EQUIP_ERR_OK
;
10686 // not specific bag or have space for partly store only in specific bag
10688 // search stack for merge to
10689 if (pProto
->Stackable
!= 1)
10692 res
= _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START
, BANK_SLOT_ITEM_END
, dest
, pProto
, count
, true, pItem
, bag
, slot
);
10693 if (res
!= EQUIP_ERR_OK
)
10697 return EQUIP_ERR_OK
;
10700 if (pProto
->BagFamily
)
10702 for (int i
= BANK_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
10704 res
= _CanStoreItem_InBag(i
, dest
, pProto
, count
, true, false, pItem
, bag
, slot
);
10705 if (res
!= EQUIP_ERR_OK
)
10709 return EQUIP_ERR_OK
;
10713 for (int i
= BANK_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
10715 res
= _CanStoreItem_InBag(i
, dest
, pProto
, count
, true, true, pItem
, bag
, slot
);
10716 if (res
!= EQUIP_ERR_OK
)
10720 return EQUIP_ERR_OK
;
10724 // search free place in special bag
10725 if (pProto
->BagFamily
)
10727 for (int i
= BANK_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
10729 res
= _CanStoreItem_InBag(i
, dest
, pProto
, count
, false, false, pItem
, bag
, slot
);
10730 if (res
!= EQUIP_ERR_OK
)
10734 return EQUIP_ERR_OK
;
10738 // search free space
10739 res
= _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START
, BANK_SLOT_ITEM_END
, dest
, pProto
, count
, false, pItem
, bag
, slot
);
10740 if (res
!= EQUIP_ERR_OK
)
10744 return EQUIP_ERR_OK
;
10746 for (int i
= BANK_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
10748 res
= _CanStoreItem_InBag(i
, dest
, pProto
, count
, false, true, pItem
, bag
, slot
);
10749 if (res
!= EQUIP_ERR_OK
)
10753 return EQUIP_ERR_OK
;
10755 return EQUIP_ERR_BANK_FULL
;
10758 InventoryResult
Player::CanUseItem(Item
* pItem
, bool direct_action
) const
10762 DEBUG_LOG("STORAGE: CanUseItem item = %u", pItem
->GetEntry());
10764 if (!isAlive() && direct_action
)
10765 return EQUIP_ERR_YOU_ARE_DEAD
;
10767 // if (isStunned())
10768 // return EQUIP_ERR_YOU_ARE_STUNNED;
10770 ItemPrototype
const* pProto
= pItem
->GetProto();
10773 if (pItem
->IsBindedNotWith(this))
10774 return EQUIP_ERR_DONT_OWN_THAT_ITEM
;
10776 InventoryResult msg
= CanUseItem(pProto
);
10777 if (msg
!= EQUIP_ERR_OK
)
10780 if (uint32 item_use_skill
= pItem
->GetSkill())
10782 if (GetSkillValue(item_use_skill
) == 0)
10784 // armor items with scaling stats can downgrade armor skill reqs if related class can learn armor use at some level
10785 if (pProto
->Class
!= ITEM_CLASS_ARMOR
)
10786 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY
;
10788 ScalingStatDistributionEntry
const* ssd
= pProto
->ScalingStatDistribution
? sScalingStatDistributionStore
.LookupEntry(pProto
->ScalingStatDistribution
) : NULL
;
10790 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY
;
10792 bool allowScaleSkill
= false;
10793 for (uint32 i
= 0; i
< sSkillLineAbilityStore
.GetNumRows(); ++i
)
10795 SkillLineAbilityEntry
const* skillInfo
= sSkillLineAbilityStore
.LookupEntry(i
);
10799 if (skillInfo
->skillId
!= item_use_skill
)
10803 if (skillInfo
->classmask
&& (skillInfo
->classmask
& getClassMask()) == 0)
10806 if (skillInfo
->racemask
&& (skillInfo
->racemask
& getRaceMask()) == 0)
10809 allowScaleSkill
= true;
10813 if (!allowScaleSkill
)
10814 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY
;
10818 if (pProto
->RequiredReputationFaction
&& uint32(GetReputationRank(pProto
->RequiredReputationFaction
)) < pProto
->RequiredReputationRank
)
10819 return EQUIP_ERR_CANT_EQUIP_REPUTATION
;
10821 return EQUIP_ERR_OK
;
10824 return EQUIP_ERR_ITEM_NOT_FOUND
;
10827 InventoryResult
Player::CanUseItem(ItemPrototype
const* pProto
) const
10829 // Used by group, function NeedBeforeGreed, to know if a prototype can be used by a player
10833 if ((pProto
->Flags2
& ITEM_FLAG2_HORDE_ONLY
) && GetTeam() != HORDE
)
10834 return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM
;
10836 if ((pProto
->Flags2
& ITEM_FLAG2_ALLIANCE_ONLY
) && GetTeam() != ALLIANCE
)
10837 return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM
;
10839 if ((pProto
->AllowableClass
& getClassMask()) == 0 || (pProto
->AllowableRace
& getRaceMask()) == 0)
10840 return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM
;
10842 if (pProto
->RequiredSkill
!= 0)
10844 if (GetSkillValue(pProto
->RequiredSkill
) == 0)
10845 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY
;
10846 else if (GetSkillValue(pProto
->RequiredSkill
) < pProto
->RequiredSkillRank
)
10847 return EQUIP_ERR_CANT_EQUIP_SKILL
;
10850 if (pProto
->RequiredSpell
!= 0 && !HasSpell(pProto
->RequiredSpell
))
10851 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY
;
10853 if (getLevel() < pProto
->RequiredLevel
)
10854 return EQUIP_ERR_CANT_EQUIP_LEVEL_I
;
10856 return EQUIP_ERR_OK
;
10858 return EQUIP_ERR_ITEM_NOT_FOUND
;
10861 InventoryResult
Player::CanUseAmmo(uint32 item
) const
10863 DEBUG_LOG("STORAGE: CanUseAmmo item = %u", item
);
10865 return EQUIP_ERR_YOU_ARE_DEAD
;
10866 // if( isStunned() )
10867 // return EQUIP_ERR_YOU_ARE_STUNNED;
10868 ItemPrototype
const* pProto
= ObjectMgr::GetItemPrototype(item
);
10871 if (pProto
->InventoryType
!= INVTYPE_AMMO
)
10872 return EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE
;
10874 InventoryResult msg
= CanUseItem(pProto
);
10875 if (msg
!= EQUIP_ERR_OK
)
10878 /*if ( GetReputationMgr().GetReputation() < pProto->RequiredReputation )
10879 return EQUIP_ERR_CANT_EQUIP_REPUTATION;
10882 // Requires No Ammo
10883 if (GetDummyAura(46699))
10884 return EQUIP_ERR_BAG_FULL6
;
10886 return EQUIP_ERR_OK
;
10888 return EQUIP_ERR_ITEM_NOT_FOUND
;
10891 void Player::SetAmmo(uint32 item
)
10897 //if( GetUInt32Value(PLAYER_AMMO_ID) == item )
10903 // InventoryResult msg = CanUseAmmo( item );
10904 // if (msg != EQUIP_ERR_OK)
10906 // SendEquipError(msg, NULL, NULL, item);
10911 //SetUInt32Value(PLAYER_AMMO_ID, item);
10913 //_ApplyAmmoBonuses();
10916 void Player::RemoveAmmo()
10918 //SetUInt32Value(PLAYER_AMMO_ID, 0);
10920 //m_ammoDPS = 0.0f;
10922 //if (CanModifyStats())
10923 // UpdateDamagePhysical(RANGED_ATTACK);
10926 // Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
10927 Item
* Player::StoreNewItem(ItemPosCountVec
const& dest
, uint32 item
, bool update
, int32 randomPropertyId
)
10930 for (ItemPosCountVec::const_iterator itr
= dest
.begin(); itr
!= dest
.end(); ++itr
)
10931 count
+= itr
->count
;
10933 Item
* pItem
= Item::CreateItem(item
, count
, this, randomPropertyId
);
10936 ItemAddedQuestCheck(item
, count
);
10937 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM
, item
, count
);
10938 pItem
= StoreItem(dest
, pItem
, update
);
10943 Item
* Player::StoreItem(ItemPosCountVec
const& dest
, Item
* pItem
, bool update
)
10948 Item
* lastItem
= pItem
;
10949 uint32 entry
= pItem
->GetEntry();
10951 for (ItemPosCountVec::const_iterator itr
= dest
.begin(); itr
!= dest
.end();)
10953 uint16 pos
= itr
->pos
;
10954 uint32 count
= itr
->count
;
10958 if (itr
== dest
.end())
10960 lastItem
= _StoreItem(pos
, pItem
, count
, false, update
);
10964 lastItem
= _StoreItem(pos
, pItem
, count
, true, update
);
10967 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
, entry
);
10971 // Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
10972 Item
* Player::_StoreItem(uint16 pos
, Item
* pItem
, uint32 count
, bool clone
, bool update
)
10977 uint8 bag
= pos
>> 8;
10978 uint8 slot
= pos
& 255;
10980 DEBUG_LOG("STORAGE: StoreItem bag = %u, slot = %u, item = %u, count = %u", bag
, slot
, pItem
->GetEntry(), count
);
10982 Item
* pItem2
= GetItemByPos(bag
, slot
);
10987 pItem
= pItem
->CloneItem(count
, this);
10989 pItem
->SetCount(count
);
10994 if (pItem
->GetProto()->Bonding
== BIND_WHEN_PICKED_UP
||
10995 pItem
->GetProto()->Bonding
== BIND_QUEST_ITEM
||
10996 (pItem
->GetProto()->Bonding
== BIND_WHEN_EQUIPPED
&& IsBagPos(pos
)))
10997 pItem
->SetBinding(true);
10999 if (bag
== INVENTORY_SLOT_BAG_0
)
11001 m_items
[slot
] = pItem
;
11002 SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD
+ (slot
* 2), pItem
->GetObjectGuid());
11003 pItem
->SetGuidValue(ITEM_FIELD_CONTAINED
, GetObjectGuid());
11004 pItem
->SetGuidValue(ITEM_FIELD_OWNER
, GetObjectGuid());
11006 pItem
->SetSlot(slot
);
11007 pItem
->SetContainer(NULL
);
11009 if (IsInWorld() && update
)
11011 pItem
->AddToWorld();
11012 pItem
->SendCreateUpdateToPlayer(this);
11015 pItem
->SetState(ITEM_CHANGED
, this);
11017 else if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, bag
))
11019 pBag
->StoreItem(slot
, pItem
, update
);
11020 if (IsInWorld() && update
)
11022 pItem
->AddToWorld();
11023 pItem
->SendCreateUpdateToPlayer(this);
11025 pItem
->SetState(ITEM_CHANGED
, this);
11026 pBag
->SetState(ITEM_CHANGED
, this);
11029 AddEnchantmentDurations(pItem
);
11030 AddItemDurations(pItem
);
11032 // at place into not appropriate slot (bank, for example) remove aura
11033 ApplyItemOnStoreSpell(pItem
, IsEquipmentPos(pItem
->GetBagSlot(), pItem
->GetSlot()) || IsInventoryPos(pItem
->GetBagSlot(), pItem
->GetSlot()));
11039 if (pItem2
->GetProto()->Bonding
== BIND_WHEN_PICKED_UP
||
11040 pItem2
->GetProto()->Bonding
== BIND_QUEST_ITEM
||
11041 (pItem2
->GetProto()->Bonding
== BIND_WHEN_EQUIPPED
&& IsBagPos(pos
)))
11042 pItem2
->SetBinding(true);
11044 pItem2
->SetCount(pItem2
->GetCount() + count
);
11045 if (IsInWorld() && update
)
11046 pItem2
->SendCreateUpdateToPlayer(this);
11050 // delete item (it not in any slot currently)
11051 if (IsInWorld() && update
)
11053 pItem
->RemoveFromWorld();
11054 pItem
->DestroyForPlayer(this);
11057 RemoveEnchantmentDurations(pItem
);
11058 RemoveItemDurations(pItem
);
11060 pItem
->SetOwnerGuid(GetObjectGuid()); // prevent error at next SetState in case trade/mail/buy from vendor
11061 pItem
->SetState(ITEM_REMOVED
, this);
11064 // AddItemDurations(pItem2); - pItem2 already have duration listed for player
11065 AddEnchantmentDurations(pItem2
);
11067 pItem2
->SetState(ITEM_CHANGED
, this);
11073 Item
* Player::EquipNewItem(uint16 pos
, uint32 item
, bool update
)
11075 if (Item
* pItem
= Item::CreateItem(item
, 1, this))
11077 ItemAddedQuestCheck(item
, 1);
11078 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM
, item
, 1);
11079 return EquipItem(pos
, pItem
, update
);
11085 Item
* Player::EquipItem(uint16 pos
, Item
* pItem
, bool update
)
11087 AddEnchantmentDurations(pItem
);
11088 AddItemDurations(pItem
);
11090 uint8 bag
= pos
>> 8;
11091 uint8 slot
= pos
& 255;
11093 Item
* pItem2
= GetItemByPos(bag
, slot
);
11096 VisualizeItem(slot
, pItem
);
11100 ItemPrototype
const* pProto
= pItem
->GetProto();
11102 // item set bonuses applied only at equip and removed at unequip, and still active for broken items
11103 if (pProto
&& pProto
->ItemSet
)
11104 AddItemsSetItem(this, pItem
);
11106 _ApplyItemMods(pItem
, slot
, true);
11108 ApplyItemOnStoreSpell(pItem
, true);
11110 // Weapons and also Totem/Relic/Sigil/etc
11111 if (pProto
&& isInCombat() && (pProto
->Class
== ITEM_CLASS_WEAPON
|| pProto
->InventoryType
== INVTYPE_RELIC
) && m_weaponChangeTimer
== 0)
11113 uint32 cooldownSpell
= SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_5s
;
11115 if (getClass() == CLASS_ROGUE
)
11116 cooldownSpell
= SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_0s
;
11118 SpellEntry
const* spellProto
= sSpellStore
.LookupEntry(cooldownSpell
);
11121 sLog
.outError("Weapon switch cooldown spell %u couldn't be found in Spell.dbc", cooldownSpell
);
11124 m_weaponChangeTimer
= spellProto
->GetStartRecoveryTime();
11126 WorldPacket
data(SMSG_SPELL_COOLDOWN
, 8 + 1 + 4);
11127 data
<< GetObjectGuid();
11129 data
<< uint32(cooldownSpell
);
11131 GetSession()->SendPacket(&data
);
11136 if (IsInWorld() && update
)
11138 pItem
->AddToWorld();
11139 pItem
->SendCreateUpdateToPlayer(this);
11142 ApplyEquipCooldown(pItem
);
11144 if (slot
== EQUIPMENT_SLOT_MAINHAND
)
11146 UpdateExpertise(BASE_ATTACK
);
11147 UpdateArmorPenetration();
11149 else if (slot
== EQUIPMENT_SLOT_OFFHAND
)
11151 UpdateExpertise(OFF_ATTACK
);
11152 UpdateArmorPenetration();
11155 UpdateArmorSpecializations();
11159 pItem2
->SetCount(pItem2
->GetCount() + pItem
->GetCount());
11160 if (IsInWorld() && update
)
11161 pItem2
->SendCreateUpdateToPlayer(this);
11163 // delete item (it not in any slot currently)
11164 // pItem->DeleteFromDB();
11165 if (IsInWorld() && update
)
11167 pItem
->RemoveFromWorld();
11168 pItem
->DestroyForPlayer(this);
11171 RemoveEnchantmentDurations(pItem
);
11172 RemoveItemDurations(pItem
);
11174 pItem
->SetOwnerGuid(GetObjectGuid()); // prevent error at next SetState in case trade/mail/buy from vendor
11175 pItem
->SetState(ITEM_REMOVED
, this);
11176 pItem2
->SetState(ITEM_CHANGED
, this);
11178 ApplyEquipCooldown(pItem2
);
11182 // Apply Titan's Grip damage penalty if necessary
11183 if ((slot
== EQUIPMENT_SLOT_MAINHAND
|| slot
== EQUIPMENT_SLOT_OFFHAND
) && CanTitanGrip() && HasTwoHandWeaponInOneHand() && !HasAura(49152))
11184 CastSpell(this, 49152, true);
11186 // only for full equip instead adding to stack
11187 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM
, pItem
->GetEntry());
11188 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM
, slot
+ 1);
11193 void Player::QuickEquipItem(uint16 pos
, Item
* pItem
)
11197 AddEnchantmentDurations(pItem
);
11198 AddItemDurations(pItem
);
11199 ApplyItemOnStoreSpell(pItem
, true);
11201 uint8 slot
= pos
& 255;
11202 VisualizeItem(slot
, pItem
);
11206 pItem
->AddToWorld();
11207 pItem
->SendCreateUpdateToPlayer(this);
11209 // Apply Titan's Grip damage penalty if necessary
11210 if ((slot
== EQUIPMENT_SLOT_MAINHAND
|| slot
== EQUIPMENT_SLOT_OFFHAND
) && CanTitanGrip() && HasTwoHandWeaponInOneHand() && !HasAura(49152))
11211 CastSpell(this, 49152, true);
11213 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM
, pItem
->GetEntry());
11214 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM
, slot
+ 1);
11216 UpdateArmorSpecializations();
11220 void Player::SetVisibleItemSlot(uint8 slot
, Item
* pItem
)
11224 SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID
+ (slot
* 2), pItem
->GetEntry());
11225 SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT
+ (slot
* 2), 0, pItem
->GetEnchantmentId(PERM_ENCHANTMENT_SLOT
));
11226 SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT
+ (slot
* 2), 1, pItem
->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT
));
11230 SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID
+ (slot
* 2), 0);
11231 SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT
+ (slot
* 2), 0);
11235 void Player::VisualizeItem(uint8 slot
, Item
* pItem
)
11240 // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
11241 if (pItem
->GetProto()->Bonding
== BIND_WHEN_EQUIPPED
|| pItem
->GetProto()->Bonding
== BIND_WHEN_PICKED_UP
|| pItem
->GetProto()->Bonding
== BIND_QUEST_ITEM
)
11242 pItem
->SetBinding(true);
11244 DEBUG_LOG("STORAGE: EquipItem slot = %u, item = %u", slot
, pItem
->GetEntry());
11246 m_items
[slot
] = pItem
;
11247 SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD
+ (slot
* 2), pItem
->GetObjectGuid());
11248 pItem
->SetGuidValue(ITEM_FIELD_CONTAINED
, GetObjectGuid());
11249 pItem
->SetGuidValue(ITEM_FIELD_OWNER
, GetObjectGuid());
11250 pItem
->SetSlot(slot
);
11251 pItem
->SetContainer(NULL
);
11253 if (slot
< EQUIPMENT_SLOT_END
)
11254 SetVisibleItemSlot(slot
, pItem
);
11256 pItem
->SetState(ITEM_CHANGED
, this);
11259 void Player::RemoveItem(uint8 bag
, uint8 slot
, bool update
)
11261 // note: removeitem does not actually change the item
11262 // it only takes the item out of storage temporarily
11263 // note2: if removeitem is to be used for delinking
11264 // the item must be removed from the player's updatequeue
11266 if (Item
* pItem
= GetItemByPos(bag
, slot
))
11268 DEBUG_LOG("STORAGE: RemoveItem bag = %u, slot = %u, item = %u", bag
, slot
, pItem
->GetEntry());
11270 RemoveEnchantmentDurations(pItem
);
11271 RemoveItemDurations(pItem
);
11273 if (bag
== INVENTORY_SLOT_BAG_0
)
11275 if (slot
< INVENTORY_SLOT_BAG_END
)
11277 ItemPrototype
const* pProto
= pItem
->GetProto();
11278 // item set bonuses applied only at equip and removed at unequip, and still active for broken items
11280 if (pProto
&& pProto
->ItemSet
)
11281 RemoveItemsSetItem(this, pProto
);
11283 _ApplyItemMods(pItem
, slot
, false);
11285 // remove item dependent auras and casts (only weapon and armor slots)
11286 if (slot
< EQUIPMENT_SLOT_END
)
11288 RemoveItemDependentAurasAndCasts(pItem
);
11290 // remove held enchantments, update expertise
11291 if (slot
== EQUIPMENT_SLOT_MAINHAND
)
11293 if (pItem
->GetItemSuffixFactor())
11295 pItem
->ClearEnchantment(PROP_ENCHANTMENT_SLOT_3
);
11296 pItem
->ClearEnchantment(PROP_ENCHANTMENT_SLOT_4
);
11300 pItem
->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0
);
11301 pItem
->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1
);
11304 UpdateExpertise(BASE_ATTACK
);
11305 UpdateArmorPenetration();
11307 else if (slot
== EQUIPMENT_SLOT_OFFHAND
)
11309 UpdateExpertise(OFF_ATTACK
);
11310 UpdateArmorPenetration();
11315 m_items
[slot
] = NULL
;
11316 SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD
+ (slot
* 2), ObjectGuid());
11318 if (slot
< EQUIPMENT_SLOT_END
)
11320 SetVisibleItemSlot(slot
, NULL
);
11321 // Remove Titan's Grip damage penalty if necessary
11322 if ((slot
== EQUIPMENT_SLOT_MAINHAND
|| slot
== EQUIPMENT_SLOT_OFFHAND
) && CanTitanGrip() && !HasTwoHandWeaponInOneHand())
11323 RemoveAurasDueToSpell(49152);
11326 UpdateArmorSpecializations();
11330 Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, bag
);
11332 pBag
->RemoveItem(slot
, update
);
11334 pItem
->SetGuidValue(ITEM_FIELD_CONTAINED
, ObjectGuid());
11335 // pItem->SetGuidValue(ITEM_FIELD_OWNER, ObjectGuid()); not clear owner at remove (it will be set at store). This used in mail and auction code
11336 pItem
->SetSlot(NULL_SLOT
);
11338 // ApplyItemOnStoreSpell, for avoid re-apply will remove at _adding_ to not appropriate slot
11340 if (IsInWorld() && update
)
11341 pItem
->SendCreateUpdateToPlayer(this);
11345 // Common operation need to remove item from inventory without delete in trade, auction, guild bank, mail....
11346 void Player::MoveItemFromInventory(uint8 bag
, uint8 slot
, bool update
)
11348 if (Item
* it
= GetItemByPos(bag
, slot
))
11350 ItemRemovedQuestCheck(it
->GetEntry(), it
->GetCount());
11351 RemoveItem(bag
, slot
, update
);
11353 // item atStore spell not removed in RemoveItem (for avoid reappaly in slots changes), so do it directly
11354 if (IsEquipmentPos(bag
, slot
) || IsInventoryPos(bag
, slot
))
11355 ApplyItemOnStoreSpell(it
, false);
11357 it
->RemoveFromUpdateQueueOf(this);
11358 if (it
->IsInWorld())
11360 it
->RemoveFromWorld();
11361 it
->DestroyForPlayer(this);
11366 // Common operation need to add item from inventory without delete in trade, guild bank, mail....
11367 void Player::MoveItemToInventory(ItemPosCountVec
const& dest
, Item
* pItem
, bool update
, bool in_characterInventoryDB
)
11369 // update quest counters
11370 ItemAddedQuestCheck(pItem
->GetEntry(), pItem
->GetCount());
11371 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM
, pItem
->GetEntry(), pItem
->GetCount());
11374 Item
* pLastItem
= StoreItem(dest
, pItem
, update
);
11376 // only set if not merged to existing stack (pItem can be deleted already but we can compare pointers any way)
11377 if (pLastItem
== pItem
)
11379 // update owner for last item (this can be original item with wrong owner
11380 if (pLastItem
->GetOwnerGuid() != GetObjectGuid())
11381 pLastItem
->SetOwnerGuid(GetObjectGuid());
11383 // if this original item then it need create record in inventory
11384 // in case trade we already have item in other player inventory
11385 pLastItem
->SetState(in_characterInventoryDB
? ITEM_CHANGED
: ITEM_NEW
, this);
11389 void Player::DestroyItem(uint8 bag
, uint8 slot
, bool update
)
11391 Item
* pItem
= GetItemByPos(bag
, slot
);
11394 DEBUG_LOG("STORAGE: DestroyItem bag = %u, slot = %u, item = %u", bag
, slot
, pItem
->GetEntry());
11396 // start from destroy contained items (only equipped bag can have its)
11397 if (pItem
->IsBag() && pItem
->IsEquipped()) // this also prevent infinity loop if empty bag stored in bag==slot
11399 for (int i
= 0; i
< MAX_BAG_SIZE
; ++i
)
11400 DestroyItem(slot
, i
, update
);
11403 if (pItem
->HasFlag(ITEM_FIELD_FLAGS
, ITEM_DYNFLAG_WRAPPED
))
11405 static SqlStatementID delGifts
;
11407 SqlStatement stmt
= CharacterDatabase
.CreateStatement(delGifts
, "DELETE FROM character_gifts WHERE item_guid = ?");
11408 stmt
.PExecute(pItem
->GetGUIDLow());
11411 RemoveEnchantmentDurations(pItem
);
11412 RemoveItemDurations(pItem
);
11414 if (IsEquipmentPos(bag
, slot
) || IsInventoryPos(bag
, slot
))
11415 ApplyItemOnStoreSpell(pItem
, false);
11417 ItemRemovedQuestCheck(pItem
->GetEntry(), pItem
->GetCount());
11419 if (bag
== INVENTORY_SLOT_BAG_0
)
11421 SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD
+ (slot
* 2), ObjectGuid());
11423 // equipment and equipped bags can have applied bonuses
11424 if (slot
< INVENTORY_SLOT_BAG_END
)
11426 ItemPrototype
const* pProto
= pItem
->GetProto();
11428 // item set bonuses applied only at equip and removed at unequip, and still active for broken items
11429 if (pProto
&& pProto
->ItemSet
)
11430 RemoveItemsSetItem(this, pProto
);
11432 _ApplyItemMods(pItem
, slot
, false);
11435 if (slot
< EQUIPMENT_SLOT_END
)
11437 // remove item dependent auras and casts (only weapon and armor slots)
11438 RemoveItemDependentAurasAndCasts(pItem
);
11440 // update expertise
11441 if (slot
== EQUIPMENT_SLOT_MAINHAND
)
11443 UpdateExpertise(BASE_ATTACK
);
11444 UpdateArmorPenetration();
11446 else if (slot
== EQUIPMENT_SLOT_OFFHAND
)
11448 UpdateExpertise(OFF_ATTACK
);
11449 UpdateArmorPenetration();
11452 // equipment visual show
11453 SetVisibleItemSlot(slot
, NULL
);
11456 m_items
[slot
] = NULL
;
11458 else if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, bag
))
11459 pBag
->RemoveItem(slot
, update
);
11461 if (IsInWorld() && update
)
11463 pItem
->RemoveFromWorld();
11464 pItem
->DestroyForPlayer(this);
11467 // pItem->SetOwnerGUID(0);
11468 pItem
->SetGuidValue(ITEM_FIELD_CONTAINED
, ObjectGuid());
11469 pItem
->SetSlot(NULL_SLOT
);
11470 pItem
->SetState(ITEM_REMOVED
, this);
11474 void Player::DestroyItemCount(uint32 item
, uint32 count
, bool update
, bool unequip_check
, bool inBankAlso
)
11476 DEBUG_LOG("STORAGE: DestroyItemCount item = %u, count = %u", item
, count
);
11477 uint32 remcount
= 0;
11480 for (int i
= INVENTORY_SLOT_ITEM_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
11482 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
11484 if (pItem
->GetEntry() == item
&& !pItem
->IsInTrade())
11486 if (pItem
->GetCount() + remcount
<= count
)
11488 // all items in inventory can unequipped
11489 remcount
+= pItem
->GetCount();
11490 DestroyItem(INVENTORY_SLOT_BAG_0
, i
, update
);
11492 if (remcount
>= count
)
11497 ItemRemovedQuestCheck(pItem
->GetEntry(), count
- remcount
);
11498 pItem
->SetCount(pItem
->GetCount() - count
+ remcount
);
11499 if (IsInWorld() && update
)
11500 pItem
->SendCreateUpdateToPlayer(this);
11501 pItem
->SetState(ITEM_CHANGED
, this);
11508 // in inventory bags
11509 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
11511 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
11513 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
11515 if (Item
* pItem
= pBag
->GetItemByPos(j
))
11517 if (pItem
->GetEntry() == item
&& !pItem
->IsInTrade())
11519 // all items in bags can be unequipped
11520 if (pItem
->GetCount() + remcount
<= count
)
11522 remcount
+= pItem
->GetCount();
11523 DestroyItem(i
, j
, update
);
11525 if (remcount
>= count
)
11530 ItemRemovedQuestCheck(pItem
->GetEntry(), count
- remcount
);
11531 pItem
->SetCount(pItem
->GetCount() - count
+ remcount
);
11532 if (IsInWorld() && update
)
11533 pItem
->SendCreateUpdateToPlayer(this);
11534 pItem
->SetState(ITEM_CHANGED
, this);
11543 // in equipment and bag list
11544 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
11546 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
11548 if (pItem
&& pItem
->GetEntry() == item
&& !pItem
->IsInTrade())
11550 if (pItem
->GetCount() + remcount
<= count
)
11552 if (!unequip_check
|| CanUnequipItem(INVENTORY_SLOT_BAG_0
<< 8 | i
, false) == EQUIP_ERR_OK
)
11554 remcount
+= pItem
->GetCount();
11555 DestroyItem(INVENTORY_SLOT_BAG_0
, i
, update
);
11557 if (remcount
>= count
)
11563 ItemRemovedQuestCheck(pItem
->GetEntry(), count
- remcount
);
11564 pItem
->SetCount(pItem
->GetCount() - count
+ remcount
);
11565 if (IsInWorld() && update
)
11566 pItem
->SendCreateUpdateToPlayer(this);
11567 pItem
->SetState(ITEM_CHANGED
, this);
11574 if (inBankAlso
) // Remove items from bank as well
11576 for (int i
= BANK_SLOT_ITEM_START
; i
< BANK_SLOT_ITEM_END
; ++i
)
11578 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
11579 if (pItem
&& pItem
->GetEntry() == item
&& !pItem
->IsInTrade())
11581 if (pItem
->GetCount() + remcount
<= count
)
11583 remcount
+= pItem
->GetCount();
11584 DestroyItem(INVENTORY_SLOT_BAG_0
, i
, update
);
11586 if (remcount
>= count
)
11591 ItemRemovedQuestCheck(pItem
->GetEntry(), count
- remcount
);
11592 pItem
->SetCount(pItem
->GetCount() - count
+ remcount
);
11593 if (IsInWorld() && update
)
11594 pItem
->SendCreateUpdateToPlayer(this);
11595 pItem
->SetState(ITEM_CHANGED
, this);
11601 for (int i
= BANK_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
11603 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
11605 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
11607 Item
* pItem
= pBag
->GetItemByPos(j
);
11608 if (pItem
&& pItem
->GetEntry() == item
&& !pItem
->IsInTrade())
11610 if (pItem
->GetCount() + remcount
<= count
)
11612 remcount
+= pItem
->GetCount();
11613 DestroyItem(i
, j
, update
);
11615 if (remcount
>= count
)
11620 ItemRemovedQuestCheck(pItem
->GetEntry(), count
- remcount
);
11621 pItem
->SetCount(pItem
->GetCount() - count
+ remcount
);
11622 if (IsInWorld() && update
)
11623 pItem
->SendCreateUpdateToPlayer(this);
11624 pItem
->SetState(ITEM_CHANGED
, this);
11634 void Player::DestroyZoneLimitedItem(bool update
, uint32 new_zone
)
11636 DEBUG_LOG("STORAGE: DestroyZoneLimitedItem in map %u and area %u", GetMapId(), new_zone
);
11639 for (int i
= INVENTORY_SLOT_ITEM_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
11640 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
11641 if (pItem
->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone
))
11642 DestroyItem(INVENTORY_SLOT_BAG_0
, i
, update
);
11644 // in inventory bags
11645 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
11646 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
11647 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
11648 if (Item
* pItem
= pBag
->GetItemByPos(j
))
11649 if (pItem
->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone
))
11650 DestroyItem(i
, j
, update
);
11652 // in equipment and bag list
11653 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
11654 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
11655 if (pItem
->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone
))
11656 DestroyItem(INVENTORY_SLOT_BAG_0
, i
, update
);
11659 void Player::DestroyConjuredItems(bool update
)
11661 // used when entering arena
11662 // destroys all conjured items
11663 DEBUG_LOG("STORAGE: DestroyConjuredItems");
11666 for (int i
= INVENTORY_SLOT_ITEM_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
11667 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
11668 if (pItem
->IsConjuredConsumable())
11669 DestroyItem(INVENTORY_SLOT_BAG_0
, i
, update
);
11671 // in inventory bags
11672 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
11673 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
11674 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
11675 if (Item
* pItem
= pBag
->GetItemByPos(j
))
11676 if (pItem
->IsConjuredConsumable())
11677 DestroyItem(i
, j
, update
);
11679 // in equipment and bag list
11680 for (int i
= EQUIPMENT_SLOT_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
11681 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
11682 if (pItem
->IsConjuredConsumable())
11683 DestroyItem(INVENTORY_SLOT_BAG_0
, i
, update
);
11686 void Player::DestroyItemCount(Item
* pItem
, uint32
& count
, bool update
)
11691 DEBUG_LOG("STORAGE: DestroyItemCount item (GUID: %u, Entry: %u) count = %u", pItem
->GetGUIDLow(), pItem
->GetEntry(), count
);
11693 if (pItem
->GetCount() <= count
)
11695 count
-= pItem
->GetCount();
11697 DestroyItem(pItem
->GetBagSlot(), pItem
->GetSlot(), update
);
11701 ItemRemovedQuestCheck(pItem
->GetEntry(), count
);
11702 pItem
->SetCount(pItem
->GetCount() - count
);
11704 if (IsInWorld() && update
)
11705 pItem
->SendCreateUpdateToPlayer(this);
11706 pItem
->SetState(ITEM_CHANGED
, this);
11710 void Player::SplitItem(uint16 src
, uint16 dst
, uint32 count
)
11712 uint8 srcbag
= src
>> 8;
11713 uint8 srcslot
= src
& 255;
11715 uint8 dstbag
= dst
>> 8;
11716 uint8 dstslot
= dst
& 255;
11718 Item
* pSrcItem
= GetItemByPos(srcbag
, srcslot
);
11721 SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND
, pSrcItem
, NULL
);
11725 if (pSrcItem
->HasGeneratedLoot()) // prevent split looting item (stackable items can has only temporary loot and this meaning that loot window open)
11727 // best error message found for attempting to split while looting
11728 SendEquipError(EQUIP_ERR_COULDNT_SPLIT_ITEMS
, pSrcItem
, NULL
);
11732 // not let split all items (can be only at cheating)
11733 if (pSrcItem
->GetCount() == count
)
11735 SendEquipError(EQUIP_ERR_COULDNT_SPLIT_ITEMS
, pSrcItem
, NULL
);
11739 // not let split more existing items (can be only at cheating)
11740 if (pSrcItem
->GetCount() < count
)
11742 SendEquipError(EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT
, pSrcItem
, NULL
);
11746 DEBUG_LOG("STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag
, dstslot
, pSrcItem
->GetEntry(), count
);
11747 Item
* pNewItem
= pSrcItem
->CloneItem(count
, this);
11750 SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND
, pSrcItem
, NULL
);
11754 if (IsInventoryPos(dst
))
11756 // change item amount before check (for unique max count check)
11757 pSrcItem
->SetCount(pSrcItem
->GetCount() - count
);
11759 ItemPosCountVec dest
;
11760 InventoryResult msg
= CanStoreItem(dstbag
, dstslot
, dest
, pNewItem
, false);
11761 if (msg
!= EQUIP_ERR_OK
)
11764 pSrcItem
->SetCount(pSrcItem
->GetCount() + count
);
11765 SendEquipError(msg
, pSrcItem
, NULL
);
11770 pSrcItem
->SendCreateUpdateToPlayer(this);
11771 pSrcItem
->SetState(ITEM_CHANGED
, this);
11772 StoreItem(dest
, pNewItem
, true);
11774 else if (IsBankPos(dst
))
11776 // change item amount before check (for unique max count check)
11777 pSrcItem
->SetCount(pSrcItem
->GetCount() - count
);
11779 ItemPosCountVec dest
;
11780 InventoryResult msg
= CanBankItem(dstbag
, dstslot
, dest
, pNewItem
, false);
11781 if (msg
!= EQUIP_ERR_OK
)
11784 pSrcItem
->SetCount(pSrcItem
->GetCount() + count
);
11785 SendEquipError(msg
, pSrcItem
, NULL
);
11790 pSrcItem
->SendCreateUpdateToPlayer(this);
11791 pSrcItem
->SetState(ITEM_CHANGED
, this);
11792 BankItem(dest
, pNewItem
, true);
11794 else if (IsEquipmentPos(dst
))
11796 // change item amount before check (for unique max count check), provide space for splitted items
11797 pSrcItem
->SetCount(pSrcItem
->GetCount() - count
);
11800 InventoryResult msg
= CanEquipItem(dstslot
, dest
, pNewItem
, false);
11801 if (msg
!= EQUIP_ERR_OK
)
11804 pSrcItem
->SetCount(pSrcItem
->GetCount() + count
);
11805 SendEquipError(msg
, pSrcItem
, NULL
);
11810 pSrcItem
->SendCreateUpdateToPlayer(this);
11811 pSrcItem
->SetState(ITEM_CHANGED
, this);
11812 EquipItem(dest
, pNewItem
, true);
11813 AutoUnequipOffhandIfNeed();
11817 void Player::SwapItem(uint16 src
, uint16 dst
)
11819 uint8 srcbag
= src
>> 8;
11820 uint8 srcslot
= src
& 255;
11822 uint8 dstbag
= dst
>> 8;
11823 uint8 dstslot
= dst
& 255;
11825 Item
* pSrcItem
= GetItemByPos(srcbag
, srcslot
);
11826 Item
* pDstItem
= GetItemByPos(dstbag
, dstslot
);
11831 DEBUG_LOG("STORAGE: SwapItem bag = %u, slot = %u, item = %u", dstbag
, dstslot
, pSrcItem
->GetEntry());
11835 SendEquipError(EQUIP_ERR_YOU_ARE_DEAD
, pSrcItem
, pDstItem
);
11841 // check unequip potability for equipped items and bank bags
11842 if (IsEquipmentPos(src
) || IsBagPos(src
))
11844 // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later)
11845 InventoryResult msg
= CanUnequipItem(src
, !IsBagPos(src
) || IsBagPos(dst
) || (pDstItem
&& pDstItem
->IsBag() && ((Bag
*)pDstItem
)->IsEmpty()));
11846 if (msg
!= EQUIP_ERR_OK
)
11848 SendEquipError(msg
, pSrcItem
, pDstItem
);
11853 // prevent put equipped/bank bag in self
11854 if (IsBagPos(src
) && srcslot
== dstbag
)
11856 SendEquipError(EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG
, pSrcItem
, pDstItem
);
11860 // prevent put equipped/bank bag in self
11861 if (IsBagPos(dst
) && dstslot
== srcbag
)
11863 SendEquipError(EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG
, pDstItem
, pSrcItem
);
11871 // check unequip potability for equipped items and bank bags
11872 if (IsEquipmentPos(dst
) || IsBagPos(dst
))
11874 // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later)
11875 InventoryResult msg
= CanUnequipItem(dst
, !IsBagPos(dst
) || IsBagPos(src
) || (pSrcItem
->IsBag() && ((Bag
*)pSrcItem
)->IsEmpty()));
11876 if (msg
!= EQUIP_ERR_OK
)
11878 SendEquipError(msg
, pSrcItem
, pDstItem
);
11884 // NOW this is or item move (swap with empty), or swap with another item (including bags in bag possitions)
11885 // or swap empty bag with another empty or not empty bag (with items exchange)
11890 if (IsInventoryPos(dst
))
11892 ItemPosCountVec dest
;
11893 InventoryResult msg
= CanStoreItem(dstbag
, dstslot
, dest
, pSrcItem
, false);
11894 if (msg
!= EQUIP_ERR_OK
)
11896 SendEquipError(msg
, pSrcItem
, NULL
);
11900 RemoveItem(srcbag
, srcslot
, true);
11901 StoreItem(dest
, pSrcItem
, true);
11903 else if (IsBankPos(dst
))
11905 ItemPosCountVec dest
;
11906 InventoryResult msg
= CanBankItem(dstbag
, dstslot
, dest
, pSrcItem
, false);
11907 if (msg
!= EQUIP_ERR_OK
)
11909 SendEquipError(msg
, pSrcItem
, NULL
);
11913 RemoveItem(srcbag
, srcslot
, true);
11914 BankItem(dest
, pSrcItem
, true);
11916 else if (IsEquipmentPos(dst
))
11919 InventoryResult msg
= CanEquipItem(dstslot
, dest
, pSrcItem
, false);
11920 if (msg
!= EQUIP_ERR_OK
)
11922 SendEquipError(msg
, pSrcItem
, NULL
);
11926 RemoveItem(srcbag
, srcslot
, true);
11927 EquipItem(dest
, pSrcItem
, true);
11928 AutoUnequipOffhandIfNeed();
11934 // attempt merge to / fill target item
11935 if (!pSrcItem
->IsBag() && !pDstItem
->IsBag())
11937 InventoryResult msg
;
11938 ItemPosCountVec sDest
;
11940 if (IsInventoryPos(dst
))
11941 msg
= CanStoreItem(dstbag
, dstslot
, sDest
, pSrcItem
, false);
11942 else if (IsBankPos(dst
))
11943 msg
= CanBankItem(dstbag
, dstslot
, sDest
, pSrcItem
, false);
11944 else if (IsEquipmentPos(dst
))
11945 msg
= CanEquipItem(dstslot
, eDest
, pSrcItem
, false);
11949 // can be merge/fill
11950 if (msg
== EQUIP_ERR_OK
)
11952 if (pSrcItem
->GetCount() + pDstItem
->GetCount() <= pSrcItem
->GetProto()->GetMaxStackSize())
11954 RemoveItem(srcbag
, srcslot
, true);
11956 if (IsInventoryPos(dst
))
11957 StoreItem(sDest
, pSrcItem
, true);
11958 else if (IsBankPos(dst
))
11959 BankItem(sDest
, pSrcItem
, true);
11960 else if (IsEquipmentPos(dst
))
11962 EquipItem(eDest
, pSrcItem
, true);
11963 AutoUnequipOffhandIfNeed();
11968 pSrcItem
->SetCount(pSrcItem
->GetCount() + pDstItem
->GetCount() - pSrcItem
->GetProto()->GetMaxStackSize());
11969 pDstItem
->SetCount(pSrcItem
->GetProto()->GetMaxStackSize());
11970 pSrcItem
->SetState(ITEM_CHANGED
, this);
11971 pDstItem
->SetState(ITEM_CHANGED
, this);
11974 pSrcItem
->SendCreateUpdateToPlayer(this);
11975 pDstItem
->SendCreateUpdateToPlayer(this);
11982 // impossible merge/fill, do real swap
11983 InventoryResult msg
;
11985 // check src->dest move possibility
11986 ItemPosCountVec sDest
;
11988 if (IsInventoryPos(dst
))
11989 msg
= CanStoreItem(dstbag
, dstslot
, sDest
, pSrcItem
, true);
11990 else if (IsBankPos(dst
))
11991 msg
= CanBankItem(dstbag
, dstslot
, sDest
, pSrcItem
, true);
11992 else if (IsEquipmentPos(dst
))
11994 msg
= CanEquipItem(dstslot
, eDest
, pSrcItem
, true);
11995 if (msg
== EQUIP_ERR_OK
)
11996 msg
= CanUnequipItem(eDest
, true);
11999 if (msg
!= EQUIP_ERR_OK
)
12001 SendEquipError(msg
, pSrcItem
, pDstItem
);
12005 // check dest->src move possibility
12006 ItemPosCountVec sDest2
;
12008 if (IsInventoryPos(src
))
12009 msg
= CanStoreItem(srcbag
, srcslot
, sDest2
, pDstItem
, true);
12010 else if (IsBankPos(src
))
12011 msg
= CanBankItem(srcbag
, srcslot
, sDest2
, pDstItem
, true);
12012 else if (IsEquipmentPos(src
))
12014 msg
= CanEquipItem(srcslot
, eDest2
, pDstItem
, true);
12015 if (msg
== EQUIP_ERR_OK
)
12016 msg
= CanUnequipItem(eDest2
, true);
12019 if (msg
!= EQUIP_ERR_OK
)
12021 SendEquipError(msg
, pDstItem
, pSrcItem
);
12025 // Check bag swap with item exchange (one from empty in not bag possition (equipped (not possible in fact) or store)
12026 if (pSrcItem
->IsBag() && pDstItem
->IsBag())
12028 Bag
* emptyBag
= NULL
;
12029 Bag
* fullBag
= NULL
;
12030 if (((Bag
*)pSrcItem
)->IsEmpty() && !IsBagPos(src
))
12032 emptyBag
= (Bag
*)pSrcItem
;
12033 fullBag
= (Bag
*)pDstItem
;
12035 else if (((Bag
*)pDstItem
)->IsEmpty() && !IsBagPos(dst
))
12037 emptyBag
= (Bag
*)pDstItem
;
12038 fullBag
= (Bag
*)pSrcItem
;
12041 // bag swap (with items exchange) case
12042 if (emptyBag
&& fullBag
)
12044 ItemPrototype
const* emotyProto
= emptyBag
->GetProto();
12048 for (uint32 i
= 0; i
< fullBag
->GetBagSize(); ++i
)
12050 Item
* bagItem
= fullBag
->GetItemByPos(i
);
12054 ItemPrototype
const* bagItemProto
= bagItem
->GetProto();
12055 if (!bagItemProto
|| !ItemCanGoIntoBag(bagItemProto
, emotyProto
))
12057 // one from items not go to empty target bag
12058 SendEquipError(EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG
, pSrcItem
, pDstItem
);
12065 if (count
> emptyBag
->GetBagSize())
12067 // too small targeted bag
12068 SendEquipError(EQUIP_ERR_ITEMS_CANT_BE_SWAPPED
, pSrcItem
, pDstItem
);
12073 count
= 0; // will pos in new bag
12074 for (uint32 i
= 0; i
< fullBag
->GetBagSize(); ++i
)
12076 Item
* bagItem
= fullBag
->GetItemByPos(i
);
12080 fullBag
->RemoveItem(i
, true);
12081 emptyBag
->StoreItem(count
, bagItem
, true);
12082 bagItem
->SetState(ITEM_CHANGED
, this);
12089 // now do moves, remove...
12090 RemoveItem(dstbag
, dstslot
, false);
12091 RemoveItem(srcbag
, srcslot
, false);
12094 if (IsInventoryPos(dst
))
12095 StoreItem(sDest
, pSrcItem
, true);
12096 else if (IsBankPos(dst
))
12097 BankItem(sDest
, pSrcItem
, true);
12098 else if (IsEquipmentPos(dst
))
12099 EquipItem(eDest
, pSrcItem
, true);
12102 if (IsInventoryPos(src
))
12103 StoreItem(sDest2
, pDstItem
, true);
12104 else if (IsBankPos(src
))
12105 BankItem(sDest2
, pDstItem
, true);
12106 else if (IsEquipmentPos(src
))
12107 EquipItem(eDest2
, pDstItem
, true);
12109 AutoUnequipOffhandIfNeed();
12112 void Player::AddItemToBuyBackSlot(Item
* pItem
)
12116 uint32 slot
= m_currentBuybackSlot
;
12117 // if current back slot non-empty search oldest or free
12120 uint32 oldest_time
= GetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1
);
12121 uint32 oldest_slot
= BUYBACK_SLOT_START
;
12123 for (uint32 i
= BUYBACK_SLOT_START
+ 1; i
< BUYBACK_SLOT_END
; ++i
)
12132 uint32 i_time
= GetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1
+ i
- BUYBACK_SLOT_START
);
12134 if (oldest_time
> i_time
)
12136 oldest_time
= i_time
;
12142 slot
= oldest_slot
;
12145 RemoveItemFromBuyBackSlot(slot
, true);
12146 DEBUG_LOG("STORAGE: AddItemToBuyBackSlot item = %u, slot = %u", pItem
->GetEntry(), slot
);
12148 m_items
[slot
] = pItem
;
12149 time_t base
= time(NULL
);
12150 uint32 etime
= uint32(base
- m_logintime
+ (30 * 3600));
12151 uint32 eslot
= slot
- BUYBACK_SLOT_START
;
12153 SetGuidValue(PLAYER_FIELD_VENDORBUYBACK_SLOT_1
+ (eslot
* 2), pItem
->GetObjectGuid());
12154 if (ItemPrototype
const* pProto
= pItem
->GetProto())
12155 SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1
+ eslot
, pProto
->SellPrice
* pItem
->GetCount());
12157 SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1
+ eslot
, 0);
12158 SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1
+ eslot
, (uint32
)etime
);
12160 // move to next (for non filled list is move most optimized choice)
12161 if (m_currentBuybackSlot
< BUYBACK_SLOT_END
- 1)
12162 ++m_currentBuybackSlot
;
12166 Item
* Player::GetItemFromBuyBackSlot(uint32 slot
)
12168 DEBUG_LOG("STORAGE: GetItemFromBuyBackSlot slot = %u", slot
);
12169 if (slot
>= BUYBACK_SLOT_START
&& slot
< BUYBACK_SLOT_END
)
12170 return m_items
[slot
];
12174 void Player::RemoveItemFromBuyBackSlot(uint32 slot
, bool del
)
12176 DEBUG_LOG("STORAGE: RemoveItemFromBuyBackSlot slot = %u", slot
);
12177 if (slot
>= BUYBACK_SLOT_START
&& slot
< BUYBACK_SLOT_END
)
12179 Item
* pItem
= m_items
[slot
];
12182 pItem
->RemoveFromWorld();
12183 if (del
) pItem
->SetState(ITEM_REMOVED
, this);
12186 m_items
[slot
] = NULL
;
12188 uint32 eslot
= slot
- BUYBACK_SLOT_START
;
12189 SetGuidValue(PLAYER_FIELD_VENDORBUYBACK_SLOT_1
+ (eslot
* 2), ObjectGuid());
12190 SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1
+ eslot
, 0);
12191 SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1
+ eslot
, 0);
12193 // if current backslot is filled set to now free slot
12194 if (m_items
[m_currentBuybackSlot
])
12195 m_currentBuybackSlot
= slot
;
12199 void Player::SendEquipError(InventoryResult msg
, Item
* pItem
, Item
* pItem2
, uint32 itemid
/*= 0*/) const
12201 DEBUG_LOG("WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)", msg
);
12202 WorldPacket
data(SMSG_INVENTORY_CHANGE_FAILURE
, 1 + 8 + 8 + 1);
12203 data
<< uint8(msg
);
12205 if (msg
!= EQUIP_ERR_OK
)
12207 data
<< (pItem
? pItem
->GetObjectGuid() : ObjectGuid());
12208 data
<< (pItem2
? pItem2
->GetObjectGuid() : ObjectGuid());
12209 data
<< uint8(0); // bag type subclass, used with EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM and EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG2
12213 case EQUIP_ERR_CANT_EQUIP_LEVEL_I
:
12214 case EQUIP_ERR_PURCHASE_LEVEL_TOO_LOW
:
12216 ItemPrototype
const* proto
= pItem
? pItem
->GetProto() : ObjectMgr::GetItemPrototype(itemid
);
12217 data
<< uint32(proto
? proto
->RequiredLevel
: 0);
12220 case EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM
: // no idea about this one...
12227 case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS
:
12228 case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED_IS
:
12229 case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS
:
12231 ItemPrototype
const* proto
= pItem
? pItem
->GetProto() : ObjectMgr::GetItemPrototype(itemid
);
12232 data
<< uint32(proto
? proto
->ItemLimitCategory
: 0);
12239 GetSession()->SendPacket(&data
);
12242 void Player::SendBuyError(BuyResult msg
, Creature
* pCreature
, uint32 item
, uint32 param
)
12244 DEBUG_LOG("WORLD: Sent SMSG_BUY_FAILED");
12245 WorldPacket
data(SMSG_BUY_FAILED
, (8 + 4 + 4 + 1));
12246 data
<< (pCreature
? pCreature
->GetObjectGuid() : ObjectGuid());
12247 data
<< uint32(item
);
12249 data
<< uint32(param
);
12250 data
<< uint8(msg
);
12251 GetSession()->SendPacket(&data
);
12254 void Player::SendSellError(SellResult msg
, Creature
* pCreature
, ObjectGuid itemGuid
, uint32 param
)
12256 DEBUG_LOG("WORLD: Sent SMSG_SELL_ITEM");
12257 WorldPacket
data(SMSG_SELL_ITEM
, (8 + 8 + (param
? 4 : 0) + 1)); // last check 2.0.10
12258 data
<< (pCreature
? pCreature
->GetObjectGuid() : ObjectGuid());
12259 data
<< ObjectGuid(itemGuid
);
12261 data
<< uint32(param
);
12262 data
<< uint8(msg
);
12263 GetSession()->SendPacket(&data
);
12266 void Player::TradeCancel(bool sendback
)
12270 Player
* trader
= m_trade
->GetTrader();
12272 // send yellow "Trade canceled" message to both traders
12274 GetSession()->SendCancelTrade();
12276 trader
->GetSession()->SendCancelTrade();
12281 delete trader
->m_trade
;
12282 trader
->m_trade
= NULL
;
12286 void Player::UpdateItemDuration(uint32 time
, bool realtimeonly
)
12288 if (m_itemDuration
.empty())
12291 DEBUG_LOG("Player::UpdateItemDuration(%u,%u)", time
, realtimeonly
);
12293 for (ItemDurationList::const_iterator itr
= m_itemDuration
.begin(); itr
!= m_itemDuration
.end();)
12296 ++itr
; // current element can be erased in UpdateDuration
12298 if ((realtimeonly
&& (item
->GetProto()->ExtraFlags
& ITEM_EXTRA_REAL_TIME_DURATION
)) || !realtimeonly
)
12299 item
->UpdateDuration(this, time
);
12303 void Player::UpdateEnchantTime(uint32 time
)
12305 for (EnchantDurationList::iterator itr
= m_enchantDuration
.begin(), next
; itr
!= m_enchantDuration
.end(); itr
= next
)
12307 MANGOS_ASSERT(itr
->item
);
12309 if (!itr
->item
->GetEnchantmentId(itr
->slot
))
12311 next
= m_enchantDuration
.erase(itr
);
12313 else if (itr
->leftduration
<= time
)
12315 ApplyEnchantment(itr
->item
, itr
->slot
, false, false);
12316 itr
->item
->ClearEnchantment(itr
->slot
);
12317 next
= m_enchantDuration
.erase(itr
);
12319 else if (itr
->leftduration
> time
)
12321 itr
->leftduration
-= time
;
12327 void Player::AddEnchantmentDurations(Item
* item
)
12329 for (int x
= 0; x
< MAX_ENCHANTMENT_SLOT
; ++x
)
12331 if (x
> PRISMATIC_ENCHANTMENT_SLOT
&& x
< PROP_ENCHANTMENT_SLOT_0
)
12334 if (!item
->GetEnchantmentId(EnchantmentSlot(x
)))
12337 uint32 duration
= item
->GetEnchantmentDuration(EnchantmentSlot(x
));
12339 AddEnchantmentDuration(item
, EnchantmentSlot(x
), duration
);
12343 void Player::RemoveEnchantmentDurations(Item
* item
)
12345 for (EnchantDurationList::iterator itr
= m_enchantDuration
.begin(); itr
!= m_enchantDuration
.end();)
12347 if (itr
->item
== item
)
12349 // save duration in item
12350 item
->SetEnchantmentDuration(EnchantmentSlot(itr
->slot
), itr
->leftduration
);
12351 itr
= m_enchantDuration
.erase(itr
);
12358 void Player::RemoveAllEnchantments(EnchantmentSlot slot
)
12360 // remove enchantments from equipped items first to clean up the m_enchantDuration list
12361 for (EnchantDurationList::iterator itr
= m_enchantDuration
.begin(), next
; itr
!= m_enchantDuration
.end(); itr
= next
)
12364 if (itr
->slot
== slot
)
12366 if (itr
->item
&& itr
->item
->GetEnchantmentId(slot
))
12368 // remove from stats
12369 ApplyEnchantment(itr
->item
, slot
, false, false);
12371 itr
->item
->ClearEnchantment(slot
);
12373 // remove from update list
12374 next
= m_enchantDuration
.erase(itr
);
12380 // remove enchants from inventory items
12381 // NOTE: no need to remove these from stats, since these aren't equipped
12383 for (int i
= INVENTORY_SLOT_ITEM_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
12384 if (Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
12385 if (pItem
->GetEnchantmentId(slot
))
12386 pItem
->ClearEnchantment(slot
);
12388 // in inventory bags
12389 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
12390 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
12391 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
12392 if (Item
* pItem
= pBag
->GetItemByPos(j
))
12393 if (pItem
->GetEnchantmentId(slot
))
12394 pItem
->ClearEnchantment(slot
);
12397 // duration == 0 will remove item enchant
12398 void Player::AddEnchantmentDuration(Item
* item
, EnchantmentSlot slot
, uint32 duration
)
12403 if (slot
>= MAX_ENCHANTMENT_SLOT
)
12406 for (EnchantDurationList::iterator itr
= m_enchantDuration
.begin(); itr
!= m_enchantDuration
.end(); ++itr
)
12408 if (itr
->item
== item
&& itr
->slot
== slot
)
12410 itr
->item
->SetEnchantmentDuration(itr
->slot
, itr
->leftduration
);
12411 m_enchantDuration
.erase(itr
);
12415 if (item
&& duration
> 0)
12417 GetSession()->SendItemEnchantTimeUpdate(GetObjectGuid(), item
->GetObjectGuid(), slot
, uint32(duration
/ 1000));
12418 m_enchantDuration
.push_back(EnchantDuration(item
, slot
, duration
));
12422 void Player::ApplyEnchantment(Item
* item
, bool apply
)
12424 for (uint32 slot
= 0; slot
< MAX_ENCHANTMENT_SLOT
; ++slot
)
12425 ApplyEnchantment(item
, EnchantmentSlot(slot
), apply
);
12428 void Player::ApplyEnchantment(Item
* item
, EnchantmentSlot slot
, bool apply
, bool apply_dur
, bool ignore_condition
)
12430 if (slot
> PRISMATIC_ENCHANTMENT_SLOT
&& slot
< PROP_ENCHANTMENT_SLOT_0
)
12436 if (!item
->IsEquipped())
12439 if (slot
>= MAX_ENCHANTMENT_SLOT
)
12442 uint32 enchant_id
= item
->GetEnchantmentId(slot
);
12446 SpellItemEnchantmentEntry
const* pEnchant
= sSpellItemEnchantmentStore
.LookupEntry(enchant_id
);
12450 if (!ignore_condition
&& pEnchant
->EnchantmentCondition
&& !EnchantmentFitsRequirements(pEnchant
->EnchantmentCondition
, -1))
12453 if (!item
->IsBroken())
12455 for (int s
= 0; s
< 3; ++s
)
12457 uint32 enchant_display_type
= pEnchant
->type
[s
];
12458 uint32 enchant_amount
= pEnchant
->amount
[s
];
12459 uint32 enchant_spell_id
= pEnchant
->spellid
[s
];
12461 switch (enchant_display_type
)
12463 case ITEM_ENCHANTMENT_TYPE_NONE
:
12465 case ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL
:
12466 // processed in Player::CastItemCombatSpell
12468 case ITEM_ENCHANTMENT_TYPE_DAMAGE
:
12469 if (item
->GetSlot() == EQUIPMENT_SLOT_MAINHAND
)
12470 HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12471 else if (item
->GetSlot() == EQUIPMENT_SLOT_OFFHAND
)
12472 HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12473 else if (item
->GetSlot() == EQUIPMENT_SLOT_RANGED
)
12474 HandleStatModifier(UNIT_MOD_DAMAGE_RANGED
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12476 case ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL
:
12478 if (enchant_spell_id
)
12482 int32 basepoints
= 0;
12483 // Random Property Exist - try found basepoints for spell (basepoints depends from item suffix factor)
12484 if (item
->GetItemRandomPropertyId())
12486 ItemRandomSuffixEntry
const* item_rand
= sItemRandomSuffixStore
.LookupEntry(abs(item
->GetItemRandomPropertyId()));
12489 // Search enchant_amount
12490 for (int k
= 0; k
< 3; ++k
)
12492 if (item_rand
->enchant_id
[k
] == enchant_id
)
12494 basepoints
= int32((item_rand
->prefix
[k
] * item
->GetItemSuffixFactor()) / 10000);
12500 // Cast custom spell vs all equal basepoints got from enchant_amount
12502 CastCustomSpell(this, enchant_spell_id
, &basepoints
, &basepoints
, &basepoints
, true, item
);
12504 CastSpell(this, enchant_spell_id
, true, item
);
12507 RemoveAurasDueToItemSpell(item
, enchant_spell_id
);
12511 case ITEM_ENCHANTMENT_TYPE_RESISTANCE
:
12512 if (!enchant_amount
)
12514 ItemRandomSuffixEntry
const* item_rand
= sItemRandomSuffixStore
.LookupEntry(abs(item
->GetItemRandomPropertyId()));
12517 for (int k
= 0; k
< 3; ++k
)
12519 if (item_rand
->enchant_id
[k
] == enchant_id
)
12521 enchant_amount
= uint32((item_rand
->prefix
[k
] * item
->GetItemSuffixFactor()) / 10000);
12528 HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START
+ enchant_spell_id
), TOTAL_VALUE
, float(enchant_amount
), apply
);
12530 case ITEM_ENCHANTMENT_TYPE_STAT
:
12532 if (!enchant_amount
)
12534 ItemRandomSuffixEntry
const* item_rand_suffix
= sItemRandomSuffixStore
.LookupEntry(abs(item
->GetItemRandomPropertyId()));
12535 if (item_rand_suffix
)
12537 for (int k
= 0; k
< 3; ++k
)
12539 if (item_rand_suffix
->enchant_id
[k
] == enchant_id
)
12541 enchant_amount
= uint32((item_rand_suffix
->prefix
[k
] * item
->GetItemSuffixFactor()) / 10000);
12548 DEBUG_LOG("Adding %u to stat nb %u", enchant_amount
, enchant_spell_id
);
12549 switch (enchant_spell_id
)
12551 /*case ITEM_MOD_MANA:
12552 DEBUG_LOG("+ %u MANA", enchant_amount);
12553 HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(enchant_amount), apply);
12555 case ITEM_MOD_HEALTH
:
12556 DEBUG_LOG("+ %u HEALTH", enchant_amount
);
12557 HandleStatModifier(UNIT_MOD_HEALTH
, BASE_VALUE
, float(enchant_amount
), apply
);
12559 case ITEM_MOD_AGILITY
:
12560 DEBUG_LOG("+ %u AGILITY", enchant_amount
);
12561 HandleStatModifier(UNIT_MOD_STAT_AGILITY
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12562 ApplyStatBuffMod(STAT_AGILITY
, float(enchant_amount
), apply
);
12564 case ITEM_MOD_STRENGTH
:
12565 DEBUG_LOG("+ %u STRENGTH", enchant_amount
);
12566 HandleStatModifier(UNIT_MOD_STAT_STRENGTH
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12567 ApplyStatBuffMod(STAT_STRENGTH
, float(enchant_amount
), apply
);
12569 case ITEM_MOD_INTELLECT
:
12570 DEBUG_LOG("+ %u INTELLECT", enchant_amount
);
12571 HandleStatModifier(UNIT_MOD_STAT_INTELLECT
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12572 ApplyStatBuffMod(STAT_INTELLECT
, float(enchant_amount
), apply
);
12574 case ITEM_MOD_SPIRIT
:
12575 DEBUG_LOG("+ %u SPIRIT", enchant_amount
);
12576 HandleStatModifier(UNIT_MOD_STAT_SPIRIT
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12577 ApplyStatBuffMod(STAT_SPIRIT
, float(enchant_amount
), apply
);
12579 case ITEM_MOD_STAMINA
:
12580 DEBUG_LOG("+ %u STAMINA", enchant_amount
);
12581 HandleStatModifier(UNIT_MOD_STAT_STAMINA
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12582 ApplyStatBuffMod(STAT_STAMINA
, float(enchant_amount
), apply
);
12584 case ITEM_MOD_DODGE_RATING
:
12585 ApplyRatingMod(CR_DODGE
, enchant_amount
, apply
);
12586 DEBUG_LOG("+ %u DODGE", enchant_amount
);
12588 case ITEM_MOD_PARRY_RATING
:
12589 ApplyRatingMod(CR_PARRY
, enchant_amount
, apply
);
12590 DEBUG_LOG("+ %u PARRY", enchant_amount
);
12592 case ITEM_MOD_CRIT_RANGED_RATING
:
12593 ApplyRatingMod(CR_CRIT_RANGED
, enchant_amount
, apply
);
12594 DEBUG_LOG("+ %u RANGED_CRIT", enchant_amount
);
12596 case ITEM_MOD_HIT_RATING
:
12597 ApplyRatingMod(CR_HIT_MELEE
, enchant_amount
, apply
);
12598 ApplyRatingMod(CR_HIT_RANGED
, enchant_amount
, apply
);
12599 ApplyRatingMod(CR_HIT_SPELL
, enchant_amount
, apply
);
12600 DEBUG_LOG("+ %u HIT", enchant_amount
);
12602 case ITEM_MOD_CRIT_RATING
:
12603 ApplyRatingMod(CR_CRIT_MELEE
, enchant_amount
, apply
);
12604 ApplyRatingMod(CR_CRIT_RANGED
, enchant_amount
, apply
);
12605 ApplyRatingMod(CR_CRIT_SPELL
, enchant_amount
, apply
);
12606 DEBUG_LOG("+ %u CRITICAL", enchant_amount
);
12608 case ITEM_MOD_RESILIENCE_RATING
:
12609 ApplyRatingMod(CR_RESILIENCE_DAMAGE_TAKEN
, enchant_amount
, apply
);
12610 DEBUG_LOG("+ %u RESILIENCE", enchant_amount
);
12612 case ITEM_MOD_HASTE_RATING
:
12613 ApplyRatingMod(CR_HASTE_MELEE
, enchant_amount
, apply
);
12614 ApplyRatingMod(CR_HASTE_RANGED
, enchant_amount
, apply
);
12615 ApplyRatingMod(CR_HASTE_SPELL
, enchant_amount
, apply
);
12616 DEBUG_LOG("+ %u HASTE", enchant_amount
);
12618 case ITEM_MOD_EXPERTISE_RATING
:
12619 ApplyRatingMod(CR_EXPERTISE
, enchant_amount
, apply
);
12620 DEBUG_LOG("+ %u EXPERTISE", enchant_amount
);
12622 case ITEM_MOD_ATTACK_POWER
:
12623 HandleStatModifier(UNIT_MOD_ATTACK_POWER
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12624 HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12625 DEBUG_LOG("+ %u ATTACK_POWER", enchant_amount
);
12627 case ITEM_MOD_RANGED_ATTACK_POWER
:
12628 HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED
, TOTAL_VALUE
, float(enchant_amount
), apply
);
12629 DEBUG_LOG("+ %u RANGED_ATTACK_POWER", enchant_amount
);
12631 case ITEM_MOD_SPELL_POWER
:
12632 ApplySpellPowerBonus(enchant_amount
, apply
);
12633 DEBUG_LOG("+ %u SPELL_POWER", enchant_amount
);
12635 case ITEM_MOD_HEALTH_REGEN
:
12636 ApplyHealthRegenBonus(enchant_amount
, apply
);
12637 DEBUG_LOG("+ %u HEALTH_REGENERATION", enchant_amount
);
12639 case ITEM_MOD_SPELL_PENETRATION
:
12640 ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE
, -int32(enchant_amount
), apply
);
12641 m_spellPenetrationItemMod
+= apply
? enchant_amount
: -int32(enchant_amount
);
12642 DEBUG_LOG("+ %u SPELL_PENETRATION", -int32(enchant_amount
));
12644 case ITEM_MOD_MASTERY_RATING
:
12645 ApplyRatingMod(CR_MASTERY
, enchant_amount
, apply
);
12646 DEBUG_LOG("+ %u MASTERY_RATING", enchant_amount
);
12649 case ITEM_MOD_DEFENSE_SKILL_RATING
:
12650 case ITEM_MOD_BLOCK_RATING
:
12651 case ITEM_MOD_HIT_MELEE_RATING
:
12652 case ITEM_MOD_HIT_RANGED_RATING
:
12653 case ITEM_MOD_HIT_SPELL_RATING
:
12654 case ITEM_MOD_CRIT_MELEE_RATING
:
12655 case ITEM_MOD_CRIT_SPELL_RATING
:
12656 case ITEM_MOD_HIT_TAKEN_MELEE_RATING
:
12657 case ITEM_MOD_HIT_TAKEN_RANGED_RATING
:
12658 case ITEM_MOD_HIT_TAKEN_SPELL_RATING
:
12659 case ITEM_MOD_CRIT_TAKEN_MELEE_RATING
:
12660 case ITEM_MOD_CRIT_TAKEN_RANGED_RATING
:
12661 case ITEM_MOD_CRIT_TAKEN_SPELL_RATING
:
12662 case ITEM_MOD_HASTE_MELEE_RATING
:
12663 case ITEM_MOD_HASTE_RANGED_RATING
:
12664 case ITEM_MOD_HASTE_SPELL_RATING
:
12665 case ITEM_MOD_HIT_TAKEN_RATING
:
12666 case ITEM_MOD_CRIT_TAKEN_RATING
:
12667 case ITEM_MOD_FERAL_ATTACK_POWER
:
12668 case ITEM_MOD_SPELL_HEALING_DONE
:
12669 case ITEM_MOD_SPELL_DAMAGE_DONE
:
12670 case ITEM_MOD_MANA_REGENERATION
:
12671 case ITEM_MOD_ARMOR_PENETRATION_RATING
:
12672 case ITEM_MOD_BLOCK_VALUE
:
12678 case ITEM_ENCHANTMENT_TYPE_TOTEM
: // Shaman Rockbiter Weapon
12680 if (getClass() == CLASS_SHAMAN
)
12682 float addValue
= 0.0f
;
12683 if (item
->GetSlot() == EQUIPMENT_SLOT_MAINHAND
)
12685 addValue
= float(enchant_amount
* item
->GetProto()->Delay
/ 1000.0f
);
12686 HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND
, TOTAL_VALUE
, addValue
, apply
);
12688 else if (item
->GetSlot() == EQUIPMENT_SLOT_OFFHAND
)
12690 addValue
= float(enchant_amount
* item
->GetProto()->Delay
/ 1000.0f
);
12691 HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND
, TOTAL_VALUE
, addValue
, apply
);
12696 case ITEM_ENCHANTMENT_TYPE_USE_SPELL
:
12697 // processed in Player::CastItemUseSpell
12699 case ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET
:
12703 sLog
.outError("Unknown item enchantment (id = %d) display type: %d", enchant_id
, enchant_display_type
);
12705 } /*switch(enchant_display_type)*/
12709 // visualize enchantment at player and equipped items
12710 if (slot
== PERM_ENCHANTMENT_SLOT
)
12711 SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT
+ (item
->GetSlot() * 2), 0, apply
? item
->GetEnchantmentId(slot
) : 0);
12713 if (slot
== TEMP_ENCHANTMENT_SLOT
)
12714 SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT
+ (item
->GetSlot() * 2), 1, apply
? item
->GetEnchantmentId(slot
) : 0);
12722 uint32 duration
= item
->GetEnchantmentDuration(slot
);
12724 AddEnchantmentDuration(item
, slot
, duration
);
12728 // duration == 0 will remove EnchantDuration
12729 AddEnchantmentDuration(item
, slot
, 0);
12734 void Player::SendEnchantmentDurations()
12736 for (EnchantDurationList::const_iterator itr
= m_enchantDuration
.begin(); itr
!= m_enchantDuration
.end(); ++itr
)
12738 GetSession()->SendItemEnchantTimeUpdate(GetObjectGuid(), itr
->item
->GetObjectGuid(), itr
->slot
, uint32(itr
->leftduration
) / 1000);
12742 void Player::SendItemDurations()
12744 for (ItemDurationList::const_iterator itr
= m_itemDuration
.begin(); itr
!= m_itemDuration
.end(); ++itr
)
12746 (*itr
)->SendTimeUpdate(this);
12750 void Player::SendNewItem(Item
* item
, uint32 count
, bool received
, bool created
, bool broadcast
)
12752 if (!item
) // prevent crash
12755 // last check 2.0.10
12756 WorldPacket
data(SMSG_ITEM_PUSH_RESULT
, (8 + 4 + 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4));
12757 data
<< GetObjectGuid(); // player GUID
12758 data
<< uint32(received
); // 0=looted, 1=from npc
12759 data
<< uint32(created
); // 0=received, 1=created
12760 data
<< uint32(1); // IsShowChatMessage
12761 data
<< uint8(item
->GetBagSlot()); // bagslot
12762 // item slot, but when added to stack: 0xFFFFFFFF
12763 data
<< uint32((item
->GetCount() == count
) ? item
->GetSlot() : -1);
12764 data
<< uint32(item
->GetEntry()); // item id
12765 data
<< uint32(item
->GetItemSuffixFactor()); // SuffixFactor
12766 data
<< uint32(item
->GetItemRandomPropertyId()); // random item property id
12767 data
<< uint32(count
); // count of items
12768 data
<< uint32(GetItemCount(item
->GetEntry())); // count of items in inventory
12770 if (broadcast
&& GetGroup())
12771 GetGroup()->BroadcastPacket(&data
, true);
12773 GetSession()->SendPacket(&data
);
12776 /*********************************************************/
12777 /*** GOSSIP SYSTEM ***/
12778 /*********************************************************/
12780 void Player::PrepareGossipMenu(WorldObject
* pSource
, uint32 menuId
)
12782 PlayerMenu
* pMenu
= PlayerTalkClass
;
12783 pMenu
->ClearMenus();
12785 pMenu
->GetGossipMenu().SetMenuId(menuId
);
12787 GossipMenuItemsMapBounds pMenuItemBounds
= sObjectMgr
.GetGossipMenuItemsMapBounds(menuId
);
12789 // prepares quest menu when true
12790 bool canSeeQuests
= menuId
== GetDefaultGossipMenuForSource(pSource
);
12792 // if canSeeQuests (the default, top level menu) and no menu options exist for this, use options from default options
12793 if (pMenuItemBounds
.first
== pMenuItemBounds
.second
&& canSeeQuests
)
12794 pMenuItemBounds
= sObjectMgr
.GetGossipMenuItemsMapBounds(0);
12796 bool canTalkToCredit
= pSource
->GetTypeId() == TYPEID_UNIT
;
12798 for (GossipMenuItemsMap::const_iterator itr
= pMenuItemBounds
.first
; itr
!= pMenuItemBounds
.second
; ++itr
)
12800 bool hasMenuItem
= true;
12802 if (!isGameMaster()) // Let GM always see menu items regardless of conditions
12804 if (itr
->second
.conditionId
&& !sObjectMgr
.IsPlayerMeetToCondition(this, itr
->second
.conditionId
))
12806 if (itr
->second
.option_id
== GOSSIP_OPTION_QUESTGIVER
)
12807 canSeeQuests
= false;
12812 if (pSource
->GetTypeId() == TYPEID_UNIT
)
12814 Creature
* pCreature
= (Creature
*)pSource
;
12816 uint32 npcflags
= pCreature
->GetUInt32Value(UNIT_NPC_FLAGS
);
12818 if (!(itr
->second
.npc_option_npcflag
& npcflags
))
12821 switch (itr
->second
.option_id
)
12823 case GOSSIP_OPTION_GOSSIP
:
12824 if (itr
->second
.action_menu_id
!= 0) // has sub menu (or close gossip), so do not "talk" with this NPC yet
12825 canTalkToCredit
= false;
12827 case GOSSIP_OPTION_QUESTGIVER
:
12828 hasMenuItem
= false;
12830 case GOSSIP_OPTION_ARMORER
:
12831 hasMenuItem
= false; // added in special mode
12833 case GOSSIP_OPTION_SPIRITHEALER
:
12835 hasMenuItem
= false;
12837 case GOSSIP_OPTION_VENDOR
:
12839 VendorItemData
const* vItems
= pCreature
->GetVendorItems();
12840 VendorItemData
const* tItems
= pCreature
->GetVendorTemplateItems();
12841 if ((!vItems
|| vItems
->Empty()) && (!tItems
|| tItems
->Empty()))
12843 sLog
.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.", pCreature
->GetGUIDLow(), pCreature
->GetEntry());
12844 hasMenuItem
= false;
12848 case GOSSIP_OPTION_TRAINER
:
12849 // pet trainers not have spells in fact now
12850 /* FIXME: gossip menu with single unlearn pet talents option not show by some reason
12851 if (pCreature->GetCreatureInfo()->trainer_type == TRAINER_TYPE_PETS)
12852 hasMenuItem = false;
12854 if (!pCreature
->IsTrainerOf(this, false))
12855 hasMenuItem
= false;
12857 case GOSSIP_OPTION_UNLEARNTALENTS
:
12858 if (!pCreature
->CanTrainAndResetTalentsOf(this))
12859 hasMenuItem
= false;
12861 case GOSSIP_OPTION_UNLEARNPETSKILLS
:
12862 if (pCreature
->GetCreatureInfo()->trainer_type
!= TRAINER_TYPE_PETS
|| pCreature
->GetCreatureInfo()->trainer_class
!= CLASS_HUNTER
)
12863 hasMenuItem
= false;
12864 else if (Pet
* pet
= GetPet())
12866 if (pet
->getPetType() != HUNTER_PET
|| pet
->m_spells
.size() <= 1)
12867 hasMenuItem
= false;
12870 hasMenuItem
= false;
12872 case GOSSIP_OPTION_TAXIVENDOR
:
12873 if (GetSession()->SendLearnNewTaxiNode(pCreature
))
12876 case GOSSIP_OPTION_BATTLEFIELD
:
12877 if (!pCreature
->CanInteractWithBattleMaster(this, false))
12878 hasMenuItem
= false;
12880 case GOSSIP_OPTION_STABLEPET
:
12881 if (getClass() != CLASS_HUNTER
)
12882 hasMenuItem
= false;
12884 case GOSSIP_OPTION_SPIRITGUIDE
:
12885 case GOSSIP_OPTION_INNKEEPER
:
12886 case GOSSIP_OPTION_BANKER
:
12887 case GOSSIP_OPTION_PETITIONER
:
12888 case GOSSIP_OPTION_TABARDDESIGNER
:
12889 case GOSSIP_OPTION_AUCTIONEER
:
12890 case GOSSIP_OPTION_MAILBOX
:
12891 break; // no checks
12893 sLog
.outErrorDb("Creature entry %u have unknown gossip option %u for menu %u", pCreature
->GetEntry(), itr
->second
.option_id
, itr
->second
.menu_id
);
12894 hasMenuItem
= false;
12898 else if (pSource
->GetTypeId() == TYPEID_GAMEOBJECT
)
12900 GameObject
* pGo
= (GameObject
*)pSource
;
12902 switch (itr
->second
.option_id
)
12904 case GOSSIP_OPTION_QUESTGIVER
:
12905 hasMenuItem
= false;
12907 case GOSSIP_OPTION_GOSSIP
:
12908 if (pGo
->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER
&& pGo
->GetGoType() != GAMEOBJECT_TYPE_GOOBER
)
12909 hasMenuItem
= false;
12912 hasMenuItem
= false;
12919 std::string strOptionText
= itr
->second
.option_text
;
12920 std::string strBoxText
= itr
->second
.box_text
;
12922 int loc_idx
= GetSession()->GetSessionDbLocaleIndex();
12926 uint32 idxEntry
= MAKE_PAIR32(menuId
, itr
->second
.id
);
12928 if (GossipMenuItemsLocale
const* no
= sObjectMgr
.GetGossipMenuItemsLocale(idxEntry
))
12930 if (no
->OptionText
.size() > (size_t)loc_idx
&& !no
->OptionText
[loc_idx
].empty())
12931 strOptionText
= no
->OptionText
[loc_idx
];
12933 if (no
->BoxText
.size() > (size_t)loc_idx
&& !no
->BoxText
[loc_idx
].empty())
12934 strBoxText
= no
->BoxText
[loc_idx
];
12938 pMenu
->GetGossipMenu().AddMenuItem(itr
->second
.option_icon
, strOptionText
, 0, itr
->second
.option_id
, strBoxText
, itr
->second
.box_money
, itr
->second
.box_coded
);
12939 pMenu
->GetGossipMenu().AddGossipMenuItemData(itr
->second
.action_menu_id
, itr
->second
.action_poi_id
, itr
->second
.action_script_id
);
12944 PrepareQuestMenu(pSource
->GetObjectGuid());
12946 if (canTalkToCredit
)
12948 if (pSource
->HasFlag(UNIT_NPC_FLAGS
, UNIT_NPC_FLAG_GOSSIP
) && !(((Creature
*)pSource
)->GetCreatureInfo()->flags_extra
& CREATURE_FLAG_EXTRA_NO_TALKTO_CREDIT
))
12949 TalkedToCreature(pSource
->GetEntry(), pSource
->GetObjectGuid());
12952 // some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
12953 /*if (pMenu->Empty())
12955 if (pCreature->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER))
12957 // output error message if need
12958 pCreature->IsTrainerOf(this, true);
12961 if (pCreature->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_BATTLEMASTER))
12963 // output error message if need
12964 pCreature->CanInteractWithBattleMaster(this, true);
12969 void Player::SendPreparedGossip(WorldObject
* pSource
)
12974 if (pSource
->GetTypeId() == TYPEID_UNIT
)
12976 // in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
12977 if (!((Creature
*)pSource
)->HasFlag(UNIT_NPC_FLAGS
, UNIT_NPC_FLAG_GOSSIP
) && !PlayerTalkClass
->GetQuestMenu().Empty())
12979 SendPreparedQuest(pSource
->GetObjectGuid());
12983 else if (pSource
->GetTypeId() == TYPEID_GAMEOBJECT
)
12985 // probably need to find a better way here
12986 if (!PlayerTalkClass
->GetGossipMenu().GetMenuId() && !PlayerTalkClass
->GetQuestMenu().Empty())
12988 SendPreparedQuest(pSource
->GetObjectGuid());
12993 // in case non empty gossip menu (that not included quests list size) show it
12994 // (quest entries from quest menu will be included in list)
12996 uint32 textId
= GetGossipTextId(pSource
);
12998 if (uint32 menuId
= PlayerTalkClass
->GetGossipMenu().GetMenuId())
12999 textId
= GetGossipTextId(menuId
, pSource
);
13001 PlayerTalkClass
->SendGossipMenu(textId
, pSource
->GetObjectGuid());
13004 void Player::OnGossipSelect(WorldObject
* pSource
, uint32 gossipListId
, uint32 menuId
)
13006 GossipMenu
& gossipmenu
= PlayerTalkClass
->GetGossipMenu();
13008 if (gossipListId
>= gossipmenu
.MenuItemCount())
13011 // if not same, then something funky is going on
13012 if (menuId
!= gossipmenu
.GetMenuId())
13015 GossipMenuItem
const& menu_item
= gossipmenu
.GetItem(gossipListId
);
13017 uint32 gossipOptionId
= menu_item
.m_gOptionId
;
13018 ObjectGuid guid
= pSource
->GetObjectGuid();
13019 uint32 moneyTake
= menu_item
.m_gBoxMoney
;
13021 // if this function called and player have money for pay MoneyTake or cheating, proccess both cases
13024 if (GetMoney() >= moneyTake
)
13025 ModifyMoney(-int64(moneyTake
));
13027 return; // cheating
13030 if (pSource
->GetTypeId() == TYPEID_GAMEOBJECT
)
13032 if (gossipOptionId
> GOSSIP_OPTION_QUESTGIVER
)
13034 sLog
.outError("Player guid %u request invalid gossip option for GameObject entry %u", GetGUIDLow(), pSource
->GetEntry());
13039 GossipMenuItemData pMenuData
= gossipmenu
.GetItemData(gossipListId
);
13041 switch (gossipOptionId
)
13043 case GOSSIP_OPTION_GOSSIP
:
13045 if (pMenuData
.m_gAction_poi
)
13046 PlayerTalkClass
->SendPointOfInterest(pMenuData
.m_gAction_poi
);
13048 // send new menu || close gossip || stay at current menu
13049 if (pMenuData
.m_gAction_menu
> 0)
13051 PrepareGossipMenu(pSource
, uint32(pMenuData
.m_gAction_menu
));
13052 SendPreparedGossip(pSource
);
13054 else if (pMenuData
.m_gAction_menu
< 0)
13056 PlayerTalkClass
->CloseGossip();
13057 TalkedToCreature(pSource
->GetEntry(), pSource
->GetObjectGuid());
13062 case GOSSIP_OPTION_SPIRITHEALER
:
13064 ((Creature
*)pSource
)->CastSpell(((Creature
*)pSource
), 17251, true, NULL
, NULL
, GetObjectGuid());
13066 case GOSSIP_OPTION_QUESTGIVER
:
13067 PrepareQuestMenu(guid
);
13068 SendPreparedQuest(guid
);
13070 case GOSSIP_OPTION_VENDOR
:
13071 case GOSSIP_OPTION_ARMORER
:
13072 GetSession()->SendListInventory(guid
);
13074 case GOSSIP_OPTION_STABLEPET
:
13075 GetSession()->SendStablePet(guid
);
13077 case GOSSIP_OPTION_TRAINER
:
13078 GetSession()->SendTrainerList(guid
);
13080 case GOSSIP_OPTION_UNLEARNTALENTS
:
13081 PlayerTalkClass
->CloseGossip();
13082 SendTalentWipeConfirm(guid
);
13084 case GOSSIP_OPTION_UNLEARNPETSKILLS
:
13085 PlayerTalkClass
->CloseGossip();
13086 SendPetSkillWipeConfirm();
13088 case GOSSIP_OPTION_TAXIVENDOR
:
13089 GetSession()->SendTaxiMenu(((Creature
*)pSource
));
13091 case GOSSIP_OPTION_INNKEEPER
:
13092 PlayerTalkClass
->CloseGossip();
13093 SetBindPoint(guid
);
13095 case GOSSIP_OPTION_BANKER
:
13096 GetSession()->SendShowBank(guid
);
13098 case GOSSIP_OPTION_PETITIONER
:
13099 PlayerTalkClass
->CloseGossip();
13100 GetSession()->SendPetitionShowList(guid
);
13102 case GOSSIP_OPTION_TABARDDESIGNER
:
13103 PlayerTalkClass
->CloseGossip();
13104 GetSession()->SendTabardVendorActivate(guid
);
13106 case GOSSIP_OPTION_AUCTIONEER
:
13107 GetSession()->SendAuctionHello(((Creature
*)pSource
));
13109 case GOSSIP_OPTION_MAILBOX
:
13110 PlayerTalkClass
->CloseGossip();
13111 GetSession()->SendShowMailBox(guid
);
13113 case GOSSIP_OPTION_SPIRITGUIDE
:
13114 PrepareGossipMenu(pSource
);
13115 SendPreparedGossip(pSource
);
13117 case GOSSIP_OPTION_BATTLEFIELD
:
13119 BattleGroundTypeId bgTypeId
= sBattleGroundMgr
.GetBattleMasterBG(pSource
->GetEntry());
13121 if (bgTypeId
== BATTLEGROUND_TYPE_NONE
)
13123 sLog
.outError("a user (guid %u) requested battlegroundlist from a npc who is no battlemaster", GetGUIDLow());
13127 GetSession()->SendBattlegGroundList(guid
, bgTypeId
);
13132 if (pMenuData
.m_gAction_script
)
13134 if (pSource
->GetTypeId() == TYPEID_UNIT
)
13135 GetMap()->ScriptsStart(sGossipScripts
, pMenuData
.m_gAction_script
, pSource
, this);
13136 else if (pSource
->GetTypeId() == TYPEID_GAMEOBJECT
)
13137 GetMap()->ScriptsStart(sGossipScripts
, pMenuData
.m_gAction_script
, this, pSource
);
13141 uint32
Player::GetGossipTextId(WorldObject
* pSource
)
13143 if (!pSource
|| pSource
->GetTypeId() != TYPEID_UNIT
)
13144 return DEFAULT_GOSSIP_MESSAGE
;
13146 if (uint32 pos
= sObjectMgr
.GetNpcGossip(((Creature
*)pSource
)->GetGUIDLow()))
13149 return DEFAULT_GOSSIP_MESSAGE
;
13152 uint32
Player::GetGossipTextId(uint32 menuId
, WorldObject
* pSource
)
13154 uint32 textId
= DEFAULT_GOSSIP_MESSAGE
;
13159 uint32 scriptId
= 0;
13160 uint32 lastConditionId
= 0;
13162 GossipMenusMapBounds pMenuBounds
= sObjectMgr
.GetGossipMenusMapBounds(menuId
);
13163 for (GossipMenusMap::const_iterator itr
= pMenuBounds
.first
; itr
!= pMenuBounds
.second
; ++itr
)
13165 // Take the text that has the highest conditionId of all fitting
13166 // No condition and no text with condition found OR higher and fitting condition found
13167 if ((!itr
->second
.conditionId
&& !lastConditionId
) ||
13168 (itr
->second
.conditionId
> lastConditionId
&& sObjectMgr
.IsPlayerMeetToCondition(this, itr
->second
.conditionId
)))
13170 lastConditionId
= itr
->second
.conditionId
;
13171 textId
= itr
->second
.text_id
;
13172 scriptId
= itr
->second
.script_id
;
13176 // Start related script
13178 GetMap()->ScriptsStart(sGossipScripts
, scriptId
, this, pSource
);
13183 uint32
Player::GetDefaultGossipMenuForSource(WorldObject
* pSource
)
13185 if (pSource
->GetTypeId() == TYPEID_UNIT
)
13186 return ((Creature
*)pSource
)->GetCreatureInfo()->GossipMenuId
;
13187 else if (pSource
->GetTypeId() == TYPEID_GAMEOBJECT
)
13188 return((GameObject
*)pSource
)->GetGOInfo()->GetGossipMenuId();
13193 /*********************************************************/
13194 /*** QUEST SYSTEM ***/
13195 /*********************************************************/
13197 void Player::PrepareQuestMenu(ObjectGuid guid
)
13199 QuestRelationsMapBounds rbounds
;
13200 QuestRelationsMapBounds irbounds
;
13202 // pets also can have quests
13203 if (Creature
* pCreature
= GetMap()->GetAnyTypeCreature(guid
))
13205 rbounds
= sObjectMgr
.GetCreatureQuestRelationsMapBounds(pCreature
->GetEntry());
13206 irbounds
= sObjectMgr
.GetCreatureQuestInvolvedRelationsMapBounds(pCreature
->GetEntry());
13210 // we should obtain map pointer from GetMap() in 99% of cases. Special case
13211 // only for quests which cast teleport spells on player
13212 Map
* _map
= IsInWorld() ? GetMap() : sMapMgr
.FindMap(GetMapId(), GetInstanceId());
13213 MANGOS_ASSERT(_map
);
13215 if (GameObject
* pGameObject
= _map
->GetGameObject(guid
))
13217 rbounds
= sObjectMgr
.GetGOQuestRelationsMapBounds(pGameObject
->GetEntry());
13218 irbounds
= sObjectMgr
.GetGOQuestInvolvedRelationsMapBounds(pGameObject
->GetEntry());
13224 QuestMenu
& qm
= PlayerTalkClass
->GetQuestMenu();
13227 for (QuestRelationsMap::const_iterator itr
= irbounds
.first
; itr
!= irbounds
.second
; ++itr
)
13229 uint32 quest_id
= itr
->second
;
13231 Quest
const* pQuest
= sObjectMgr
.GetQuestTemplate(quest_id
);
13233 if (!pQuest
|| !pQuest
->IsActive())
13236 QuestStatus status
= GetQuestStatus(quest_id
);
13238 if (status
== QUEST_STATUS_COMPLETE
&& !GetQuestRewardStatus(quest_id
))
13239 qm
.AddMenuItem(quest_id
, 4);
13240 else if (status
== QUEST_STATUS_INCOMPLETE
)
13241 qm
.AddMenuItem(quest_id
, 4);
13242 else if (status
== QUEST_STATUS_AVAILABLE
)
13243 qm
.AddMenuItem(quest_id
, 2);
13246 for (QuestRelationsMap::const_iterator itr
= rbounds
.first
; itr
!= rbounds
.second
; ++itr
)
13248 uint32 quest_id
= itr
->second
;
13250 Quest
const* pQuest
= sObjectMgr
.GetQuestTemplate(quest_id
);
13252 if (!pQuest
|| !pQuest
->IsActive())
13255 QuestStatus status
= GetQuestStatus(quest_id
);
13257 if (pQuest
->IsAutoComplete() && CanTakeQuest(pQuest
, false))
13258 qm
.AddMenuItem(quest_id
, 4);
13259 else if (status
== QUEST_STATUS_NONE
&& CanTakeQuest(pQuest
, false))
13260 qm
.AddMenuItem(quest_id
, 2);
13264 void Player::SendPreparedQuest(ObjectGuid guid
)
13266 QuestMenu
& questMenu
= PlayerTalkClass
->GetQuestMenu();
13268 if (questMenu
.Empty())
13271 QuestMenuItem
const& qmi0
= questMenu
.GetItem(0);
13273 uint32 icon
= qmi0
.m_qIcon
;
13275 // single element case
13276 if (questMenu
.MenuItemCount() == 1)
13278 // Auto open -- maybe also should verify there is no greeting
13279 uint32 quest_id
= qmi0
.m_qId
;
13280 Quest
const* pQuest
= sObjectMgr
.GetQuestTemplate(quest_id
);
13284 if (icon
== 4 && !GetQuestRewardStatus(quest_id
))
13285 PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, CanRewardQuest(pQuest
, false), true);
13286 else if (icon
== 4)
13287 PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, CanRewardQuest(pQuest
, false), true);
13288 // Send completable on repeatable and autoCompletable quest if player don't have quest
13289 // TODO: verify if check for !pQuest->IsDaily() is really correct (possibly not)
13290 else if (pQuest
->IsAutoComplete() && pQuest
->IsRepeatable() && !pQuest
->IsDailyOrWeekly())
13291 PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, CanCompleteRepeatableQuest(pQuest
), true);
13293 PlayerTalkClass
->SendQuestGiverQuestDetails(pQuest
, guid
, true);
13296 // multiply entries
13302 std::string title
= "";
13304 // need pet case for some quests
13305 if (Creature
* pCreature
= GetMap()->GetAnyTypeCreature(guid
))
13307 uint32 textid
= GetGossipTextId(pCreature
);
13309 GossipText
const* gossiptext
= sObjectMgr
.GetGossipText(textid
);
13312 qe
._Delay
= 0; // TEXTEMOTE_MESSAGE; // zyg: player emote
13313 qe
._Emote
= 0; // TEXTEMOTE_HELLO; // zyg: NPC emote
13318 qe
= gossiptext
->Options
[0].Emotes
[0];
13320 int loc_idx
= GetSession()->GetSessionDbLocaleIndex();
13322 std::string title0
= gossiptext
->Options
[0].Text_0
;
13323 std::string title1
= gossiptext
->Options
[0].Text_1
;
13324 sObjectMgr
.GetNpcTextLocaleStrings0(textid
, loc_idx
, &title0
, &title1
);
13326 title
= !title0
.empty() ? title0
: title1
;
13329 PlayerTalkClass
->SendQuestGiverQuestList(qe
, title
, guid
);
13333 bool Player::IsActiveQuest(uint32 quest_id
) const
13335 QuestStatusMap::const_iterator itr
= mQuestStatus
.find(quest_id
);
13337 return itr
!= mQuestStatus
.end() && itr
->second
.m_status
!= QUEST_STATUS_NONE
;
13340 bool Player::IsCurrentQuest(uint32 quest_id
, uint8 completed_or_not
) const
13342 QuestStatusMap::const_iterator itr
= mQuestStatus
.find(quest_id
);
13343 if (itr
== mQuestStatus
.end())
13346 switch (completed_or_not
)
13349 return itr
->second
.m_status
== QUEST_STATUS_INCOMPLETE
;
13351 return itr
->second
.m_status
== QUEST_STATUS_COMPLETE
&& !itr
->second
.m_rewarded
;
13353 return itr
->second
.m_status
== QUEST_STATUS_INCOMPLETE
|| (itr
->second
.m_status
== QUEST_STATUS_COMPLETE
&& !itr
->second
.m_rewarded
);
13357 Quest
const* Player::GetNextQuest(ObjectGuid guid
, Quest
const* pQuest
)
13359 QuestRelationsMapBounds rbounds
;
13361 if (Creature
* pCreature
= GetMap()->GetAnyTypeCreature(guid
))
13363 rbounds
= sObjectMgr
.GetCreatureQuestRelationsMapBounds(pCreature
->GetEntry());
13367 // we should obtain map pointer from GetMap() in 99% of cases. Special case
13368 // only for quests which cast teleport spells on player
13369 Map
* _map
= IsInWorld() ? GetMap() : sMapMgr
.FindMap(GetMapId(), GetInstanceId());
13370 MANGOS_ASSERT(_map
);
13372 if (GameObject
* pGameObject
= _map
->GetGameObject(guid
))
13374 rbounds
= sObjectMgr
.GetGOQuestRelationsMapBounds(pGameObject
->GetEntry());
13380 uint32 nextQuestID
= pQuest
->GetNextQuestInChain();
13381 for (QuestRelationsMap::const_iterator itr
= rbounds
.first
; itr
!= rbounds
.second
; ++itr
)
13383 if (itr
->second
== nextQuestID
)
13384 return sObjectMgr
.GetQuestTemplate(nextQuestID
);
13390 bool Player::CanSeeStartQuest(Quest
const* pQuest
) const
13392 if (SatisfyQuestClass(pQuest
, false) && SatisfyQuestRace(pQuest
, false) && SatisfyQuestSkill(pQuest
, false) &&
13393 SatisfyQuestExclusiveGroup(pQuest
, false) && SatisfyQuestReputation(pQuest
, false) &&
13394 SatisfyQuestPreviousQuest(pQuest
, false) && SatisfyQuestNextChain(pQuest
, false) &&
13395 SatisfyQuestPrevChain(pQuest
, false) && SatisfyQuestDay(pQuest
, false) && SatisfyQuestWeek(pQuest
, false) &&
13396 SatisfyQuestMonth(pQuest
, false) &&
13397 pQuest
->IsActive())
13399 return int32(getLevel()) + sWorld
.getConfig(CONFIG_INT32_QUEST_HIGH_LEVEL_HIDE_DIFF
) >= int32(pQuest
->GetMinLevel());
13405 bool Player::CanTakeQuest(Quest
const* pQuest
, bool msg
) const
13407 return SatisfyQuestStatus(pQuest
, msg
) && SatisfyQuestExclusiveGroup(pQuest
, msg
) &&
13408 SatisfyQuestClass(pQuest
, msg
) && SatisfyQuestRace(pQuest
, msg
) && SatisfyQuestLevel(pQuest
, msg
) &&
13409 SatisfyQuestSkill(pQuest
, msg
) && SatisfyQuestReputation(pQuest
, msg
) &&
13410 SatisfyQuestPreviousQuest(pQuest
, msg
) && SatisfyQuestTimed(pQuest
, msg
) &&
13411 SatisfyQuestNextChain(pQuest
, msg
) && SatisfyQuestPrevChain(pQuest
, msg
) &&
13412 SatisfyQuestDay(pQuest
, msg
) && SatisfyQuestWeek(pQuest
, msg
) && SatisfyQuestMonth(pQuest
, msg
) &&
13413 pQuest
->IsActive();
13416 bool Player::CanAddQuest(Quest
const* pQuest
, bool msg
) const
13418 if (!SatisfyQuestLog(msg
))
13421 if (!CanGiveQuestSourceItemIfNeed(pQuest
))
13427 bool Player::CanCompleteQuest(uint32 quest_id
) const
13432 QuestStatusMap::const_iterator q_itr
= mQuestStatus
.find(quest_id
);
13434 // some quests can be auto taken and auto completed in one step
13435 QuestStatus status
= q_itr
!= mQuestStatus
.end() ? q_itr
->second
.m_status
: QUEST_STATUS_NONE
;
13437 if (status
== QUEST_STATUS_COMPLETE
)
13438 return false; // not allow re-complete quest
13440 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(quest_id
);
13445 // only used for "flag" quests and not real in-game quests
13446 if (qInfo
->HasQuestFlag(QUEST_FLAGS_AUTO_REWARDED
))
13448 // a few checks, not all "satisfy" is needed
13449 if (SatisfyQuestPreviousQuest(qInfo
, false) && SatisfyQuestLevel(qInfo
, false) && SatisfyQuestSpell(qInfo
, false) &&
13450 SatisfyQuestSkill(qInfo
, false) && SatisfyQuestRace(qInfo
, false) && SatisfyQuestClass(qInfo
, false))
13456 // auto complete quest
13457 if (qInfo
->IsAutoComplete() && CanTakeQuest(qInfo
, false))
13460 if (status
!= QUEST_STATUS_INCOMPLETE
)
13463 // incomplete quest have status data
13464 QuestStatusData
const& q_status
= q_itr
->second
;
13466 if (qInfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
))
13468 for (int i
= 0; i
< QUEST_ITEM_OBJECTIVES_COUNT
; ++i
)
13470 if (qInfo
->ReqItemCount
[i
] != 0 && q_status
.m_itemcount
[i
] < qInfo
->ReqItemCount
[i
])
13474 for (int i
= 0; i
< QUEST_REQUIRED_CURRENCY_COUNT
; ++i
)
13476 if (qInfo
->ReqCurrencyId
[i
] && !HasCurrencyCount(qInfo
->ReqCurrencyId
[i
], int32(qInfo
->ReqCurrencyCount
[i
] * GetCurrencyPrecision(qInfo
->ReqCurrencyId
[i
]))))
13481 if (qInfo
->HasSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAG_KILL_OR_CAST
| QUEST_SPECIAL_FLAG_SPEAKTO
)))
13483 for (int i
= 0; i
< QUEST_OBJECTIVES_COUNT
; ++i
)
13485 if (qInfo
->ReqCreatureOrGOId
[i
] == 0)
13488 if (qInfo
->ReqCreatureOrGOCount
[i
] != 0 && q_status
.m_creatureOrGOcount
[i
] < qInfo
->ReqCreatureOrGOCount
[i
])
13493 if (qInfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT
) && !q_status
.m_explored
)
13496 if (qInfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED
) && q_status
.m_timer
== 0)
13499 if (qInfo
->GetRewOrReqMoney() < 0)
13501 if (GetMoney() < uint64(-qInfo
->GetRewOrReqMoney()))
13505 uint32 repFacId
= qInfo
->GetRepObjectiveFaction();
13506 if (repFacId
&& GetReputationMgr().GetReputation(repFacId
) < qInfo
->GetRepObjectiveValue())
13509 if (uint32 spell
= qInfo
->GetReqSpellLearned())
13510 if (!HasSpell(spell
))
13516 bool Player::CanCompleteRepeatableQuest(Quest
const* pQuest
) const
13518 // Solve problem that player don't have the quest and try complete it.
13519 // if repeatable she must be able to complete event if player don't have it.
13520 // Seem that all repeatable quest are DELIVER Flag so, no need to add more.
13521 if (!CanTakeQuest(pQuest
, false))
13524 if (pQuest
->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
))
13526 for (int i
= 0; i
< QUEST_ITEM_OBJECTIVES_COUNT
; ++i
)
13527 if (pQuest
->ReqItemId
[i
] && pQuest
->ReqItemCount
[i
] && !HasItemCount(pQuest
->ReqItemId
[i
], pQuest
->ReqItemCount
[i
]))
13530 for (int i
= 0; i
< QUEST_REQUIRED_CURRENCY_COUNT
; ++i
)
13532 if (pQuest
->ReqCurrencyId
[i
] && !HasCurrencyCount(pQuest
->ReqCurrencyId
[i
], int32(pQuest
->ReqCurrencyCount
[i
] * GetCurrencyPrecision(pQuest
->ReqCurrencyId
[i
]))))
13537 if (!CanRewardQuest(pQuest
, false))
13543 bool Player::CanRewardQuest(Quest
const* pQuest
, bool msg
) const
13545 // not auto complete quest and not completed quest (only cheating case, then ignore without message)
13546 if (!pQuest
->IsAutoComplete() && GetQuestStatus(pQuest
->GetQuestId()) != QUEST_STATUS_COMPLETE
)
13549 // daily quest can't be rewarded (25 daily quest already completed)
13550 if (!SatisfyQuestDay(pQuest
, true) || !SatisfyQuestWeek(pQuest
, true) || !SatisfyQuestMonth(pQuest
, true))
13553 // rewarded and not repeatable quest (only cheating case, then ignore without message)
13554 if (GetQuestRewardStatus(pQuest
->GetQuestId()))
13557 if (pQuest
->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
))
13559 // prevent receive reward with quest items in bank
13560 for (int i
= 0; i
< QUEST_ITEM_OBJECTIVES_COUNT
; ++i
)
13562 if (pQuest
->ReqItemCount
[i
] != 0 &&
13563 GetItemCount(pQuest
->ReqItemId
[i
]) < pQuest
->ReqItemCount
[i
])
13566 SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
, pQuest
->ReqItemId
[i
]);
13572 // prevent receive reward with low currency
13573 for (int i
= 0; i
< QUEST_REQUIRED_CURRENCY_COUNT
; ++i
)
13575 if (pQuest
->ReqCurrencyId
[i
] &&
13576 !HasCurrencyCount(pQuest
->ReqCurrencyId
[i
], int32(pQuest
->ReqCurrencyCount
[i
] * GetCurrencyPrecision(pQuest
->ReqCurrencyId
[i
]))))
13579 SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS
, NULL
);
13586 // prevent receive reward with low money and GetRewOrReqMoney() < 0
13587 if (pQuest
->GetRewOrReqMoney() < 0 && GetMoney() < uint64(-pQuest
->GetRewOrReqMoney()))
13590 if (uint32 spell
= pQuest
->GetReqSpellLearned())
13591 if (!HasSpell(spell
))
13597 bool Player::CanRewardQuest(Quest
const* pQuest
, uint32 reward
, bool msg
) const
13599 // prevent receive reward with quest items in bank or for not completed quest
13600 if (!CanRewardQuest(pQuest
, msg
))
13603 if (pQuest
->GetRewChoiceItemsCount() > 0)
13605 if (pQuest
->RewChoiceItemId
[reward
])
13607 ItemPosCountVec dest
;
13608 InventoryResult res
= CanStoreNewItem(NULL_BAG
, NULL_SLOT
, dest
, pQuest
->RewChoiceItemId
[reward
], pQuest
->RewChoiceItemCount
[reward
]);
13609 if (res
!= EQUIP_ERR_OK
)
13611 SendEquipError(res
, NULL
, NULL
, pQuest
->RewChoiceItemId
[reward
]);
13617 if (pQuest
->GetRewItemsCount() > 0)
13619 for (uint32 i
= 0; i
< pQuest
->GetRewItemsCount(); ++i
)
13621 if (pQuest
->RewItemId
[i
])
13623 ItemPosCountVec dest
;
13624 InventoryResult res
= CanStoreNewItem(NULL_BAG
, NULL_SLOT
, dest
, pQuest
->RewItemId
[i
], pQuest
->RewItemCount
[i
]);
13625 if (res
!= EQUIP_ERR_OK
)
13627 SendEquipError(res
, NULL
, NULL
);
13637 void Player::SendPetTameFailure(PetTameFailureReason reason
)
13639 WorldPacket
data(SMSG_PET_TAME_FAILURE
, 1);
13640 data
<< uint8(reason
);
13641 GetSession()->SendPacket(&data
);
13644 void Player::AddQuest(Quest
const* pQuest
, Object
* questGiver
)
13646 uint16 log_slot
= FindQuestSlot(0);
13647 MANGOS_ASSERT(log_slot
< MAX_QUEST_LOG_SIZE
);
13649 uint32 quest_id
= pQuest
->GetQuestId();
13651 // if not exist then created with set uState==NEW and rewarded=false
13652 QuestStatusData
& questStatusData
= mQuestStatus
[quest_id
];
13654 // check for repeatable quests status reset
13655 questStatusData
.m_status
= QUEST_STATUS_INCOMPLETE
;
13656 questStatusData
.m_explored
= false;
13658 if (pQuest
->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
))
13660 for (int i
= 0; i
< QUEST_ITEM_OBJECTIVES_COUNT
; ++i
)
13661 questStatusData
.m_itemcount
[i
] = 0;
13664 if (pQuest
->HasSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAG_KILL_OR_CAST
| QUEST_SPECIAL_FLAG_SPEAKTO
)))
13666 for (int i
= 0; i
< QUEST_OBJECTIVES_COUNT
; ++i
)
13667 questStatusData
.m_creatureOrGOcount
[i
] = 0;
13670 if (pQuest
->GetRepObjectiveFaction())
13671 if (FactionEntry
const* factionEntry
= sFactionStore
.LookupEntry(pQuest
->GetRepObjectiveFaction()))
13672 GetReputationMgr().SetVisible(factionEntry
);
13675 if (pQuest
->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED
))
13677 uint32 limittime
= pQuest
->GetLimitTime();
13679 // shared timed quest
13680 if (questGiver
&& questGiver
->GetTypeId() == TYPEID_PLAYER
)
13681 limittime
= ((Player
*)questGiver
)->getQuestStatusMap()[quest_id
].m_timer
/ IN_MILLISECONDS
;
13683 AddTimedQuest(quest_id
);
13684 questStatusData
.m_timer
= limittime
* IN_MILLISECONDS
;
13685 qtime
= static_cast<uint32
>(time(NULL
)) + limittime
;
13688 questStatusData
.m_timer
= 0;
13690 SetQuestSlot(log_slot
, quest_id
, qtime
);
13692 if (questStatusData
.uState
!= QUEST_NEW
)
13693 questStatusData
.uState
= QUEST_CHANGED
;
13695 // quest accept scripts
13698 switch (questGiver
->GetTypeId())
13701 sScriptMgr
.OnQuestAccept(this, (Creature
*)questGiver
, pQuest
);
13704 case TYPEID_CONTAINER
:
13705 sScriptMgr
.OnQuestAccept(this, (Item
*)questGiver
, pQuest
);
13707 case TYPEID_GAMEOBJECT
:
13708 sScriptMgr
.OnQuestAccept(this, (GameObject
*)questGiver
, pQuest
);
13712 // starting initial DB quest script
13713 if (pQuest
->GetQuestStartScript() != 0)
13714 GetMap()->ScriptsStart(sQuestStartScripts
, pQuest
->GetQuestStartScript(), questGiver
, this);
13718 // remove start item if not need
13719 if (questGiver
&& questGiver
->isType(TYPEMASK_ITEM
))
13721 // destroy not required for quest finish quest starting item
13722 bool notRequiredItem
= true;
13723 for (int i
= 0; i
< QUEST_ITEM_OBJECTIVES_COUNT
; ++i
)
13725 if (pQuest
->ReqItemId
[i
] == questGiver
->GetEntry())
13727 notRequiredItem
= false;
13732 if (pQuest
->GetSrcItemId() == questGiver
->GetEntry())
13733 notRequiredItem
= false;
13735 if (notRequiredItem
)
13736 DestroyItem(((Item
*)questGiver
)->GetBagSlot(), ((Item
*)questGiver
)->GetSlot(), true);
13739 GiveQuestSourceItemIfNeed(pQuest
);
13741 AdjustQuestReqItemCount(pQuest
, questStatusData
);
13743 // Some spells applied at quest activation
13744 SpellAreaForQuestMapBounds saBounds
= sSpellMgr
.GetSpellAreaForQuestMapBounds(quest_id
, true);
13745 if (saBounds
.first
!= saBounds
.second
)
13748 GetZoneAndAreaId(zone
, area
);
13750 for (SpellAreaForAreaMap::const_iterator itr
= saBounds
.first
; itr
!= saBounds
.second
; ++itr
)
13751 if (itr
->second
->autocast
&& itr
->second
->IsFitToRequirements(this, zone
, area
))
13752 if (!HasAura(itr
->second
->spellId
, EFFECT_INDEX_0
))
13753 CastSpell(this, itr
->second
->spellId
, true);
13756 UpdateForQuestWorldObjects();
13759 void Player::CompleteQuest(uint32 quest_id
)
13763 SetQuestStatus(quest_id
, QUEST_STATUS_COMPLETE
);
13765 uint16 log_slot
= FindQuestSlot(quest_id
);
13766 if (log_slot
< MAX_QUEST_LOG_SIZE
)
13767 SetQuestSlotState(log_slot
, QUEST_STATE_COMPLETE
);
13769 if (Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(quest_id
))
13771 if (qInfo
->HasQuestFlag(QUEST_FLAGS_AUTO_REWARDED
))
13772 RewardQuest(qInfo
, 0, this, false);
13777 void Player::IncompleteQuest(uint32 quest_id
)
13781 SetQuestStatus(quest_id
, QUEST_STATUS_INCOMPLETE
);
13783 uint16 log_slot
= FindQuestSlot(quest_id
);
13784 if (log_slot
< MAX_QUEST_LOG_SIZE
)
13785 RemoveQuestSlotState(log_slot
, QUEST_STATE_COMPLETE
);
13789 void Player::RewardQuest(Quest
const* pQuest
, uint32 reward
, Object
* questGiver
, bool announce
)
13791 uint32 quest_id
= pQuest
->GetQuestId();
13793 for (int i
= 0; i
< QUEST_ITEM_OBJECTIVES_COUNT
; ++i
)
13795 if (pQuest
->ReqItemId
[i
])
13796 DestroyItemCount(pQuest
->ReqItemId
[i
], pQuest
->ReqItemCount
[i
], true);
13799 for (int i
= 0; i
< QUEST_SOURCE_ITEM_IDS_COUNT
; ++i
)
13801 if (pQuest
->ReqSourceId
[i
])
13803 ItemPrototype
const* iProto
= ObjectMgr::GetItemPrototype(pQuest
->ReqSourceId
[i
]);
13804 if (iProto
&& iProto
->Bonding
== BIND_QUEST_ITEM
)
13805 DestroyItemCount(pQuest
->ReqSourceId
[i
], pQuest
->ReqSourceCount
[i
], true, false, true);
13810 for (uint32 i
= 0; i
< QUEST_REQUIRED_CURRENCY_COUNT
; ++i
)
13812 if (pQuest
->ReqCurrencyId
[i
])
13813 ModifyCurrencyCount(pQuest
->ReqCurrencyId
[i
], -int32(pQuest
->ReqCurrencyCount
[i
] * GetCurrencyPrecision(pQuest
->ReqCurrencyId
[i
])));
13816 RemoveTimedQuest(quest_id
);
13818 if (BattleGround
* bg
= GetBattleGround())
13819 if (bg
->GetTypeID() == BATTLEGROUND_AV
)
13820 ((BattleGroundAV
*)bg
)->HandleQuestComplete(pQuest
->GetQuestId(), this);
13822 if (pQuest
->GetRewChoiceItemsCount() > 0)
13824 if (uint32 itemId
= pQuest
->RewChoiceItemId
[reward
])
13826 ItemPosCountVec dest
;
13827 if (CanStoreNewItem(NULL_BAG
, NULL_SLOT
, dest
, itemId
, pQuest
->RewChoiceItemCount
[reward
]) == EQUIP_ERR_OK
)
13829 Item
* item
= StoreNewItem(dest
, itemId
, true, Item::GenerateItemRandomPropertyId(itemId
));
13830 SendNewItem(item
, pQuest
->RewChoiceItemCount
[reward
], true, false);
13835 if (pQuest
->GetRewItemsCount() > 0)
13837 for (uint32 i
= 0; i
< pQuest
->GetRewItemsCount(); ++i
)
13839 if (uint32 itemId
= pQuest
->RewItemId
[i
])
13841 ItemPosCountVec dest
;
13842 if (CanStoreNewItem(NULL_BAG
, NULL_SLOT
, dest
, itemId
, pQuest
->RewItemCount
[i
]) == EQUIP_ERR_OK
)
13844 Item
* item
= StoreNewItem(dest
, itemId
, true, Item::GenerateItemRandomPropertyId(itemId
));
13845 SendNewItem(item
, pQuest
->RewItemCount
[i
], true, false);
13851 RewardReputation(pQuest
);
13853 uint16 log_slot
= FindQuestSlot(quest_id
);
13854 if (log_slot
< MAX_QUEST_LOG_SIZE
)
13855 SetQuestSlot(log_slot
, 0);
13857 QuestStatusData
& q_status
= mQuestStatus
[quest_id
];
13859 // Used for client inform but rewarded only in case not max level
13860 uint32 xp
= uint32(pQuest
->XPValue(this) * sWorld
.getConfig(CONFIG_FLOAT_RATE_XP_QUEST
));
13862 if (getLevel() < sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
13866 // Give player extra money (for max level already included in pQuest->GetRewMoneyMaxLevel())
13867 if (pQuest
->GetRewOrReqMoney() > 0)
13869 ModifyMoney(pQuest
->GetRewOrReqMoney());
13870 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD
, pQuest
->GetRewOrReqMoney());
13875 // reward money for max level already included in pQuest->GetRewMoneyMaxLevel()
13876 uint64 money
= uint32(pQuest
->GetRewMoneyMaxLevel() * sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY
));
13878 // reward money used if > xp replacement money
13879 if (pQuest
->GetRewOrReqMoney() > int64(money
))
13880 money
= pQuest
->GetRewOrReqMoney();
13882 ModifyMoney(money
);
13883 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD
, money
);
13887 if (pQuest
->GetRewOrReqMoney() < 0)
13888 ModifyMoney(pQuest
->GetRewOrReqMoney());
13891 for (uint32 i
= 0; i
< QUEST_REWARD_CURRENCY_COUNT
; ++i
)
13893 if (pQuest
->RewCurrencyId
[i
])
13894 ModifyCurrencyCount(pQuest
->RewCurrencyId
[i
], int32(pQuest
->RewCurrencyCount
[i
] * GetCurrencyPrecision(pQuest
->RewCurrencyId
[i
])));
13898 if (uint32 skill
= pQuest
->GetRewSkill())
13899 UpdateSkill(skill
, pQuest
->GetRewSkillValue());
13902 if (pQuest
->GetCharTitleId())
13904 if (CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(pQuest
->GetCharTitleId()))
13905 SetTitle(titleEntry
);
13908 if (pQuest
->GetBonusTalents())
13910 m_questRewardTalentCount
+= pQuest
->GetBonusTalents();
13911 InitTalentForLevel();
13914 // Send reward mail
13915 if (uint32 mail_template_id
= pQuest
->GetRewMailTemplateId())
13916 MailDraft(mail_template_id
).SendMailTo(this, questGiver
, MAIL_CHECK_MASK_HAS_BODY
, pQuest
->GetRewMailDelaySecs());
13918 if (pQuest
->IsDaily())
13920 SetDailyQuestStatus(quest_id
);
13921 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST
, 1);
13924 if (pQuest
->IsWeekly())
13925 SetWeeklyQuestStatus(quest_id
);
13927 if (pQuest
->IsMonthly())
13928 SetMonthlyQuestStatus(quest_id
);
13930 if (!pQuest
->IsRepeatable())
13931 SetQuestStatus(quest_id
, QUEST_STATUS_COMPLETE
);
13933 SetQuestStatus(quest_id
, QUEST_STATUS_NONE
);
13935 q_status
.m_rewarded
= true;
13936 if (q_status
.uState
!= QUEST_NEW
)
13937 q_status
.uState
= QUEST_CHANGED
;
13940 SendQuestReward(pQuest
, xp
, questGiver
);
13942 bool handled
= false;
13944 switch (questGiver
->GetTypeId())
13947 handled
= sScriptMgr
.OnQuestRewarded(this, (Creature
*)questGiver
, pQuest
);
13949 case TYPEID_GAMEOBJECT
:
13950 handled
= sScriptMgr
.OnQuestRewarded(this, (GameObject
*)questGiver
, pQuest
);
13954 if (!handled
&& pQuest
->GetQuestCompleteScript() != 0)
13955 GetMap()->ScriptsStart(sQuestEndScripts
, pQuest
->GetQuestCompleteScript(), questGiver
, this);
13957 // cast spells after mark quest complete (some spells have quest completed state reqyurements in spell_area data)
13958 if (pQuest
->GetRewSpellCast() > 0)
13959 CastSpell(this, pQuest
->GetRewSpellCast(), true);
13960 else if (pQuest
->GetRewSpell() > 0)
13961 CastSpell(this, pQuest
->GetRewSpell(), true);
13963 if (pQuest
->GetZoneOrSort() > 0)
13964 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE
, pQuest
->GetZoneOrSort());
13966 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT
);
13967 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
, pQuest
->GetQuestId());
13972 // remove auras from spells with quest reward state limitations
13973 SpellAreaForQuestMapBounds saEndBounds
= sSpellMgr
.GetSpellAreaForQuestEndMapBounds(quest_id
);
13974 if (saEndBounds
.first
!= saEndBounds
.second
)
13976 GetZoneAndAreaId(zone
, area
);
13978 for (SpellAreaForAreaMap::const_iterator itr
= saEndBounds
.first
; itr
!= saEndBounds
.second
; ++itr
)
13979 if (!itr
->second
->IsFitToRequirements(this, zone
, area
))
13980 RemoveAurasDueToSpell(itr
->second
->spellId
);
13983 // Some spells applied at quest reward
13984 SpellAreaForQuestMapBounds saBounds
= sSpellMgr
.GetSpellAreaForQuestMapBounds(quest_id
, false);
13985 if (saBounds
.first
!= saBounds
.second
)
13987 if (!zone
|| !area
)
13988 GetZoneAndAreaId(zone
, area
);
13990 for (SpellAreaForAreaMap::const_iterator itr
= saBounds
.first
; itr
!= saBounds
.second
; ++itr
)
13991 if (itr
->second
->autocast
&& itr
->second
->IsFitToRequirements(this, zone
, area
))
13992 if (!HasAura(itr
->second
->spellId
, EFFECT_INDEX_0
))
13993 CastSpell(this, itr
->second
->spellId
, true);
13997 void Player::FailQuest(uint32 questId
)
13999 if (Quest
const* pQuest
= sObjectMgr
.GetQuestTemplate(questId
))
14001 SetQuestStatus(questId
, QUEST_STATUS_FAILED
);
14003 uint16 log_slot
= FindQuestSlot(questId
);
14005 if (log_slot
< MAX_QUEST_LOG_SIZE
)
14007 SetQuestSlotTimer(log_slot
, 1);
14008 SetQuestSlotState(log_slot
, QUEST_STATE_FAIL
);
14011 if (pQuest
->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED
))
14013 QuestStatusData
& q_status
= mQuestStatus
[questId
];
14015 RemoveTimedQuest(questId
);
14016 q_status
.m_timer
= 0;
14018 SendQuestTimerFailed(questId
);
14021 SendQuestFailed(questId
);
14025 bool Player::SatisfyQuestSkill(Quest
const* qInfo
, bool msg
) const
14027 uint32 skill
= qInfo
->GetRequiredSkill();
14029 // skip 0 case RequiredSkill
14033 // check skill value
14034 if (GetSkillValue(skill
) < qInfo
->GetRequiredSkillValue())
14037 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14045 bool Player::SatisfyQuestSpell(Quest
const* qInfo
, bool msg
) const
14047 uint32 spell
= qInfo
->GetReqSpellLearned();
14049 // skip 0 case ReqSpellLearned
14054 if (!HasSpell(spell
))
14057 SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_SPELL
);
14065 bool Player::SatisfyQuestLevel(Quest
const* qInfo
, bool msg
) const
14067 if (getLevel() < qInfo
->GetMinLevel())
14070 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14078 bool Player::SatisfyQuestLog(bool msg
) const
14081 if (FindQuestSlot(0) < MAX_QUEST_LOG_SIZE
)
14086 WorldPacket
data(SMSG_QUESTLOG_FULL
, 0);
14087 GetSession()->SendPacket(&data
);
14088 DEBUG_LOG("WORLD: Sent SMSG_QUESTLOG_FULL");
14093 bool Player::SatisfyQuestPreviousQuest(Quest
const* qInfo
, bool msg
) const
14095 // No previous quest (might be first quest in a series)
14096 if (qInfo
->prevQuests
.empty())
14099 for (Quest::PrevQuests::const_iterator iter
= qInfo
->prevQuests
.begin(); iter
!= qInfo
->prevQuests
.end(); ++iter
)
14101 uint32 prevId
= abs(*iter
);
14103 QuestStatusMap::const_iterator i_prevstatus
= mQuestStatus
.find(prevId
);
14104 Quest
const* qPrevInfo
= sObjectMgr
.GetQuestTemplate(prevId
);
14106 if (qPrevInfo
&& i_prevstatus
!= mQuestStatus
.end())
14108 // If any of the positive previous quests completed, return true
14109 if (*iter
> 0 && i_prevstatus
->second
.m_rewarded
)
14111 // skip one-from-all exclusive group
14112 if (qPrevInfo
->GetExclusiveGroup() >= 0)
14115 // each-from-all exclusive group ( < 0)
14116 // can be start if only all quests in prev quest exclusive group completed and rewarded
14117 ExclusiveQuestGroupsMapBounds bounds
= sObjectMgr
.GetExclusiveQuestGroupsMapBounds(qPrevInfo
->GetExclusiveGroup());
14119 MANGOS_ASSERT(bounds
.first
!= bounds
.second
); // always must be found if qPrevInfo->ExclusiveGroup != 0
14121 for (ExclusiveQuestGroupsMap::const_iterator iter2
= bounds
.first
; iter2
!= bounds
.second
; ++iter2
)
14123 uint32 exclude_Id
= iter2
->second
;
14125 // skip checked quest id, only state of other quests in group is interesting
14126 if (exclude_Id
== prevId
)
14129 QuestStatusMap::const_iterator i_exstatus
= mQuestStatus
.find(exclude_Id
);
14131 // alternative quest from group also must be completed and rewarded(reported)
14132 if (i_exstatus
== mQuestStatus
.end() || !i_exstatus
->second
.m_rewarded
)
14135 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14142 // If any of the negative previous quests active, return true
14143 if (*iter
< 0 && IsCurrentQuest(prevId
))
14145 // skip one-from-all exclusive group
14146 if (qPrevInfo
->GetExclusiveGroup() >= 0)
14149 // each-from-all exclusive group ( < 0)
14150 // can be start if only all quests in prev quest exclusive group active
14151 ExclusiveQuestGroupsMapBounds bounds
= sObjectMgr
.GetExclusiveQuestGroupsMapBounds(qPrevInfo
->GetExclusiveGroup());
14153 MANGOS_ASSERT(bounds
.first
!= bounds
.second
); // always must be found if qPrevInfo->ExclusiveGroup != 0
14155 for (ExclusiveQuestGroupsMap::const_iterator iter2
= bounds
.first
; iter2
!= bounds
.second
; ++iter2
)
14157 uint32 exclude_Id
= iter2
->second
;
14159 // skip checked quest id, only state of other quests in group is interesting
14160 if (exclude_Id
== prevId
)
14163 // alternative quest from group also must be active
14164 if (!IsCurrentQuest(exclude_Id
))
14167 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14177 // Has only positive prev. quests in non-rewarded state
14178 // and negative prev. quests in non-active state
14180 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14185 bool Player::SatisfyQuestClass(Quest
const* qInfo
, bool msg
) const
14187 uint32 reqClass
= qInfo
->GetRequiredClasses();
14192 if ((reqClass
& getClassMask()) == 0)
14195 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14203 bool Player::SatisfyQuestRace(Quest
const* qInfo
, bool msg
) const
14205 uint32 reqraces
= qInfo
->GetRequiredRaces();
14210 if ((reqraces
& getRaceMask()) == 0)
14213 SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_WRONG_RACE
);
14221 bool Player::SatisfyQuestReputation(Quest
const* qInfo
, bool msg
) const
14223 uint32 fIdMin
= qInfo
->GetRequiredMinRepFaction(); // Min required rep
14224 if (fIdMin
&& GetReputationMgr().GetReputation(fIdMin
) < qInfo
->GetRequiredMinRepValue())
14227 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14232 uint32 fIdMax
= qInfo
->GetRequiredMaxRepFaction(); // Max required rep
14233 if (fIdMax
&& GetReputationMgr().GetReputation(fIdMax
) >= qInfo
->GetRequiredMaxRepValue())
14236 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14244 bool Player::SatisfyQuestStatus(Quest
const* qInfo
, bool msg
) const
14246 QuestStatusMap::const_iterator itr
= mQuestStatus
.find(qInfo
->GetQuestId());
14248 if (itr
!= mQuestStatus
.end() && itr
->second
.m_status
!= QUEST_STATUS_NONE
)
14251 SendCanTakeQuestResponse(INVALIDREASON_QUEST_ALREADY_ON
);
14259 bool Player::SatisfyQuestTimed(Quest
const* qInfo
, bool msg
) const
14261 if (!m_timedquests
.empty() && qInfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED
))
14264 SendCanTakeQuestResponse(INVALIDREASON_QUEST_ONLY_ONE_TIMED
);
14272 bool Player::SatisfyQuestExclusiveGroup(Quest
const* qInfo
, bool msg
) const
14274 // non positive exclusive group, if > 0 then can be start if any other quest in exclusive group already started/completed
14275 if (qInfo
->GetExclusiveGroup() <= 0)
14278 ExclusiveQuestGroupsMapBounds bounds
= sObjectMgr
.GetExclusiveQuestGroupsMapBounds(qInfo
->GetExclusiveGroup());
14280 MANGOS_ASSERT(bounds
.first
!= bounds
.second
); // must always be found if qInfo->ExclusiveGroup != 0
14282 for (ExclusiveQuestGroupsMap::const_iterator iter
= bounds
.first
; iter
!= bounds
.second
; ++iter
)
14284 uint32 exclude_Id
= iter
->second
;
14286 // skip checked quest id, only state of other quests in group is interesting
14287 if (exclude_Id
== qInfo
->GetQuestId())
14290 // not allow have daily quest if daily quest from exclusive group already recently completed
14291 Quest
const* Nquest
= sObjectMgr
.GetQuestTemplate(exclude_Id
);
14292 if (!SatisfyQuestDay(Nquest
, false) || !SatisfyQuestWeek(Nquest
, false))
14295 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14300 QuestStatusMap::const_iterator i_exstatus
= mQuestStatus
.find(exclude_Id
);
14302 // alternative quest already started or completed
14303 if (i_exstatus
!= mQuestStatus
.end() &&
14304 (i_exstatus
->second
.m_status
== QUEST_STATUS_COMPLETE
|| i_exstatus
->second
.m_status
== QUEST_STATUS_INCOMPLETE
))
14307 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14316 bool Player::SatisfyQuestNextChain(Quest
const* qInfo
, bool msg
) const
14318 if (!qInfo
->GetNextQuestInChain())
14321 // next quest in chain already started or completed
14322 QuestStatusMap::const_iterator itr
= mQuestStatus
.find(qInfo
->GetNextQuestInChain());
14323 if (itr
!= mQuestStatus
.end() &&
14324 (itr
->second
.m_status
== QUEST_STATUS_COMPLETE
|| itr
->second
.m_status
== QUEST_STATUS_INCOMPLETE
))
14327 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14332 // check for all quests further up the chain
14333 // only necessary if there are quest chains with more than one quest that can be skipped
14334 // return SatisfyQuestNextChain( qInfo->GetNextQuestInChain(), msg );
14338 bool Player::SatisfyQuestPrevChain(Quest
const* qInfo
, bool msg
) const
14340 // No previous quest in chain
14341 if (qInfo
->prevChainQuests
.empty())
14344 for (Quest::PrevChainQuests::const_iterator iter
= qInfo
->prevChainQuests
.begin(); iter
!= qInfo
->prevChainQuests
.end(); ++iter
)
14346 uint32 prevId
= *iter
;
14348 // If any of the previous quests in chain active, return false
14349 if (IsCurrentQuest(prevId
))
14352 SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ
);
14357 // check for all quests further down the chain
14358 // only necessary if there are quest chains with more than one quest that can be skipped
14359 // if( !SatisfyQuestPrevChain( prevId, msg ) )
14363 // No previous quest in chain active
14367 bool Player::SatisfyQuestDay(Quest
const* qInfo
, bool msg
) const
14369 if (!qInfo
->IsDaily())
14372 bool have_slot
= false;
14373 for (uint32 quest_daily_idx
= 0; quest_daily_idx
< PLAYER_MAX_DAILY_QUESTS
; ++quest_daily_idx
)
14375 uint32 id
= GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1
+ quest_daily_idx
);
14376 if (qInfo
->GetQuestId() == id
)
14386 SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_TOO_MANY_DAILY_QUESTS
);
14394 bool Player::SatisfyQuestWeek(Quest
const* qInfo
, bool /*msg*/) const
14396 if (!qInfo
->IsWeekly() || m_weeklyquests
.empty())
14399 // if not found in cooldown list
14400 return m_weeklyquests
.find(qInfo
->GetQuestId()) == m_weeklyquests
.end();
14403 bool Player::SatisfyQuestMonth(Quest
const* qInfo
, bool /*msg*/) const
14405 if (!qInfo
->IsMonthly() || m_monthlyquests
.empty())
14408 // if not found in cooldown list
14409 return m_monthlyquests
.find(qInfo
->GetQuestId()) == m_monthlyquests
.end();
14412 bool Player::CanGiveQuestSourceItemIfNeed(Quest
const* pQuest
, ItemPosCountVec
* dest
) const
14414 if (uint32 srcitem
= pQuest
->GetSrcItemId())
14416 uint32 count
= pQuest
->GetSrcItemCount();
14418 // player already have max amount required item (including bank), just report success
14419 uint32 has_count
= GetItemCount(srcitem
, true);
14420 if (has_count
>= count
)
14423 count
-= has_count
; // real need amount
14425 InventoryResult msg
;
14428 ItemPosCountVec destTemp
;
14429 msg
= CanStoreNewItem(NULL_BAG
, NULL_SLOT
, destTemp
, srcitem
, count
);
14432 msg
= CanStoreNewItem(NULL_BAG
, NULL_SLOT
, *dest
, srcitem
, count
);
14434 if (msg
== EQUIP_ERR_OK
)
14437 SendEquipError(msg
, NULL
, NULL
, srcitem
);
14444 void Player::GiveQuestSourceItemIfNeed(Quest
const* pQuest
)
14446 ItemPosCountVec dest
;
14447 if (CanGiveQuestSourceItemIfNeed(pQuest
, &dest
) && !dest
.empty())
14450 for (ItemPosCountVec::const_iterator c_itr
= dest
.begin(); c_itr
!= dest
.end(); ++c_itr
)
14451 count
+= c_itr
->count
;
14453 Item
* item
= StoreNewItem(dest
, pQuest
->GetSrcItemId(), true);
14454 SendNewItem(item
, count
, true, false);
14459 bool Player::TakeQuestSourceItem(uint32 quest_id
, bool msg
)
14461 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(quest_id
);
14464 uint32 srcitem
= qInfo
->GetSrcItemId();
14467 uint32 count
= qInfo
->GetSrcItemCount();
14471 // exist one case when destroy source quest item not possible:
14472 // non un-equippable item (equipped non-empty bag, for example)
14473 InventoryResult res
= CanUnequipItems(srcitem
, count
);
14474 if (res
!= EQUIP_ERR_OK
)
14477 SendEquipError(res
, NULL
, NULL
, srcitem
);
14481 DestroyItemCount(srcitem
, count
, true, true);
14487 bool Player::GetQuestRewardStatus(uint32 quest_id
) const
14489 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(quest_id
);
14492 // for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
14493 QuestStatusMap::const_iterator itr
= mQuestStatus
.find(quest_id
);
14494 if (itr
!= mQuestStatus
.end() && itr
->second
.m_status
!= QUEST_STATUS_NONE
14495 && !qInfo
->IsRepeatable())
14496 return itr
->second
.m_rewarded
;
14503 QuestStatus
Player::GetQuestStatus(uint32 quest_id
) const
14507 QuestStatusMap::const_iterator itr
= mQuestStatus
.find(quest_id
);
14508 if (itr
!= mQuestStatus
.end())
14509 return itr
->second
.m_status
;
14511 return QUEST_STATUS_NONE
;
14514 bool Player::CanShareQuest(uint32 quest_id
) const
14516 if (Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(quest_id
))
14517 if (qInfo
->HasQuestFlag(QUEST_FLAGS_SHARABLE
))
14518 return IsCurrentQuest(quest_id
);
14523 void Player::SetQuestStatus(uint32 quest_id
, QuestStatus status
)
14525 if (sObjectMgr
.GetQuestTemplate(quest_id
))
14527 QuestStatusData
& q_status
= mQuestStatus
[quest_id
];
14529 q_status
.m_status
= status
;
14531 if (q_status
.uState
!= QUEST_NEW
)
14532 q_status
.uState
= QUEST_CHANGED
;
14535 UpdateForQuestWorldObjects();
14538 // not used in MaNGOS, but used in scripting code
14539 uint32
Player::GetReqKillOrCastCurrentCount(uint32 quest_id
, int32 entry
)
14541 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(quest_id
);
14545 for (int j
= 0; j
< QUEST_OBJECTIVES_COUNT
; ++j
)
14546 if (qInfo
->ReqCreatureOrGOId
[j
] == entry
)
14547 return mQuestStatus
[quest_id
].m_creatureOrGOcount
[j
];
14552 void Player::AdjustQuestReqItemCount(Quest
const* pQuest
, QuestStatusData
& questStatusData
)
14554 if (pQuest
->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
))
14556 for (int i
= 0; i
< QUEST_ITEM_OBJECTIVES_COUNT
; ++i
)
14558 uint32 reqitemcount
= pQuest
->ReqItemCount
[i
];
14559 if (reqitemcount
!= 0)
14561 uint32 curitemcount
= GetItemCount(pQuest
->ReqItemId
[i
], true);
14563 questStatusData
.m_itemcount
[i
] = std::min(curitemcount
, reqitemcount
);
14564 if (questStatusData
.uState
!= QUEST_NEW
) questStatusData
.uState
= QUEST_CHANGED
;
14570 uint16
Player::FindQuestSlot(uint32 quest_id
) const
14572 for (uint16 i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
14573 if (GetQuestSlotQuestId(i
) == quest_id
)
14576 return MAX_QUEST_LOG_SIZE
;
14579 void Player::AreaExploredOrEventHappens(uint32 questId
)
14583 uint16 log_slot
= FindQuestSlot(questId
);
14584 if (log_slot
< MAX_QUEST_LOG_SIZE
)
14586 QuestStatusData
& q_status
= mQuestStatus
[questId
];
14588 if (!q_status
.m_explored
)
14590 SetQuestSlotState(log_slot
, QUEST_STATE_COMPLETE
);
14591 SendQuestCompleteEvent(questId
);
14592 q_status
.m_explored
= true;
14594 if (q_status
.uState
!= QUEST_NEW
)
14595 q_status
.uState
= QUEST_CHANGED
;
14598 if (CanCompleteQuest(questId
))
14599 CompleteQuest(questId
);
14603 // not used in mangosd, function for external script library
14604 void Player::GroupEventHappens(uint32 questId
, WorldObject
const* pEventObject
)
14606 if (Group
* pGroup
= GetGroup())
14608 for (GroupReference
* itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
14610 Player
* pGroupGuy
= itr
->getSource();
14612 // for any leave or dead (with not released body) group member at appropriate distance
14613 if (pGroupGuy
&& pGroupGuy
->IsAtGroupRewardDistance(pEventObject
) && !pGroupGuy
->HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GHOST
))
14614 pGroupGuy
->AreaExploredOrEventHappens(questId
);
14618 AreaExploredOrEventHappens(questId
);
14621 void Player::CurrencyAddedQuestCheck(uint32 entry
)
14623 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
14625 uint32 questid
= GetQuestSlotQuestId(i
);
14629 QuestStatusData
& q_status
= mQuestStatus
[questid
];
14631 if (q_status
.m_status
!= QUEST_STATUS_INCOMPLETE
)
14634 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
);
14635 if (!qInfo
|| !qInfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
))
14638 for (int j
= 0; j
< QUEST_REQUIRED_CURRENCY_COUNT
; ++j
)
14640 uint32 reqcurrency
= qInfo
->ReqCurrencyId
[j
];
14641 if (reqcurrency
== entry
)
14643 if (CanCompleteQuest(questid
))
14644 CompleteQuest(questid
);
14650 void Player::CurrencyRemovedQuestCheck(uint32 entry
)
14652 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
14654 uint32 questid
= GetQuestSlotQuestId(i
);
14657 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
);
14658 if (!qInfo
|| !qInfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
))
14661 for (int j
= 0; j
< QUEST_REQUIRED_CURRENCY_COUNT
; ++j
)
14663 uint32 reqcurrency
= qInfo
->ReqCurrencyId
[j
];
14664 if (reqcurrency
== entry
)
14666 if (!HasCurrencyCount(entry
, int32(qInfo
->ReqCurrencyCount
[j
] * GetCurrencyPrecision(entry
))))
14667 IncompleteQuest(questid
);
14673 void Player::ItemAddedQuestCheck(uint32 entry
, uint32 count
)
14675 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
14677 uint32 questid
= GetQuestSlotQuestId(i
);
14681 QuestStatusData
& q_status
= mQuestStatus
[questid
];
14683 if (q_status
.m_status
!= QUEST_STATUS_INCOMPLETE
)
14686 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
);
14687 if (!qInfo
|| !qInfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
))
14690 for (int j
= 0; j
< QUEST_ITEM_OBJECTIVES_COUNT
; ++j
)
14692 uint32 reqitem
= qInfo
->ReqItemId
[j
];
14693 if (reqitem
== entry
)
14695 uint32 reqitemcount
= qInfo
->ReqItemCount
[j
];
14696 uint32 curitemcount
= q_status
.m_itemcount
[j
];
14697 if (curitemcount
< reqitemcount
)
14699 uint32 additemcount
= (curitemcount
+ count
<= reqitemcount
? count
: reqitemcount
- curitemcount
);
14700 q_status
.m_itemcount
[j
] += additemcount
;
14701 if (q_status
.uState
!= QUEST_NEW
)
14702 q_status
.uState
= QUEST_CHANGED
;
14704 if (CanCompleteQuest(questid
))
14705 CompleteQuest(questid
);
14710 UpdateForQuestWorldObjects();
14713 void Player::ItemRemovedQuestCheck(uint32 entry
, uint32 count
)
14715 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
14717 uint32 questid
= GetQuestSlotQuestId(i
);
14720 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
);
14723 if (!qInfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
))
14726 for (int j
= 0; j
< QUEST_ITEM_OBJECTIVES_COUNT
; ++j
)
14728 uint32 reqitem
= qInfo
->ReqItemId
[j
];
14729 if (reqitem
== entry
)
14731 QuestStatusData
& q_status
= mQuestStatus
[questid
];
14733 uint32 reqitemcount
= qInfo
->ReqItemCount
[j
];
14734 uint32 curitemcount
;
14735 if (q_status
.m_status
!= QUEST_STATUS_COMPLETE
)
14736 curitemcount
= q_status
.m_itemcount
[j
];
14738 curitemcount
= GetItemCount(entry
, true);
14739 if (curitemcount
< reqitemcount
+ count
)
14741 uint32 remitemcount
= (curitemcount
<= reqitemcount
? count
: count
+ reqitemcount
- curitemcount
);
14742 q_status
.m_itemcount
[j
] = curitemcount
- remitemcount
;
14743 if (q_status
.uState
!= QUEST_NEW
) q_status
.uState
= QUEST_CHANGED
;
14745 IncompleteQuest(questid
);
14751 UpdateForQuestWorldObjects();
14754 void Player::SpellAddedQuestCheck(uint32 entry
)
14756 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
14758 uint32 questid
= GetQuestSlotQuestId(i
);
14762 QuestStatusData
& q_status
= mQuestStatus
[questid
];
14764 if (q_status
.m_status
!= QUEST_STATUS_INCOMPLETE
)
14767 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
);
14771 uint32 reqspelllearned
= qInfo
->GetReqSpellLearned();
14772 if (!reqspelllearned
)
14775 if (reqspelllearned
== entry
)
14777 if (CanCompleteQuest(questid
))
14778 CompleteQuest(questid
);
14783 void Player::SpellRemovedQuestCheck(uint32 entry
)
14785 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
14787 uint32 questid
= GetQuestSlotQuestId(i
);
14790 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
);
14794 uint32 reqspelllearned
= qInfo
->GetReqSpellLearned();
14795 if (!reqspelllearned
)
14798 if (reqspelllearned
== entry
)
14800 if (!HasSpell(entry
))
14801 IncompleteQuest(questid
);
14806 void Player::KilledMonster(CreatureInfo
const* cInfo
, ObjectGuid guid
)
14809 KilledMonsterCredit(cInfo
->Entry
, guid
);
14811 for (int i
= 0; i
< MAX_KILL_CREDIT
; ++i
)
14812 if (cInfo
->KillCredit
[i
])
14813 KilledMonsterCredit(cInfo
->KillCredit
[i
], guid
);
14816 void Player::KilledMonsterCredit(uint32 entry
, ObjectGuid guid
)
14818 uint32 addkillcount
= 1;
14819 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
, entry
, addkillcount
);
14821 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
14823 uint32 questid
= GetQuestSlotQuestId(i
);
14827 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
);
14830 // just if !ingroup || !noraidgroup || raidgroup
14831 QuestStatusData
& q_status
= mQuestStatus
[questid
];
14832 if (q_status
.m_status
== QUEST_STATUS_INCOMPLETE
&& (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo
->IsAllowedInRaid()))
14834 if (qInfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_KILL_OR_CAST
))
14836 for (int j
= 0; j
< QUEST_OBJECTIVES_COUNT
; ++j
)
14838 // skip GO activate objective or none
14839 if (qInfo
->ReqCreatureOrGOId
[j
] <= 0)
14842 // skip Cast at creature objective
14843 if (qInfo
->ReqSpell
[j
] != 0)
14846 uint32 reqkill
= qInfo
->ReqCreatureOrGOId
[j
];
14848 if (reqkill
== entry
)
14850 uint32 reqkillcount
= qInfo
->ReqCreatureOrGOCount
[j
];
14851 uint32 curkillcount
= q_status
.m_creatureOrGOcount
[j
];
14852 if (curkillcount
< reqkillcount
)
14854 q_status
.m_creatureOrGOcount
[j
] = curkillcount
+ addkillcount
;
14855 if (q_status
.uState
!= QUEST_NEW
)
14856 q_status
.uState
= QUEST_CHANGED
;
14858 SendQuestUpdateAddCreatureOrGo(qInfo
, guid
, j
, q_status
.m_creatureOrGOcount
[j
]);
14861 if (CanCompleteQuest(questid
))
14862 CompleteQuest(questid
);
14864 // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
14873 void Player::CastedCreatureOrGO(uint32 entry
, ObjectGuid guid
, uint32 spell_id
, bool original_caster
)
14875 bool isCreature
= guid
.IsCreatureOrVehicle();
14877 uint32 addCastCount
= 1;
14878 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
14880 uint32 questid
= GetQuestSlotQuestId(i
);
14884 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
);
14888 if (!original_caster
&& !qInfo
->HasQuestFlag(QUEST_FLAGS_SHARABLE
))
14891 if (!qInfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_KILL_OR_CAST
))
14894 QuestStatusData
& q_status
= mQuestStatus
[questid
];
14896 if (q_status
.m_status
!= QUEST_STATUS_INCOMPLETE
)
14899 for (int j
= 0; j
< QUEST_OBJECTIVES_COUNT
; ++j
)
14901 // skip kill creature objective (0) or wrong spell casts
14902 if (qInfo
->ReqSpell
[j
] != spell_id
)
14905 uint32 reqTarget
= 0;
14909 // creature activate objectives
14910 if (qInfo
->ReqCreatureOrGOId
[j
] > 0)
14911 // checked at quest_template loading
14912 reqTarget
= qInfo
->ReqCreatureOrGOId
[j
];
14916 // GO activate objective
14917 if (qInfo
->ReqCreatureOrGOId
[j
] < 0)
14918 // checked at quest_template loading
14919 reqTarget
= - qInfo
->ReqCreatureOrGOId
[j
];
14922 // other not this creature/GO related objectives
14923 if (reqTarget
!= entry
)
14926 uint32 reqCastCount
= qInfo
->ReqCreatureOrGOCount
[j
];
14927 uint32 curCastCount
= q_status
.m_creatureOrGOcount
[j
];
14928 if (curCastCount
< reqCastCount
)
14930 q_status
.m_creatureOrGOcount
[j
] = curCastCount
+ addCastCount
;
14931 if (q_status
.uState
!= QUEST_NEW
)
14932 q_status
.uState
= QUEST_CHANGED
;
14934 SendQuestUpdateAddCreatureOrGo(qInfo
, guid
, j
, q_status
.m_creatureOrGOcount
[j
]);
14937 if (CanCompleteQuest(questid
))
14938 CompleteQuest(questid
);
14940 // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
14946 void Player::TalkedToCreature(uint32 entry
, ObjectGuid guid
)
14948 uint32 addTalkCount
= 1;
14949 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
14951 uint32 questid
= GetQuestSlotQuestId(i
);
14955 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
);
14959 QuestStatusData
& q_status
= mQuestStatus
[questid
];
14961 if (q_status
.m_status
== QUEST_STATUS_INCOMPLETE
)
14963 if (qInfo
->HasSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAG_KILL_OR_CAST
| QUEST_SPECIAL_FLAG_SPEAKTO
)))
14965 for (int j
= 0; j
< QUEST_OBJECTIVES_COUNT
; ++j
)
14967 // skip spell casts and Gameobject objectives
14968 if (qInfo
->ReqSpell
[j
] > 0 || qInfo
->ReqCreatureOrGOId
[j
] < 0)
14971 uint32 reqTarget
= 0;
14973 if (qInfo
->ReqCreatureOrGOId
[j
] > 0) // creature activate objectives
14974 // checked at quest_template loading
14975 reqTarget
= qInfo
->ReqCreatureOrGOId
[j
];
14979 if (reqTarget
== entry
)
14981 uint32 reqTalkCount
= qInfo
->ReqCreatureOrGOCount
[j
];
14982 uint32 curTalkCount
= q_status
.m_creatureOrGOcount
[j
];
14983 if (curTalkCount
< reqTalkCount
)
14985 q_status
.m_creatureOrGOcount
[j
] = curTalkCount
+ addTalkCount
;
14986 if (q_status
.uState
!= QUEST_NEW
) q_status
.uState
= QUEST_CHANGED
;
14988 SendQuestUpdateAddCreatureOrGo(qInfo
, guid
, j
, q_status
.m_creatureOrGOcount
[j
]);
14990 if (CanCompleteQuest(questid
))
14991 CompleteQuest(questid
);
14993 // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
15002 void Player::MoneyChanged(uint32 count
)
15004 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
15006 uint32 questid
= GetQuestSlotQuestId(i
);
15010 Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
);
15011 if (qInfo
&& qInfo
->GetRewOrReqMoney() < 0)
15013 QuestStatusData
& q_status
= mQuestStatus
[questid
];
15015 if (q_status
.m_status
== QUEST_STATUS_INCOMPLETE
)
15017 if (int32(count
) >= -qInfo
->GetRewOrReqMoney())
15019 if (CanCompleteQuest(questid
))
15020 CompleteQuest(questid
);
15023 else if (q_status
.m_status
== QUEST_STATUS_COMPLETE
)
15025 if (int32(count
) < -qInfo
->GetRewOrReqMoney())
15026 IncompleteQuest(questid
);
15032 void Player::ReputationChanged(FactionEntry
const* factionEntry
)
15034 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
15036 if (uint32 questid
= GetQuestSlotQuestId(i
))
15038 if (Quest
const* qInfo
= sObjectMgr
.GetQuestTemplate(questid
))
15040 if (qInfo
->GetRepObjectiveFaction() == factionEntry
->ID
)
15042 QuestStatusData
& q_status
= mQuestStatus
[questid
];
15043 if (q_status
.m_status
== QUEST_STATUS_INCOMPLETE
)
15045 if (GetReputationMgr().GetReputation(factionEntry
) >= qInfo
->GetRepObjectiveValue())
15046 if (CanCompleteQuest(questid
))
15047 CompleteQuest(questid
);
15049 else if (q_status
.m_status
== QUEST_STATUS_COMPLETE
)
15051 if (GetReputationMgr().GetReputation(factionEntry
) < qInfo
->GetRepObjectiveValue())
15052 IncompleteQuest(questid
);
15060 bool Player::HasQuestForItem(uint32 itemid
) const
15062 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
15064 uint32 questid
= GetQuestSlotQuestId(i
);
15068 QuestStatusMap::const_iterator qs_itr
= mQuestStatus
.find(questid
);
15069 if (qs_itr
== mQuestStatus
.end())
15072 QuestStatusData
const& q_status
= qs_itr
->second
;
15074 if (q_status
.m_status
== QUEST_STATUS_INCOMPLETE
)
15076 Quest
const* qinfo
= sObjectMgr
.GetQuestTemplate(questid
);
15080 // hide quest if player is in raid-group and quest is no raid quest
15081 if (GetGroup() && GetGroup()->isRaidGroup() && !qinfo
->IsAllowedInRaid() && !InBattleGround())
15084 // There should be no mixed ReqItem/ReqSource drop
15085 // This part for ReqItem drop
15086 for (int j
= 0; j
< QUEST_ITEM_OBJECTIVES_COUNT
; ++j
)
15088 if (itemid
== qinfo
->ReqItemId
[j
] && q_status
.m_itemcount
[j
] < qinfo
->ReqItemCount
[j
])
15091 // This part - for ReqSource
15092 for (int j
= 0; j
< QUEST_SOURCE_ITEM_IDS_COUNT
; ++j
)
15094 // examined item is a source item
15095 if (qinfo
->ReqSourceId
[j
] == itemid
)
15097 ItemPrototype
const* pProto
= ObjectMgr::GetItemPrototype(itemid
);
15100 if (pProto
->MaxCount
&& (int32
)GetItemCount(itemid
, true) < pProto
->MaxCount
)
15103 // allows custom amount drop when not 0
15104 if (qinfo
->ReqSourceCount
[j
])
15106 if (GetItemCount(itemid
, true) < qinfo
->ReqSourceCount
[j
])
15109 else if ((int32
)GetItemCount(itemid
, true) < pProto
->Stackable
)
15118 // Used for quests having some event (explore, escort, "external event") as quest objective.
15119 void Player::SendQuestCompleteEvent(uint32 quest_id
)
15123 WorldPacket
data(SMSG_QUESTUPDATE_COMPLETE
, 4);
15124 data
<< uint32(quest_id
);
15125 GetSession()->SendPacket(&data
);
15126 DEBUG_LOG("WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = %u", quest_id
);
15130 void Player::SendQuestReward(Quest
const* pQuest
, uint32 XP
, Object
* /*questGiver*/)
15132 uint32 questid
= pQuest
->GetQuestId();
15133 DEBUG_LOG("WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questid
);
15134 WorldPacket
data(SMSG_QUESTGIVER_QUEST_COMPLETE
, (4 + 4 + 4 + 4 + 4));
15135 data
<< uint32(pQuest
->GetBonusTalents());
15136 data
<< uint32(pQuest
->GetRewSkillValue());
15138 if (getLevel() < sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
15140 data
<< uint32(pQuest
->GetRewOrReqMoney());
15141 data
<< uint32(XP
);
15145 data
<< uint32(pQuest
->GetRewOrReqMoney() + int32(pQuest
->GetRewMoneyMaxLevel() * sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY
)));
15149 data
<< uint32(questid
);
15150 data
<< uint32(pQuest
->GetRewSkill());
15152 data
.WriteBit(0); // unk
15153 data
.WriteBit(1); // unk
15155 GetSession()->SendPacket(&data
);
15157 if (QuestPhaseMapsVector
const* QuestPhaseVector
= sObjectMgr
.GetQuestPhaseMapVector(questid
))
15159 for (QuestPhaseMapsVector::const_iterator itr
= QuestPhaseVector
->begin(); itr
!= QuestPhaseVector
->end(); ++itr
)
15161 GetSession()->SendSetPhaseShift(itr
->PhaseMask
, itr
->MapId
);
15162 CharacterDatabase
.PExecute("REPLACE INTO character_phase_data` (`guid`, `map`, `phase`) VALUES (%u, %u, %u)", GetSession()->GetPlayer()->GetGUIDLow(), itr
->MapId
, itr
->PhaseMask
);
15167 void Player::SendQuestFailed(uint32 quest_id
, InventoryResult reason
)
15171 WorldPacket
data(SMSG_QUESTGIVER_QUEST_FAILED
, 4 + 4);
15172 data
<< uint32(quest_id
);
15173 data
<< uint32(reason
); // failed reason (valid reasons: 4, 16, 50, 17, 74, other values show default message)
15174 GetSession()->SendPacket(&data
);
15175 DEBUG_LOG("WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED");
15179 void Player::SendQuestTimerFailed(uint32 quest_id
)
15183 WorldPacket
data(SMSG_QUESTUPDATE_FAILEDTIMER
, 4);
15184 data
<< uint32(quest_id
);
15185 GetSession()->SendPacket(&data
);
15186 DEBUG_LOG("WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
15190 void Player::SendCanTakeQuestResponse(uint32 msg
) const
15192 WorldPacket
data(SMSG_QUESTGIVER_QUEST_INVALID
, 4);
15193 data
<< uint32(msg
);
15194 GetSession()->SendPacket(&data
);
15195 DEBUG_LOG("WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID");
15198 void Player::SendQuestConfirmAccept(const Quest
* pQuest
, Player
* pReceiver
)
15202 int loc_idx
= pReceiver
->GetSession()->GetSessionDbLocaleIndex();
15203 std::string title
= pQuest
->GetTitle();
15204 sObjectMgr
.GetQuestLocaleStrings(pQuest
->GetQuestId(), loc_idx
, &title
);
15206 WorldPacket
data(SMSG_QUEST_CONFIRM_ACCEPT
, (4 + title
.size() + 8));
15207 data
<< uint32(pQuest
->GetQuestId());
15209 data
<< GetObjectGuid();
15210 pReceiver
->GetSession()->SendPacket(&data
);
15212 DEBUG_LOG("WORLD: Sent SMSG_QUEST_CONFIRM_ACCEPT");
15216 void Player::SendPushToPartyResponse(Player
* pPlayer
, uint32 msg
)
15220 WorldPacket
data(MSG_QUEST_PUSH_RESULT
, (8 + 1));
15221 data
<< pPlayer
->GetObjectGuid();
15222 data
<< uint8(msg
); // valid values: 0-8
15223 GetSession()->SendPacket(&data
);
15224 DEBUG_LOG("WORLD: Sent MSG_QUEST_PUSH_RESULT");
15228 void Player::SendQuestUpdateAddCreatureOrGo(Quest
const* pQuest
, ObjectGuid guid
, uint32 creatureOrGO_idx
, uint32 count
)
15230 MANGOS_ASSERT(count
< 65536 && "mob/GO count store in 16 bits 2^16 = 65536 (0..65536)");
15232 int32 entry
= pQuest
->ReqCreatureOrGOId
[ creatureOrGO_idx
];
15234 // client expected gameobject template id in form (id|0x80000000)
15235 entry
= (-entry
) | 0x80000000;
15237 WorldPacket
data(SMSG_QUESTUPDATE_ADD_KILL
, (4 * 4 + 8));
15238 DEBUG_LOG("WORLD: Sent SMSG_QUESTUPDATE_ADD_KILL");
15239 data
<< uint32(pQuest
->GetQuestId());
15240 data
<< uint32(entry
);
15241 data
<< uint32(count
);
15242 data
<< uint32(pQuest
->ReqCreatureOrGOCount
[ creatureOrGO_idx
]);
15244 GetSession()->SendPacket(&data
);
15246 uint16 log_slot
= FindQuestSlot(pQuest
->GetQuestId());
15247 if (log_slot
< MAX_QUEST_LOG_SIZE
)
15248 SetQuestSlotCounter(log_slot
, creatureOrGO_idx
, count
);
15251 /*********************************************************/
15252 /*** LOAD SYSTEM ***/
15253 /*********************************************************/
15255 void Player::_LoadDeclinedNames(QueryResult
* result
)
15260 delete m_declinedname
;
15261 m_declinedname
= new DeclinedName
;
15263 Field
* fields
= result
->Fetch();
15264 for (int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
15265 m_declinedname
->name
[i
] = fields
[i
].GetCppString();
15270 void Player::_LoadArenaTeamInfo(QueryResult
* result
)
15272 // arenateamid, played_week, played_season, personal_rating
15273 memset((void*)&m_uint32Values
[PLAYER_FIELD_ARENA_TEAM_INFO_1_1
], 0, sizeof(uint32
) * MAX_ARENA_SLOT
* ARENA_TEAM_END
);
15279 Field
* fields
= result
->Fetch();
15281 uint32 arenateamid
= fields
[0].GetUInt32();
15282 uint32 played_week
= fields
[1].GetUInt32();
15283 uint32 played_season
= fields
[2].GetUInt32();
15284 uint32 wons_season
= fields
[3].GetUInt32();
15285 uint32 personal_rating
= fields
[4].GetUInt32();
15287 ArenaTeam
* aTeam
= sObjectMgr
.GetArenaTeamById(arenateamid
);
15290 sLog
.outError("Player::_LoadArenaTeamInfo: couldn't load arenateam %u", arenateamid
);
15293 uint8 arenaSlot
= aTeam
->GetSlot();
15295 SetArenaTeamInfoField(arenaSlot
, ARENA_TEAM_ID
, arenateamid
);
15296 SetArenaTeamInfoField(arenaSlot
, ARENA_TEAM_TYPE
, aTeam
->GetType());
15297 SetArenaTeamInfoField(arenaSlot
, ARENA_TEAM_MEMBER
, (aTeam
->GetCaptainGuid() == GetObjectGuid()) ? 0 : 1);
15298 SetArenaTeamInfoField(arenaSlot
, ARENA_TEAM_GAMES_WEEK
, played_week
);
15299 SetArenaTeamInfoField(arenaSlot
, ARENA_TEAM_GAMES_SEASON
, played_season
);
15300 SetArenaTeamInfoField(arenaSlot
, ARENA_TEAM_WINS_SEASON
, wons_season
);
15301 SetArenaTeamInfoField(arenaSlot
, ARENA_TEAM_PERSONAL_RATING
, personal_rating
);
15304 while (result
->NextRow());
15308 void Player::_LoadEquipmentSets(QueryResult
* result
)
15310 // SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, ignore_mask, item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = '%u' ORDER BY setindex", GUID_LOPART(m_guid));
15317 Field
* fields
= result
->Fetch();
15319 EquipmentSet eqSet
;
15321 eqSet
.Guid
= fields
[0].GetUInt64();
15322 uint32 index
= fields
[1].GetUInt32();
15323 eqSet
.Name
= fields
[2].GetCppString();
15324 eqSet
.IconName
= fields
[3].GetCppString();
15325 eqSet
.IgnoreMask
= fields
[4].GetUInt32();
15326 eqSet
.state
= EQUIPMENT_SET_UNCHANGED
;
15328 for (uint32 i
= 0; i
< EQUIPMENT_SLOT_END
; ++i
)
15329 eqSet
.Items
[i
] = fields
[5 + i
].GetUInt32();
15331 m_EquipmentSets
[index
] = eqSet
;
15335 if (count
>= MAX_EQUIPMENT_SET_INDEX
) // client limit
15338 while (result
->NextRow());
15342 void Player::_LoadBGData(QueryResult
* result
)
15347 // Expecting only one row
15348 Field
* fields
= result
->Fetch();
15349 /* bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */
15350 m_bgData
.bgInstanceID
= fields
[0].GetUInt32();
15351 m_bgData
.bgTeam
= Team(fields
[1].GetUInt32());
15352 m_bgData
.joinPos
= WorldLocation(fields
[6].GetUInt32(), // Map
15353 fields
[2].GetFloat(), // X
15354 fields
[3].GetFloat(), // Y
15355 fields
[4].GetFloat(), // Z
15356 fields
[5].GetFloat()); // Orientation
15357 m_bgData
.taxiPath
[0] = fields
[7].GetUInt32();
15358 m_bgData
.taxiPath
[1] = fields
[8].GetUInt32();
15359 m_bgData
.mountSpell
= fields
[9].GetUInt32();
15364 bool Player::LoadPositionFromDB(ObjectGuid guid
, uint32
& mapid
, float& x
, float& y
, float& z
, float& o
, bool& in_flight
)
15366 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT position_x,position_y,position_z,orientation,map,taxi_path FROM characters WHERE guid = '%u'", guid
.GetCounter());
15370 Field
* fields
= result
->Fetch();
15372 x
= fields
[0].GetFloat();
15373 y
= fields
[1].GetFloat();
15374 z
= fields
[2].GetFloat();
15375 o
= fields
[3].GetFloat();
15376 mapid
= fields
[4].GetUInt32();
15377 in_flight
= !fields
[5].GetCppString().empty();
15383 void Player::_LoadIntoDataField(const char* data
, uint32 startOffset
, uint32 count
)
15388 Tokens tokens
= StrSplit(data
, " ");
15390 if (tokens
.size() != count
)
15393 Tokens::iterator iter
;
15395 for (iter
= tokens
.begin(), index
= 0; index
< count
; ++iter
, ++index
)
15397 m_uint32Values
[startOffset
+ index
] = atol((*iter
).c_str());
15401 bool Player::LoadFromDB(ObjectGuid guid
, SqlQueryHolder
* holder
)
15403 // 0 1 2 3 4 5 6 7 8 9 10 11
15404 // SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags,"
15405 // 12 13 14 15 16 17 18 19 20 21 22 23 24
15406 //"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost,"
15407 // 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
15408 //"resettalents_time, primary_trees, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty,"
15409 // 40 41 42 43 44 45
15410 //"totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk,"
15411 // 46 47 48 49 50 51 52 53 54 55 56 57 58
15412 //"health, power1, power2, power3, power4, power5, specCount, activeSpec, exploredZones, equipmentCache, knownTitles, actionBars, slot FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
15413 QueryResult
*result
= holder
->GetResult(PLAYER_LOGIN_QUERY_LOADFROM
);
15417 sLog
.outError("%s not found in table `characters`, can't load. ", guid
.GetString().c_str());
15421 Field
* fields
= result
->Fetch();
15423 uint32 dbAccountId
= fields
[1].GetUInt32();
15425 // check if the character's account in the db and the logged in account match.
15426 // player should be able to load/delete character only with correct account!
15427 if (dbAccountId
!= GetSession()->GetAccountId())
15429 sLog
.outError("%s loading from wrong account (is: %u, should be: %u)",
15430 guid
.GetString().c_str(), GetSession()->GetAccountId(), dbAccountId
);
15435 Object::_Create(guid
.GetCounter(), 0, HIGHGUID_PLAYER
);
15437 m_name
= fields
[2].GetCppString();
15439 // check name limitations
15440 if (ObjectMgr::CheckPlayerName(m_name
) != CHAR_NAME_SUCCESS
||
15441 (GetSession()->GetSecurity() == SEC_PLAYER
&& sObjectMgr
.IsReservedName(m_name
)))
15444 CharacterDatabase
.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid ='%u'",
15445 uint32(AT_LOGIN_RENAME
), guid
.GetCounter());
15449 // overwrite possible wrong/corrupted guid
15450 SetGuidValue(OBJECT_FIELD_GUID
, guid
);
15452 // overwrite some data fields
15453 SetByteValue(UNIT_FIELD_BYTES_0
, 0, fields
[3].GetUInt8()); // race
15454 SetByteValue(UNIT_FIELD_BYTES_0
, 1, fields
[4].GetUInt8()); // class
15456 uint8 gender
= fields
[5].GetUInt8() & 0x01; // allowed only 1 bit values male/female cases (for fit drunk gender part)
15457 SetByteValue(UNIT_FIELD_BYTES_0
, 2, gender
); // gender
15459 SetUInt32Value(UNIT_FIELD_LEVEL
, fields
[6].GetUInt8());
15460 SetUInt32Value(PLAYER_XP
, fields
[7].GetUInt32());
15462 _LoadIntoDataField(fields
[54].GetString(), PLAYER_EXPLORED_ZONES_1
, PLAYER_EXPLORED_ZONES_SIZE
);
15463 _LoadIntoDataField(fields
[56].GetString(), PLAYER__FIELD_KNOWN_TITLES
, KNOWN_TITLES_SIZE
*2);
15465 InitDisplayIds(); // model, scale and model data
15467 SetFloatValue(UNIT_FIELD_HOVERHEIGHT
, 1.0f
);
15469 // just load criteria/achievement data, safe call before any load, and need, because some spell/item/quest loading
15470 // can triggering achievement criteria update that will be lost if this call will later
15471 m_achievementMgr
.LoadFromDB(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS
), holder
->GetResult(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS
));
15473 uint64 money
= fields
[8].GetUInt64();
15474 if (money
> MAX_MONEY_AMOUNT
)
15475 money
= MAX_MONEY_AMOUNT
;
15478 SetUInt32Value(PLAYER_BYTES
, fields
[9].GetUInt32());
15479 SetUInt32Value(PLAYER_BYTES_2
, fields
[10].GetUInt32());
15481 m_drunk
= fields
[45].GetUInt16();
15483 SetUInt16Value(PLAYER_BYTES_3
, 0, (m_drunk
& 0xFFFE) | gender
);
15485 SetUInt32Value(PLAYER_FLAGS
, fields
[11].GetUInt32());
15486 SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX
, fields
[44].GetInt32());
15488 // Action bars state
15489 SetByteValue(PLAYER_FIELD_BYTES
, 2, fields
[57].GetUInt8());
15491 m_slot
= fields
[58].GetUInt8();
15493 // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
15494 for (uint8 slot
= EQUIPMENT_SLOT_START
; slot
< EQUIPMENT_SLOT_END
; ++slot
)
15496 SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD
+ (slot
* 2), ObjectGuid());
15497 SetVisibleItemSlot(slot
, NULL
);
15499 delete m_items
[slot
];
15500 m_items
[slot
] = NULL
;
15503 DEBUG_FILTER_LOG(LOG_FILTER_PLAYER_STATS
, "Load Basic value of player %s is: ", m_name
.c_str());
15504 outDebugStatsValues();
15506 // Need to call it to initialize m_team (m_team can be calculated from race)
15507 // Other way is to saves m_team into characters table.
15508 setFactionForRace(getRace());
15511 // load home bind and check in same time class/race pair, it used later for restore broken positions
15512 if (!_LoadHomeBind(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND
)))
15518 InitPrimaryProfessions(); // to max set before any spell loaded
15520 // init saved position, and fix it later if problematic
15521 uint32 transGUID
= fields
[31].GetUInt32();
15522 Relocate(fields
[12].GetFloat(), fields
[13].GetFloat(), fields
[14].GetFloat(), fields
[16].GetFloat());
15523 SetLocationMapId(fields
[15].GetUInt32());
15525 uint32 difficulty
= fields
[39].GetUInt32();
15526 if (difficulty
>= MAX_DUNGEON_DIFFICULTY
|| getLevel() < LEVELREQUIREMENT_HEROIC
)
15527 difficulty
= DUNGEON_DIFFICULTY_NORMAL
;
15528 SetDungeonDifficulty(Difficulty(difficulty
)); // may be changed in _LoadGroup
15530 _LoadGroup(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP
));
15532 _LoadArenaTeamInfo(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADARENAINFO
));
15534 // check arena teams integrity
15535 for (uint32 arena_slot
= 0; arena_slot
< MAX_ARENA_SLOT
; ++arena_slot
)
15537 uint32 arena_team_id
= GetArenaTeamId(arena_slot
);
15538 if (!arena_team_id
)
15541 if (ArenaTeam
* at
= sObjectMgr
.GetArenaTeamById(arena_team_id
))
15542 if (at
->HaveMember(GetObjectGuid()))
15545 // arena team not exist or not member, cleanup fields
15546 for (int j
= 0; j
< ARENA_TEAM_END
; ++j
)
15547 SetArenaTeamInfoField(arena_slot
, ArenaTeamInfoType(j
), 0);
15550 SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS
, fields
[40].GetUInt32());
15551 SetUInt16Value(PLAYER_FIELD_KILLS
, 0, fields
[41].GetUInt16()); // today
15552 SetUInt16Value(PLAYER_FIELD_KILLS
, 1, fields
[42].GetUInt16()); // yesterday
15554 _LoadBoundInstances(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES
));
15556 if (!IsPositionValid())
15558 sLog
.outError("%s have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
15559 guid
.GetString().c_str(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
15560 RelocateToHomebind();
15564 m_movementInfo
.ClearTransportData();
15567 _LoadBGData(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADBGDATA
));
15569 if (m_bgData
.bgInstanceID
) // saved in BattleGround
15571 BattleGround
* currentBg
= sBattleGroundMgr
.GetBattleGround(m_bgData
.bgInstanceID
, BATTLEGROUND_TYPE_NONE
);
15573 bool player_at_bg
= currentBg
&& currentBg
->IsPlayerInBattleGround(GetObjectGuid());
15575 if (player_at_bg
&& currentBg
->GetStatus() != STATUS_WAIT_LEAVE
)
15577 BattleGroundQueueTypeId bgQueueTypeId
= BattleGroundMgr::BGQueueTypeId(currentBg
->GetTypeID(), currentBg
->GetArenaType());
15578 AddBattleGroundQueueId(bgQueueTypeId
);
15580 m_bgData
.bgTypeID
= currentBg
->GetTypeID(); // bg data not marked as modified
15582 // join player to battleground group
15583 currentBg
->EventPlayerLoggedIn(this, GetObjectGuid());
15584 currentBg
->AddOrSetPlayerToCorrectBgGroup(this, GetObjectGuid(), m_bgData
.bgTeam
);
15586 SetInviteForBattleGroundQueueType(bgQueueTypeId
, currentBg
->GetInstanceID());
15592 currentBg
->RemovePlayerAtLeave(GetObjectGuid(), false, true);
15594 // move to bg enter point
15595 const WorldLocation
& _loc
= GetBattleGroundEntryPoint();
15596 SetLocationMapId(_loc
.mapid
);
15597 Relocate(_loc
.coord_x
, _loc
.coord_y
, _loc
.coord_z
, _loc
.orientation
);
15599 // We are not in BG anymore
15600 SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE
);
15601 // remove outdated DB data in DB
15607 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(GetMapId());
15608 // if server restart after player save in BG or area
15609 // player can have current coordinates in to BG/Arena map, fix this
15610 if (!mapEntry
|| mapEntry
->IsBattleGroundOrArena())
15612 const WorldLocation
& _loc
= GetBattleGroundEntryPoint();
15613 SetLocationMapId(_loc
.mapid
);
15614 Relocate(_loc
.coord_x
, _loc
.coord_y
, _loc
.coord_z
, _loc
.orientation
);
15616 // We are not in BG anymore
15617 SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE
);
15618 // remove outdated DB data in DB
15623 if (transGUID
!= 0)
15625 m_movementInfo
.SetTransportData(ObjectGuid(HIGHGUID_MO_TRANSPORT
, transGUID
), fields
[27].GetFloat(), fields
[28].GetFloat(), fields
[29].GetFloat(), fields
[30].GetFloat(), 0, -1);
15627 if (!MaNGOS::IsValidMapCoord(
15628 GetPositionX() + m_movementInfo
.GetTransportPos()->x
, GetPositionY() + m_movementInfo
.GetTransportPos()->y
,
15629 GetPositionZ() + m_movementInfo
.GetTransportPos()->z
, GetOrientation() + m_movementInfo
.GetTransportPos()->o
) ||
15630 // transport size limited
15631 m_movementInfo
.GetTransportPos()->x
> 50 || m_movementInfo
.GetTransportPos()->y
> 50 || m_movementInfo
.GetTransportPos()->z
> 50)
15633 sLog
.outError("%s have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
15634 guid
.GetString().c_str(), GetPositionX() + m_movementInfo
.GetTransportPos()->x
, GetPositionY() + m_movementInfo
.GetTransportPos()->y
,
15635 GetPositionZ() + m_movementInfo
.GetTransportPos()->z
, GetOrientation() + m_movementInfo
.GetTransportPos()->o
);
15637 RelocateToHomebind();
15639 m_movementInfo
.ClearTransportData();
15645 if (transGUID
!= 0)
15647 for (MapManager::TransportSet::const_iterator iter
= sMapMgr
.m_Transports
.begin(); iter
!= sMapMgr
.m_Transports
.end(); ++iter
)
15649 if ((*iter
)->GetGUIDLow() == transGUID
)
15651 MapEntry
const* transMapEntry
= sMapStore
.LookupEntry((*iter
)->GetMapId());
15652 // client without expansion support
15653 if (GetSession()->Expansion() < transMapEntry
->Expansion())
15655 DEBUG_LOG("Player %s using client without required expansion tried login at transport at non accessible map %u", GetName(), (*iter
)->GetMapId());
15659 m_transport
= *iter
;
15660 m_transport
->AddPassenger(this);
15661 SetLocationMapId(m_transport
->GetMapId());
15668 sLog
.outError("%s have problems with transport guid (%u). Teleport to default race/class locations.",
15669 guid
.GetString().c_str(), transGUID
);
15671 RelocateToHomebind();
15673 m_movementInfo
.ClearTransportData();
15678 else // not transport case
15680 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(GetMapId());
15681 // client without expansion support
15682 if (GetSession()->Expansion() < mapEntry
->Expansion())
15684 DEBUG_LOG("Player %s using client without required expansion tried login at non accessible map %u", GetName(), GetMapId());
15685 RelocateToHomebind();
15689 // player bounded instance saves loaded in _LoadBoundInstances, group versions at group loading
15690 DungeonPersistentState
* state
= GetBoundInstanceSaveForSelfOrGroup(GetMapId());
15692 // load the player's map here if it's not already loaded
15693 SetMap(sMapMgr
.CreateMap(GetMapId(), this));
15695 // if the player is in an instance and it has been reset in the meantime teleport him to the entrance
15696 if (GetInstanceId() && !state
)
15698 AreaTrigger
const* at
= sObjectMgr
.GetMapEntranceTrigger(GetMapId());
15700 Relocate(at
->target_X
, at
->target_Y
, at
->target_Z
, at
->target_Orientation
);
15702 sLog
.outError("Player %s(GUID: %u) logged in to a reset instance (map: %u) and there is no area-trigger leading to this map. Thus he can't be ported back to the entrance. This _might_ be an exploit attempt.", GetName(), GetGUIDLow(), GetMapId());
15705 SaveRecallPosition();
15707 time_t now
= time(NULL
);
15708 time_t logoutTime
= time_t(fields
[22].GetUInt64());
15710 // since last logout (in seconds)
15711 uint32 time_diff
= uint32(now
- logoutTime
);
15713 // set value, including drunk invisibility detection
15714 // calculate sobering. after 15 minutes logged out, the player will be sober again
15716 if (time_diff
> 15 * MINUTE
)
15719 soberFactor
= 1 - time_diff
/ (15.0f
* MINUTE
);
15720 uint16 newDrunkenValue
= uint16(soberFactor
* m_drunk
);
15721 SetDrunkValue(newDrunkenValue
);
15723 m_cinematic
= fields
[18].GetUInt32();
15724 m_Played_time
[PLAYED_TIME_TOTAL
] = fields
[19].GetUInt32();
15725 m_Played_time
[PLAYED_TIME_LEVEL
] = fields
[20].GetUInt32();
15727 m_resetTalentsCost
= fields
[24].GetUInt32();
15728 m_resetTalentsTime
= time_t(fields
[25].GetUInt64());
15730 // reserve some flags
15731 uint32 old_safe_flags
= GetUInt32Value(PLAYER_FLAGS
) & (PLAYER_FLAGS_HIDE_CLOAK
| PLAYER_FLAGS_HIDE_HELM
);
15733 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GM
))
15734 SetUInt32Value(PLAYER_FLAGS
, 0 | old_safe_flags
);
15736 m_taxi
.LoadTaxiMask(fields
[17].GetString()); // must be before InitTaxiNodesForLevel
15738 uint32 extraflags
= fields
[32].GetUInt32();
15740 m_stableSlots
= fields
[33].GetUInt32();
15741 if (m_stableSlots
> MAX_PET_STABLES
)
15743 sLog
.outError("Player can have not more %u stable slots, but have in DB %u", MAX_PET_STABLES
, uint32(m_stableSlots
));
15744 m_stableSlots
= MAX_PET_STABLES
;
15747 m_atLoginFlags
= fields
[34].GetUInt32();
15749 // Update Honor kills data
15750 m_lastHonorKillsUpdateTime
= logoutTime
;
15751 UpdateHonorKills();
15753 m_deathExpireTime
= (time_t)fields
[37].GetUInt64();
15754 if (m_deathExpireTime
> now
+ MAX_DEATH_COUNT
* DEATH_EXPIRE_STEP
)
15755 m_deathExpireTime
= now
+ MAX_DEATH_COUNT
* DEATH_EXPIRE_STEP
- 1;
15757 std::string taxi_nodes
= fields
[38].GetCppString();
15759 // clear channel spell data (if saved at channel spell casting)
15760 SetChannelObjectGuid(ObjectGuid());
15761 SetUInt32Value(UNIT_CHANNEL_SPELL
, 0);
15763 // clear charm/summon related fields
15766 SetTargetGuid(ObjectGuid());
15767 SetCharmerGuid(ObjectGuid());
15768 SetOwnerGuid(ObjectGuid());
15769 SetCreatorGuid(ObjectGuid());
15771 // reset some aura modifiers before aura apply
15773 SetGuidValue(PLAYER_FARSIGHT
, ObjectGuid());
15774 SetUInt32Value(PLAYER_TRACK_CREATURES
, 0);
15775 SetUInt32Value(PLAYER_TRACK_RESOURCES
, 0);
15777 // cleanup aura list explicitly before skill load where some spells can be applied
15780 // make sure the unit is considered out of combat for proper loading
15783 // make sure the unit is considered not in duel for proper loading
15784 SetGuidValue(PLAYER_DUEL_ARBITER
, ObjectGuid());
15785 SetUInt32Value(PLAYER_DUEL_TEAM
, 0);
15787 m_specsCount
= fields
[52].GetUInt8();
15788 m_activeSpec
= fields
[53].GetUInt8();
15790 Tokens talentTrees
= StrSplit(fields
[26].GetString(), " ");
15791 for (uint8 i
= 0; i
< MAX_TALENT_SPEC_COUNT
; ++i
)
15793 if (i
>= talentTrees
.size())
15796 uint32 talentTree
= atol(talentTrees
[i
].c_str());
15797 if (!talentTree
|| sTalentTabStore
.LookupEntry(talentTree
))
15798 m_talentsPrimaryTree
[i
] = talentTree
;
15799 else if (i
== m_activeSpec
)
15800 SetAtLoginFlag(AT_LOGIN_RESET_TALENTS
); // invalid tree, reset talents
15803 // reset stats before loading any modifiers
15804 InitStatsForLevel();
15805 InitGlyphsForLevel();
15806 InitTaxiNodesForLevel();
15809 // rest bonus can only be calculated after InitStatsForLevel()
15810 m_rest_bonus
= fields
[21].GetFloat();
15814 // speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour)
15815 float bubble0
= 0.031f
;
15816 // speed collect rest bonus in offline, in logout, in tavern, city (section/in hour)
15817 float bubble1
= 0.125f
;
15818 float bubble
= fields
[23].GetUInt32() > 0
15819 ? bubble1
* sWorld
.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_TAVERN_OR_CITY
)
15820 : bubble0
* sWorld
.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_WILDERNESS
);
15822 SetRestBonus(GetRestBonus() + time_diff
* ((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP
) / 72000)*bubble
);
15825 // load skills after InitStatsForLevel because it triggering aura apply also
15826 _LoadSkills(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADSKILLS
));
15828 // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
15831 _LoadMails(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADMAILS
));
15832 _LoadMailedItems(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADMAILEDITEMS
));
15833 UpdateNextMailTimeAndUnreads();
15835 _LoadGlyphs(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADGLYPHS
));
15837 _LoadAuras(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADAURAS
), time_diff
);
15840 // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
15841 if (HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GHOST
))
15842 m_deathState
= DEAD
;
15844 _LoadCurrencies(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADCURRENCIES
));
15845 _LoadSpells(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS
));
15847 // after spell load, learn rewarded spell if need also
15848 _LoadQuestStatus(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS
));
15849 _LoadDailyQuestStatus(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS
));
15850 _LoadWeeklyQuestStatus(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS
));
15851 _LoadMonthlyQuestStatus(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADMONTHLYQUESTSTATUS
));
15853 _LoadTalents(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADTALENTS
));
15855 // after spell and quest load
15856 InitTalentForLevel();
15857 learnDefaultSpells();
15859 // must be before inventory (some items required reputation check)
15860 m_reputationMgr
.LoadFromDB(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADREPUTATION
));
15862 _LoadInventory(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADINVENTORY
), time_diff
);
15863 _LoadItemLoot(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADITEMLOOT
));
15865 // update items with duration and realtime
15866 UpdateItemDuration(time_diff
, true);
15868 _LoadActions(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADACTIONS
));
15870 m_social
= sSocialMgr
.LoadFromDB(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADSOCIALLIST
), GetObjectGuid());
15872 // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES
15873 // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded
15874 uint32 curTitle
= fields
[43].GetUInt32();
15875 if (curTitle
&& !HasTitle(curTitle
))
15878 SetUInt32Value(PLAYER_CHOSEN_TITLE
, curTitle
);
15880 // Not finish taxi flight path
15881 if (m_bgData
.HasTaxiPath())
15883 m_taxi
.ClearTaxiDestinations();
15884 for (int i
= 0; i
< 2; ++i
)
15885 m_taxi
.AddTaxiDestination(m_bgData
.taxiPath
[i
]);
15887 else if (!m_taxi
.LoadTaxiDestinationsFromString(taxi_nodes
, GetTeam()))
15889 // problems with taxi path loading
15890 TaxiNodesEntry
const* nodeEntry
= NULL
;
15891 if (uint32 node_id
= m_taxi
.GetTaxiSource())
15892 nodeEntry
= sTaxiNodesStore
.LookupEntry(node_id
);
15894 if (!nodeEntry
) // don't know taxi start node, to homebind
15896 sLog
.outError("Character %u have wrong data in taxi destination list, teleport to homebind.", GetGUIDLow());
15897 RelocateToHomebind();
15899 else // have start node, to it
15901 sLog
.outError("Character %u have too short taxi destination list, teleport to original node.", GetGUIDLow());
15902 SetLocationMapId(nodeEntry
->map_id
);
15903 Relocate(nodeEntry
->x
, nodeEntry
->y
, nodeEntry
->z
, 0.0f
);
15906 // we can be relocated from taxi and still have an outdated Map pointer!
15907 // so we need to get a new Map pointer!
15908 SetMap(sMapMgr
.CreateMap(GetMapId(), this));
15909 SaveRecallPosition(); // save as recall also to prevent recall and fall from sky
15911 m_taxi
.ClearTaxiDestinations();
15914 if (uint32 node_id
= m_taxi
.GetTaxiSource())
15916 // save source node as recall coord to prevent recall and fall from sky
15917 TaxiNodesEntry
const* nodeEntry
= sTaxiNodesStore
.LookupEntry(node_id
);
15918 MANGOS_ASSERT(nodeEntry
); // checked in m_taxi.LoadTaxiDestinationsFromString
15919 m_recallMap
= nodeEntry
->map_id
;
15920 m_recallX
= nodeEntry
->x
;
15921 m_recallY
= nodeEntry
->y
;
15922 m_recallZ
= nodeEntry
->z
;
15924 // flight will started later
15927 // has to be called after last Relocate() in Player::LoadFromDB
15928 SetFallInformation(0, GetPositionZ());
15930 _LoadSpellCooldowns(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS
));
15932 // Spell code allow apply any auras to dead character in load time in aura/spell/item loading
15933 // Do now before stats re-calculation cleanup for ghost state unexpected auras
15935 RemoveAllAurasOnDeath();
15937 // apply all stat bonuses from items and auras
15938 SetCanModifyStats(true);
15941 // restore remembered power/health values (but not more max values)
15942 uint32 savedhealth
= fields
[46].GetUInt32();
15943 SetHealth(savedhealth
> GetMaxHealth() ? GetMaxHealth() : savedhealth
);
15945 static_assert(MAX_STORED_POWERS
== 5, "Query not updated.");
15946 for (uint32 i
= 0; i
< MAX_STORED_POWERS
; ++i
)
15948 uint32 savedpower
= fields
[47 + i
].GetUInt32();
15949 SetPowerByIndex(i
, std::min(savedpower
, GetMaxPowerByIndex(i
)));
15952 DEBUG_FILTER_LOG(LOG_FILTER_PLAYER_STATS
, "The value of player %s after load item and aura is: ", m_name
.c_str());
15953 outDebugStatsValues();
15959 if (GetSession()->GetSecurity() > SEC_PLAYER
)
15961 switch (sWorld
.getConfig(CONFIG_UINT32_GM_LOGIN_STATE
))
15964 case 0: break; // disable
15965 case 1: SetGameMaster(true); break; // enable
15966 case 2: // save state
15967 if (extraflags
& PLAYER_EXTRA_GM_ON
)
15968 SetGameMaster(true);
15972 switch (sWorld
.getConfig(CONFIG_UINT32_GM_VISIBLE_STATE
))
15975 case 0: SetGMVisible(false); break; // invisible
15976 case 1: break; // visible
15977 case 2: // save state
15978 if (extraflags
& PLAYER_EXTRA_GM_INVISIBLE
)
15979 SetGMVisible(false);
15983 switch (sWorld
.getConfig(CONFIG_UINT32_GM_ACCEPT_TICKETS
))
15986 case 0: break; // disable
15987 case 1: SetAcceptTicket(true); break; // enable
15988 case 2: // save state
15989 if (extraflags
& PLAYER_EXTRA_GM_ACCEPT_TICKETS
)
15990 SetAcceptTicket(true);
15994 switch (sWorld
.getConfig(CONFIG_UINT32_GM_CHAT
))
15997 case 0: break; // disable
15998 case 1: SetGMChat(true); break; // enable
15999 case 2: // save state
16000 if (extraflags
& PLAYER_EXTRA_GM_CHAT
)
16005 switch (sWorld
.getConfig(CONFIG_UINT32_GM_WISPERING_TO
))
16008 case 0: break; // disable
16009 case 1: SetAcceptWhispers(true); break; // enable
16010 case 2: // save state
16011 if (extraflags
& PLAYER_EXTRA_ACCEPT_WHISPERS
)
16012 SetAcceptWhispers(true);
16017 _LoadDeclinedNames(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES
));
16019 m_achievementMgr
.CheckAllAchievementCriteria();
16021 _LoadEquipmentSets(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS
));
16026 bool Player::isAllowedToLoot(Creature
* creature
)
16028 // never tapped by any (mob solo kill)
16029 if (!creature
->HasFlag(UNIT_DYNAMIC_FLAGS
, UNIT_DYNFLAG_TAPPED
))
16032 if (Player
* recipient
= creature
->GetLootRecipient())
16034 if (recipient
== this)
16037 if (Group
* otherGroup
= recipient
->GetGroup())
16039 Group
* thisGroup
= GetGroup();
16043 return thisGroup
== otherGroup
;
16048 // prevent other players from looting if the recipient got disconnected
16049 return !creature
->HasLootRecipient();
16052 void Player::_LoadActions(QueryResult
* result
)
16054 for (int i
= 0; i
< MAX_TALENT_SPEC_COUNT
; ++i
)
16055 m_actionButtons
[i
].clear();
16057 // QueryResult *result = CharacterDatabase.PQuery("SELECT spec, button,action,type FROM character_action WHERE guid = '%u' ORDER BY button",GetGUIDLow());
16063 Field
* fields
= result
->Fetch();
16065 uint8 spec
= fields
[0].GetUInt8();
16066 uint8 button
= fields
[1].GetUInt8();
16067 uint32 action
= fields
[2].GetUInt32();
16068 uint8 type
= fields
[3].GetUInt8();
16070 if (ActionButton
* ab
= addActionButton(spec
, button
, action
, type
))
16071 ab
->uState
= ACTIONBUTTON_UNCHANGED
;
16074 sLog
.outError(" ...at loading, and will deleted in DB also");
16076 // Will deleted in DB at next save (it can create data until save but marked as deleted)
16077 m_actionButtons
[spec
][button
].uState
= ACTIONBUTTON_DELETED
;
16080 while (result
->NextRow());
16086 void Player::_LoadAuras(QueryResult
* result
, uint32 timediff
)
16088 // RemoveAllAuras(); -- some spells casted before aura load, for example in LoadSkills, aura list explicitly cleaned early
16090 // QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,item_guid,spell,stackcount,remaincharges,basepoints0,basepoints1,basepoints2,periodictime0,periodictime1,periodictime2,maxduration,remaintime,effIndexMask FROM character_aura WHERE guid = '%u'",GetGUIDLow());
16096 Field
* fields
= result
->Fetch();
16097 ObjectGuid caster_guid
= ObjectGuid(fields
[0].GetUInt64());
16098 uint32 item_lowguid
= fields
[1].GetUInt32();
16099 uint32 spellid
= fields
[2].GetUInt32();
16100 uint32 stackcount
= fields
[3].GetUInt32();
16101 uint32 remaincharges
= fields
[4].GetUInt32();
16102 int32 damage
[MAX_EFFECT_INDEX
];
16103 uint32 periodicTime
[MAX_EFFECT_INDEX
];
16105 for (int32 i
= 0; i
< MAX_EFFECT_INDEX
; ++i
)
16107 damage
[i
] = fields
[i
+ 5].GetInt32();
16108 periodicTime
[i
] = fields
[i
+ 8].GetUInt32();
16111 int32 maxduration
= fields
[11].GetInt32();
16112 int32 remaintime
= fields
[12].GetInt32();
16113 uint32 effIndexMask
= fields
[13].GetUInt32();
16115 SpellEntry
const* spellproto
= sSpellStore
.LookupEntry(spellid
);
16118 sLog
.outError("Unknown spell (spellid %u), ignore.", spellid
);
16122 if (remaintime
!= -1 && !IsPositiveSpell(spellproto
))
16124 if (remaintime
/ IN_MILLISECONDS
<= int32(timediff
))
16127 remaintime
-= timediff
* IN_MILLISECONDS
;
16130 // prevent wrong values of remaincharges
16131 if (uint32 procCharges
= spellproto
->GetProcCharges())
16133 if (remaincharges
<= 0 || remaincharges
> procCharges
)
16134 remaincharges
= procCharges
;
16139 uint32 defstackamount
= spellproto
->GetStackAmount();
16140 if (!defstackamount
)
16142 else if (defstackamount
< stackcount
)
16143 stackcount
= defstackamount
;
16144 else if (!stackcount
)
16147 SpellAuraHolder
* holder
= CreateSpellAuraHolder(spellproto
, this, NULL
);
16148 holder
->SetLoadedState(caster_guid
, ObjectGuid(HIGHGUID_ITEM
, item_lowguid
), stackcount
, remaincharges
, maxduration
, remaintime
);
16150 for (int32 i
= 0; i
< MAX_EFFECT_INDEX
; ++i
)
16152 if ((effIndexMask
& (1 << i
)) == 0)
16155 Aura
* aura
= CreateAura(spellproto
, SpellEffectIndex(i
), NULL
, holder
, this);
16157 damage
[i
] = aura
->GetModifier()->m_amount
;
16159 aura
->SetLoadedState(damage
[i
], periodicTime
[i
]);
16160 holder
->AddAura(aura
, SpellEffectIndex(i
));
16163 if (!holder
->IsEmptyHolder())
16165 // reset stolen single target auras
16166 if (caster_guid
!= GetObjectGuid() && holder
->GetTrackedAuraType() == TRACK_AURA_TYPE_SINGLE_TARGET
)
16167 holder
->SetTrackedAuraType(TRACK_AURA_TYPE_NOT_TRACKED
);
16169 AddSpellAuraHolder(holder
);
16170 DETAIL_LOG("Added auras from spellid %u", spellproto
->Id
);
16175 while (result
->NextRow());
16179 if (getClass() == CLASS_WARRIOR
&& !HasAuraType(SPELL_AURA_MOD_SHAPESHIFT
))
16180 CastSpell(this, SPELL_ID_PASSIVE_BATTLE_STANCE
, true);
16183 void Player::_LoadGlyphs(QueryResult
* result
)
16189 // "SELECT spec, slot, glyph FROM character_glyphs WHERE guid='%u'"
16193 Field
* fields
= result
->Fetch();
16194 uint8 spec
= fields
[0].GetUInt8();
16195 uint8 slot
= fields
[1].GetUInt8();
16196 uint32 glyph
= fields
[2].GetUInt32();
16198 GlyphPropertiesEntry
const* gp
= sGlyphPropertiesStore
.LookupEntry(glyph
);
16201 sLog
.outError("Player %s has not existing glyph entry %u on index %u, spec %u", m_name
.c_str(), glyph
, slot
, spec
);
16205 GlyphSlotEntry
const* gs
= sGlyphSlotStore
.LookupEntry(GetGlyphSlot(slot
));
16208 sLog
.outError("Player %s has not existing glyph slot entry %u on index %u, spec %u", m_name
.c_str(), GetGlyphSlot(slot
), slot
, spec
);
16212 if (gp
->TypeFlags
!= gs
->TypeFlags
)
16214 sLog
.outError("Player %s has glyph with typeflags %u in slot with typeflags %u, removing.", m_name
.c_str(), gp
->TypeFlags
, gs
->TypeFlags
);
16218 m_glyphs
[spec
][slot
].id
= glyph
;
16221 while (result
->NextRow());
16226 void Player::LoadCorpse()
16230 sObjectAccessor
.ConvertCorpseForPlayer(GetObjectGuid());
16234 if (Corpse
* corpse
= GetCorpse())
16236 ApplyModByteFlag(PLAYER_FIELD_BYTES
, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER
, corpse
&& !sMapStore
.LookupEntry(corpse
->GetMapId())->Instanceable());
16240 // Prevent Dead Player login without corpse
16241 ResurrectPlayer(0.5f
);
16246 void Player::_LoadInventory(QueryResult
* result
, uint32 timediff
)
16248 // QueryResult *result = CharacterDatabase.PQuery("SELECT data,text,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GetGUIDLow());
16249 std::map
<uint32
, Bag
*> bagMap
; // fast guid lookup for bags
16250 // NOTE: the "order by `bag`" is important because it makes sure
16251 // the bagMap is filled before items in the bags are loaded
16252 // NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?)
16253 // expected to be equipped before offhand items (TODO: fixme)
16255 uint32 zone
= GetZoneId();
16259 std::list
<Item
*> problematicItems
;
16261 // prevent items from being added to the queue when stored
16262 m_itemUpdateQueueBlocked
= true;
16265 Field
* fields
= result
->Fetch();
16266 uint32 bag_guid
= fields
[2].GetUInt32();
16267 uint8 slot
= fields
[3].GetUInt8();
16268 uint32 item_lowguid
= fields
[4].GetUInt32();
16269 uint32 item_id
= fields
[5].GetUInt32();
16271 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(item_id
);
16275 CharacterDatabase
.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_lowguid
);
16276 CharacterDatabase
.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_lowguid
);
16277 sLog
.outError("Player::_LoadInventory: Player %s has an unknown item (id: #%u) in inventory, deleted.", GetName(), item_id
);
16281 Item
* item
= NewItemOrBag(proto
);
16283 if (!item
->LoadFromDB(item_lowguid
, fields
, GetObjectGuid()))
16285 sLog
.outError("Player::_LoadInventory: Player %s has broken item (id: #%u) in inventory, deleted.", GetName(), item_id
);
16286 CharacterDatabase
.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_lowguid
);
16287 item
->FSetState(ITEM_REMOVED
);
16288 item
->SaveToDB(); // it also deletes item object !
16292 // not allow have in alive state item limited to another map/zone
16293 if (isAlive() && item
->IsLimitedToAnotherMapOrZone(GetMapId(), zone
))
16295 CharacterDatabase
.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_lowguid
);
16296 item
->FSetState(ITEM_REMOVED
);
16297 item
->SaveToDB(); // it also deletes item object !
16301 // "Conjured items disappear if you are logged out for more than 15 minutes"
16302 if (timediff
> 15 * MINUTE
&& (item
->GetProto()->Flags
& ITEM_FLAG_CONJURED
))
16304 CharacterDatabase
.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_lowguid
);
16305 item
->FSetState(ITEM_REMOVED
);
16306 item
->SaveToDB(); // it also deletes item object !
16310 bool success
= true;
16312 // the item/bag is not in a bag
16315 item
->SetContainer(NULL
);
16316 item
->SetSlot(slot
);
16318 if (IsInventoryPos(INVENTORY_SLOT_BAG_0
, slot
))
16320 ItemPosCountVec dest
;
16321 if (CanStoreItem(INVENTORY_SLOT_BAG_0
, slot
, dest
, item
, false) == EQUIP_ERR_OK
)
16322 item
= StoreItem(dest
, item
, true);
16326 else if (IsEquipmentPos(INVENTORY_SLOT_BAG_0
, slot
))
16329 if (CanEquipItem(slot
, dest
, item
, false, false) == EQUIP_ERR_OK
)
16330 QuickEquipItem(dest
, item
);
16334 else if (IsBankPos(INVENTORY_SLOT_BAG_0
, slot
))
16336 ItemPosCountVec dest
;
16337 if (CanBankItem(INVENTORY_SLOT_BAG_0
, slot
, dest
, item
, false, false) == EQUIP_ERR_OK
)
16338 item
= BankItem(dest
, item
, true);
16345 // store bags that may contain items in them
16346 if (item
->IsBag() && IsBagPos(item
->GetPos()))
16347 bagMap
[item_lowguid
] = (Bag
*)item
;
16350 // the item/bag in a bag
16353 item
->SetSlot(NULL_SLOT
);
16354 // the item is in a bag, find the bag
16355 std::map
<uint32
, Bag
*>::const_iterator itr
= bagMap
.find(bag_guid
);
16356 if (itr
!= bagMap
.end() && slot
< itr
->second
->GetBagSize())
16358 ItemPosCountVec dest
;
16359 if (CanStoreItem(itr
->second
->GetSlot(), slot
, dest
, item
, false) == EQUIP_ERR_OK
)
16360 item
= StoreItem(dest
, item
, true);
16368 // item's state may have changed after stored
16371 item
->SetState(ITEM_UNCHANGED
, this);
16373 // restore container unchanged state also
16374 if (item
->GetContainer())
16375 item
->GetContainer()->SetState(ITEM_UNCHANGED
, this);
16377 // recharged mana gem
16378 if (timediff
> 15 * MINUTE
&& proto
->ItemLimitCategory
== ITEM_LIMIT_CATEGORY_MANA_GEM
)
16379 item
->RestoreCharges();
16383 sLog
.outError("Player::_LoadInventory: Player %s has item (GUID: %u Entry: %u) can't be loaded to inventory (Bag GUID: %u Slot: %u) by some reason, will send by mail.", GetName(), item_lowguid
, item_id
, bag_guid
, slot
);
16384 CharacterDatabase
.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_lowguid
);
16385 problematicItems
.push_back(item
);
16388 while (result
->NextRow());
16391 m_itemUpdateQueueBlocked
= false;
16393 // send by mail problematic items
16394 while (!problematicItems
.empty())
16396 std::string subject
= GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM
);
16399 MailDraft
draft(subject
, "There's were problems with equipping item(s).");
16401 for (int i
= 0; !problematicItems
.empty() && i
< MAX_MAIL_ITEMS
; ++i
)
16403 Item
* item
= problematicItems
.front();
16404 problematicItems
.pop_front();
16406 draft
.AddItem(item
);
16409 draft
.SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM
), MAIL_CHECK_MASK_COPIED
);
16414 _ApplyAllItemMods();
16417 void Player::_LoadItemLoot(QueryResult
* result
)
16419 // QueryResult *result = CharacterDatabase.PQuery("SELECT guid,itemid,amount,suffix,property FROM item_loot WHERE guid = '%u'", GetGUIDLow());
16425 Field
* fields
= result
->Fetch();
16426 uint32 item_guid
= fields
[0].GetUInt32();
16428 Item
* item
= GetItemByGuid(ObjectGuid(HIGHGUID_ITEM
, item_guid
));
16432 CharacterDatabase
.PExecute("DELETE FROM item_loot WHERE guid = '%u'", item_guid
);
16433 sLog
.outError("Player::_LoadItemLoot: Player %s has loot for nonexistent item (GUID: %u) in `item_loot`, deleted.", GetName(), item_guid
);
16437 item
->LoadLootFromDB(fields
);
16440 while (result
->NextRow());
16446 // load mailed item which should receive current player
16447 void Player::_LoadMailedItems(QueryResult
* result
)
16449 // data needs to be at first place for Item::LoadFromDB
16451 // "SELECT data, text, mail_id, item_guid, item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE receiver = '%u'", GUID_LOPART(m_guid)
16457 Field
* fields
= result
->Fetch();
16458 uint32 mail_id
= fields
[2].GetUInt32();
16459 uint32 item_guid_low
= fields
[3].GetUInt32();
16460 uint32 item_template
= fields
[4].GetUInt32();
16462 Mail
* mail
= GetMail(mail_id
);
16465 mail
->AddItem(item_guid_low
, item_template
);
16467 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(item_template
);
16471 sLog
.outError("Player %u has unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), item_guid_low
, item_template
, mail
->messageID
);
16472 CharacterDatabase
.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low
);
16473 CharacterDatabase
.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid_low
);
16477 Item
* item
= NewItemOrBag(proto
);
16479 if (!item
->LoadFromDB(item_guid_low
, fields
, GetObjectGuid()))
16481 sLog
.outError("Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail
->messageID
, item_guid_low
);
16482 CharacterDatabase
.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low
);
16483 item
->FSetState(ITEM_REMOVED
);
16484 item
->SaveToDB(); // it also deletes item object !
16490 while (result
->NextRow());
16495 void Player::_LoadMails(QueryResult
* result
)
16498 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
16499 //"SELECT id,messageType,sender,receiver,subject,body,expire_time,deliver_time,money,cod,checked,stationery,mailTemplateId,has_items FROM mail WHERE receiver = '%u' ORDER BY id DESC", GetGUIDLow()
16505 Field
* fields
= result
->Fetch();
16506 Mail
* m
= new Mail
;
16507 m
->messageID
= fields
[0].GetUInt32();
16508 m
->messageType
= fields
[1].GetUInt8();
16509 m
->sender
= fields
[2].GetUInt32();
16510 m
->receiverGuid
= ObjectGuid(HIGHGUID_PLAYER
, fields
[3].GetUInt32());
16511 m
->subject
= fields
[4].GetCppString();
16512 m
->body
= fields
[5].GetCppString();
16513 m
->expire_time
= (time_t)fields
[6].GetUInt64();
16514 m
->deliver_time
= (time_t)fields
[7].GetUInt64();
16515 m
->money
= fields
[8].GetUInt32();
16516 m
->COD
= fields
[9].GetUInt32();
16517 m
->checked
= fields
[10].GetUInt32();
16518 m
->stationery
= fields
[11].GetUInt8();
16519 m
->mailTemplateId
= fields
[12].GetInt16();
16520 m
->has_items
= fields
[13].GetBool(); // true, if mail have items or mail have template and items generated (maybe none)
16522 if (m
->mailTemplateId
&& !sMailTemplateStore
.LookupEntry(m
->mailTemplateId
))
16524 sLog
.outError("Player::_LoadMail - Mail (%u) have nonexistent MailTemplateId (%u), remove at load", m
->messageID
, m
->mailTemplateId
);
16525 m
->mailTemplateId
= 0;
16528 m
->state
= MAIL_STATE_UNCHANGED
;
16530 m_mail
.push_back(m
);
16532 if (m
->mailTemplateId
&& !m
->has_items
)
16533 m
->prepareTemplateItems(this);
16536 while (result
->NextRow());
16540 void Player::LoadPet()
16542 // fixme: the pet should still be loaded if the player is not in world
16543 // just not added to the map
16546 Pet
* pet
= new Pet
;
16547 if (!pet
->LoadPetFromDB(this, 0, 0, true))
16552 void Player::_LoadQuestStatus(QueryResult
* result
)
16554 mQuestStatus
.clear();
16558 //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
16559 // QueryResult *result = CharacterDatabase.PQuery("SELECT quest, status, rewarded, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3, itemcount4, itemcount5, itemcount6 FROM character_queststatus WHERE guid = '%u'", GetGUIDLow());
16565 Field
* fields
= result
->Fetch();
16567 uint32 quest_id
= fields
[0].GetUInt32();
16568 // used to be new, no delete?
16569 Quest
const* pQuest
= sObjectMgr
.GetQuestTemplate(quest_id
);
16573 QuestStatusData
& questStatusData
= mQuestStatus
[quest_id
];
16575 uint32 qstatus
= fields
[1].GetUInt32();
16576 if (qstatus
< MAX_QUEST_STATUS
)
16577 questStatusData
.m_status
= QuestStatus(qstatus
);
16580 questStatusData
.m_status
= QUEST_STATUS_NONE
;
16581 sLog
.outError("Player %s have invalid quest %d status (%d), replaced by QUEST_STATUS_NONE(0).", GetName(), quest_id
, qstatus
);
16584 questStatusData
.m_rewarded
= (fields
[2].GetUInt8() > 0);
16585 questStatusData
.m_explored
= (fields
[3].GetUInt8() > 0);
16587 time_t quest_time
= time_t(fields
[4].GetUInt64());
16589 if (pQuest
->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED
) && !GetQuestRewardStatus(quest_id
) && questStatusData
.m_status
!= QUEST_STATUS_NONE
)
16591 AddTimedQuest(quest_id
);
16593 if (quest_time
<= sWorld
.GetGameTime())
16594 questStatusData
.m_timer
= 1;
16596 questStatusData
.m_timer
= uint32(quest_time
- sWorld
.GetGameTime()) * IN_MILLISECONDS
;
16601 questStatusData
.m_creatureOrGOcount
[0] = fields
[5].GetUInt32();
16602 questStatusData
.m_creatureOrGOcount
[1] = fields
[6].GetUInt32();
16603 questStatusData
.m_creatureOrGOcount
[2] = fields
[7].GetUInt32();
16604 questStatusData
.m_creatureOrGOcount
[3] = fields
[8].GetUInt32();
16605 questStatusData
.m_itemcount
[0] = fields
[9].GetUInt32();
16606 questStatusData
.m_itemcount
[1] = fields
[10].GetUInt32();
16607 questStatusData
.m_itemcount
[2] = fields
[11].GetUInt32();
16608 questStatusData
.m_itemcount
[3] = fields
[12].GetUInt32();
16609 questStatusData
.m_itemcount
[4] = fields
[13].GetUInt32();
16610 questStatusData
.m_itemcount
[5] = fields
[14].GetUInt32();
16612 questStatusData
.uState
= QUEST_UNCHANGED
;
16614 // add to quest log
16615 if (slot
< MAX_QUEST_LOG_SIZE
&&
16616 ((questStatusData
.m_status
== QUEST_STATUS_INCOMPLETE
||
16617 questStatusData
.m_status
== QUEST_STATUS_COMPLETE
||
16618 questStatusData
.m_status
== QUEST_STATUS_FAILED
) &&
16619 (!questStatusData
.m_rewarded
|| pQuest
->IsRepeatable())))
16621 SetQuestSlot(slot
, quest_id
, uint32(quest_time
));
16623 if (questStatusData
.m_explored
)
16624 SetQuestSlotState(slot
, QUEST_STATE_COMPLETE
);
16626 if (questStatusData
.m_status
== QUEST_STATUS_COMPLETE
)
16627 SetQuestSlotState(slot
, QUEST_STATE_COMPLETE
);
16629 if (questStatusData
.m_status
== QUEST_STATUS_FAILED
)
16630 SetQuestSlotState(slot
, QUEST_STATE_FAIL
);
16632 for (uint8 idx
= 0; idx
< QUEST_OBJECTIVES_COUNT
; ++idx
)
16633 if (questStatusData
.m_creatureOrGOcount
[idx
])
16634 SetQuestSlotCounter(slot
, idx
, questStatusData
.m_creatureOrGOcount
[idx
]);
16639 if (questStatusData
.m_rewarded
)
16641 // learn rewarded spell if unknown
16642 learnQuestRewardedSpells(pQuest
);
16644 // set rewarded title if any
16645 if (pQuest
->GetCharTitleId())
16647 if (CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(pQuest
->GetCharTitleId()))
16648 SetTitle(titleEntry
);
16651 if (pQuest
->GetBonusTalents())
16652 m_questRewardTalentCount
+= pQuest
->GetBonusTalents();
16655 DEBUG_LOG("Quest status is {%u} for quest {%u} for player (GUID: %u)", questStatusData
.m_status
, quest_id
, GetGUIDLow());
16658 while (result
->NextRow());
16663 // clear quest log tail
16664 for (uint16 i
= slot
; i
< MAX_QUEST_LOG_SIZE
; ++i
)
16665 SetQuestSlot(i
, 0);
16668 void Player::_LoadDailyQuestStatus(QueryResult
* result
)
16670 for (uint32 quest_daily_idx
= 0; quest_daily_idx
< PLAYER_MAX_DAILY_QUESTS
; ++quest_daily_idx
)
16671 SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1
+ quest_daily_idx
, 0);
16673 // QueryResult *result = CharacterDatabase.PQuery("SELECT quest FROM character_queststatus_daily WHERE guid = '%u'", GetGUIDLow());
16677 uint32 quest_daily_idx
= 0;
16681 if (quest_daily_idx
>= PLAYER_MAX_DAILY_QUESTS
) // max amount with exist data in query
16683 sLog
.outError("Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`", GetGUIDLow());
16687 Field
* fields
= result
->Fetch();
16689 uint32 quest_id
= fields
[0].GetUInt32();
16691 Quest
const* pQuest
= sObjectMgr
.GetQuestTemplate(quest_id
);
16695 SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1
+ quest_daily_idx
, quest_id
);
16698 DEBUG_LOG("Daily quest {%u} cooldown for player (GUID: %u)", quest_id
, GetGUIDLow());
16700 while (result
->NextRow());
16705 m_DailyQuestChanged
= false;
16708 void Player::_LoadWeeklyQuestStatus(QueryResult
* result
)
16710 m_weeklyquests
.clear();
16712 // QueryResult *result = CharacterDatabase.PQuery("SELECT quest FROM character_queststatus_weekly WHERE guid = '%u'", GetGUIDLow());
16718 Field
* fields
= result
->Fetch();
16720 uint32 quest_id
= fields
[0].GetUInt32();
16722 Quest
const* pQuest
= sObjectMgr
.GetQuestTemplate(quest_id
);
16726 m_weeklyquests
.insert(quest_id
);
16728 DEBUG_LOG("Weekly quest {%u} cooldown for player (GUID: %u)", quest_id
, GetGUIDLow());
16730 while (result
->NextRow());
16734 m_WeeklyQuestChanged
= false;
16737 void Player::_LoadMonthlyQuestStatus(QueryResult
* result
)
16739 m_monthlyquests
.clear();
16741 // QueryResult *result = CharacterDatabase.PQuery("SELECT quest FROM character_queststatus_weekly WHERE guid = '%u'", GetGUIDLow());
16747 Field
* fields
= result
->Fetch();
16749 uint32 quest_id
= fields
[0].GetUInt32();
16751 Quest
const* pQuest
= sObjectMgr
.GetQuestTemplate(quest_id
);
16755 m_monthlyquests
.insert(quest_id
);
16757 DEBUG_LOG("Monthly quest {%u} cooldown for player (GUID: %u)", quest_id
, GetGUIDLow());
16759 while (result
->NextRow());
16764 m_MonthlyQuestChanged
= false;
16767 void Player::_LoadSpells(QueryResult
* result
)
16769 // QueryResult *result = CharacterDatabase.PQuery("SELECT spell,active,disabled FROM character_spell WHERE guid = '%u'",GetGUIDLow());
16775 Field
* fields
= result
->Fetch();
16777 uint32 spell_id
= fields
[0].GetUInt32();
16779 // skip talents & drop unneeded data
16780 if (GetTalentSpellPos(spell_id
))
16782 sLog
.outError("Player::_LoadSpells: %s has talent spell %u in character_spell, removing it.",
16783 GetGuidStr().c_str(), spell_id
);
16784 CharacterDatabase
.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id
);
16788 addSpell(spell_id
, fields
[1].GetBool(), false, false, fields
[2].GetBool());
16790 while (result
->NextRow());
16796 void Player::_LoadTalents(QueryResult
* result
)
16798 // QueryResult *result = CharacterDatabase.PQuery("SELECT talent_id, current_rank, spec FROM character_talent WHERE guid = '%u'",GetGUIDLow());
16803 Field
* fields
= result
->Fetch();
16805 uint32 talent_id
= fields
[0].GetUInt32();
16806 TalentEntry
const* talentInfo
= sTalentStore
.LookupEntry(talent_id
);
16810 sLog
.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent_id: %u , this talent will be deleted from character_talent", GetGUIDLow(), talent_id
);
16811 CharacterDatabase
.PExecute("DELETE FROM character_talent WHERE talent_id = '%u'", talent_id
);
16815 TalentTabEntry
const* talentTabInfo
= sTalentTabStore
.LookupEntry(talentInfo
->TalentTab
);
16817 if (!talentTabInfo
)
16819 sLog
.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talentTabInfo: %u for talentID: %u , this talent will be deleted from character_talent", GetGUIDLow(), talentInfo
->TalentTab
, talentInfo
->TalentID
);
16820 CharacterDatabase
.PExecute("DELETE FROM character_talent WHERE talent_id = '%u'", talent_id
);
16824 // prevent load talent for different class (cheating)
16825 if ((getClassMask() & talentTabInfo
->ClassMask
) == 0)
16827 sLog
.outError("Player::_LoadTalents:Player (GUID: %u) has talent with ClassMask: %u , but Player's ClassMask is: %u , talentID: %u , this talent will be deleted from character_talent", GetGUIDLow(), talentTabInfo
->ClassMask
, getClassMask() , talentInfo
->TalentID
);
16828 CharacterDatabase
.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND talent_id = '%u'", GetGUIDLow(), talent_id
);
16832 uint32 currentRank
= fields
[1].GetUInt32();
16834 if (currentRank
> MAX_TALENT_RANK
|| talentInfo
->RankID
[currentRank
] == 0)
16836 sLog
.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent rank: %u , talentID: %u , this talent will be deleted from character_talent", GetGUIDLow(), currentRank
, talentInfo
->TalentID
);
16837 CharacterDatabase
.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND talent_id = '%u'", GetGUIDLow(), talent_id
);
16841 uint32 spec
= fields
[2].GetUInt32();
16843 if (spec
> MAX_TALENT_SPEC_COUNT
)
16845 sLog
.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent spec: %u, spec will be deleted from character_talent", GetGUIDLow(), spec
);
16846 CharacterDatabase
.PExecute("DELETE FROM character_talent WHERE spec = '%u' ", spec
);
16850 if (spec
>= m_specsCount
)
16852 sLog
.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent spec: %u , this spec will be deleted from character_talent.", GetGUIDLow(), spec
);
16853 CharacterDatabase
.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND spec = '%u' ", GetGUIDLow(), spec
);
16857 if (m_activeSpec
== spec
)
16858 addSpell(talentInfo
->RankID
[currentRank
], true, false, false, false);
16861 PlayerTalent talent
;
16862 talent
.currentRank
= currentRank
;
16863 talent
.talentEntry
= talentInfo
;
16864 talent
.state
= PLAYERSPELL_UNCHANGED
;
16865 m_talents
[spec
][talentInfo
->TalentID
] = talent
;
16868 while (result
->NextRow());
16873 void Player::_LoadGroup(QueryResult
* result
)
16875 // QueryResult *result = CharacterDatabase.PQuery("SELECT groupId FROM group_member WHERE memberGuid='%u'", GetGUIDLow());
16878 uint32 groupId
= (*result
)[0].GetUInt32();
16881 if (Group
* group
= sObjectMgr
.GetGroupById(groupId
))
16883 uint8 subgroup
= group
->GetMemberGroup(GetObjectGuid());
16884 SetGroup(group
, subgroup
);
16885 if (getLevel() >= LEVELREQUIREMENT_HEROIC
)
16887 // the group leader may change the instance difficulty while the player is offline
16888 SetDungeonDifficulty(group
->GetDungeonDifficulty());
16889 SetRaidDifficulty(group
->GetRaidDifficulty());
16895 void Player::_LoadBoundInstances(QueryResult
* result
)
16897 for (uint8 i
= 0; i
< MAX_DIFFICULTY
; ++i
)
16898 m_boundInstances
[i
].clear();
16900 Group
* group
= GetGroup();
16902 // QueryResult *result = CharacterDatabase.PQuery("SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
16907 Field
* fields
= result
->Fetch();
16908 bool perm
= fields
[1].GetBool();
16909 uint32 mapId
= fields
[2].GetUInt32();
16910 uint32 instanceId
= fields
[0].GetUInt32();
16911 uint8 difficulty
= fields
[3].GetUInt8();
16913 time_t resetTime
= (time_t)fields
[4].GetUInt64();
16914 // the resettime for normal instances is only saved when the InstanceSave is unloaded
16915 // so the value read from the DB may be wrong here but only if the InstanceSave is loaded
16916 // and in that case it is not used
16918 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(mapId
);
16919 if (!mapEntry
|| !mapEntry
->IsDungeon())
16921 sLog
.outError("_LoadBoundInstances: player %s(%d) has bind to nonexistent or not dungeon map %d", GetName(), GetGUIDLow(), mapId
);
16922 CharacterDatabase
.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), instanceId
);
16926 if (difficulty
>= MAX_DIFFICULTY
)
16928 sLog
.outError("_LoadBoundInstances: player %s(%d) has bind to nonexistent difficulty %d instance for map %u", GetName(), GetGUIDLow(), difficulty
, mapId
);
16929 CharacterDatabase
.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), instanceId
);
16933 MapDifficultyEntry
const* mapDiff
= GetMapDifficultyData(mapId
, Difficulty(difficulty
));
16936 sLog
.outError("_LoadBoundInstances: player %s(%d) has bind to nonexistent difficulty %d instance for map %u", GetName(), GetGUIDLow(), difficulty
, mapId
);
16937 CharacterDatabase
.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), instanceId
);
16941 if (!perm
&& group
)
16943 sLog
.outError("_LoadBoundInstances: %s is in group (Id: %d) but has a non-permanent character bind to map %d,%d,%d",
16944 GetGuidStr().c_str(), group
->GetId(), mapId
, instanceId
, difficulty
);
16945 CharacterDatabase
.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'",
16946 GetGUIDLow(), instanceId
);
16950 // since non permanent binds are always solo bind, they can always be reset
16951 DungeonPersistentState
* state
= (DungeonPersistentState
*)sMapPersistentStateMgr
.AddPersistentState(mapEntry
, instanceId
, Difficulty(difficulty
), resetTime
, !perm
, true);
16952 if (state
) BindToInstance(state
, perm
, true);
16954 while (result
->NextRow());
16959 InstancePlayerBind
* Player::GetBoundInstance(uint32 mapid
, Difficulty difficulty
)
16961 // some instances only have one difficulty
16962 MapDifficultyEntry
const* mapDiff
= GetMapDifficultyData(mapid
, difficulty
);
16966 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(mapid
);
16967 if (itr
!= m_boundInstances
[difficulty
].end())
16968 return &itr
->second
;
16973 void Player::UnbindInstance(uint32 mapid
, Difficulty difficulty
, bool unload
)
16975 BoundInstancesMap::iterator itr
= m_boundInstances
[difficulty
].find(mapid
);
16976 UnbindInstance(itr
, difficulty
, unload
);
16979 void Player::UnbindInstance(BoundInstancesMap::iterator
& itr
, Difficulty difficulty
, bool unload
)
16981 if (itr
!= m_boundInstances
[difficulty
].end())
16984 CharacterDatabase
.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'",
16985 GetGUIDLow(), itr
->second
.state
->GetInstanceId());
16986 itr
->second
.state
->RemovePlayer(this); // state can become invalid
16987 m_boundInstances
[difficulty
].erase(itr
++);
16991 InstancePlayerBind
* Player::BindToInstance(DungeonPersistentState
* state
, bool permanent
, bool load
)
16995 InstancePlayerBind
& bind
= m_boundInstances
[state
->GetDifficulty()][state
->GetMapId()];
16998 // update the state when the group kills a boss
16999 if (permanent
!= bind
.perm
|| state
!= bind
.state
)
17001 CharacterDatabase
.PExecute("UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'",
17002 state
->GetInstanceId(), permanent
, GetGUIDLow(), bind
.state
->GetInstanceId());
17007 CharacterDatabase
.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')",
17008 GetGUIDLow(), state
->GetInstanceId(), permanent
);
17011 if (bind
.state
!= state
)
17014 bind
.state
->RemovePlayer(this);
17015 state
->AddPlayer(this);
17019 state
->SetCanReset(false);
17021 bind
.state
= state
;
17022 bind
.perm
= permanent
;
17024 DEBUG_LOG("Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d",
17025 GetName(), GetGUIDLow(), state
->GetMapId(), state
->GetInstanceId(), state
->GetDifficulty());
17032 DungeonPersistentState
* Player::GetBoundInstanceSaveForSelfOrGroup(uint32 mapid
)
17034 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(mapid
);
17038 InstancePlayerBind
* pBind
= GetBoundInstance(mapid
, GetDifficulty(mapEntry
->IsRaid()));
17039 DungeonPersistentState
* state
= pBind
? pBind
->state
: NULL
;
17041 // the player's permanent player bind is taken into consideration first
17042 // then the player's group bind and finally the solo bind.
17043 if (!pBind
|| !pBind
->perm
)
17045 InstanceGroupBind
* groupBind
= NULL
;
17046 // use the player's difficulty setting (it may not be the same as the group's)
17047 if (Group
* group
= GetGroup())
17048 if (groupBind
= group
->GetBoundInstance(mapid
, this))
17049 state
= groupBind
->state
;
17055 void Player::SendRaidInfo()
17057 uint32 counter
= 0;
17059 WorldPacket
data(SMSG_RAID_INSTANCE_INFO
, 4);
17061 size_t p_counter
= data
.wpos();
17062 data
<< uint32(counter
); // placeholder
17064 time_t now
= time(NULL
);
17066 for (int i
= 0; i
< MAX_DIFFICULTY
; ++i
)
17068 for (BoundInstancesMap::const_iterator itr
= m_boundInstances
[i
].begin(); itr
!= m_boundInstances
[i
].end(); ++itr
)
17070 if (itr
->second
.perm
)
17072 DungeonPersistentState
* state
= itr
->second
.state
;
17073 data
<< uint32(state
->GetMapId()); // map id
17074 data
<< uint32(state
->GetDifficulty()); // difficulty
17075 data
<< ObjectGuid(state
->GetInstanceGuid());// instance guid
17076 data
<< uint8(1); // expired = 0
17077 data
<< uint8(0); // extended = 1
17078 data
<< uint32(state
->GetResetTime() - now
);// reset time
17083 data
.put
<uint32
>(p_counter
, counter
);
17084 GetSession()->SendPacket(&data
);
17088 - called on every successful teleportation to a map
17090 void Player::SendSavedInstances()
17092 bool hasBeenSaved
= false;
17095 for (uint8 i
= 0; i
< MAX_DIFFICULTY
; ++i
)
17097 for (BoundInstancesMap::const_iterator itr
= m_boundInstances
[i
].begin(); itr
!= m_boundInstances
[i
].end(); ++itr
)
17099 if (itr
->second
.perm
) // only permanent binds are sent
17101 hasBeenSaved
= true;
17107 // Send opcode 811. true or false means, whether you have current raid/heroic instances
17108 data
.Initialize(SMSG_UPDATE_INSTANCE_OWNERSHIP
);
17109 data
<< uint32(hasBeenSaved
);
17110 GetSession()->SendPacket(&data
);
17115 for (uint8 i
= 0; i
< MAX_DIFFICULTY
; ++i
)
17117 for (BoundInstancesMap::const_iterator itr
= m_boundInstances
[i
].begin(); itr
!= m_boundInstances
[i
].end(); ++itr
)
17119 if (itr
->second
.perm
)
17121 data
.Initialize(SMSG_UPDATE_LAST_INSTANCE
);
17122 data
<< uint32(itr
->second
.state
->GetMapId());
17123 GetSession()->SendPacket(&data
);
17129 /// convert the player's binds to the group
17130 void Player::ConvertInstancesToGroup(Player
* player
, Group
* group
, ObjectGuid player_guid
)
17132 bool has_binds
= false;
17133 bool has_solo
= false;
17137 player_guid
= player
->GetObjectGuid();
17139 group
= player
->GetGroup();
17142 MANGOS_ASSERT(player_guid
);
17144 // copy all binds to the group, when changing leader it's assumed the character
17145 // will not have any solo binds
17149 for (uint8 i
= 0; i
< MAX_DIFFICULTY
; ++i
)
17151 for (BoundInstancesMap::iterator itr
= player
->m_boundInstances
[i
].begin(); itr
!= player
->m_boundInstances
[i
].end();)
17156 group
->BindToInstance(itr
->second
.state
, itr
->second
.perm
, true);
17158 // permanent binds are not removed
17159 if (!itr
->second
.perm
)
17161 // increments itr in call
17162 player
->UnbindInstance(itr
, Difficulty(i
), true);
17171 uint32 player_lowguid
= player_guid
.GetCounter();
17173 // if the player's not online we don't know what binds it has
17174 if (!player
|| !group
|| has_binds
)
17175 CharacterDatabase
.PExecute("INSERT INTO group_instance SELECT guid, instance, permanent FROM character_instance WHERE guid = '%u'", player_lowguid
);
17177 // the following should not get executed when changing leaders
17178 if (!player
|| has_solo
)
17179 CharacterDatabase
.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND permanent = 0", player_lowguid
);
17182 bool Player::_LoadHomeBind(QueryResult
* result
)
17184 PlayerInfo
const* info
= sObjectMgr
.GetPlayerInfo(getRace(), getClass());
17187 sLog
.outError("Player have incorrect race/class pair. Can't be loaded.");
17192 // QueryResult *result = CharacterDatabase.PQuery("SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(playerGuid));
17195 Field
* fields
= result
->Fetch();
17196 m_homebindMapId
= fields
[0].GetUInt32();
17197 m_homebindAreaId
= fields
[1].GetUInt16();
17198 m_homebindX
= fields
[2].GetFloat();
17199 m_homebindY
= fields
[3].GetFloat();
17200 m_homebindZ
= fields
[4].GetFloat();
17203 MapEntry
const* bindMapEntry
= sMapStore
.LookupEntry(m_homebindMapId
);
17205 // accept saved data only for valid position (and non instanceable), and accessable
17206 if (MapManager::IsValidMapCoord(m_homebindMapId
, m_homebindX
, m_homebindY
, m_homebindZ
) &&
17207 !bindMapEntry
->Instanceable() && GetSession()->Expansion() >= bindMapEntry
->Expansion())
17212 CharacterDatabase
.PExecute("DELETE FROM character_homebind WHERE guid = '%u'", GetGUIDLow());
17217 m_homebindMapId
= info
->mapId
;
17218 m_homebindAreaId
= info
->areaId
;
17219 m_homebindX
= info
->positionX
;
17220 m_homebindY
= info
->positionY
;
17221 m_homebindZ
= info
->positionZ
;
17223 CharacterDatabase
.PExecute("INSERT INTO character_homebind (guid,map,zone,position_x,position_y,position_z) VALUES ('%u', '%u', '%u', '%f', '%f', '%f')", GetGUIDLow(), m_homebindMapId
, (uint32
)m_homebindAreaId
, m_homebindX
, m_homebindY
, m_homebindZ
);
17226 DEBUG_LOG("Setting player home position: mapid is: %u, zoneid is %u, X is %f, Y is %f, Z is %f",
17227 m_homebindMapId
, m_homebindAreaId
, m_homebindX
, m_homebindY
, m_homebindZ
);
17232 /*********************************************************/
17233 /*** SAVE SYSTEM ***/
17234 /*********************************************************/
17236 void Player::SaveToDB()
17238 // we should assure this: ASSERT((m_nextSave != sWorld.getConfig(CONFIG_UINT32_INTERVAL_SAVE)));
17239 // delay auto save at any saves (manual, in code, or autosave)
17240 m_nextSave
= sWorld
.getConfig(CONFIG_UINT32_INTERVAL_SAVE
);
17242 // lets allow only players in world to be saved
17243 if (IsBeingTeleportedFar())
17245 ScheduleDelayedOperation(DELAYED_SAVE_PLAYER
);
17249 // first save/honor gain after midnight will also update the player's honor fields
17250 UpdateHonorKills();
17252 DEBUG_FILTER_LOG(LOG_FILTER_PLAYER_STATS
, "The value of player %s at save: ", m_name
.c_str());
17253 outDebugStatsValues();
17255 CharacterDatabase
.BeginTransaction();
17257 static SqlStatementID delChar
;
17258 static SqlStatementID insChar
;
17260 SqlStatement stmt
= CharacterDatabase
.CreateStatement(delChar
, "DELETE FROM characters WHERE guid = ?");
17261 stmt
.PExecute(GetGUIDLow());
17263 SqlStatement uberInsert
= CharacterDatabase
.CreateStatement(insChar
, "INSERT INTO characters (guid,account,name,race,class,gender,level,xp,money,playerBytes,playerBytes2,playerFlags,"
17264 "map, dungeon_difficulty, position_x, position_y, position_z, orientation, "
17265 "taximask, online, cinematic, "
17266 "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, primary_trees, "
17267 "trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, "
17268 "death_expire_time, taxi_path, totalKills, "
17269 "todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, "
17270 "power4, power5, specCount, activeSpec, exploredZones, equipmentCache, knownTitles, actionBars, slot) "
17271 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
17272 "?, ?, ?, ?, ?, ?, "
17274 "?, ?, ?, ?, ?, ?, ?, ?, "
17275 "?, ?, ?, ?, ?, ?, ?, ?, ?, "
17277 "?, ?, ?, ?, ?, ?, ?, ?, ?, "
17278 "?, ?, ?, ?, ?, ?, ?, ?, ?) ");
17280 uberInsert
.addUInt32(GetGUIDLow());
17281 uberInsert
.addUInt32(GetSession()->GetAccountId());
17282 uberInsert
.addString(m_name
);
17283 uberInsert
.addUInt8(getRace());
17284 uberInsert
.addUInt8(getClass());
17285 uberInsert
.addUInt8(getGender());
17286 uberInsert
.addUInt32(getLevel());
17287 uberInsert
.addUInt32(GetUInt32Value(PLAYER_XP
));
17288 uberInsert
.addUInt64(GetMoney());
17289 uberInsert
.addUInt32(GetUInt32Value(PLAYER_BYTES
));
17290 uberInsert
.addUInt32(GetUInt32Value(PLAYER_BYTES_2
));
17291 uberInsert
.addUInt32(GetUInt32Value(PLAYER_FLAGS
));
17293 if (!IsBeingTeleported())
17295 uberInsert
.addUInt32(GetMapId());
17296 uberInsert
.addUInt32(uint32(GetDungeonDifficulty()));
17297 uberInsert
.addFloat(finiteAlways(GetPositionX()));
17298 uberInsert
.addFloat(finiteAlways(GetPositionY()));
17299 uberInsert
.addFloat(finiteAlways(GetPositionZ()));
17300 uberInsert
.addFloat(finiteAlways(GetOrientation()));
17304 uberInsert
.addUInt32(GetTeleportDest().mapid
);
17305 uberInsert
.addUInt32(uint32(GetDungeonDifficulty()));
17306 uberInsert
.addFloat(finiteAlways(GetTeleportDest().coord_x
));
17307 uberInsert
.addFloat(finiteAlways(GetTeleportDest().coord_y
));
17308 uberInsert
.addFloat(finiteAlways(GetTeleportDest().coord_z
));
17309 uberInsert
.addFloat(finiteAlways(GetTeleportDest().orientation
));
17312 std::ostringstream ss
;
17313 ss
<< m_taxi
; // string with TaxiMaskSize numbers
17314 uberInsert
.addString(ss
);
17316 uberInsert
.addUInt32(IsInWorld() ? 1 : 0);
17318 uberInsert
.addUInt32(m_cinematic
);
17320 uberInsert
.addUInt32(m_Played_time
[PLAYED_TIME_TOTAL
]);
17321 uberInsert
.addUInt32(m_Played_time
[PLAYED_TIME_LEVEL
]);
17323 uberInsert
.addFloat(finiteAlways(m_rest_bonus
));
17324 uberInsert
.addUInt64(uint64(time(NULL
)));
17325 uberInsert
.addUInt32(HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_RESTING
) ? 1 : 0);
17326 // save, far from tavern/city
17327 // save, but in tavern/city
17328 uberInsert
.addUInt32(m_resetTalentsCost
);
17329 uberInsert
.addUInt64(uint64(m_resetTalentsTime
));
17331 for (int i
= 0; i
< MAX_TALENT_SPEC_COUNT
; ++i
)
17332 ss
<< m_talentsPrimaryTree
[i
] << " ";
17333 uberInsert
.addString(ss
);
17335 uberInsert
.addFloat(finiteAlways(m_movementInfo
.GetTransportPos()->x
));
17336 uberInsert
.addFloat(finiteAlways(m_movementInfo
.GetTransportPos()->y
));
17337 uberInsert
.addFloat(finiteAlways(m_movementInfo
.GetTransportPos()->z
));
17338 uberInsert
.addFloat(finiteAlways(m_movementInfo
.GetTransportPos()->o
));
17340 uberInsert
.addUInt32(m_transport
->GetGUIDLow());
17342 uberInsert
.addUInt32(0);
17344 uberInsert
.addUInt32(m_ExtraFlags
);
17346 uberInsert
.addUInt32(uint32(m_stableSlots
)); // to prevent save uint8 as char
17348 uberInsert
.addUInt32(uint32(m_atLoginFlags
));
17350 uberInsert
.addUInt32(IsInWorld() ? GetZoneId() : GetCachedZoneId());
17352 uberInsert
.addUInt64(uint64(m_deathExpireTime
));
17354 ss
<< m_taxi
.SaveTaxiDestinationsToString(); // string
17355 uberInsert
.addString(ss
);
17357 uberInsert
.addUInt32(GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS
));
17359 uberInsert
.addUInt16(GetUInt16Value(PLAYER_FIELD_KILLS
, 0));
17361 uberInsert
.addUInt16(GetUInt16Value(PLAYER_FIELD_KILLS
, 1));
17363 uberInsert
.addUInt32(GetUInt32Value(PLAYER_CHOSEN_TITLE
));
17365 // FIXME: at this moment send to DB as unsigned, including unit32(-1)
17366 uberInsert
.addUInt32(GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX
));
17368 uberInsert
.addUInt16(uint16(GetUInt32Value(PLAYER_BYTES_3
) & 0xFFFE));
17370 uberInsert
.addUInt32(GetHealth());
17372 static_assert(MAX_STORED_POWERS
== 5, "Query not updated.");
17373 for (uint32 i
= 0; i
< MAX_STORED_POWERS
; ++i
)
17374 uberInsert
.addUInt32(GetPowerByIndex(i
));
17376 uberInsert
.addUInt32(uint32(m_specsCount
));
17377 uberInsert
.addUInt32(uint32(m_activeSpec
));
17379 for (uint32 i
= 0; i
< PLAYER_EXPLORED_ZONES_SIZE
; ++i
) // string
17381 ss
<< GetUInt32Value(PLAYER_EXPLORED_ZONES_1
+ i
) << " ";
17383 uberInsert
.addString(ss
);
17385 for (uint32 i
= 0; i
< EQUIPMENT_SLOT_END
* 2; ++i
) // string
17387 ss
<< GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID
+ i
) << " ";
17389 uberInsert
.addString(ss
);
17391 for(uint32 i
= 0; i
< KNOWN_TITLES_SIZE
*2; ++i
) //string
17393 ss
<< GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES
+ i
) << " ";
17395 uberInsert
.addString(ss
);
17397 uberInsert
.addUInt32(uint32(GetByteValue(PLAYER_FIELD_BYTES
, 2)));
17399 uberInsert
.addUInt8(m_slot
);
17401 uberInsert
.Execute();
17403 if (m_mailsUpdated
) // save mails only when needed
17408 _SaveQuestStatus();
17409 _SaveDailyQuestStatus();
17410 _SaveWeeklyQuestStatus();
17411 _SaveMonthlyQuestStatus();
17413 _SaveSpellCooldowns();
17417 m_achievementMgr
.SaveToDB();
17418 m_reputationMgr
.SaveToDB();
17420 _SaveEquipmentSets();
17421 GetSession()->SaveTutorialsData(); // changed only while character in game
17425 CharacterDatabase
.CommitTransaction();
17427 // check if stats should only be saved on logout
17428 // save stats can be out of transaction
17429 if (m_session
->isLogingOut() || !sWorld
.getConfig(CONFIG_BOOL_STATS_SAVE_ONLY_ON_LOGOUT
))
17432 // save pet (hunter pet level and experience and all type pets health/mana).
17433 if (Pet
* pet
= GetPet())
17434 pet
->SavePetToDB(PET_SAVE_AS_CURRENT
);
17437 // fast save function for item/money cheating preventing - save only inventory and money state
17438 void Player::SaveInventoryAndGoldToDB()
17444 void Player::SaveGoldToDB()
17446 static SqlStatementID updateGold
;
17448 SqlStatement stmt
= CharacterDatabase
.CreateStatement(updateGold
, "UPDATE characters SET money = ? WHERE guid = ?");
17449 stmt
.PExecute(GetMoney(), GetGUIDLow());
17452 void Player::_SaveActions()
17454 static SqlStatementID insertAction
;
17455 static SqlStatementID updateAction
;
17456 static SqlStatementID deleteAction
;
17458 for (int i
= 0; i
< MAX_TALENT_SPEC_COUNT
; ++i
)
17460 for (ActionButtonList::iterator itr
= m_actionButtons
[i
].begin(); itr
!= m_actionButtons
[i
].end();)
17462 switch (itr
->second
.uState
)
17464 case ACTIONBUTTON_NEW
:
17466 SqlStatement stmt
= CharacterDatabase
.CreateStatement(insertAction
, "INSERT INTO character_action (guid,spec, button,action,type) VALUES (?, ?, ?, ?, ?)");
17467 stmt
.addUInt32(GetGUIDLow());
17469 stmt
.addUInt32(uint32(itr
->first
));
17470 stmt
.addUInt32(itr
->second
.GetAction());
17471 stmt
.addUInt32(uint32(itr
->second
.GetType()));
17473 itr
->second
.uState
= ACTIONBUTTON_UNCHANGED
;
17477 case ACTIONBUTTON_CHANGED
:
17479 SqlStatement stmt
= CharacterDatabase
.CreateStatement(updateAction
, "UPDATE character_action SET action = ?, type = ? WHERE guid = ? AND button = ? AND spec = ?");
17480 stmt
.addUInt32(itr
->second
.GetAction());
17481 stmt
.addUInt32(uint32(itr
->second
.GetType()));
17482 stmt
.addUInt32(GetGUIDLow());
17483 stmt
.addUInt32(uint32(itr
->first
));
17486 itr
->second
.uState
= ACTIONBUTTON_UNCHANGED
;
17490 case ACTIONBUTTON_DELETED
:
17492 SqlStatement stmt
= CharacterDatabase
.CreateStatement(deleteAction
, "DELETE FROM character_action WHERE guid = ? AND button = ? AND spec = ?");
17493 stmt
.addUInt32(GetGUIDLow());
17494 stmt
.addUInt32(uint32(itr
->first
));
17497 m_actionButtons
[i
].erase(itr
++);
17508 void Player::_SaveAuras()
17510 static SqlStatementID deleteAuras
;
17511 static SqlStatementID insertAuras
;
17513 SqlStatement stmt
= CharacterDatabase
.CreateStatement(deleteAuras
, "DELETE FROM character_aura WHERE guid = ?");
17514 stmt
.PExecute(GetGUIDLow());
17516 SpellAuraHolderMap
const& auraHolders
= GetSpellAuraHolderMap();
17518 if (auraHolders
.empty())
17521 stmt
= CharacterDatabase
.CreateStatement(insertAuras
, "INSERT INTO character_aura (guid, caster_guid, item_guid, spell, stackcount, remaincharges, "
17522 "basepoints0, basepoints1, basepoints2, periodictime0, periodictime1, periodictime2, maxduration, remaintime, effIndexMask) "
17523 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
17525 for (SpellAuraHolderMap::const_iterator itr
= auraHolders
.begin(); itr
!= auraHolders
.end(); ++itr
)
17527 SpellAuraHolder
* holder
= itr
->second
;
17528 // skip all holders from spells that are passive or channeled
17529 // save singleTarget auras if self cast.
17530 bool selfCastHolder
= holder
->GetCasterGuid() == GetObjectGuid();
17531 TrackedAuraType trackedType
= holder
->GetTrackedAuraType();
17532 if (!holder
->IsPassive() && !IsChanneledSpell(holder
->GetSpellProto()) &&
17533 (trackedType
== TRACK_AURA_TYPE_NOT_TRACKED
|| (trackedType
== TRACK_AURA_TYPE_SINGLE_TARGET
&& selfCastHolder
)))
17535 int32 damage
[MAX_EFFECT_INDEX
];
17536 uint32 periodicTime
[MAX_EFFECT_INDEX
];
17537 uint32 effIndexMask
= 0;
17539 for (uint32 i
= 0; i
< MAX_EFFECT_INDEX
; ++i
)
17542 periodicTime
[i
] = 0;
17544 if (Aura
* aur
= holder
->GetAuraByEffectIndex(SpellEffectIndex(i
)))
17546 // don't save not own area auras
17547 if (aur
->IsAreaAura() && holder
->GetCasterGuid() != GetObjectGuid())
17550 damage
[i
] = aur
->GetModifier()->m_amount
;
17551 periodicTime
[i
] = aur
->GetModifier()->periodictime
;
17552 effIndexMask
|= (1 << i
);
17559 stmt
.addUInt32(GetGUIDLow());
17560 stmt
.addUInt64(holder
->GetCasterGuid().GetRawValue());
17561 stmt
.addUInt32(holder
->GetCastItemGuid().GetCounter());
17562 stmt
.addUInt32(holder
->GetId());
17563 stmt
.addUInt32(holder
->GetStackAmount());
17564 stmt
.addUInt8(holder
->GetAuraCharges());
17566 for (uint32 i
= 0; i
< MAX_EFFECT_INDEX
; ++i
)
17567 stmt
.addInt32(damage
[i
]);
17569 for (uint32 i
= 0; i
< MAX_EFFECT_INDEX
; ++i
)
17570 stmt
.addUInt32(periodicTime
[i
]);
17572 stmt
.addInt32(holder
->GetAuraMaxDuration());
17573 stmt
.addInt32(holder
->GetAuraDuration());
17574 stmt
.addUInt32(effIndexMask
);
17580 void Player::_SaveGlyphs()
17582 static SqlStatementID insertGlyph
;
17583 static SqlStatementID updateGlyph
;
17584 static SqlStatementID deleteGlyph
;
17586 for (uint8 spec
= 0; spec
< m_specsCount
; ++spec
)
17588 for (uint8 slot
= 0; slot
< MAX_GLYPH_SLOT_INDEX
; ++slot
)
17590 switch (m_glyphs
[spec
][slot
].uState
)
17594 SqlStatement stmt
= CharacterDatabase
.CreateStatement(insertGlyph
, "INSERT INTO character_glyphs (guid, spec, slot, glyph) VALUES (?, ?, ?, ?)");
17595 stmt
.PExecute(GetGUIDLow(), spec
, slot
, m_glyphs
[spec
][slot
].GetId());
17598 case GLYPH_CHANGED
:
17600 SqlStatement stmt
= CharacterDatabase
.CreateStatement(updateGlyph
, "UPDATE character_glyphs SET glyph = ? WHERE guid = ? AND spec = ? AND slot = ?");
17601 stmt
.PExecute(m_glyphs
[spec
][slot
].GetId(), GetGUIDLow(), spec
, slot
);
17604 case GLYPH_DELETED
:
17606 SqlStatement stmt
= CharacterDatabase
.CreateStatement(deleteGlyph
, "DELETE FROM character_glyphs WHERE guid = ? AND spec = ? AND slot = ?");
17607 stmt
.PExecute(GetGUIDLow(), spec
, slot
);
17610 case GLYPH_UNCHANGED
:
17613 m_glyphs
[spec
][slot
].uState
= GLYPH_UNCHANGED
;
17618 void Player::_SaveInventory()
17620 // force items in buyback slots to new state
17621 // and remove those that aren't already
17622 for (uint8 i
= BUYBACK_SLOT_START
; i
< BUYBACK_SLOT_END
; ++i
)
17624 Item
* item
= m_items
[i
];
17625 if (!item
|| item
->GetState() == ITEM_NEW
) continue;
17627 static SqlStatementID delInv
;
17628 static SqlStatementID delItemInst
;
17630 SqlStatement stmt
= CharacterDatabase
.CreateStatement(delInv
, "DELETE FROM character_inventory WHERE item = ?");
17631 stmt
.PExecute(item
->GetGUIDLow());
17633 stmt
= CharacterDatabase
.CreateStatement(delItemInst
, "DELETE FROM item_instance WHERE guid = ?");
17634 stmt
.PExecute(item
->GetGUIDLow());
17636 m_items
[i
]->FSetState(ITEM_NEW
);
17639 // update enchantment durations
17640 for (EnchantDurationList::const_iterator itr
= m_enchantDuration
.begin(); itr
!= m_enchantDuration
.end(); ++itr
)
17642 itr
->item
->SetEnchantmentDuration(itr
->slot
, itr
->leftduration
);
17646 if (m_itemUpdateQueue
.empty()) return;
17648 // do not save if the update queue is corrupt
17649 bool error
= false;
17650 for (size_t i
= 0; i
< m_itemUpdateQueue
.size(); ++i
)
17652 Item
* item
= m_itemUpdateQueue
[i
];
17653 if (!item
|| item
->GetState() == ITEM_REMOVED
) continue;
17654 Item
* test
= GetItemByPos(item
->GetBagSlot(), item
->GetSlot());
17658 sLog
.outError("Player(GUID: %u Name: %s)::_SaveInventory - the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the player doesn't have an item at that position!", GetGUIDLow(), GetName(), item
->GetBagSlot(), item
->GetSlot(), item
->GetGUIDLow());
17661 else if (test
!= item
)
17663 sLog
.outError("Player(GUID: %u Name: %s)::_SaveInventory - the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the item with guid %d is there instead!", GetGUIDLow(), GetName(), item
->GetBagSlot(), item
->GetSlot(), item
->GetGUIDLow(), test
->GetGUIDLow());
17670 sLog
.outError("Player::_SaveInventory - one or more errors occurred save aborted!");
17671 ChatHandler(this).SendSysMessage(LANG_ITEM_SAVE_FAILED
);
17675 static SqlStatementID insertInventory
;
17676 static SqlStatementID updateInventory
;
17677 static SqlStatementID deleteInventory
;
17679 for (size_t i
= 0; i
< m_itemUpdateQueue
.size(); ++i
)
17681 Item
* item
= m_itemUpdateQueue
[i
];
17682 if (!item
) continue;
17684 Bag
* container
= item
->GetContainer();
17685 uint32 bag_guid
= container
? container
->GetGUIDLow() : 0;
17687 switch (item
->GetState())
17691 SqlStatement stmt
= CharacterDatabase
.CreateStatement(insertInventory
, "INSERT INTO character_inventory (guid,bag,slot,item,item_template) VALUES (?, ?, ?, ?, ?)");
17692 stmt
.addUInt32(GetGUIDLow());
17693 stmt
.addUInt32(bag_guid
);
17694 stmt
.addUInt8(item
->GetSlot());
17695 stmt
.addUInt32(item
->GetGUIDLow());
17696 stmt
.addUInt32(item
->GetEntry());
17702 SqlStatement stmt
= CharacterDatabase
.CreateStatement(updateInventory
, "UPDATE character_inventory SET guid = ?, bag = ?, slot = ?, item_template = ? WHERE item = ?");
17703 stmt
.addUInt32(GetGUIDLow());
17704 stmt
.addUInt32(bag_guid
);
17705 stmt
.addUInt8(item
->GetSlot());
17706 stmt
.addUInt32(item
->GetEntry());
17707 stmt
.addUInt32(item
->GetGUIDLow());
17713 SqlStatement stmt
= CharacterDatabase
.CreateStatement(deleteInventory
, "DELETE FROM character_inventory WHERE item = ?");
17714 stmt
.PExecute(item
->GetGUIDLow());
17717 case ITEM_UNCHANGED
:
17721 item
->SaveToDB(); // item have unchanged inventory record and can be save standalone
17723 m_itemUpdateQueue
.clear();
17726 void Player::_SaveMail()
17728 static SqlStatementID updateMail
;
17729 static SqlStatementID deleteMailItems
;
17731 static SqlStatementID deleteItem
;
17732 static SqlStatementID deleteMain
;
17733 static SqlStatementID deleteItems
;
17735 for (PlayerMails::iterator itr
= m_mail
.begin(); itr
!= m_mail
.end(); ++itr
)
17738 if (m
->state
== MAIL_STATE_CHANGED
)
17740 SqlStatement stmt
= CharacterDatabase
.CreateStatement(updateMail
, "UPDATE mail SET has_items = ?, expire_time = ?, deliver_time = ?, money = ?, cod = ?, checked = ? WHERE id = ?");
17741 stmt
.addUInt32(m
->HasItems() ? 1 : 0);
17742 stmt
.addUInt64(uint64(m
->expire_time
));
17743 stmt
.addUInt64(uint64(m
->deliver_time
));
17744 stmt
.addUInt32(m
->money
);
17745 stmt
.addUInt32(m
->COD
);
17746 stmt
.addUInt32(m
->checked
);
17747 stmt
.addUInt32(m
->messageID
);
17750 if (m
->removedItems
.size())
17752 stmt
= CharacterDatabase
.CreateStatement(deleteMailItems
, "DELETE FROM mail_items WHERE item_guid = ?");
17754 for (std::vector
<uint32
>::const_iterator itr2
= m
->removedItems
.begin(); itr2
!= m
->removedItems
.end(); ++itr2
)
17755 stmt
.PExecute(*itr2
);
17757 m
->removedItems
.clear();
17759 m
->state
= MAIL_STATE_UNCHANGED
;
17761 else if (m
->state
== MAIL_STATE_DELETED
)
17765 SqlStatement stmt
= CharacterDatabase
.CreateStatement(deleteItem
, "DELETE FROM item_instance WHERE guid = ?");
17766 for (MailItemInfoVec::const_iterator itr2
= m
->items
.begin(); itr2
!= m
->items
.end(); ++itr2
)
17767 stmt
.PExecute(itr2
->item_guid
);
17770 SqlStatement stmt
= CharacterDatabase
.CreateStatement(deleteMain
, "DELETE FROM mail WHERE id = ?");
17771 stmt
.PExecute(m
->messageID
);
17773 stmt
= CharacterDatabase
.CreateStatement(deleteItems
, "DELETE FROM mail_items WHERE mail_id = ?");
17774 stmt
.PExecute(m
->messageID
);
17778 // deallocate deleted mails...
17779 for (PlayerMails::iterator itr
= m_mail
.begin(); itr
!= m_mail
.end();)
17781 if ((*itr
)->state
== MAIL_STATE_DELETED
)
17786 itr
= m_mail
.begin();
17792 m_mailsUpdated
= false;
17795 void Player::_SaveQuestStatus()
17797 static SqlStatementID insertQuestStatus
;
17799 static SqlStatementID updateQuestStatus
;
17801 // we don't need transactions here.
17802 for (QuestStatusMap::iterator i
= mQuestStatus
.begin(); i
!= mQuestStatus
.end(); ++i
)
17804 switch (i
->second
.uState
)
17808 SqlStatement stmt
= CharacterDatabase
.CreateStatement(insertQuestStatus
, "INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4,itemcount5,itemcount6) "
17809 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
17811 stmt
.addUInt32(GetGUIDLow());
17812 stmt
.addUInt32(i
->first
);
17813 stmt
.addUInt8(i
->second
.m_status
);
17814 stmt
.addUInt8(i
->second
.m_rewarded
);
17815 stmt
.addUInt8(i
->second
.m_explored
);
17816 stmt
.addUInt64(uint64(i
->second
.m_timer
/ IN_MILLISECONDS
+ sWorld
.GetGameTime()));
17817 for (int k
= 0; k
< QUEST_OBJECTIVES_COUNT
; ++k
)
17818 stmt
.addUInt32(i
->second
.m_creatureOrGOcount
[k
]);
17819 for (int k
= 0; k
< QUEST_ITEM_OBJECTIVES_COUNT
; ++k
)
17820 stmt
.addUInt32(i
->second
.m_itemcount
[k
]);
17824 case QUEST_CHANGED
:
17826 SqlStatement stmt
= CharacterDatabase
.CreateStatement(updateQuestStatus
, "UPDATE character_queststatus SET status = ?,rewarded = ?,explored = ?,timer = ?,"
17827 "mobcount1 = ?,mobcount2 = ?,mobcount3 = ?,mobcount4 = ?,itemcount1 = ?,itemcount2 = ?,itemcount3 = ?,itemcount4 = ?,itemcount5 = ?,itemcount6 = ? WHERE guid = ? AND quest = ?");
17829 stmt
.addUInt8(i
->second
.m_status
);
17830 stmt
.addUInt8(i
->second
.m_rewarded
);
17831 stmt
.addUInt8(i
->second
.m_explored
);
17832 stmt
.addUInt64(uint64(i
->second
.m_timer
/ IN_MILLISECONDS
+ sWorld
.GetGameTime()));
17833 for (int k
= 0; k
< QUEST_OBJECTIVES_COUNT
; ++k
)
17834 stmt
.addUInt32(i
->second
.m_creatureOrGOcount
[k
]);
17835 for (int k
= 0; k
< QUEST_ITEM_OBJECTIVES_COUNT
; ++k
)
17836 stmt
.addUInt32(i
->second
.m_itemcount
[k
]);
17837 stmt
.addUInt32(GetGUIDLow());
17838 stmt
.addUInt32(i
->first
);
17842 case QUEST_UNCHANGED
:
17845 i
->second
.uState
= QUEST_UNCHANGED
;
17849 void Player::_SaveDailyQuestStatus()
17851 if (!m_DailyQuestChanged
)
17854 // we don't need transactions here.
17855 static SqlStatementID delQuestStatus
;
17856 static SqlStatementID insQuestStatus
;
17858 SqlStatement stmtDel
= CharacterDatabase
.CreateStatement(delQuestStatus
, "DELETE FROM character_queststatus_daily WHERE guid = ?");
17859 SqlStatement stmtIns
= CharacterDatabase
.CreateStatement(insQuestStatus
, "INSERT INTO character_queststatus_daily (guid,quest) VALUES (?, ?)");
17861 stmtDel
.PExecute(GetGUIDLow());
17863 for (uint32 quest_daily_idx
= 0; quest_daily_idx
< PLAYER_MAX_DAILY_QUESTS
; ++quest_daily_idx
)
17864 if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1
+ quest_daily_idx
))
17865 stmtIns
.PExecute(GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1
+ quest_daily_idx
));
17867 m_DailyQuestChanged
= false;
17870 void Player::_SaveWeeklyQuestStatus()
17872 if (!m_WeeklyQuestChanged
|| m_weeklyquests
.empty())
17875 // we don't need transactions here.
17876 static SqlStatementID delQuestStatus
;
17877 static SqlStatementID insQuestStatus
;
17879 SqlStatement stmtDel
= CharacterDatabase
.CreateStatement(delQuestStatus
, "DELETE FROM character_queststatus_weekly WHERE guid = ?");
17880 SqlStatement stmtIns
= CharacterDatabase
.CreateStatement(insQuestStatus
, "INSERT INTO character_queststatus_weekly (guid,quest) VALUES (?, ?)");
17882 stmtDel
.PExecute(GetGUIDLow());
17884 for (QuestSet::const_iterator iter
= m_weeklyquests
.begin(); iter
!= m_weeklyquests
.end(); ++iter
)
17886 uint32 quest_id
= *iter
;
17887 stmtIns
.PExecute(GetGUIDLow(), quest_id
);
17890 m_WeeklyQuestChanged
= false;
17893 void Player::_SaveMonthlyQuestStatus()
17895 if (!m_MonthlyQuestChanged
|| m_monthlyquests
.empty())
17898 // we don't need transactions here.
17899 static SqlStatementID deleteQuest
;
17900 static SqlStatementID insertQuest
;
17902 SqlStatement stmtDel
= CharacterDatabase
.CreateStatement(deleteQuest
, "DELETE FROM character_queststatus_monthly WHERE guid = ?");
17903 SqlStatement stmtIns
= CharacterDatabase
.CreateStatement(insertQuest
, "INSERT INTO character_queststatus_monthly (guid, quest) VALUES (?, ?)");
17905 stmtDel
.PExecute(GetGUIDLow());
17907 for (QuestSet::const_iterator iter
= m_monthlyquests
.begin(); iter
!= m_monthlyquests
.end(); ++iter
)
17909 uint32 quest_id
= *iter
;
17910 stmtIns
.PExecute(GetGUIDLow(), quest_id
);
17913 m_MonthlyQuestChanged
= false;
17916 void Player::_SaveSkills()
17918 static SqlStatementID delSkills
;
17919 static SqlStatementID insSkills
;
17920 static SqlStatementID updSkills
;
17922 // we don't need transactions here.
17923 for (SkillStatusMap::iterator itr
= mSkillStatus
.begin(); itr
!= mSkillStatus
.end();)
17925 if (itr
->second
.uState
== SKILL_UNCHANGED
)
17931 if (itr
->second
.uState
== SKILL_DELETED
)
17933 SqlStatement stmt
= CharacterDatabase
.CreateStatement(delSkills
, "DELETE FROM character_skills WHERE guid = ? AND skill = ?");
17934 stmt
.PExecute(GetGUIDLow(), itr
->first
);
17935 mSkillStatus
.erase(itr
++);
17939 uint16 field
= itr
->second
.pos
/ 2;
17940 uint8 offset
= itr
->second
.pos
& 1;
17942 uint16 value
= GetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
);
17943 uint16 max
= GetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
);
17945 switch (itr
->second
.uState
)
17949 SqlStatement stmt
= CharacterDatabase
.CreateStatement(insSkills
, "INSERT INTO character_skills (guid, skill, value, max) VALUES (?, ?, ?, ?)");
17950 stmt
.PExecute(GetGUIDLow(), itr
->first
, value
, max
);
17953 case SKILL_CHANGED
:
17955 SqlStatement stmt
= CharacterDatabase
.CreateStatement(updSkills
, "UPDATE character_skills SET value = ?, max = ? WHERE guid = ? AND skill = ?");
17956 stmt
.PExecute(value
, max
, GetGUIDLow(), itr
->first
);
17959 case SKILL_UNCHANGED
:
17960 case SKILL_DELETED
:
17961 MANGOS_ASSERT(false);
17964 itr
->second
.uState
= SKILL_UNCHANGED
;
17970 void Player::_SaveSpells()
17972 static SqlStatementID delSpells
;
17973 static SqlStatementID insSpells
;
17975 SqlStatement stmtDel
= CharacterDatabase
.CreateStatement(delSpells
, "DELETE FROM character_spell WHERE guid = ? and spell = ?");
17976 SqlStatement stmtIns
= CharacterDatabase
.CreateStatement(insSpells
, "INSERT INTO character_spell (guid,spell,active,disabled) VALUES (?, ?, ?, ?)");
17978 for (PlayerSpellMap::iterator itr
= m_spells
.begin(), next
= m_spells
.begin(); itr
!= m_spells
.end();)
17980 uint32 talentCosts
= GetTalentSpellCost(itr
->first
);
17984 if (itr
->second
.state
== PLAYERSPELL_REMOVED
|| itr
->second
.state
== PLAYERSPELL_CHANGED
)
17985 stmtDel
.PExecute(GetGUIDLow(), itr
->first
);
17987 // add only changed/new not dependent spells
17988 if (!itr
->second
.dependent
&& (itr
->second
.state
== PLAYERSPELL_NEW
|| itr
->second
.state
== PLAYERSPELL_CHANGED
))
17989 stmtIns
.PExecute(GetGUIDLow(), itr
->first
, uint8(itr
->second
.active
? 1 : 0), uint8(itr
->second
.disabled
? 1 : 0));
17992 if (itr
->second
.state
== PLAYERSPELL_REMOVED
)
17993 m_spells
.erase(itr
++);
17996 itr
->second
.state
= PLAYERSPELL_UNCHANGED
;
18003 void Player::_SaveTalents()
18005 static SqlStatementID delTalents
;
18006 static SqlStatementID insTalents
;
18008 SqlStatement stmtDel
= CharacterDatabase
.CreateStatement(delTalents
, "DELETE FROM character_talent WHERE guid = ? and talent_id = ? and spec = ?");
18009 SqlStatement stmtIns
= CharacterDatabase
.CreateStatement(insTalents
, "INSERT INTO character_talent (guid, talent_id, current_rank , spec) VALUES (?, ?, ?, ?)");
18011 for (uint32 i
= 0; i
< MAX_TALENT_SPEC_COUNT
; ++i
)
18013 for (PlayerTalentMap::iterator itr
= m_talents
[i
].begin(); itr
!= m_talents
[i
].end();)
18015 if (itr
->second
.state
== PLAYERSPELL_REMOVED
|| itr
->second
.state
== PLAYERSPELL_CHANGED
)
18016 stmtDel
.PExecute(GetGUIDLow(), itr
->first
, i
);
18018 // add only changed/new talents
18019 if (itr
->second
.state
== PLAYERSPELL_NEW
|| itr
->second
.state
== PLAYERSPELL_CHANGED
)
18020 stmtIns
.PExecute(GetGUIDLow(), itr
->first
, itr
->second
.currentRank
, i
);
18022 if (itr
->second
.state
== PLAYERSPELL_REMOVED
)
18023 m_talents
[i
].erase(itr
++);
18026 itr
->second
.state
= PLAYERSPELL_UNCHANGED
;
18033 // save player stats -- only for external usage
18034 // real stats will be recalculated on player login
18035 void Player::_SaveStats()
18037 // check if stat saving is enabled and if char level is high enough
18038 if (!sWorld
.getConfig(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE
) || getLevel() < sWorld
.getConfig(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE
))
18041 static SqlStatementID delStats
;
18042 static SqlStatementID insertStats
;
18044 SqlStatement stmt
= CharacterDatabase
.CreateStatement(delStats
, "DELETE FROM character_stats WHERE guid = ?");
18045 stmt
.PExecute(GetGUIDLow());
18047 stmt
= CharacterDatabase
.CreateStatement(insertStats
, "INSERT INTO character_stats (guid, maxhealth, maxpower1, maxpower2, maxpower3, maxpower4, maxpower5,"
18048 "strength, agility, stamina, intellect, spirit, armor, resHoly, resFire, resNature, resFrost, resShadow, resArcane, "
18049 "blockPct, dodgePct, parryPct, critPct, rangedCritPct, spellCritPct, attackPower, rangedAttackPower, spellPower) "
18050 "VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
18052 stmt
.addUInt32(GetGUIDLow());
18053 stmt
.addUInt32(GetMaxHealth());
18054 static_assert(MAX_STORED_POWERS
== 5, "Query not updated.");
18055 for (uint32 i
= 0; i
< MAX_STORED_POWERS
; ++i
)
18056 stmt
.addUInt32(GetMaxPowerByIndex(i
));
18057 for (int i
= 0; i
< MAX_STATS
; ++i
)
18058 stmt
.addFloat(GetStat(Stats(i
)));
18059 // armor + school resistances
18060 for (int i
= 0; i
< MAX_SPELL_SCHOOL
; ++i
)
18061 stmt
.addUInt32(GetResistance(SpellSchools(i
)));
18062 stmt
.addFloat(GetFloatValue(PLAYER_BLOCK_PERCENTAGE
));
18063 stmt
.addFloat(GetFloatValue(PLAYER_DODGE_PERCENTAGE
));
18064 stmt
.addFloat(GetFloatValue(PLAYER_PARRY_PERCENTAGE
));
18065 stmt
.addFloat(GetFloatValue(PLAYER_CRIT_PERCENTAGE
));
18066 stmt
.addFloat(GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE
));
18067 stmt
.addFloat(GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1
));
18068 stmt
.addUInt32(GetUInt32Value(UNIT_FIELD_ATTACK_POWER
));
18069 stmt
.addUInt32(GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER
));
18070 stmt
.addUInt32(GetBaseSpellPowerBonus());
18075 void Player::outDebugStatsValues() const
18077 // optimize disabled debug output
18078 if (!sLog
.HasLogLevelOrHigher(LOG_LVL_DEBUG
) || sLog
.HasLogFilter(LOG_FILTER_PLAYER_STATS
))
18081 sLog
.outDebug("HP is: \t\t\t%u\t\tMP is: \t\t\t%u", GetMaxHealth(), GetMaxPower(POWER_MANA
));
18082 sLog
.outDebug("AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f", GetStat(STAT_AGILITY
), GetStat(STAT_STRENGTH
));
18083 sLog
.outDebug("INTELLECT is: \t\t%f\t\tSPIRIT is: \t\t%f", GetStat(STAT_INTELLECT
), GetStat(STAT_SPIRIT
));
18084 sLog
.outDebug("STAMINA is: \t\t%f", GetStat(STAT_STAMINA
));
18085 sLog
.outDebug("Armor is: \t\t%u\t\tBlock is: \t\t%f", GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE
));
18086 sLog
.outDebug("HolyRes is: \t\t%u\t\tFireRes is: \t\t%u", GetResistance(SPELL_SCHOOL_HOLY
), GetResistance(SPELL_SCHOOL_FIRE
));
18087 sLog
.outDebug("NatureRes is: \t\t%u\t\tFrostRes is: \t\t%u", GetResistance(SPELL_SCHOOL_NATURE
), GetResistance(SPELL_SCHOOL_FROST
));
18088 sLog
.outDebug("ShadowRes is: \t\t%u\t\tArcaneRes is: \t\t%u", GetResistance(SPELL_SCHOOL_SHADOW
), GetResistance(SPELL_SCHOOL_ARCANE
));
18089 sLog
.outDebug("MIN_DAMAGE is: \t\t%f\tMAX_DAMAGE is: \t\t%f", GetFloatValue(UNIT_FIELD_MINDAMAGE
), GetFloatValue(UNIT_FIELD_MAXDAMAGE
));
18090 sLog
.outDebug("MIN_OFFHAND_DAMAGE is: \t%f\tMAX_OFFHAND_DAMAGE is: \t%f", GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE
), GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE
));
18091 sLog
.outDebug("MIN_RANGED_DAMAGE is: \t%f\tMAX_RANGED_DAMAGE is: \t%f", GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE
), GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE
));
18092 sLog
.outDebug("ATTACK_TIME is: \t%u\t\tRANGE_ATTACK_TIME is: \t%u", GetAttackTime(BASE_ATTACK
), GetAttackTime(RANGED_ATTACK
));
18095 /*********************************************************/
18096 /*** FLOOD FILTER SYSTEM ***/
18097 /*********************************************************/
18099 void Player::UpdateSpeakTime()
18101 // ignore chat spam protection for GMs in any mode
18102 if (GetSession()->GetSecurity() > SEC_PLAYER
)
18105 time_t current
= time(NULL
);
18106 if (m_speakTime
> current
)
18108 uint32 max_count
= sWorld
.getConfig(CONFIG_UINT32_CHATFLOOD_MESSAGE_COUNT
);
18113 if (m_speakCount
>= max_count
)
18115 // prevent overwrite mute time, if message send just before mutes set, for example.
18116 time_t new_mute
= current
+ sWorld
.getConfig(CONFIG_UINT32_CHATFLOOD_MUTE_TIME
);
18117 if (GetSession()->m_muteTime
< new_mute
)
18118 GetSession()->m_muteTime
= new_mute
;
18126 m_speakTime
= current
+ sWorld
.getConfig(CONFIG_UINT32_CHATFLOOD_MESSAGE_DELAY
);
18129 bool Player::CanSpeak() const
18131 return GetSession()->m_muteTime
<= time(NULL
);
18134 /*********************************************************/
18135 /*** LOW LEVEL FUNCTIONS:Notifiers ***/
18136 /*********************************************************/
18138 void Player::SendAttackSwingNotInRange()
18140 WorldPacket
data(SMSG_ATTACKSWING_NOTINRANGE
, 0);
18141 GetSession()->SendPacket(&data
);
18144 void Player::SavePositionInDB(ObjectGuid guid
, uint32 mapid
, float x
, float y
, float z
, float o
, uint32 zone
)
18146 std::ostringstream ss
;
18147 ss
<< "UPDATE characters SET position_x='" << x
<< "',position_y='" << y
18148 << "',position_z='" << z
<< "',orientation='" << o
<< "',map='" << mapid
18149 << "',zone='" << zone
<< "',trans_x='0',trans_y='0',trans_z='0',"
18150 << "transguid='0',taxi_path='' WHERE guid='" << guid
.GetCounter() << "'";
18151 DEBUG_LOG("%s", ss
.str().c_str());
18152 CharacterDatabase
.Execute(ss
.str().c_str());
18155 void Player::SetUInt32ValueInArray(Tokens
& tokens
, uint16 index
, uint32 value
)
18158 snprintf(buf
, 11, "%u", value
);
18160 if (index
>= tokens
.size())
18163 tokens
[index
] = buf
;
18166 void Player::Customize(ObjectGuid guid
, uint8 gender
, uint8 skin
, uint8 face
, uint8 hairStyle
, uint8 hairColor
, uint8 facialHair
)
18169 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT playerBytes2 FROM characters WHERE guid = '%u'", guid
.GetCounter());
18173 Field
* fields
= result
->Fetch();
18175 uint32 player_bytes2
= fields
[0].GetUInt32();
18176 player_bytes2
&= ~0xFF;
18177 player_bytes2
|= facialHair
;
18179 CharacterDatabase
.PExecute("UPDATE characters SET gender = '%u', playerBytes = '%u', playerBytes2 = '%u' WHERE guid = '%u'", gender
, skin
| (face
<< 8) | (hairStyle
<< 16) | (hairColor
<< 24), player_bytes2
, guid
.GetCounter());
18184 void Player::SendAttackSwingDeadTarget()
18186 WorldPacket
data(SMSG_ATTACKSWING_DEADTARGET
, 0);
18187 GetSession()->SendPacket(&data
);
18190 void Player::SendAttackSwingCantAttack()
18192 WorldPacket
data(SMSG_ATTACKSWING_CANT_ATTACK
, 0);
18193 GetSession()->SendPacket(&data
);
18196 void Player::SendAttackSwingCancelAttack()
18198 WorldPacket
data(SMSG_CANCEL_COMBAT
, 0);
18199 GetSession()->SendPacket(&data
);
18202 void Player::SendAttackSwingBadFacingAttack()
18204 WorldPacket
data(SMSG_ATTACKSWING_BADFACING
, 0);
18205 GetSession()->SendPacket(&data
);
18208 void Player::SendAutoRepeatCancel(Unit
* target
)
18210 WorldPacket
data(SMSG_CANCEL_AUTO_REPEAT
, target
->GetPackGUID().size());
18211 data
<< target
->GetPackGUID();
18212 GetSession()->SendPacket(&data
);
18215 void Player::SendExplorationExperience(uint32 Area
, uint32 Experience
)
18217 WorldPacket
data(SMSG_EXPLORATION_EXPERIENCE
, 8);
18218 data
<< uint32(Area
);
18219 data
<< uint32(Experience
);
18220 GetSession()->SendPacket(&data
);
18223 void Player::SendDungeonDifficulty(bool IsInGroup
)
18225 uint8 val
= 0x00000001;
18226 WorldPacket
data(MSG_SET_DUNGEON_DIFFICULTY
, 12);
18227 data
<< uint32(GetDungeonDifficulty());
18228 data
<< uint32(val
);
18229 data
<< uint32(IsInGroup
);
18230 GetSession()->SendPacket(&data
);
18233 void Player::SendRaidDifficulty(bool IsInGroup
)
18235 uint8 val
= 0x00000001;
18236 WorldPacket
data(MSG_SET_RAID_DIFFICULTY
, 12);
18237 data
<< uint32(GetRaidDifficulty());
18238 data
<< uint32(val
);
18239 data
<< uint32(IsInGroup
);
18240 GetSession()->SendPacket(&data
);
18243 void Player::SendResetFailedNotify(uint32 mapid
)
18245 WorldPacket
data(SMSG_RESET_FAILED_NOTIFY
, 4);
18246 data
<< uint32(mapid
);
18247 GetSession()->SendPacket(&data
);
18250 /// Reset all solo instances and optionally send a message on success for each
18251 void Player::ResetInstances(InstanceResetMethod method
, bool isRaid
)
18253 // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN
18255 // we assume that when the difficulty changes, all instances that can be reset will be
18256 Difficulty diff
= GetDifficulty(isRaid
);
18258 for (BoundInstancesMap::iterator itr
= m_boundInstances
[diff
].begin(); itr
!= m_boundInstances
[diff
].end();)
18260 DungeonPersistentState
* state
= itr
->second
.state
;
18261 const MapEntry
* entry
= sMapStore
.LookupEntry(itr
->first
);
18262 if (!entry
|| entry
->IsRaid() != isRaid
|| !state
->CanReset())
18268 if (method
== INSTANCE_RESET_ALL
)
18270 // the "reset all instances" method can only reset normal maps
18271 if (entry
->map_type
== MAP_RAID
|| diff
== DUNGEON_DIFFICULTY_HEROIC
)
18278 // if the map is loaded, reset it
18279 if (Map
* map
= sMapMgr
.FindMap(state
->GetMapId(), state
->GetInstanceId()))
18280 if (map
->IsDungeon())
18281 ((DungeonMap
*)map
)->Reset(method
);
18283 // since this is a solo instance there should not be any players inside
18284 if (method
== INSTANCE_RESET_ALL
|| method
== INSTANCE_RESET_CHANGE_DIFFICULTY
)
18285 SendResetInstanceSuccess(state
->GetMapId());
18287 state
->DeleteFromDB();
18288 m_boundInstances
[diff
].erase(itr
++);
18290 // the following should remove the instance save from the manager and delete it as well
18291 state
->RemovePlayer(this);
18295 void Player::SendResetInstanceSuccess(uint32 MapId
)
18297 WorldPacket
data(SMSG_INSTANCE_RESET
, 4);
18298 data
<< uint32(MapId
);
18299 GetSession()->SendPacket(&data
);
18302 void Player::SendResetInstanceFailed(uint32 reason
, uint32 MapId
)
18304 // TODO: find what other fail reasons there are besides players in the instance
18305 WorldPacket
data(SMSG_INSTANCE_RESET_FAILED
, 4);
18306 data
<< uint32(reason
);
18307 data
<< uint32(MapId
);
18308 GetSession()->SendPacket(&data
);
18311 /*********************************************************/
18312 /*** Update timers ***/
18313 /*********************************************************/
18315 /// checks the 15 afk reports per 5 minutes limit
18316 void Player::UpdateAfkReport(time_t currTime
)
18318 if (m_bgData
.bgAfkReportedTimer
<= currTime
)
18320 m_bgData
.bgAfkReportedCount
= 0;
18321 m_bgData
.bgAfkReportedTimer
= currTime
+ 5 * MINUTE
;
18325 void Player::UpdateContestedPvP(uint32 diff
)
18327 if (!m_contestedPvPTimer
|| isInCombat())
18329 if (m_contestedPvPTimer
<= diff
)
18331 ResetContestedPvP();
18334 m_contestedPvPTimer
-= diff
;
18337 void Player::UpdatePvPFlag(time_t currTime
)
18341 if (pvpInfo
.endTimer
== 0 || currTime
< (pvpInfo
.endTimer
+ 300))
18347 void Player::UpdateDuelFlag(time_t currTime
)
18349 if (!duel
|| duel
->startTimer
== 0 || currTime
< duel
->startTimer
+ 3)
18352 SetUInt32Value(PLAYER_DUEL_TEAM
, 1);
18353 duel
->opponent
->SetUInt32Value(PLAYER_DUEL_TEAM
, 2);
18355 duel
->startTimer
= 0;
18356 duel
->startTime
= currTime
;
18357 duel
->opponent
->duel
->startTimer
= 0;
18358 duel
->opponent
->duel
->startTime
= currTime
;
18361 void Player::RemovePet(PetSaveMode mode
)
18363 if (Pet
* pet
= GetPet())
18364 pet
->Unsummon(mode
, this);
18367 void Player::BuildPlayerChat(WorldPacket
* data
, uint8 msgtype
, const std::string
& text
, uint32 language
, const char* addonPrefix
) const
18369 *data
<< uint8(msgtype
);
18370 *data
<< uint32(language
);
18371 *data
<< GetObjectGuid();
18372 *data
<< uint32(0); // constant unknown time 4.3.4
18374 *data
<< addonPrefix
;
18376 *data
<< GetObjectGuid();
18377 *data
<< uint32(text
.length() + 1);
18379 *data
<< uint8(GetChatTag());
18382 void Player::Say(const std::string
& text
, const uint32 language
)
18384 WorldPacket
data(SMSG_MESSAGECHAT
, 200);
18385 BuildPlayerChat(&data
, CHAT_MSG_SAY
, text
, language
);
18386 SendMessageToSetInRange(&data
, sWorld
.getConfig(CONFIG_FLOAT_LISTEN_RANGE_SAY
), true);
18389 void Player::Yell(const std::string
& text
, const uint32 language
)
18391 WorldPacket
data(SMSG_MESSAGECHAT
, 200);
18392 BuildPlayerChat(&data
, CHAT_MSG_YELL
, text
, language
);
18393 SendMessageToSetInRange(&data
, sWorld
.getConfig(CONFIG_FLOAT_LISTEN_RANGE_YELL
), true);
18396 void Player::TextEmote(const std::string
& text
)
18398 WorldPacket
data(SMSG_MESSAGECHAT
, 200);
18399 BuildPlayerChat(&data
, CHAT_MSG_EMOTE
, text
, LANG_UNIVERSAL
);
18400 SendMessageToSetInRange(&data
, sWorld
.getConfig(CONFIG_FLOAT_LISTEN_RANGE_TEXTEMOTE
), true, !sWorld
.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_CHAT
));
18403 void Player::Whisper(const std::string
& text
, uint32 language
, ObjectGuid receiver
)
18405 Player
* rPlayer
= sObjectMgr
.GetPlayer(receiver
);
18407 WorldPacket
data(SMSG_MESSAGECHAT
, 200);
18408 BuildPlayerChat(&data
, CHAT_MSG_WHISPER
, text
, language
);
18409 rPlayer
->GetSession()->SendPacket(&data
);
18411 data
.Initialize(SMSG_MESSAGECHAT
, 200);
18412 rPlayer
->BuildPlayerChat(&data
, CHAT_MSG_WHISPER_INFORM
, text
, language
);
18413 GetSession()->SendPacket(&data
);
18415 if (!isAcceptWhispers())
18417 SetAcceptWhispers(true);
18418 ChatHandler(this).SendSysMessage(LANG_COMMAND_WHISPERON
);
18421 // announce afk or dnd message
18422 if (rPlayer
->isAFK())
18423 ChatHandler(this).PSendSysMessage(LANG_PLAYER_AFK
, rPlayer
->GetName(), rPlayer
->autoReplyMsg
.c_str());
18424 else if (rPlayer
->isDND())
18425 ChatHandler(this).PSendSysMessage(LANG_PLAYER_DND
, rPlayer
->GetName(), rPlayer
->autoReplyMsg
.c_str());
18428 void Player::WhisperAddon(const std::string
& text
, const std::string
& prefix
, ObjectGuid receiver
)
18430 Player
* rPlayer
= sObjectMgr
.GetPlayer(receiver
);
18432 std::string
_text(text
);
18434 WorldPacket
data(SMSG_MESSAGECHAT
, 200);
18435 BuildPlayerChat(&data
, CHAT_MSG_WHISPER
, _text
, LANG_UNIVERSAL
, prefix
.c_str());
18436 rPlayer
->GetSession()->SendPacket(&data
);
18439 void Player::PetSpellInitialize()
18441 Pet
* pet
= GetPet();
18446 DEBUG_LOG("Pet Spells Groups");
18448 CharmInfo
* charmInfo
= pet
->GetCharmInfo();
18450 WorldPacket
data(SMSG_PET_SPELLS
, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX
+ 1 + 1);
18451 data
<< pet
->GetObjectGuid();
18452 data
<< uint16(pet
->GetCreatureInfo()->family
); // creature family (required for pet talents)
18454 data
<< uint8(charmInfo
->GetReactState()) << uint8(charmInfo
->GetCommandState()) << uint16(0);
18457 charmInfo
->BuildActionBar(&data
);
18459 size_t spellsCountPos
= data
.wpos();
18463 data
<< uint8(addlist
); // placeholder
18465 if (pet
->IsPermanentPetFor(this))
18468 for (PetSpellMap::const_iterator itr
= pet
->m_spells
.begin(); itr
!= pet
->m_spells
.end(); ++itr
)
18470 if (itr
->second
.state
== PETSPELL_REMOVED
)
18473 data
<< uint32(MAKE_UNIT_ACTION_BUTTON(itr
->first
, itr
->second
.active
));
18478 data
.put
<uint8
>(spellsCountPos
, addlist
);
18480 uint8 cooldownsCount
= pet
->m_CreatureSpellCooldowns
.size() + pet
->m_CreatureCategoryCooldowns
.size();
18481 data
<< uint8(cooldownsCount
);
18483 time_t curTime
= time(NULL
);
18485 for (CreatureSpellCooldowns::const_iterator itr
= pet
->m_CreatureSpellCooldowns
.begin(); itr
!= pet
->m_CreatureSpellCooldowns
.end(); ++itr
)
18487 time_t cooldown
= (itr
->second
> curTime
) ? (itr
->second
- curTime
) * IN_MILLISECONDS
: 0;
18489 data
<< uint32(itr
->first
); // spellid
18490 data
<< uint16(0); // spell category?
18491 data
<< uint32(cooldown
); // cooldown
18492 data
<< uint32(0); // category cooldown
18495 for (CreatureSpellCooldowns::const_iterator itr
= pet
->m_CreatureCategoryCooldowns
.begin(); itr
!= pet
->m_CreatureCategoryCooldowns
.end(); ++itr
)
18497 time_t cooldown
= (itr
->second
> curTime
) ? (itr
->second
- curTime
) * IN_MILLISECONDS
: 0;
18499 data
<< uint32(itr
->first
); // spellid
18500 data
<< uint16(0); // spell category?
18501 data
<< uint32(0); // cooldown
18502 data
<< uint32(cooldown
); // category cooldown
18505 GetSession()->SendPacket(&data
);
18508 void Player::SendPetGUIDs()
18513 // Later this function might get modified for multiple guids
18514 WorldPacket
data(SMSG_PET_GUIDS
, 12);
18515 data
<< uint32(1); // count
18516 data
<< ObjectGuid(GetPetGuid());
18517 GetSession()->SendPacket(&data
);
18520 void Player::PossessSpellInitialize()
18522 Unit
* charm
= GetCharm();
18527 CharmInfo
* charmInfo
= charm
->GetCharmInfo();
18531 sLog
.outError("Player::PossessSpellInitialize(): charm (GUID: %u TypeId: %u) has no charminfo!", charm
->GetGUIDLow(), charm
->GetTypeId());
18535 WorldPacket
data(SMSG_PET_SPELLS
, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX
+ 1 + 1);
18536 data
<< charm
->GetObjectGuid();
18541 charmInfo
->BuildActionBar(&data
);
18543 data
<< uint8(0); // spells count
18544 data
<< uint8(0); // cooldowns count
18546 GetSession()->SendPacket(&data
);
18549 void Player::CharmSpellInitialize()
18551 Unit
* charm
= GetCharm();
18556 CharmInfo
* charmInfo
= charm
->GetCharmInfo();
18559 sLog
.outError("Player::CharmSpellInitialize(): the player's charm (GUID: %u TypeId: %u) has no charminfo!", charm
->GetGUIDLow(), charm
->GetTypeId());
18565 if (charm
->GetTypeId() != TYPEID_PLAYER
)
18567 CreatureInfo
const* cinfo
= ((Creature
*)charm
)->GetCreatureInfo();
18569 if (cinfo
&& cinfo
->type
== CREATURE_TYPE_DEMON
&& getClass() == CLASS_WARLOCK
)
18571 for (uint32 i
= 0; i
< CREATURE_MAX_SPELLS
; ++i
)
18573 if (charmInfo
->GetCharmSpell(i
)->GetAction())
18579 WorldPacket
data(SMSG_PET_SPELLS
, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX
+ 1 + 4 * addlist
+ 1);
18580 data
<< charm
->GetObjectGuid();
18584 if (charm
->GetTypeId() != TYPEID_PLAYER
)
18585 data
<< uint8(charmInfo
->GetReactState()) << uint8(charmInfo
->GetCommandState()) << uint16(0);
18587 data
<< uint8(0) << uint8(0) << uint16(0);
18589 charmInfo
->BuildActionBar(&data
);
18591 data
<< uint8(addlist
);
18595 for (uint32 i
= 0; i
< CREATURE_MAX_SPELLS
; ++i
)
18597 CharmSpellEntry
* cspell
= charmInfo
->GetCharmSpell(i
);
18598 if (cspell
->GetAction())
18599 data
<< uint32(cspell
->packedData
);
18603 data
<< uint8(0); // cooldowns count
18605 GetSession()->SendPacket(&data
);
18608 void Player::RemovePetActionBar()
18610 WorldPacket
data(SMSG_PET_SPELLS
, 8);
18611 data
<< ObjectGuid();
18612 SendDirectMessage(&data
);
18615 void Player::AddSpellMod(Aura
* aura
, bool apply
)
18617 Modifier
const* mod
= aura
->GetModifier();
18618 uint16 Opcode
= (mod
->m_auraname
== SPELL_AURA_ADD_FLAT_MODIFIER
) ? SMSG_SET_FLAT_SPELL_MODIFIER
: SMSG_SET_PCT_SPELL_MODIFIER
;
18620 for (int eff
= 0; eff
< 96; ++eff
)
18626 _mask
= uint64(1) << (eff
- 0);
18628 _mask2
= uint32(1) << (eff
- 64);
18630 if (aura
->GetAuraSpellClassMask().IsFitToFamilyMask(_mask
, _mask2
))
18633 for (AuraList::const_iterator itr
= m_spellMods
[mod
->m_miscvalue
].begin(); itr
!= m_spellMods
[mod
->m_miscvalue
].end(); ++itr
)
18635 if ((*itr
)->GetModifier()->m_auraname
== mod
->m_auraname
&& ((*itr
)->GetAuraSpellClassMask().IsFitToFamilyMask(_mask
, _mask2
)))
18636 val
+= (*itr
)->GetModifier()->m_amount
;
18638 val
+= apply
? mod
->m_amount
: -(mod
->m_amount
);
18639 WorldPacket
data(Opcode
, 4 + 4 + 1 + 1 + 4);
18642 data
<< uint8(mod
->m_miscvalue
);
18643 data
<< uint8(eff
);
18644 data
<< int32(val
);
18645 SendDirectMessage(&data
);
18650 m_spellMods
[mod
->m_miscvalue
].push_back(aura
);
18652 m_spellMods
[mod
->m_miscvalue
].remove(aura
);
18655 template <class T
> T
Player::ApplySpellMod(uint32 spellId
, SpellModOp op
, T
& basevalue
, Spell
const* /*spell*/)
18657 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(spellId
);
18661 int32 totalpct
= 0;
18662 int32 totalflat
= 0;
18663 for (AuraList::iterator itr
= m_spellMods
[op
].begin(); itr
!= m_spellMods
[op
].end(); ++itr
)
18667 Modifier
const* mod
= aura
->GetModifier();
18669 if (!aura
->isAffectedOnSpell(spellInfo
))
18672 if (mod
->m_auraname
== SPELL_AURA_ADD_FLAT_MODIFIER
)
18673 totalflat
+= mod
->m_amount
;
18676 // skip percent mods for null basevalue (most important for spell mods with charges )
18677 if (basevalue
== T(0))
18680 // special case (skip >10sec spell casts for instant cast setting)
18681 if (mod
->m_miscvalue
== SPELLMOD_CASTING_TIME
18682 && basevalue
>= T(10 * IN_MILLISECONDS
) && mod
->m_amount
<= -100)
18685 totalpct
+= mod
->m_amount
;
18689 float diff
= (float)basevalue
* (float)totalpct
/ 100.0f
+ (float)totalflat
;
18690 basevalue
= T((float)basevalue
+ diff
);
18694 template int32
Player::ApplySpellMod
<int32
>(uint32 spellId
, SpellModOp op
, int32
& basevalue
, Spell
const* spell
);
18695 template uint32
Player::ApplySpellMod
<uint32
>(uint32 spellId
, SpellModOp op
, uint32
& basevalue
, Spell
const* spell
);
18696 template float Player::ApplySpellMod
<float>(uint32 spellId
, SpellModOp op
, float& basevalue
, Spell
const* spell
);
18698 // send Proficiency
18699 void Player::SendProficiency(ItemClass itemClass
, uint32 itemSubclassMask
)
18701 WorldPacket
data(SMSG_SET_PROFICIENCY
, 1 + 4);
18702 data
<< uint8(itemClass
) << uint32(itemSubclassMask
);
18703 GetSession()->SendPacket(&data
);
18706 void Player::RemovePetitionsAndSigns(ObjectGuid guid
)
18708 uint32 lowguid
= guid
.GetCounter();
18710 QueryResult
* result
= NULL
;
18711 result
= CharacterDatabase
.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u'", lowguid
);
18714 do // this part effectively does nothing, since the deletion / modification only takes place _after_ the PetitionQuery. Though I don't know if the result remains intact if I execute the delete query beforehand.
18716 // and SendPetitionQueryOpcode reads data from the DB
18717 Field
* fields
= result
->Fetch();
18718 ObjectGuid ownerguid
= ObjectGuid(HIGHGUID_PLAYER
, fields
[0].GetUInt32());
18719 ObjectGuid petitionguid
= ObjectGuid(HIGHGUID_ITEM
, fields
[1].GetUInt32());
18721 // send update if charter owner in game
18722 Player
* owner
= sObjectMgr
.GetPlayer(ownerguid
);
18724 owner
->GetSession()->SendPetitionQueryOpcode(petitionguid
);
18727 while (result
->NextRow());
18731 CharacterDatabase
.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u'", lowguid
);
18734 CharacterDatabase
.BeginTransaction();
18735 CharacterDatabase
.PExecute("DELETE FROM petition WHERE ownerguid = '%u'", lowguid
);
18736 CharacterDatabase
.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u'", lowguid
);
18737 CharacterDatabase
.CommitTransaction();
18740 void Player::LeaveAllArenaTeams(ObjectGuid guid
)
18742 uint32 lowguid
= guid
.GetCounter();
18743 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid='%u'", lowguid
);
18749 Field
* fields
= result
->Fetch();
18750 if (uint32 at_id
= fields
[0].GetUInt32())
18751 if (ArenaTeam
* at
= sObjectMgr
.GetArenaTeamById(at_id
))
18752 at
->DelMember(guid
);
18755 while (result
->NextRow());
18760 void Player::SetRestBonus(float rest_bonus_new
)
18762 // Prevent resting on max level
18763 if (getLevel() >= sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
18764 rest_bonus_new
= 0;
18766 if (rest_bonus_new
< 0)
18767 rest_bonus_new
= 0;
18769 float rest_bonus_max
= (float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP
) * 1.5f
/ 2.0f
;
18771 if (rest_bonus_new
> rest_bonus_max
)
18772 m_rest_bonus
= rest_bonus_max
;
18774 m_rest_bonus
= rest_bonus_new
;
18776 // update data for client
18777 if (m_rest_bonus
> 10)
18778 SetByteValue(PLAYER_BYTES_2
, 3, REST_STATE_RESTED
);
18779 else if (m_rest_bonus
<= 1)
18780 SetByteValue(PLAYER_BYTES_2
, 3, REST_STATE_NORMAL
);
18783 SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE
, uint32(m_rest_bonus
));
18786 void Player::HandleStealthedUnitsDetection()
18788 std::list
<Unit
*> stealthedUnits
;
18790 MaNGOS::AnyStealthedCheck
u_check(this);
18791 MaNGOS::UnitListSearcher
<MaNGOS::AnyStealthedCheck
> searcher(stealthedUnits
, u_check
);
18792 Cell::VisitAllObjects(this, searcher
, MAX_PLAYER_STEALTH_DETECT_RANGE
);
18794 WorldObject
const* viewPoint
= GetCamera().GetBody();
18796 for (std::list
<Unit
*>::const_iterator i
= stealthedUnits
.begin(); i
!= stealthedUnits
.end(); ++i
)
18801 bool hasAtClient
= HaveAtClient((*i
));
18802 bool hasDetected
= (*i
)->isVisibleForOrDetect(this, viewPoint
, true);
18808 ObjectGuid i_guid
= (*i
)->GetObjectGuid();
18809 (*i
)->SendCreateUpdateToPlayer(this);
18810 m_clientGUIDs
.insert(i_guid
);
18812 DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES
, "%s is detected in stealth by player %u. Distance = %f", i_guid
.GetString().c_str(), GetGUIDLow(), GetDistance(*i
));
18814 // target aura duration for caster show only if target exist at caster client
18815 // send data at target visibility change (adding to client)
18816 if ((*i
) != this && (*i
)->isType(TYPEMASK_UNIT
))
18817 SendAurasForTarget(*i
);
18824 (*i
)->DestroyForPlayer(this);
18825 m_clientGUIDs
.erase((*i
)->GetObjectGuid());
18831 bool Player::ActivateTaxiPathTo(std::vector
<uint32
> const& nodes
, Creature
* npc
/*= NULL*/, uint32 spellid
/*= 0*/)
18833 if (nodes
.size() < 2)
18836 // not let cheating with start flight in time of logout process || if casting not finished || while in combat || if not use Spell's with EffectSendTaxi
18837 if (GetSession()->isLogingOut() || isInCombat())
18839 GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERBUSY
);
18843 if (HasFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_DISABLE_MOVE
))
18849 // not let cheating with start flight mounted
18852 GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERALREADYMOUNTED
);
18856 if (IsInDisallowedMountForm())
18858 GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERSHAPESHIFTED
);
18862 // not let cheating with start flight in time of logout process || if casting not finished || while in combat || if not use Spell's with EffectSendTaxi
18863 if (IsNonMeleeSpellCasted(false))
18865 GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERBUSY
);
18869 // cast case or scripted call case
18872 RemoveSpellsCausingAura(SPELL_AURA_MOUNTED
);
18874 if (IsInDisallowedMountForm())
18875 RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT
);
18877 if (Spell
* spell
= GetCurrentSpell(CURRENT_GENERIC_SPELL
))
18878 if (spell
->m_spellInfo
->Id
!= spellid
)
18879 InterruptSpell(CURRENT_GENERIC_SPELL
, false);
18881 InterruptSpell(CURRENT_AUTOREPEAT_SPELL
, false);
18883 if (Spell
* spell
= GetCurrentSpell(CURRENT_CHANNELED_SPELL
))
18884 if (spell
->m_spellInfo
->Id
!= spellid
)
18885 InterruptSpell(CURRENT_CHANNELED_SPELL
, true);
18888 uint32 sourcenode
= nodes
[0];
18890 // starting node too far away (cheat?)
18891 TaxiNodesEntry
const* node
= sTaxiNodesStore
.LookupEntry(sourcenode
);
18894 GetSession()->SendActivateTaxiReply(ERR_TAXINOSUCHPATH
);
18898 // check node starting pos data set case if provided
18899 if (node
->x
!= 0.0f
|| node
->y
!= 0.0f
|| node
->z
!= 0.0f
)
18901 if (node
->map_id
!= GetMapId() ||
18902 (node
->x
- GetPositionX()) * (node
->x
- GetPositionX()) +
18903 (node
->y
- GetPositionY()) * (node
->y
- GetPositionY()) +
18904 (node
->z
- GetPositionZ()) * (node
->z
- GetPositionZ()) >
18905 (2 * INTERACTION_DISTANCE
) * (2 * INTERACTION_DISTANCE
) * (2 * INTERACTION_DISTANCE
))
18907 GetSession()->SendActivateTaxiReply(ERR_TAXITOOFARAWAY
);
18911 // node must have pos if taxi master case (npc != NULL)
18914 GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR
);
18918 // Prepare to flight start now
18920 // stop combat at start taxi flight if any
18923 // stop trade (client cancel trade at taxi map open but cheating tools can be used for reopen it)
18926 // clean not finished taxi path if any
18927 m_taxi
.ClearTaxiDestinations();
18929 // 0 element current node
18930 m_taxi
.AddTaxiDestination(sourcenode
);
18932 // fill destinations path tail
18933 uint32 sourcepath
= 0;
18934 uint32 totalcost
= 0;
18936 uint32 prevnode
= sourcenode
;
18937 uint32 lastnode
= 0;
18939 for (uint32 i
= 1; i
< nodes
.size(); ++i
)
18943 lastnode
= nodes
[i
];
18944 sObjectMgr
.GetTaxiPath(prevnode
, lastnode
, path
, cost
);
18948 m_taxi
.ClearTaxiDestinations();
18954 if (prevnode
== sourcenode
)
18957 m_taxi
.AddTaxiDestination(lastnode
);
18959 prevnode
= lastnode
;
18962 // get mount model (in case non taximaster (npc==NULL) allow more wide lookup)
18963 uint32 mount_display_id
= sObjectMgr
.GetTaxiMountDisplayId(sourcenode
, GetTeam(), npc
== NULL
);
18965 // in spell case allow 0 model
18966 if ((mount_display_id
== 0 && spellid
== 0) || sourcepath
== 0)
18968 GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR
);
18970 m_taxi
.ClearTaxiDestinations();
18974 uint64 money
= GetMoney();
18977 totalcost
= (uint32
)ceil(totalcost
* GetReputationPriceDiscount(npc
));
18979 if (money
< totalcost
)
18981 GetSession()->SendActivateTaxiReply(ERR_TAXINOTENOUGHMONEY
);
18983 m_taxi
.ClearTaxiDestinations();
18987 // Checks and preparations done, DO FLIGHT
18988 ModifyMoney(-(int64
)totalcost
);
18989 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING
, totalcost
);
18990 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN
, 1);
18992 // prevent stealth flight
18993 RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH
);
18995 GetSession()->SendActivateTaxiReply(ERR_TAXIOK
);
18996 GetSession()->SendDoFlight(mount_display_id
, sourcepath
);
19001 bool Player::ActivateTaxiPathTo(uint32 taxi_path_id
, uint32 spellid
/*= 0*/)
19003 TaxiPathEntry
const* entry
= sTaxiPathStore
.LookupEntry(taxi_path_id
);
19007 std::vector
<uint32
> nodes
;
19010 nodes
[0] = entry
->from
;
19011 nodes
[1] = entry
->to
;
19013 return ActivateTaxiPathTo(nodes
, NULL
, spellid
);
19016 void Player::ContinueTaxiFlight()
19018 uint32 sourceNode
= m_taxi
.GetTaxiSource();
19022 DEBUG_LOG("WORLD: Restart character %u taxi flight", GetGUIDLow());
19024 uint32 mountDisplayId
= sObjectMgr
.GetTaxiMountDisplayId(sourceNode
, GetTeam(), true);
19025 uint32 path
= m_taxi
.GetCurrentTaxiPath();
19027 // search appropriate start path node
19028 uint32 startNode
= 0;
19030 TaxiPathNodeList
const& nodeList
= sTaxiPathNodesByPath
[path
];
19032 float distPrev
= MAP_SIZE
* MAP_SIZE
;
19034 (nodeList
[0].x
- GetPositionX()) * (nodeList
[0].x
- GetPositionX()) +
19035 (nodeList
[0].y
- GetPositionY()) * (nodeList
[0].y
- GetPositionY()) +
19036 (nodeList
[0].z
- GetPositionZ()) * (nodeList
[0].z
- GetPositionZ());
19038 for (uint32 i
= 1; i
< nodeList
.size(); ++i
)
19040 TaxiPathNodeEntry
const& node
= nodeList
[i
];
19041 TaxiPathNodeEntry
const& prevNode
= nodeList
[i
- 1];
19043 // skip nodes at another map
19044 if (node
.mapid
!= GetMapId())
19047 distPrev
= distNext
;
19050 (node
.x
- GetPositionX()) * (node
.x
- GetPositionX()) +
19051 (node
.y
- GetPositionY()) * (node
.y
- GetPositionY()) +
19052 (node
.z
- GetPositionZ()) * (node
.z
- GetPositionZ());
19055 (node
.x
- prevNode
.x
) * (node
.x
- prevNode
.x
) +
19056 (node
.y
- prevNode
.y
) * (node
.y
- prevNode
.y
) +
19057 (node
.z
- prevNode
.z
) * (node
.z
- prevNode
.z
);
19059 if (distNext
+ distPrev
< distNodes
)
19066 GetSession()->SendDoFlight(mountDisplayId
, path
, startNode
);
19069 void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask
, uint32 unTimeMs
)
19071 // last check 4.3.4
19072 WorldPacket
data(SMSG_SPELL_COOLDOWN
, 8 + 1 + m_spells
.size() * 8);
19073 data
<< GetObjectGuid();
19074 data
<< uint8(0x0); // flags (0x1, 0x2)
19075 time_t curTime
= time(NULL
);
19076 for (PlayerSpellMap::const_iterator itr
= m_spells
.begin(); itr
!= m_spells
.end(); ++itr
)
19078 if (itr
->second
.state
== PLAYERSPELL_REMOVED
)
19080 uint32 unSpellId
= itr
->first
;
19081 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(unSpellId
);
19084 MANGOS_ASSERT(spellInfo
);
19088 // Not send cooldown for this spells
19089 if (spellInfo
->HasAttribute(SPELL_ATTR_DISABLED_WHILE_ACTIVE
))
19092 if ((idSchoolMask
& GetSpellSchoolMask(spellInfo
)) && GetSpellCooldownDelay(unSpellId
) < unTimeMs
)
19094 data
<< uint32(unSpellId
);
19095 data
<< uint32(unTimeMs
); // in m.secs
19096 AddSpellCooldown(unSpellId
, 0, curTime
+ unTimeMs
/ IN_MILLISECONDS
);
19099 GetSession()->SendPacket(&data
);
19102 void Player::InitDataForForm(bool reapplyMods
)
19104 ShapeshiftForm form
= GetShapeshiftForm();
19106 SpellShapeshiftFormEntry
const* ssEntry
= sSpellShapeshiftFormStore
.LookupEntry(form
);
19107 if (ssEntry
&& ssEntry
->attackSpeed
)
19109 SetAttackTime(BASE_ATTACK
, ssEntry
->attackSpeed
);
19110 SetAttackTime(OFF_ATTACK
, ssEntry
->attackSpeed
);
19111 SetAttackTime(RANGED_ATTACK
, BASE_ATTACK_TIME
);
19114 SetRegularAttackTime();
19120 if (getPowerType() != POWER_ENERGY
)
19121 setPowerType(POWER_ENERGY
);
19126 if (getPowerType() != POWER_RAGE
)
19127 setPowerType(POWER_RAGE
);
19130 default: // 0, for example
19132 ChrClassesEntry
const* cEntry
= sChrClassesStore
.LookupEntry(getClass());
19133 if (cEntry
&& cEntry
->powerType
< MAX_POWERS
&& uint32(getPowerType()) != cEntry
->powerType
)
19134 setPowerType(Powers(cEntry
->powerType
));
19139 // update auras at form change, ignore this at mods reapply (.reset stats/etc) when form not change.
19141 UpdateEquipSpellsAtFormChange();
19143 UpdateAttackPowerAndDamage();
19144 UpdateAttackPowerAndDamage(true);
19147 void Player::InitDisplayIds()
19149 PlayerInfo
const* info
= sObjectMgr
.GetPlayerInfo(getRace(), getClass());
19152 sLog
.outError("Player %u has incorrect race/class pair. Can't init display ids.", GetGUIDLow());
19156 // reset scale before reapply auras
19157 SetObjectScale(DEFAULT_OBJECT_SCALE
);
19159 uint8 gender
= getGender();
19162 case GENDER_FEMALE
:
19163 SetDisplayId(info
->displayId_f
);
19164 SetNativeDisplayId(info
->displayId_f
);
19167 SetDisplayId(info
->displayId_m
);
19168 SetNativeDisplayId(info
->displayId_m
);
19171 sLog
.outError("Invalid gender %u for player", gender
);
19176 void Player::TakeExtendedCost(uint32 extendedCostId
, uint32 count
)
19178 ItemExtendedCostEntry
const* extendedCost
= sItemExtendedCostStore
.LookupEntry(extendedCostId
);
19180 for (uint8 i
= 0; i
< MAX_EXTENDED_COST_ITEMS
; ++i
)
19182 if (extendedCost
->reqitem
[i
])
19183 DestroyItemCount(extendedCost
->reqitem
[i
], extendedCost
->reqitemcount
[i
] * count
, true);
19186 for (int i
= 0; i
< MAX_EXTENDED_COST_CURRENCIES
; ++i
)
19188 if (extendedCost
->reqcur
[i
] == CURRENCY_NONE
)
19191 if (extendedCost
->IsSeasonCurrencyRequirement(i
))
19194 CurrencyTypesEntry
const * entry
= sCurrencyTypesStore
.LookupEntry(extendedCost
->reqcur
[i
]);
19198 int32 cost
= int32(extendedCost
->reqcurrcount
[i
] * count
);
19199 ModifyCurrencyCount(entry
->ID
, -cost
);
19203 // Return true is the bought item has a max count to force refresh of window by caller
19204 bool Player::BuyItemFromVendorSlot(ObjectGuid vendorGuid
, uint32 vendorslot
, uint32 item
, uint8 count
, uint8 bag
, uint8 slot
)
19206 // cheating attempt
19207 if (count
< 1) count
= 1;
19212 ItemPrototype
const* pProto
= ObjectMgr::GetItemPrototype(item
);
19215 SendBuyError(BUY_ERR_CANT_FIND_ITEM
, NULL
, item
, 0);
19219 Creature
* pCreature
= GetNPCIfCanInteractWith(vendorGuid
, UNIT_NPC_FLAG_VENDOR
);
19222 DEBUG_LOG("WORLD: BuyItemFromVendor - %s not found or you can't interact with him.", vendorGuid
.GetString().c_str());
19223 SendBuyError(BUY_ERR_DISTANCE_TOO_FAR
, NULL
, item
, 0);
19227 VendorItemData
const* vItems
= pCreature
->GetVendorItems();
19228 VendorItemData
const* tItems
= pCreature
->GetVendorTemplateItems();
19229 if ((!vItems
|| vItems
->Empty()) && (!tItems
|| tItems
->Empty()))
19231 SendBuyError(BUY_ERR_CANT_FIND_ITEM
, pCreature
, item
, 0);
19235 uint32 vCount
= vItems
? vItems
->GetItemCount() : 0;
19236 uint32 tCount
= tItems
? tItems
->GetItemCount() : 0;
19238 if (vendorslot
>= vCount
+ tCount
)
19240 SendBuyError(BUY_ERR_CANT_FIND_ITEM
, pCreature
, item
, 0);
19244 VendorItem
const* crItem
= vendorslot
< vCount
? vItems
->GetItem(vendorslot
) : tItems
->GetItem(vendorslot
- vCount
);
19245 if (!crItem
) // store diff item (cheating)
19247 SendBuyError(BUY_ERR_CANT_FIND_ITEM
, pCreature
, item
, 0);
19251 if (crItem
->item
!= item
) // store diff item (cheating or special convert)
19253 bool converted
= false;
19255 // possible item converted for BoA case
19256 ItemPrototype
const* crProto
= ObjectMgr::GetItemPrototype(crItem
->item
);
19257 if (crProto
->Flags
& ITEM_FLAG_BOA
&& crProto
->RequiredReputationFaction
&&
19258 uint32(GetReputationRank(crProto
->RequiredReputationFaction
)) >= crProto
->RequiredReputationRank
)
19259 converted
= (sObjectMgr
.GetItemConvert(crItem
->item
, getRaceMask()) != 0);
19263 SendBuyError(BUY_ERR_CANT_FIND_ITEM
, pCreature
, item
, 0);
19268 uint32 totalCount
= count
;
19270 // check current item amount if it limited
19271 if (crItem
->maxcount
!= 0)
19273 if (pCreature
->GetVendorItemCurrentCount(crItem
) < totalCount
)
19275 SendBuyError(BUY_ERR_ITEM_ALREADY_SOLD
, pCreature
, item
, 0);
19280 if (uint32(GetReputationRank(pProto
->RequiredReputationFaction
)) < pProto
->RequiredReputationRank
)
19282 SendBuyError(BUY_ERR_REPUTATION_REQUIRE
, pCreature
, item
, 0);
19286 if (uint32 extendedCostId
= crItem
->ExtendedCost
)
19288 ItemExtendedCostEntry
const* iece
= sItemExtendedCostStore
.LookupEntry(extendedCostId
);
19291 sLog
.outError("Item %u have wrong ExtendedCost field value %u", pProto
->ItemId
, extendedCostId
);
19296 for (uint8 i
= 0; i
< MAX_EXTENDED_COST_ITEMS
; ++i
)
19298 if (iece
->reqitem
[i
] && !HasItemCount(iece
->reqitem
[i
], iece
->reqitemcount
[i
] * count
))
19300 SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS
, NULL
, NULL
);
19306 for (uint8 i
= 0; i
< MAX_EXTENDED_COST_CURRENCIES
; ++i
)
19308 if (iece
->reqcur
[i
] == CURRENCY_NONE
)
19311 CurrencyTypesEntry
const * costCurrency
= sCurrencyTypesStore
.LookupEntry(iece
->reqcur
[i
]);
19314 sLog
.outError("Item %u has ExtendedCost %u with unexistent currency id %u", pProto
->ItemId
, extendedCostId
, iece
->reqcur
[i
]);
19318 int32 cost
= int32(iece
->reqcurrcount
[i
] * count
);
19320 bool hasCount
= iece
->IsSeasonCurrencyRequirement(i
) ? HasCurrencySeasonCount(iece
->reqcur
[i
], cost
) : HasCurrencyCount(iece
->reqcur
[i
], cost
);
19323 SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS
, NULL
);
19328 // check for personal arena rating requirement
19329 if (GetMaxPersonalArenaRatingRequirement(iece
->reqarenaslot
) < iece
->reqpersonalarenarating
)
19331 // probably not the proper equip err
19332 SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK
, NULL
, NULL
);
19337 uint64 price
= (crItem
->ExtendedCost
== 0 || pProto
->Flags2
& ITEM_FLAG2_EXT_COST_REQUIRES_GOLD
) ? pProto
->BuyPrice
* count
: 0;
19339 // reputation discount
19341 price
= uint64(floor(price
* GetReputationPriceDiscount(pCreature
)));
19343 if (GetMoney() < price
)
19345 SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY
, pCreature
, item
, 0);
19349 Item
* pItem
= NULL
;
19351 if ((bag
== NULL_BAG
&& slot
== NULL_SLOT
) || IsInventoryPos(bag
, slot
))
19353 ItemPosCountVec dest
;
19354 InventoryResult msg
= CanStoreNewItem(bag
, slot
, dest
, item
, totalCount
);
19355 if (msg
!= EQUIP_ERR_OK
)
19357 SendEquipError(msg
, NULL
, NULL
, item
);
19361 ModifyMoney(-int64(price
));
19363 if (crItem
->ExtendedCost
)
19364 TakeExtendedCost(crItem
->ExtendedCost
, count
);
19366 pItem
= StoreNewItem(dest
, item
, true);
19368 else if (IsEquipmentPos(bag
, slot
))
19370 if (totalCount
!= 1)
19372 SendEquipError(EQUIP_ERR_ITEM_CANT_BE_EQUIPPED
, NULL
, NULL
);
19377 InventoryResult msg
= CanEquipNewItem(slot
, dest
, item
, false);
19378 if (msg
!= EQUIP_ERR_OK
)
19380 SendEquipError(msg
, NULL
, NULL
, item
);
19384 ModifyMoney(-int64(price
));
19386 if (crItem
->ExtendedCost
)
19387 TakeExtendedCost(crItem
->ExtendedCost
, count
);
19389 pItem
= EquipNewItem(dest
, item
, true);
19392 AutoUnequipOffhandIfNeed();
19396 SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT
, NULL
, NULL
);
19403 uint32 new_count
= pCreature
->UpdateVendorItemCurrentCount(crItem
, totalCount
);
19405 WorldPacket
data(SMSG_BUY_ITEM
, 8 + 4 + 4 + 4);
19406 data
<< pCreature
->GetObjectGuid();
19407 data
<< uint32(vendorslot
+ 1); // numbered from 1 at client
19408 data
<< uint32(crItem
->maxcount
> 0 ? new_count
: 0xFFFFFFFF);
19409 data
<< uint32(count
);
19410 GetSession()->SendPacket(&data
);
19412 SendNewItem(pItem
, totalCount
, true, false, false);
19414 return crItem
->maxcount
!= 0;
19417 bool Player::BuyCurrencyFromVendorSlot(ObjectGuid vendorGuid
, uint32 vendorslot
, uint32 currencyId
, uint8 count
)
19419 // cheating attempt
19420 if (count
< 1) count
= 1;
19425 CurrencyTypesEntry
const* pCurrency
= sCurrencyTypesStore
.LookupEntry(currencyId
);
19429 if (currencyId
== CURRENCY_CONQUEST_ARENA_META
|| currencyId
== CURRENCY_CONQUEST_BG_META
)
19432 Creature
* pCreature
= GetNPCIfCanInteractWith(vendorGuid
, UNIT_NPC_FLAG_VENDOR
);
19435 DEBUG_LOG("WORLD: BuyCurrencyFromVendorSlot - %s not found or you can't interact with him.", vendorGuid
.GetString().c_str());
19439 VendorItemData
const* vItems
= pCreature
->GetVendorItems();
19440 VendorItemData
const* tItems
= pCreature
->GetVendorTemplateItems();
19441 if ((!vItems
|| vItems
->Empty()) && (!tItems
|| tItems
->Empty()))
19444 uint32 vCount
= vItems
? vItems
->GetItemCount() : 0;
19445 uint32 tCount
= tItems
? tItems
->GetItemCount() : 0;
19447 if (vendorslot
>= vCount
+ tCount
)
19450 VendorItem
const* crItem
= vendorslot
< vCount
? vItems
->GetItem(vendorslot
) : tItems
->GetItem(vendorslot
- vCount
);
19451 if (!crItem
) // store diff item (cheating)
19454 if (crItem
->item
!= currencyId
) // store diff item (cheating)
19457 if (!crItem
->maxcount
)
19459 DEBUG_LOG("WORLD: BuyCurrencyFromVendorSlot - %s: crItem->maxcount (%u) == 0 for currency %u and player %s.",
19460 vendorGuid
.GetString().c_str(), crItem
->maxcount
, currencyId
, GetGuidStr().c_str());
19464 if (uint32 extendedCostId
= crItem
->ExtendedCost
)
19466 ItemExtendedCostEntry
const* iece
= sItemExtendedCostStore
.LookupEntry(extendedCostId
);
19469 sLog
.outError("WORLD: BuyCurrencyFromVendorSlot: Currency %u have wrong ExtendedCost field value %u for %s", currencyId
, extendedCostId
, vendorGuid
.GetString().c_str());
19474 for (uint8 i
= 0; i
< MAX_EXTENDED_COST_ITEMS
; ++i
)
19476 if (iece
->reqitem
[i
] && !HasItemCount(iece
->reqitem
[i
], iece
->reqitemcount
[i
] * count
))
19478 SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS
, NULL
, NULL
);
19484 for (uint8 i
= 0; i
< MAX_EXTENDED_COST_CURRENCIES
; ++i
)
19486 if (iece
->reqcur
[i
] == CURRENCY_NONE
)
19489 CurrencyTypesEntry
const * costCurrency
= sCurrencyTypesStore
.LookupEntry(iece
->reqcur
[i
]);
19492 sLog
.outError("Currency %u has ExtendedCost %u with unexistent currency id %u", currencyId
, extendedCostId
, iece
->reqcur
[i
]);
19496 int32 cost
= int32(iece
->reqcurrcount
[i
] * count
);
19497 bool hasCount
= iece
->IsSeasonCurrencyRequirement(i
) ? HasCurrencySeasonCount(iece
->reqcur
[i
], cost
) : HasCurrencyCount(iece
->reqcur
[i
], cost
);
19500 SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS
, NULL
);
19505 // check for personal arena rating requirement
19506 if (GetMaxPersonalArenaRatingRequirement(iece
->reqarenaslot
) < iece
->reqpersonalarenarating
)
19508 // probably not the proper equip err
19509 SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK
, NULL
, NULL
);
19515 SendBuyError(BUY_ERR_ITEM_SOLD_OUT
, 0, 0, 0);
19519 if (uint32 totalCap
= GetCurrencyTotalCap(pCurrency
))
19521 if (GetCurrencyCount(currencyId
) >= totalCap
)
19524 SendBuyError(BUY_ERR_CANT_CARRY_MORE
, 0, 0, 0);
19529 if (uint32 weekCap
= GetCurrencyWeekCap(pCurrency
))
19531 if (GetCurrencyWeekCount(currencyId
) >= weekCap
)
19533 SendBuyError(BUY_ERR_CANT_CARRY_MORE
, 0, 0, 0);
19538 if (crItem
->ExtendedCost
)
19539 TakeExtendedCost(crItem
->ExtendedCost
, count
);
19541 ModifyCurrencyCount(currencyId
, crItem
->maxcount
, true, false);
19544 DEBUG_LOG("WORLD: BuyCurrencyFromVendorSlot - %s: Player %s buys currency %u amount %u count %u.",
19545 vendorGuid
.GetString().c_str(), GetGuidStr().c_str(), currencyId
, crItem
->maxcount
, count
);
19550 uint32
Player::GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot
)
19552 // returns the maximal personal arena rating that can be used to purchase items requiring this condition
19553 // the personal rating of the arena team must match the required limit as well
19554 // so return max[in arenateams](min(personalrating[teamtype], teamrating[teamtype]))
19555 uint32 max_personal_rating
= 0;
19556 for (int i
= minarenaslot
; i
< MAX_ARENA_SLOT
; ++i
)
19558 if (ArenaTeam
* at
= sObjectMgr
.GetArenaTeamById(GetArenaTeamId(i
)))
19560 uint32 p_rating
= GetArenaPersonalRating(i
);
19561 uint32 t_rating
= at
->GetRating();
19562 p_rating
= p_rating
< t_rating
? p_rating
: t_rating
;
19563 if (max_personal_rating
< p_rating
)
19564 max_personal_rating
= p_rating
;
19567 return max_personal_rating
;
19570 void Player::UpdateHomebindTime(uint32 time
)
19572 // GMs never get homebind timer online
19573 if (m_InstanceValid
|| isGameMaster())
19575 if (m_HomebindTimer
) // instance valid, but timer not reset
19578 WorldPacket
data(SMSG_RAID_GROUP_ONLY
, 4 + 4);
19580 data
<< uint32(ERR_RAID_GROUP_NONE
); // error used only when timer = 0
19581 GetSession()->SendPacket(&data
);
19583 // instance is valid, reset homebind timer
19584 m_HomebindTimer
= 0;
19586 else if (m_HomebindTimer
> 0)
19588 if (time
>= m_HomebindTimer
)
19590 // teleport to nearest graveyard
19591 RepopAtGraveyard();
19594 m_HomebindTimer
-= time
;
19598 // instance is invalid, start homebind timer
19599 m_HomebindTimer
= 60000;
19600 // send message to player
19601 WorldPacket
data(SMSG_RAID_GROUP_ONLY
, 4 + 4);
19602 data
<< uint32(m_HomebindTimer
);
19603 data
<< uint32(ERR_RAID_GROUP_NONE
); // error used only when timer = 0
19604 GetSession()->SendPacket(&data
);
19605 DEBUG_LOG("PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName(), GetGUIDLow());
19609 void Player::UpdatePvP(bool state
, bool ovrride
)
19611 if (!state
|| ovrride
)
19614 pvpInfo
.endTimer
= 0;
19618 if (pvpInfo
.endTimer
!= 0)
19619 pvpInfo
.endTimer
= time(NULL
);
19625 void Player::AddSpellAndCategoryCooldowns(SpellEntry
const* spellInfo
, uint32 itemId
, Spell
* spell
, bool infinityCooldown
)
19627 // init cooldown values
19632 // some special item spells without correct cooldown in SpellInfo
19633 // cooldown information stored in item prototype
19637 if (ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(itemId
))
19639 for (int idx
= 0; idx
< MAX_ITEM_PROTO_SPELLS
; ++idx
)
19641 if (proto
->Spells
[idx
].SpellId
== spellInfo
->Id
)
19643 cat
= proto
->Spells
[idx
].SpellCategory
;
19644 rec
= proto
->Spells
[idx
].SpellCooldown
;
19645 catrec
= proto
->Spells
[idx
].SpellCategoryCooldown
;
19652 // if no cooldown found above then base at DBC data
19653 if (rec
< 0 && catrec
< 0)
19655 cat
= spellInfo
->GetCategory();
19656 rec
= spellInfo
->GetRecoveryTime();
19657 catrec
= spellInfo
->GetCategoryRecoveryTime();
19660 time_t curTime
= time(NULL
);
19665 // overwrite time for selected category
19666 if (infinityCooldown
)
19668 // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped)
19669 // but not allow ignore until reset or re-login
19670 catrecTime
= catrec
> 0 ? curTime
+ infinityCooldownDelay
: 0;
19671 recTime
= rec
> 0 ? curTime
+ infinityCooldownDelay
: catrecTime
;
19675 // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
19676 // prevent 0 cooldowns set by another way
19677 if (rec
<= 0 && catrec
<= 0 && (cat
== 76 || (IsAutoRepeatRangedSpell(spellInfo
) && spellInfo
->Id
!= SPELL_ID_AUTOSHOT
)))
19678 rec
= GetAttackTime(RANGED_ATTACK
);
19680 // Now we have cooldown data (if found any), time to apply mods
19682 ApplySpellMod(spellInfo
->Id
, SPELLMOD_COOLDOWN
, rec
, spell
);
19685 ApplySpellMod(spellInfo
->Id
, SPELLMOD_COOLDOWN
, catrec
, spell
);
19687 // replace negative cooldowns by 0
19688 if (rec
< 0) rec
= 0;
19689 if (catrec
< 0) catrec
= 0;
19691 // no cooldown after applying spell mods
19692 if (rec
== 0 && catrec
== 0)
19695 catrecTime
= catrec
? curTime
+ catrec
/ IN_MILLISECONDS
: 0;
19696 recTime
= rec
? curTime
+ rec
/ IN_MILLISECONDS
: catrecTime
;
19699 // self spell cooldown
19701 AddSpellCooldown(spellInfo
->Id
, itemId
, recTime
);
19704 if (cat
&& catrec
> 0)
19706 SpellCategoryStore::const_iterator i_scstore
= sSpellCategoryStore
.find(cat
);
19707 if (i_scstore
!= sSpellCategoryStore
.end())
19709 for (SpellCategorySet::const_iterator i_scset
= i_scstore
->second
.begin(); i_scset
!= i_scstore
->second
.end(); ++i_scset
)
19711 if (*i_scset
== spellInfo
->Id
) // skip main spell, already handled above
19714 AddSpellCooldown(*i_scset
, itemId
, catrecTime
);
19720 void Player::AddSpellCooldown(uint32 spellid
, uint32 itemid
, time_t end_time
)
19724 sc
.itemid
= itemid
;
19725 m_spellCooldowns
[spellid
] = sc
;
19728 void Player::SendCooldownEvent(SpellEntry
const* spellInfo
, uint32 itemId
, Spell
* spell
)
19730 // start cooldowns at server side, if any
19731 AddSpellAndCategoryCooldowns(spellInfo
, itemId
, spell
);
19733 // Send activate cooldown timer (possible 0) at client side
19734 WorldPacket
data(SMSG_COOLDOWN_EVENT
, (4 + 8));
19735 data
<< uint32(spellInfo
->Id
);
19736 data
<< GetObjectGuid();
19737 SendDirectMessage(&data
);
19740 void Player::UpdatePotionCooldown(Spell
* spell
)
19742 // no potion used in combat or still in combat
19743 if (!m_lastPotionId
|| isInCombat())
19746 // Call not from spell cast, send cooldown event for item spells if no in combat
19749 // spell/item pair let set proper cooldown (except nonexistent charged spell cooldown spellmods for potions)
19750 if (ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(m_lastPotionId
))
19751 for (int idx
= 0; idx
< 5; ++idx
)
19752 if (proto
->Spells
[idx
].SpellId
&& proto
->Spells
[idx
].SpellTrigger
== ITEM_SPELLTRIGGER_ON_USE
)
19753 if (SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(proto
->Spells
[idx
].SpellId
))
19754 SendCooldownEvent(spellInfo
, m_lastPotionId
);
19756 // from spell cases (m_lastPotionId set in Spell::SendSpellCooldown)
19758 SendCooldownEvent(spell
->m_spellInfo
, m_lastPotionId
, spell
);
19760 m_lastPotionId
= 0;
19763 // slot to be excluded while counting
19764 bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition
, int8 slot
)
19766 if (!enchantmentcondition
)
19769 SpellItemEnchantmentConditionEntry
const* Condition
= sSpellItemEnchantmentConditionStore
.LookupEntry(enchantmentcondition
);
19774 uint8 curcount
[4] = {0, 0, 0, 0};
19776 // counting current equipped gem colors
19777 for (uint8 i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_END
; ++i
)
19781 Item
* pItem2
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
19782 if (pItem2
&& !pItem2
->IsBroken() && pItem2
->GetProto()->Socket
[0].Color
)
19784 for (uint32 enchant_slot
= SOCK_ENCHANTMENT_SLOT
; enchant_slot
< SOCK_ENCHANTMENT_SLOT
+ 3; ++enchant_slot
)
19786 uint32 enchant_id
= pItem2
->GetEnchantmentId(EnchantmentSlot(enchant_slot
));
19790 SpellItemEnchantmentEntry
const* enchantEntry
= sSpellItemEnchantmentStore
.LookupEntry(enchant_id
);
19794 uint32 gemid
= enchantEntry
->GemID
;
19798 ItemPrototype
const* gemProto
= sItemStorage
.LookupEntry
<ItemPrototype
>(gemid
);
19802 GemPropertiesEntry
const* gemProperty
= sGemPropertiesStore
.LookupEntry(gemProto
->GemProperties
);
19806 uint8 GemColor
= gemProperty
->color
;
19808 for (uint8 b
= 0, tmpcolormask
= 1; b
< 4; ++b
, tmpcolormask
<<= 1)
19810 if (tmpcolormask
& GemColor
)
19817 bool activate
= true;
19819 for (int i
= 0; i
< 5; ++i
)
19821 if (!Condition
->Color
[i
])
19824 uint32 _cur_gem
= curcount
[Condition
->Color
[i
] - 1];
19826 // if have <CompareColor> use them as count, else use <value> from Condition
19827 uint32 _cmp_gem
= Condition
->CompareColor
[i
] ? curcount
[Condition
->CompareColor
[i
] - 1] : Condition
->Value
[i
];
19829 switch (Condition
->Comparator
[i
])
19831 case 2: // requires less <color> than (<value> || <comparecolor>) gems
19832 activate
&= (_cur_gem
< _cmp_gem
) ? true : false;
19834 case 3: // requires more <color> than (<value> || <comparecolor>) gems
19835 activate
&= (_cur_gem
> _cmp_gem
) ? true : false;
19837 case 5: // requires at least <color> than (<value> || <comparecolor>) gems
19838 activate
&= (_cur_gem
>= _cmp_gem
) ? true : false;
19843 DEBUG_LOG("Checking Condition %u, there are %u Meta Gems, %u Red Gems, %u Yellow Gems and %u Blue Gems, Activate:%s", enchantmentcondition
, curcount
[0], curcount
[1], curcount
[2], curcount
[3], activate
? "yes" : "no");
19848 void Player::CorrectMetaGemEnchants(uint8 exceptslot
, bool apply
)
19850 // cycle all equipped items
19851 for (uint32 slot
= EQUIPMENT_SLOT_START
; slot
< EQUIPMENT_SLOT_END
; ++slot
)
19853 // enchants for the slot being socketed are handled by Player::ApplyItemMods
19854 if (slot
== exceptslot
)
19857 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, slot
);
19859 if (!pItem
|| !pItem
->GetProto()->Socket
[0].Color
)
19862 for (uint32 enchant_slot
= SOCK_ENCHANTMENT_SLOT
; enchant_slot
< SOCK_ENCHANTMENT_SLOT
+ 3; ++enchant_slot
)
19864 uint32 enchant_id
= pItem
->GetEnchantmentId(EnchantmentSlot(enchant_slot
));
19868 SpellItemEnchantmentEntry
const* enchantEntry
= sSpellItemEnchantmentStore
.LookupEntry(enchant_id
);
19872 uint32 condition
= enchantEntry
->EnchantmentCondition
;
19875 // was enchant active with/without item?
19876 bool wasactive
= EnchantmentFitsRequirements(condition
, apply
? exceptslot
: -1);
19877 // should it now be?
19878 if (wasactive
!= EnchantmentFitsRequirements(condition
, apply
? -1 : exceptslot
))
19880 // ignore item gem conditions
19881 // if state changed, (dis)apply enchant
19882 ApplyEnchantment(pItem
, EnchantmentSlot(enchant_slot
), !wasactive
, true, true);
19889 // if false -> then toggled off if was on| if true -> toggled on if was off AND meets requirements
19890 void Player::ToggleMetaGemsActive(uint8 exceptslot
, bool apply
)
19892 // cycle all equipped items
19893 for (int slot
= EQUIPMENT_SLOT_START
; slot
< EQUIPMENT_SLOT_END
; ++slot
)
19895 // enchants for the slot being socketed are handled by WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
19896 if (slot
== exceptslot
)
19899 Item
* pItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, slot
);
19901 if (!pItem
|| !pItem
->GetProto()->Socket
[0].Color
) // if item has no sockets or no item is equipped go to next item
19904 // cycle all (gem)enchants
19905 for (uint32 enchant_slot
= SOCK_ENCHANTMENT_SLOT
; enchant_slot
< SOCK_ENCHANTMENT_SLOT
+ 3; ++enchant_slot
)
19907 uint32 enchant_id
= pItem
->GetEnchantmentId(EnchantmentSlot(enchant_slot
));
19908 if (!enchant_id
) // if no enchant go to next enchant(slot)
19911 SpellItemEnchantmentEntry
const* enchantEntry
= sSpellItemEnchantmentStore
.LookupEntry(enchant_id
);
19915 // only metagems to be (de)activated, so only enchants with condition
19916 uint32 condition
= enchantEntry
->EnchantmentCondition
;
19918 ApplyEnchantment(pItem
, EnchantmentSlot(enchant_slot
), apply
);
19923 void Player::SetBattleGroundEntryPoint()
19926 if (!m_taxi
.empty())
19928 m_bgData
.mountSpell
= 0;
19929 m_bgData
.taxiPath
[0] = m_taxi
.GetTaxiSource();
19930 m_bgData
.taxiPath
[1] = m_taxi
.GetTaxiDestination();
19932 // On taxi we don't need check for dungeon
19933 m_bgData
.joinPos
= WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
19934 m_bgData
.m_needSave
= true;
19939 m_bgData
.ClearTaxiPath();
19941 // Mount spell id storing
19944 AuraList
const& auras
= GetAurasByType(SPELL_AURA_MOUNTED
);
19945 if (!auras
.empty())
19946 m_bgData
.mountSpell
= (*auras
.begin())->GetId();
19949 m_bgData
.mountSpell
= 0;
19951 // If map is dungeon find linked graveyard
19952 if (GetMap()->IsDungeon())
19954 if (const WorldSafeLocsEntry
* entry
= sObjectMgr
.GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam()))
19956 m_bgData
.joinPos
= WorldLocation(entry
->map_id
, entry
->x
, entry
->y
, entry
->z
, 0.0f
);
19957 m_bgData
.m_needSave
= true;
19961 sLog
.outError("SetBattleGroundEntryPoint: Dungeon map %u has no linked graveyard, setting home location as entry point.", GetMapId());
19963 // If new entry point is not BG or arena set it
19964 else if (!GetMap()->IsBattleGroundOrArena())
19966 m_bgData
.joinPos
= WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
19967 m_bgData
.m_needSave
= true;
19972 // In error cases use homebind position
19973 m_bgData
.joinPos
= WorldLocation(m_homebindMapId
, m_homebindX
, m_homebindY
, m_homebindZ
, 0.0f
);
19974 m_bgData
.m_needSave
= true;
19977 void Player::LeaveBattleground(bool teleportToEntryPoint
)
19979 if (BattleGround
* bg
= GetBattleGround())
19981 bg
->RemovePlayerAtLeave(GetObjectGuid(), teleportToEntryPoint
, true);
19983 // call after remove to be sure that player resurrected for correct cast
19984 if (bg
->isBattleGround() && !isGameMaster() && sWorld
.getConfig(CONFIG_BOOL_BATTLEGROUND_CAST_DESERTER
))
19986 if (bg
->GetStatus() == STATUS_IN_PROGRESS
|| bg
->GetStatus() == STATUS_WAIT_JOIN
)
19988 // lets check if player was teleported from BG and schedule delayed Deserter spell cast
19989 if (IsBeingTeleportedFar())
19991 ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER
);
19995 CastSpell(this, 26013, true); // Deserter
20001 bool Player::CanJoinToBattleground() const
20003 // check Deserter debuff
20004 if (GetDummyAura(26013))
20010 bool Player::CanReportAfkDueToLimit()
20012 // a player can complain about 15 people per 5 minutes
20013 if (m_bgData
.bgAfkReportedCount
++ >= 15)
20019 /// This player has been blamed to be inactive in a battleground
20020 void Player::ReportedAfkBy(Player
* reporter
)
20022 BattleGround
* bg
= GetBattleGround();
20023 if (!bg
|| bg
!= reporter
->GetBattleGround() || GetTeam() != reporter
->GetTeam())
20026 // check if player has 'Idle' or 'Inactive' debuff
20027 if (m_bgData
.bgAfkReporter
.find(reporter
->GetGUIDLow()) == m_bgData
.bgAfkReporter
.end() && !HasAura(43680, EFFECT_INDEX_0
) && !HasAura(43681, EFFECT_INDEX_0
) && reporter
->CanReportAfkDueToLimit())
20029 m_bgData
.bgAfkReporter
.insert(reporter
->GetGUIDLow());
20030 // 3 players have to complain to apply debuff
20031 if (m_bgData
.bgAfkReporter
.size() >= 3)
20033 // cast 'Idle' spell
20034 CastSpell(this, 43680, true);
20035 m_bgData
.bgAfkReporter
.clear();
20040 bool Player::IsVisibleInGridForPlayer(Player
* pl
) const
20042 // gamemaster in GM mode see all, including ghosts
20043 if (pl
->isGameMaster() && GetSession()->GetSecurity() <= pl
->GetSession()->GetSecurity())
20046 // player see dead player/ghost from own group/raid
20047 if (IsInSameRaidWith(pl
))
20050 // Live player see live player or dead player with not realized corpse
20051 if (pl
->isAlive() || pl
->m_deathTimer
> 0)
20052 return isAlive() || m_deathTimer
> 0;
20054 // Ghost see other friendly ghosts, that's for sure
20055 if (!(isAlive() || m_deathTimer
> 0) && IsFriendlyTo(pl
))
20058 // Dead player see live players near own corpse
20061 if (Corpse
* corpse
= pl
->GetCorpse())
20063 // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level
20064 if (corpse
->IsWithinDistInMap(this, (20 + 25) * sWorld
.getConfig(CONFIG_FLOAT_RATE_CREATURE_AGGRO
)))
20069 // and not see any other
20073 bool Player::IsVisibleGloballyFor(Player
* u
) const
20078 // Always can see self
20082 // Visible units, always are visible for all players
20083 if (GetVisibility() == VISIBILITY_ON
)
20086 // GMs are visible for higher gms (or players are visible for gms)
20087 if (u
->GetSession()->GetSecurity() > SEC_PLAYER
)
20088 return GetSession()->GetSecurity() <= u
->GetSession()->GetSecurity();
20090 // non faction visibility non-breakable for non-GMs
20091 if (GetVisibility() == VISIBILITY_OFF
)
20094 // non-gm stealth/invisibility not hide from global player lists
20099 inline void BeforeVisibilityDestroy(T
* /*t*/, Player
* /*p*/)
20104 inline void BeforeVisibilityDestroy
<Creature
>(Creature
* t
, Player
* p
)
20106 if (p
->GetPetGuid() == t
->GetObjectGuid() && ((Creature
*)t
)->IsPet())
20107 ((Pet
*)t
)->Unsummon(PET_SAVE_REAGENTS
);
20110 void Player::UpdateVisibilityOf(WorldObject
const* viewPoint
, WorldObject
* target
)
20112 if (HaveAtClient(target
))
20114 if (!target
->isVisibleForInState(this, viewPoint
, true))
20116 ObjectGuid t_guid
= target
->GetObjectGuid();
20118 if (target
->GetTypeId() == TYPEID_UNIT
)
20120 BeforeVisibilityDestroy
<Creature
>((Creature
*)target
, this);
20122 // at remove from map (destroy) show kill animation (in different out of range/stealth case)
20123 target
->DestroyForPlayer(this, !target
->IsInWorld() && ((Creature
*)target
)->isDead());
20126 target
->DestroyForPlayer(this);
20128 m_clientGUIDs
.erase(t_guid
);
20130 DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES
, "%s out of range for player %u. Distance = %f", t_guid
.GetString().c_str(), GetGUIDLow(), GetDistance(target
));
20135 if (target
->isVisibleForInState(this, viewPoint
, false))
20137 target
->SendCreateUpdateToPlayer(this);
20138 if (target
->GetTypeId() != TYPEID_GAMEOBJECT
|| !((GameObject
*)target
)->IsTransport())
20139 m_clientGUIDs
.insert(target
->GetObjectGuid());
20141 DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES
, "Object %u (Type: %u) is visible now for player %u. Distance = %f", target
->GetGUIDLow(), target
->GetTypeId(), GetGUIDLow(), GetDistance(target
));
20143 // target aura duration for caster show only if target exist at caster client
20144 // send data at target visibility change (adding to client)
20145 if (target
!= this && target
->isType(TYPEMASK_UNIT
))
20146 SendAurasForTarget((Unit
*)target
);
20152 inline void UpdateVisibilityOf_helper(GuidSet
& s64
, T
* target
)
20154 s64
.insert(target
->GetObjectGuid());
20158 inline void UpdateVisibilityOf_helper(GuidSet
& s64
, GameObject
* target
)
20160 if (!target
->IsTransport())
20161 s64
.insert(target
->GetObjectGuid());
20165 void Player::UpdateVisibilityOf(WorldObject
const* viewPoint
, T
* target
, UpdateData
& data
, std::set
<WorldObject
*>& visibleNow
)
20167 if (HaveAtClient(target
))
20169 if (!target
->isVisibleForInState(this, viewPoint
, true))
20171 BeforeVisibilityDestroy
<T
>(target
, this);
20173 ObjectGuid t_guid
= target
->GetObjectGuid();
20175 target
->BuildOutOfRangeUpdateBlock(&data
);
20176 m_clientGUIDs
.erase(t_guid
);
20178 DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES
, "%s is out of range for %s. Distance = %f", t_guid
.GetString().c_str(), GetGuidStr().c_str(), GetDistance(target
));
20183 if (target
->isVisibleForInState(this, viewPoint
, false))
20185 visibleNow
.insert(target
);
20186 target
->BuildCreateUpdateBlockForPlayer(&data
, this);
20187 UpdateVisibilityOf_helper(m_clientGUIDs
, target
);
20189 DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES
, "%s is visible now for %s. Distance = %f", target
->GetGuidStr().c_str(), GetGuidStr().c_str(), GetDistance(target
));
20194 template void Player::UpdateVisibilityOf(WorldObject
const* viewPoint
, Player
* target
, UpdateData
& data
, std::set
<WorldObject
*>& visibleNow
);
20195 template void Player::UpdateVisibilityOf(WorldObject
const* viewPoint
, Creature
* target
, UpdateData
& data
, std::set
<WorldObject
*>& visibleNow
);
20196 template void Player::UpdateVisibilityOf(WorldObject
const* viewPoint
, Corpse
* target
, UpdateData
& data
, std::set
<WorldObject
*>& visibleNow
);
20197 template void Player::UpdateVisibilityOf(WorldObject
const* viewPoint
, GameObject
* target
, UpdateData
& data
, std::set
<WorldObject
*>& visibleNow
);
20198 template void Player::UpdateVisibilityOf(WorldObject
const* viewPoint
, DynamicObject
* target
, UpdateData
& data
, std::set
<WorldObject
*>& visibleNow
);
20200 void Player::SetPhaseMask(uint32 newPhaseMask
, bool update
)
20202 // GM-mode have mask PHASEMASK_ANYWHERE always
20203 if (isGameMaster())
20204 newPhaseMask
= PHASEMASK_ANYWHERE
;
20206 // phase auras normally not expected at BG but anyway better check
20207 if (BattleGround
* bg
= GetBattleGround())
20208 bg
->EventPlayerDroppedFlag(this);
20210 Unit::SetPhaseMask(newPhaseMask
, update
);
20212 GetSession()->SendSetPhaseShift(GetPhaseMask());
20215 void Player::InitPrimaryProfessions()
20217 uint32 maxProfs
= GetSession()->GetSecurity() < AccountTypes(sWorld
.getConfig(CONFIG_UINT32_TRADE_SKILL_GMIGNORE_MAX_PRIMARY_COUNT
))
20218 ? sWorld
.getConfig(CONFIG_UINT32_MAX_PRIMARY_TRADE_SKILL
) : 10;
20219 SetFreePrimaryProfessions(maxProfs
);
20222 void Player::SendComboPoints()
20224 Unit
* combotarget
= ObjectAccessor::GetUnit(*this, m_comboTargetGuid
);
20227 WorldPacket
data(SMSG_UPDATE_COMBO_POINTS
, combotarget
->GetPackGUID().size() + 1);
20228 data
<< combotarget
->GetPackGUID();
20229 data
<< uint8(m_comboPoints
);
20230 GetSession()->SendPacket(&data
);
20234 // can be NULL, and then points=0. Use unknown; to reset points of some sort?
20235 data << PackedGuid();
20237 GetSession()->SendPacket(&data);
20241 void Player::AddComboPoints(Unit
* target
, int8 count
)
20246 // without combo points lost (duration checked in aura)
20247 RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS
);
20249 if (target
->GetObjectGuid() == m_comboTargetGuid
)
20251 m_comboPoints
+= count
;
20255 if (m_comboTargetGuid
)
20256 if (Unit
* target2
= ObjectAccessor::GetUnit(*this, m_comboTargetGuid
))
20257 target2
->RemoveComboPointHolder(GetGUIDLow());
20259 m_comboTargetGuid
= target
->GetObjectGuid();
20260 m_comboPoints
= count
;
20262 target
->AddComboPointHolder(GetGUIDLow());
20265 if (m_comboPoints
> 5) m_comboPoints
= 5;
20266 if (m_comboPoints
< 0) m_comboPoints
= 0;
20271 void Player::ClearComboPoints()
20273 if (!m_comboTargetGuid
)
20276 // without combopoints lost (duration checked in aura)
20277 RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS
);
20283 if (Unit
* target
= ObjectAccessor::GetUnit(*this, m_comboTargetGuid
))
20284 target
->RemoveComboPointHolder(GetGUIDLow());
20286 m_comboTargetGuid
.Clear();
20289 void Player::SetGroup(Group
* group
, int8 subgroup
)
20295 // never use SetGroup without a subgroup unless you specify NULL for group
20296 MANGOS_ASSERT(subgroup
>= 0);
20297 m_group
.link(group
, this);
20298 m_group
.setSubGroup((uint8
)subgroup
);
20302 void Player::SendInitialPacketsBeforeAddToMap()
20304 GetSocial()->SendSocialList();
20307 WorldPacket
data(SMSG_BINDPOINTUPDATE
, 5 * 4);
20308 data
<< m_homebindX
<< m_homebindY
<< m_homebindZ
;
20309 data
<< (uint32
) m_homebindMapId
;
20310 data
<< (uint32
) m_homebindAreaId
;
20311 GetSession()->SendPacket(&data
);
20313 // SMSG_SET_PROFICIENCY
20314 // SMSG_SET_PCT_SPELL_MODIFIER
20315 // SMSG_SET_FLAT_SPELL_MODIFIER
20317 SendTalentsInfoData(false);
20319 data
.Initialize(SMSG_INSTANCE_DIFFICULTY
, 4 + 4);
20320 data
<< uint32(GetMap()->GetDifficulty());
20321 GetSession()->SendPacket(&data
);
20323 SendInitialSpells();
20325 data
.Initialize(SMSG_SEND_UNLEARN_SPELLS
, 4);
20326 data
<< uint32(0); // count, for(count) uint32;
20327 GetSession()->SendPacket(&data
);
20329 SendInitialActionButtons();
20330 m_reputationMgr
.SendInitialReputations();
20333 SendCorpseReclaimDelay(true);
20335 SendInitWorldStates(GetZoneId(), GetAreaId());
20337 SendEquipmentSetList();
20339 m_achievementMgr
.SendAllAchievementData();
20341 data
.Initialize(SMSG_LOGIN_SETTIMESPEED
, 4 + 4 + 4);
20342 data
<< uint32(secsToTimeBitFields(sWorld
.GetGameTime()));
20343 data
<< (float)0.01666667f
; // game speed
20344 data
<< uint32(0); // added in 3.1.2
20345 GetSession()->SendPacket(&data
);
20347 // SMSG_TALENTS_INFO x 2 for pet (unspent points and talents in separate packets...)
20349 // SMSG_POWER_UPDATE
20351 // set fly flag if in fly form or taxi flight to prevent visually drop at ground in showup moment
20352 if (IsFreeFlying() || IsTaxiFlying())
20353 m_movementInfo
.AddMovementFlag(MOVEFLAG_FLYING
);
20360 void Player::SendInitialPacketsAfterAddToMap()
20363 uint32 newzone
, newarea
;
20364 GetZoneAndAreaId(newzone
, newarea
);
20365 UpdateZone(newzone
, newarea
); // also call SendInitWorldStates();
20370 CastSpell(this, 836, true); // LOGINEFFECT
20372 // set some aura effects that send packet to player client after add player to map
20373 // SendMessageToSet not send it to player not it map, only for aura that not changed anything at re-apply
20374 // same auras state lost at far teleport, send it one more time in this case also
20375 static const AuraType auratypes
[] =
20377 SPELL_AURA_MOD_FEAR
, SPELL_AURA_TRANSFORM
, SPELL_AURA_WATER_WALK
,
20378 SPELL_AURA_FEATHER_FALL
, SPELL_AURA_HOVER
, SPELL_AURA_SAFE_FALL
,
20379 SPELL_AURA_FLY
, SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED
, SPELL_AURA_NONE
20381 for (AuraType
const* itr
= &auratypes
[0]; itr
&& itr
[0] != SPELL_AURA_NONE
; ++itr
)
20383 Unit::AuraList
const& auraList
= GetAurasByType(*itr
);
20384 if (!auraList
.empty())
20385 auraList
.front()->ApplyModifier(true, true);
20388 if (HasAuraType(SPELL_AURA_MOD_STUN
))
20391 // manual send package (have code in ApplyModifier(true,true); that don't must be re-applied.
20392 if (HasAuraType(SPELL_AURA_MOD_ROOT
))
20395 BuildForceMoveRootPacket(&data2
, true, 2);
20396 SendMessageToSet(&data2
, true);
20399 SendAurasForTarget(this);
20400 SendEnchantmentDurations(); // must be after add to map
20401 SendItemDurations(); // must be after add to map
20404 void Player::SendUpdateToOutOfRangeGroupMembers()
20406 if (m_groupUpdateMask
== GROUP_UPDATE_FLAG_NONE
)
20408 if (Group
* group
= GetGroup())
20409 group
->UpdatePlayerOutOfRange(this);
20411 m_groupUpdateMask
= GROUP_UPDATE_FLAG_NONE
;
20412 m_auraUpdateMask
= 0;
20413 if (Pet
* pet
= GetPet())
20414 pet
->ResetAuraUpdateMask();
20417 void Player::SendTransferAbortedByLockStatus(MapEntry
const* mapEntry
, AreaLockStatus lockStatus
, uint32 miscRequirement
)
20419 MANGOS_ASSERT(mapEntry
);
20421 DEBUG_LOG("SendTransferAbortedByLockStatus: Called for %s on map %u, LockAreaStatus %u, miscRequirement %u)", GetGuidStr().c_str(), mapEntry
->MapID
, lockStatus
, miscRequirement
);
20423 switch (lockStatus
)
20425 case AREA_LOCKSTATUS_TOO_LOW_LEVEL
:
20426 GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_LEVEL_MINREQUIRED
), miscRequirement
);
20428 case AREA_LOCKSTATUS_ZONE_IN_COMBAT
:
20429 GetSession()->SendTransferAborted(mapEntry
->MapID
, TRANSFER_ABORT_ZONE_IN_COMBAT
);
20431 case AREA_LOCKSTATUS_INSTANCE_IS_FULL
:
20432 GetSession()->SendTransferAborted(mapEntry
->MapID
, TRANSFER_ABORT_MAX_PLAYERS
);
20434 case AREA_LOCKSTATUS_QUEST_NOT_COMPLETED
:
20435 if (mapEntry
->MapID
== 269) // Exception for Black Morass
20437 GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_TELEREQ_QUEST_BLACK_MORASS
));
20440 else if (mapEntry
->IsContinent()) // do not report anything for quest areatrigge
20442 DEBUG_LOG("SendTransferAbortedByLockStatus: LockAreaStatus %u, do not teleport, no message sent (mapId %u)", lockStatus
, mapEntry
->MapID
);
20446 case AREA_LOCKSTATUS_MISSING_ITEM
:
20447 GetSession()->SendTransferAborted(mapEntry
->MapID
, TRANSFER_ABORT_DIFFICULTY
, GetDifficulty(mapEntry
->IsRaid()));
20449 case AREA_LOCKSTATUS_MISSING_DIFFICULTY
:
20451 Difficulty difficulty
= GetDifficulty(mapEntry
->IsRaid());
20452 GetSession()->SendTransferAborted(mapEntry
->MapID
, TRANSFER_ABORT_DIFFICULTY
, difficulty
> RAID_DIFFICULTY_10MAN_HEROIC
? RAID_DIFFICULTY_10MAN_HEROIC
: difficulty
);
20455 case AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION
:
20456 GetSession()->SendTransferAborted(mapEntry
->MapID
, TRANSFER_ABORT_INSUF_EXPAN_LVL
, miscRequirement
);
20458 case AREA_LOCKSTATUS_NOT_ALLOWED
:
20459 GetSession()->SendTransferAborted(mapEntry
->MapID
, TRANSFER_ABORT_MAP_NOT_ALLOWED
);
20461 case AREA_LOCKSTATUS_RAID_LOCKED
:
20462 GetSession()->SendTransferAborted(mapEntry
->MapID
, TRANSFER_ABORT_NEED_GROUP
);
20464 case AREA_LOCKSTATUS_UNKNOWN_ERROR
:
20465 GetSession()->SendTransferAborted(mapEntry
->MapID
, TRANSFER_ABORT_ERROR
);
20467 case AREA_LOCKSTATUS_OK
:
20468 sLog
.outError("SendTransferAbortedByLockStatus: LockAreaStatus AREA_LOCKSTATUS_OK received for %s (mapId %u)", GetGuidStr().c_str(), mapEntry
->MapID
);
20469 MANGOS_ASSERT(false);
20472 sLog
.outError("SendTransfertAbortedByLockstatus: unhandled LockAreaStatus %u, when %s attempts to enter in map %u", lockStatus
, GetGuidStr().c_str(), mapEntry
->MapID
);
20477 void Player::SendInstanceResetWarning(uint32 mapid
, Difficulty difficulty
, uint32 time
)
20479 // type of warning, based on the time remaining until reset
20482 type
= RAID_INSTANCE_WELCOME
;
20483 else if (time
> 900 && time
<= 3600)
20484 type
= RAID_INSTANCE_WARNING_HOURS
;
20485 else if (time
> 300 && time
<= 900)
20486 type
= RAID_INSTANCE_WARNING_MIN
;
20488 type
= RAID_INSTANCE_WARNING_MIN_SOON
;
20490 WorldPacket
data(SMSG_RAID_INSTANCE_MESSAGE
, 4 + 4 + 4 + 4);
20491 data
<< uint32(type
);
20492 data
<< uint32(mapid
);
20493 data
<< uint32(difficulty
); // difficulty
20494 data
<< uint32(time
);
20495 if (type
== RAID_INSTANCE_WELCOME
)
20497 data
<< uint8(0); // is your (1)
20498 data
<< uint8(0); // is extended (1), ignored if prev field is 0
20500 GetSession()->SendPacket(&data
);
20503 void Player::ApplyEquipCooldown(Item
* pItem
)
20505 if (pItem
->GetProto()->Flags
& ITEM_FLAG_NO_EQUIP_COOLDOWN
)
20508 for (int i
= 0; i
< MAX_ITEM_PROTO_SPELLS
; ++i
)
20510 _Spell
const& spellData
= pItem
->GetProto()->Spells
[i
];
20513 if (!spellData
.SpellId
)
20516 // wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown)
20517 if (spellData
.SpellTrigger
!= ITEM_SPELLTRIGGER_ON_USE
)
20520 AddSpellCooldown(spellData
.SpellId
, pItem
->GetEntry(), time(NULL
) + 30);
20522 WorldPacket
data(SMSG_ITEM_COOLDOWN
, 12);
20523 data
<< pItem
->GetObjectGuid();
20524 data
<< uint32(spellData
.SpellId
);
20525 GetSession()->SendPacket(&data
);
20529 void Player::resetSpells()
20531 // not need after this call
20532 if (HasAtLoginFlag(AT_LOGIN_RESET_SPELLS
))
20533 RemoveAtLoginFlag(AT_LOGIN_RESET_SPELLS
, true);
20535 // make full copy of map (spells removed and marked as deleted at another spell remove
20536 // and we can't use original map for safe iterative with visit each spell at loop end
20537 PlayerSpellMap smap
= GetSpellMap();
20539 for (PlayerSpellMap::const_iterator iter
= smap
.begin(); iter
!= smap
.end(); ++iter
)
20540 removeSpell(iter
->first
, false, false); // only iter->first can be accessed, object by iter->second can be deleted already
20542 learnDefaultSpells();
20543 learnQuestRewardedSpells();
20546 void Player::learnDefaultSpells()
20548 // learn default race/class spells
20549 PlayerInfo
const* info
= sObjectMgr
.GetPlayerInfo(getRace(), getClass());
20550 for (PlayerCreateInfoSpells::const_iterator itr
= info
->spell
.begin(); itr
!= info
->spell
.end(); ++itr
)
20552 uint32 tspell
= *itr
;
20553 DEBUG_LOG("PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell
);
20554 if (!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add
20555 addSpell(tspell
, true, true, true, false);
20556 else // but send in normal spell in game learn case
20557 learnSpell(tspell
, true);
20561 void Player::learnQuestRewardedSpells(Quest
const* quest
)
20563 uint32 spell_id
= quest
->GetRewSpellCast();
20565 // skip quests without rewarded spell
20569 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(spell_id
);
20573 // check learned spells state
20574 bool found
= false;
20575 for (int i
= 0; i
< MAX_EFFECT_INDEX
; ++i
)
20577 if(SpellEffectEntry
const* spellEffect
= spellInfo
->GetSpellEffect(SpellEffectIndex(i
)))
20579 if(spellEffect
->Effect
== SPELL_EFFECT_LEARN_SPELL
&& !HasSpell(spellEffect
->EffectTriggerSpell
))
20587 // skip quests with not teaching spell or already known spell
20591 // prevent learn non first rank unknown profession and second specialization for same profession)
20592 SpellEffectEntry
const* spellEffect
= spellInfo
->GetSpellEffect(EFFECT_INDEX_0
);
20593 uint32 learned_0
= spellEffect
? spellEffect
->EffectTriggerSpell
: 0;
20595 if( sSpellMgr
.GetSpellRank(learned_0
) > 1 && !HasSpell(learned_0
) )
20597 // not have first rank learned (unlearned prof?)
20598 uint32 first_spell
= sSpellMgr
.GetFirstSpellInChain(learned_0
);
20599 if (!HasSpell(first_spell
))
20602 SpellEntry
const* learnedInfo
= sSpellStore
.LookupEntry(learned_0
);
20607 SpellEffectEntry
const* learnedSpellEffect0
= learnedInfo
->GetSpellEffect(EFFECT_INDEX_0
);
20608 SpellEffectEntry
const* learnedSpellEffect1
= learnedInfo
->GetSpellEffect(EFFECT_INDEX_1
);
20609 if (learnedSpellEffect0
&& learnedSpellEffect0
->Effect
== SPELL_EFFECT_TRADE_SKILL
&& learnedSpellEffect1
&& learnedSpellEffect1
->Effect
== 0)
20611 // search other specialization for same prof
20612 for (PlayerSpellMap::const_iterator itr
= m_spells
.begin(); itr
!= m_spells
.end(); ++itr
)
20614 if (itr
->second
.state
== PLAYERSPELL_REMOVED
|| itr
->first
== learned_0
)
20617 SpellEntry
const* itrInfo
= sSpellStore
.LookupEntry(itr
->first
);
20621 // compare only specializations
20622 SpellEffectEntry
const* itrSpellEffect0
= learnedInfo
->GetSpellEffect(EFFECT_INDEX_0
);
20623 SpellEffectEntry
const* itrSpellEffect1
= learnedInfo
->GetSpellEffect(EFFECT_INDEX_1
);
20624 if ((itrSpellEffect0
&& itrSpellEffect0
->Effect
!= SPELL_EFFECT_TRADE_SKILL
) || (itrSpellEffect1
&& itrSpellEffect1
->Effect
!= 0))
20627 // compare same chain spells
20628 if (sSpellMgr
.GetFirstSpellInChain(itr
->first
) != first_spell
)
20631 // now we have 2 specialization, learn possible only if found is lesser specialization rank
20632 if (!sSpellMgr
.IsHighRankOfSpell(learned_0
, itr
->first
))
20638 CastSpell(this, spell_id
, true);
20641 void Player::learnQuestRewardedSpells()
20643 // learn spells received from quest completing
20644 for (QuestStatusMap::const_iterator itr
= mQuestStatus
.begin(); itr
!= mQuestStatus
.end(); ++itr
)
20646 // skip no rewarded quests
20647 if (!itr
->second
.m_rewarded
)
20650 Quest
const* quest
= sObjectMgr
.GetQuestTemplate(itr
->first
);
20654 learnQuestRewardedSpells(quest
);
20658 void Player::learnSkillRewardedSpells(uint32 skill_id
, uint32 skill_value
)
20660 uint32 raceMask
= getRaceMask();
20661 uint32 classMask
= getClassMask();
20662 for (uint32 j
= 0; j
< sSkillLineAbilityStore
.GetNumRows(); ++j
)
20664 SkillLineAbilityEntry
const* pAbility
= sSkillLineAbilityStore
.LookupEntry(j
);
20665 if (!pAbility
|| pAbility
->skillId
!= skill_id
|| pAbility
->learnOnGetSkill
!= ABILITY_LEARNED_ON_GET_PROFESSION_SKILL
)
20667 // Check race if set
20668 if (pAbility
->racemask
&& !(pAbility
->racemask
& raceMask
))
20670 // Check class if set
20671 if (pAbility
->classmask
&& !(pAbility
->classmask
& classMask
))
20674 if (sSpellStore
.LookupEntry(pAbility
->spellId
))
20676 // need unlearn spell
20677 if (skill_value
< pAbility
->req_skill_value
)
20678 removeSpell(pAbility
->spellId
);
20680 else if (!IsInWorld())
20681 addSpell(pAbility
->spellId
, true, true, true, false);
20683 learnSpell(pAbility
->spellId
, true);
20688 void Player::SendAurasForTarget(Unit
* target
)
20690 WorldPacket
data(SMSG_AURA_UPDATE_ALL
);
20691 data
<< target
->GetPackGUID();
20693 Unit::VisibleAuraMap
const& visibleAuras
= target
->GetVisibleAuras();
20694 for (Unit::VisibleAuraMap::const_iterator itr
= visibleAuras
.begin(); itr
!= visibleAuras
.end(); ++itr
)
20695 itr
->second
->BuildUpdatePacket(data
);
20697 GetSession()->SendPacket(&data
);
20700 void Player::SetDailyQuestStatus(uint32 quest_id
)
20702 for (uint32 quest_daily_idx
= 0; quest_daily_idx
< PLAYER_MAX_DAILY_QUESTS
; ++quest_daily_idx
)
20704 if (!GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1
+ quest_daily_idx
))
20706 SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1
+ quest_daily_idx
, quest_id
);
20707 m_DailyQuestChanged
= true;
20713 void Player::SetWeeklyQuestStatus(uint32 quest_id
)
20715 m_weeklyquests
.insert(quest_id
);
20716 m_WeeklyQuestChanged
= true;
20719 void Player::SetMonthlyQuestStatus(uint32 quest_id
)
20721 m_monthlyquests
.insert(quest_id
);
20722 m_MonthlyQuestChanged
= true;
20725 void Player::ResetDailyQuestStatus()
20727 for (uint32 quest_daily_idx
= 0; quest_daily_idx
< PLAYER_MAX_DAILY_QUESTS
; ++quest_daily_idx
)
20728 SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1
+ quest_daily_idx
, 0);
20730 // DB data deleted in caller
20731 m_DailyQuestChanged
= false;
20734 void Player::ResetWeeklyQuestStatus()
20736 if (m_weeklyquests
.empty())
20739 m_weeklyquests
.clear();
20740 // DB data deleted in caller
20741 m_WeeklyQuestChanged
= false;
20744 void Player::ResetMonthlyQuestStatus()
20746 if (m_monthlyquests
.empty())
20749 m_monthlyquests
.clear();
20750 // DB data deleted in caller
20751 m_MonthlyQuestChanged
= false;
20754 BattleGround
* Player::GetBattleGround() const
20756 if (GetBattleGroundId() == 0)
20759 return sBattleGroundMgr
.GetBattleGround(GetBattleGroundId(), m_bgData
.bgTypeID
);
20762 bool Player::InArena() const
20764 BattleGround
* bg
= GetBattleGround();
20765 if (!bg
|| !bg
->isArena())
20771 bool Player::GetBGAccessByLevel(BattleGroundTypeId bgTypeId
) const
20773 // get a template bg instead of running one
20774 BattleGround
* bg
= sBattleGroundMgr
.GetBattleGroundTemplate(bgTypeId
);
20778 // limit check leel to dbc compatible level range
20779 uint32 level
= getLevel();
20780 if (level
> DEFAULT_MAX_LEVEL
)
20781 level
= DEFAULT_MAX_LEVEL
;
20783 if (level
< bg
->GetMinLevel() || level
> bg
->GetMaxLevel())
20789 float Player::GetReputationPriceDiscount(Creature
const* pCreature
) const
20791 FactionTemplateEntry
const* vendor_faction
= pCreature
->getFactionTemplateEntry();
20792 if (!vendor_faction
|| !vendor_faction
->faction
)
20795 ReputationRank rank
= GetReputationRank(vendor_faction
->faction
);
20796 if (rank
<= REP_NEUTRAL
)
20799 return 1.0f
- 0.05f
* (rank
- REP_NEUTRAL
);
20803 * Check spell availability for training base at SkillLineAbility/SkillRaceClassInfo data.
20804 * Checked allowed race/class and dependent from race/class allowed min level
20806 * @param spell_id checked spell id
20807 * @param pReqlevel if arg provided then function work in view mode (level check not applied but detected minlevel returned to var by arg pointer.
20808 if arg not provided then considered train action mode and level checked
20809 * @return true if spell available for show in trainer list (with skip level check) or training.
20811 bool Player::IsSpellFitByClassAndRace(uint32 spell_id
, uint32
* pReqlevel
/*= NULL*/) const
20813 uint32 racemask
= getRaceMask();
20814 uint32 classmask
= getClassMask();
20816 SkillLineAbilityMapBounds bounds
= sSpellMgr
.GetSkillLineAbilityMapBounds(spell_id
);
20817 if (bounds
.first
== bounds
.second
)
20820 for (SkillLineAbilityMap::const_iterator _spell_idx
= bounds
.first
; _spell_idx
!= bounds
.second
; ++_spell_idx
)
20822 SkillLineAbilityEntry
const* abilityEntry
= _spell_idx
->second
;
20823 // skip wrong race skills
20824 if (abilityEntry
->racemask
&& (abilityEntry
->racemask
& racemask
) == 0)
20827 // skip wrong class skills
20828 if (abilityEntry
->classmask
&& (abilityEntry
->classmask
& classmask
) == 0)
20831 SkillRaceClassInfoMapBounds bounds
= sSpellMgr
.GetSkillRaceClassInfoMapBounds(abilityEntry
->skillId
);
20832 for (SkillRaceClassInfoMap::const_iterator itr
= bounds
.first
; itr
!= bounds
.second
; ++itr
)
20834 SkillRaceClassInfoEntry
const* skillRCEntry
= itr
->second
;
20835 if ((skillRCEntry
->raceMask
& racemask
) && (skillRCEntry
->classMask
& classmask
))
20837 if (skillRCEntry
->flags
& ABILITY_SKILL_NONTRAINABLE
)
20840 if (pReqlevel
) // show trainers list case
20842 if (skillRCEntry
->reqLevel
)
20844 *pReqlevel
= skillRCEntry
->reqLevel
;
20848 else // check availble case at train
20850 if (skillRCEntry
->reqLevel
&& getLevel() < skillRCEntry
->reqLevel
)
20862 bool Player::HasQuestForGO(int32 GOId
) const
20864 for (int i
= 0; i
< MAX_QUEST_LOG_SIZE
; ++i
)
20866 uint32 questid
= GetQuestSlotQuestId(i
);
20870 QuestStatusMap::const_iterator qs_itr
= mQuestStatus
.find(questid
);
20871 if (qs_itr
== mQuestStatus
.end())
20874 QuestStatusData
const& qs
= qs_itr
->second
;
20876 if (qs
.m_status
== QUEST_STATUS_INCOMPLETE
)
20878 Quest
const* qinfo
= sObjectMgr
.GetQuestTemplate(questid
);
20882 if (GetGroup() && GetGroup()->isRaidGroup() && !qinfo
->IsAllowedInRaid())
20885 for (int j
= 0; j
< QUEST_OBJECTIVES_COUNT
; ++j
)
20887 if (qinfo
->ReqCreatureOrGOId
[j
] >= 0) // skip non GO case
20890 if ((-1)*GOId
== qinfo
->ReqCreatureOrGOId
[j
] && qs
.m_creatureOrGOcount
[j
] < qinfo
->ReqCreatureOrGOCount
[j
])
20898 void Player::UpdateForQuestWorldObjects()
20900 if (m_clientGUIDs
.empty())
20903 UpdateData
udata(GetMapId());
20904 WorldPacket packet
;
20905 for (GuidSet::const_iterator itr
= m_clientGUIDs
.begin(); itr
!= m_clientGUIDs
.end(); ++itr
)
20907 if (itr
->IsGameObject())
20909 if (GameObject
* obj
= GetMap()->GetGameObject(*itr
))
20910 obj
->BuildValuesUpdateBlockForPlayer(&udata
, this);
20912 else if (itr
->IsCreatureOrVehicle())
20914 Creature
* obj
= GetMap()->GetAnyTypeCreature(*itr
);
20918 // check if this unit requires quest specific flags
20919 if (!obj
->HasFlag(UNIT_NPC_FLAGS
, UNIT_NPC_FLAG_SPELLCLICK
))
20922 SpellClickInfoMapBounds clickPair
= sObjectMgr
.GetSpellClickInfoMapBounds(obj
->GetEntry());
20923 for (SpellClickInfoMap::const_iterator _itr
= clickPair
.first
; _itr
!= clickPair
.second
; ++_itr
)
20925 if (_itr
->second
.questStart
|| _itr
->second
.questEnd
)
20927 obj
->BuildCreateUpdateBlockForPlayer(&udata
, this);
20933 udata
.BuildPacket(&packet
);
20934 GetSession()->SendPacket(&packet
);
20937 void Player::SummonIfPossible(bool agree
)
20941 m_summon_expire
= 0;
20945 // expire and auto declined
20946 if (m_summon_expire
< time(NULL
))
20949 // stop taxi flight at summon
20950 if (IsTaxiFlying())
20952 GetMotionMaster()->MovementExpired();
20953 m_taxi
.ClearTaxiDestinations();
20956 // drop flag at summon
20957 // this code can be reached only when GM is summoning player who carries flag, because player should be immune to summoning spells when he carries flag
20958 if (BattleGround
* bg
= GetBattleGround())
20959 bg
->EventPlayerDroppedFlag(this);
20961 m_summon_expire
= 0;
20963 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS
, 1);
20965 TeleportTo(m_summon_mapid
, m_summon_x
, m_summon_y
, m_summon_z
, GetOrientation());
20968 void Player::RemoveItemDurations(Item
* item
)
20970 for (ItemDurationList::iterator itr
= m_itemDuration
.begin(); itr
!= m_itemDuration
.end(); ++itr
)
20974 m_itemDuration
.erase(itr
);
20980 void Player::AddItemDurations(Item
* item
)
20982 if (item
->GetUInt32Value(ITEM_FIELD_DURATION
))
20984 m_itemDuration
.push_back(item
);
20985 item
->SendTimeUpdate(this);
20989 void Player::AutoUnequipOffhandIfNeed()
20991 Item
* offItem
= GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
);
20995 // need unequip offhand for 2h-weapon without TitanGrip (in any from hands)
20996 if ((CanDualWield() || offItem
->GetProto()->InventoryType
== INVTYPE_SHIELD
|| offItem
->GetProto()->InventoryType
== INVTYPE_HOLDABLE
) &&
20997 (CanTitanGrip() || (offItem
->GetProto()->InventoryType
!= INVTYPE_2HWEAPON
&& !IsTwoHandUsed())))
21000 ItemPosCountVec off_dest
;
21001 uint8 off_msg
= CanStoreItem(NULL_BAG
, NULL_SLOT
, off_dest
, offItem
, false);
21002 if (off_msg
== EQUIP_ERR_OK
)
21004 RemoveItem(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
, true);
21005 StoreItem(off_dest
, offItem
, true);
21009 MoveItemFromInventory(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
, true);
21010 CharacterDatabase
.BeginTransaction();
21011 offItem
->DeleteFromInventoryDB(); // deletes item from character's inventory
21012 offItem
->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
21013 CharacterDatabase
.CommitTransaction();
21015 std::string subject
= GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM
);
21016 MailDraft(subject
, "There's were problems with equipping this item.").AddItem(offItem
).SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM
), MAIL_CHECK_MASK_COPIED
);
21020 bool Player::HasItemFitToSpellReqirements(SpellEntry
const* spellInfo
, Item
const* ignoreItem
)
21022 int32 itemClass
= spellInfo
->GetEquippedItemClass();
21026 // scan other equipped items for same requirements (mostly 2 daggers/etc)
21027 // for optimize check 2 used cases only
21030 case ITEM_CLASS_WEAPON
:
21032 for (int i
= EQUIPMENT_SLOT_MAINHAND
; i
< EQUIPMENT_SLOT_TABARD
; ++i
)
21033 if (Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
21034 if (item
!= ignoreItem
&& item
->IsFitToSpellRequirements(spellInfo
))
21038 case ITEM_CLASS_ARMOR
:
21040 // tabard not have dependent spells
21041 for (int i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_MAINHAND
; ++i
)
21042 if (Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
21043 if (item
!= ignoreItem
&& item
->IsFitToSpellRequirements(spellInfo
))
21046 // shields can be equipped to offhand slot
21047 if (Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
))
21048 if (item
!= ignoreItem
&& item
->IsFitToSpellRequirements(spellInfo
))
21051 // ranged slot can have some armor subclasses
21052 if (Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_RANGED
))
21053 if (item
!= ignoreItem
&& item
->IsFitToSpellRequirements(spellInfo
))
21059 sLog
.outError("HasItemFitToSpellReqirements: Not handled spell requirement for item class %u", itemClass
);
21066 bool Player::CanNoReagentCast(SpellEntry
const* spellInfo
) const
21068 // don't take reagents for spells with SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP
21069 if (spellInfo
->HasAttribute(SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP
) && HasFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_PREPARATION
))
21072 // Check no reagent use mask
21073 uint64 noReagentMask_0_1
= GetUInt64Value(PLAYER_NO_REAGENT_COST_1
);
21074 uint32 noReagentMask_2
= GetUInt32Value(PLAYER_NO_REAGENT_COST_1
+ 2);
21075 if (spellInfo
->IsFitToFamilyMask(noReagentMask_0_1
, noReagentMask_2
))
21081 void Player::RemoveItemDependentAurasAndCasts(Item
* pItem
)
21083 SpellAuraHolderMap
& auras
= GetSpellAuraHolderMap();
21084 for (SpellAuraHolderMap::const_iterator itr
= auras
.begin(); itr
!= auras
.end();)
21086 SpellAuraHolder
* holder
= itr
->second
;
21088 // skip passive (passive item dependent spells work in another way) and not self applied auras
21089 SpellEntry
const* spellInfo
= holder
->GetSpellProto();
21090 if (holder
->IsPassive() || holder
->GetCasterGuid() != GetObjectGuid())
21096 // skip if not item dependent or have alternative item
21097 if (HasItemFitToSpellReqirements(spellInfo
, pItem
))
21103 // no alt item, remove aura, restart check
21104 RemoveAurasDueToSpell(holder
->GetId());
21105 itr
= auras
.begin();
21108 // currently casted spells can be dependent from item
21109 for (uint32 i
= 0; i
< CURRENT_MAX_SPELL
; ++i
)
21110 if (Spell
* spell
= GetCurrentSpell(CurrentSpellTypes(i
)))
21111 if (spell
->getState() != SPELL_STATE_DELAYED
&& !HasItemFitToSpellReqirements(spell
->m_spellInfo
, pItem
))
21112 InterruptSpell(CurrentSpellTypes(i
));
21115 uint32
Player::GetResurrectionSpellId()
21117 // search priceless resurrection possibilities
21119 uint32 spell_id
= 0;
21120 AuraList
const& dummyAuras
= GetAurasByType(SPELL_AURA_DUMMY
);
21121 for (AuraList::const_iterator itr
= dummyAuras
.begin(); itr
!= dummyAuras
.end(); ++itr
)
21123 // Soulstone Resurrection // prio: 3 (max, non death persistent)
21124 if (prio
< 2 && (*itr
)->GetSpellProto()->SpellVisual
[0] == 99 && (*itr
)->GetSpellProto()->SpellIconID
== 92)
21126 switch ((*itr
)->GetId())
21128 case 20707: spell_id
= 3026; break; // rank 1
21129 case 20762: spell_id
= 20758; break; // rank 2
21130 case 20763: spell_id
= 20759; break; // rank 3
21131 case 20764: spell_id
= 20760; break; // rank 4
21132 case 20765: spell_id
= 20761; break; // rank 5
21133 case 27239: spell_id
= 27240; break; // rank 6
21134 case 47883: spell_id
= 47882; break; // rank 7
21136 sLog
.outError("Unhandled spell %u: S.Resurrection", (*itr
)->GetId());
21142 // Twisting Nether // prio: 2 (max)
21143 else if ((*itr
)->GetId() == 23701 && roll_chance_i(10))
21150 // Reincarnation (passive spell) // prio: 1
21151 // Glyph of Renewed Life remove reagent requiremnnt
21152 if (prio
< 1 && HasSpell(20608) && !HasSpellCooldown(21169) && (HasItemCount(17030, 1) || HasAura(58059, EFFECT_INDEX_0
)))
21158 // Used in triggers for check "Only to targets that grant experience or honor" req
21159 bool Player::isHonorOrXPTarget(Unit
* pVictim
) const
21161 uint32 v_level
= pVictim
->getLevel();
21162 uint32 k_grey
= MaNGOS::XP::GetGrayLevel(getLevel());
21164 // Victim level less gray level
21165 if (v_level
<= k_grey
)
21168 if (pVictim
->GetTypeId() == TYPEID_UNIT
)
21170 if (((Creature
*)pVictim
)->IsTotem() ||
21171 ((Creature
*)pVictim
)->IsPet() ||
21172 ((Creature
*)pVictim
)->GetCreatureInfo()->flags_extra
& CREATURE_FLAG_EXTRA_NO_XP_AT_KILL
)
21178 void Player::RewardSinglePlayerAtKill(Unit
* pVictim
)
21180 bool PvP
= pVictim
->isCharmedOwnedByPlayerOrPlayer();
21181 uint32 xp
= PvP
? 0 : MaNGOS::XP::Gain(this, pVictim
);
21183 // honor can be in PvP and !PvP (racial leader) cases
21184 RewardHonor(pVictim
, 1);
21186 // xp and reputation only in !PvP case
21189 RewardReputation(pVictim
, 1);
21190 GiveXP(xp
, pVictim
);
21192 if (Pet
* pet
= GetPet())
21193 pet
->GivePetXP(xp
);
21195 // normal creature (not pet/etc) can be only in !PvP case
21196 if (pVictim
->GetTypeId() == TYPEID_UNIT
)
21197 if (CreatureInfo
const* normalInfo
= ObjectMgr::GetCreatureTemplate(pVictim
->GetEntry()))
21198 KilledMonster(normalInfo
, pVictim
->GetObjectGuid());
21202 void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id
, WorldObject
* pRewardSource
)
21204 MANGOS_ASSERT((!GetGroup() || pRewardSource
) && "Player::RewardPlayerAndGroupAtEvent called for Group-Case but no source for range searching provided");
21206 ObjectGuid creature_guid
= pRewardSource
&& pRewardSource
->GetTypeId() == TYPEID_UNIT
? pRewardSource
->GetObjectGuid() : ObjectGuid();
21208 // prepare data for near group iteration
21209 if (Group
* pGroup
= GetGroup())
21211 for (GroupReference
* itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
21213 Player
* pGroupGuy
= itr
->getSource();
21217 if (!pGroupGuy
->IsAtGroupRewardDistance(pRewardSource
))
21218 continue; // member (alive or dead) or his corpse at req. distance
21220 // quest objectives updated only for alive group member or dead but with not released body
21221 if (pGroupGuy
->isAlive() || !pGroupGuy
->HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GHOST
))
21222 pGroupGuy
->KilledMonsterCredit(creature_id
, creature_guid
);
21225 else // if (!pGroup)
21226 KilledMonsterCredit(creature_id
, creature_guid
);
21229 void Player::RewardPlayerAndGroupAtCast(WorldObject
* pRewardSource
, uint32 spellid
)
21231 // prepare data for near group iteration
21232 if (Group
* pGroup
= GetGroup())
21234 for (GroupReference
* itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
21236 Player
* pGroupGuy
= itr
->getSource();
21240 if (!pGroupGuy
->IsAtGroupRewardDistance(pRewardSource
))
21241 continue; // member (alive or dead) or his corpse at req. distance
21243 // quest objectives updated only for alive group member or dead but with not released body
21244 if (pGroupGuy
->isAlive() || !pGroupGuy
->HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_GHOST
))
21245 pGroupGuy
->CastedCreatureOrGO(pRewardSource
->GetEntry(), pRewardSource
->GetObjectGuid(), spellid
, pGroupGuy
== this);
21248 else // if (!pGroup)
21249 CastedCreatureOrGO(pRewardSource
->GetEntry(), pRewardSource
->GetObjectGuid(), spellid
);
21252 bool Player::IsAtGroupRewardDistance(WorldObject
const* pRewardSource
) const
21254 if (pRewardSource
->IsWithinDistInMap(this, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
)))
21260 Corpse
* corpse
= GetCorpse();
21264 return pRewardSource
->IsWithinDistInMap(corpse
, sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
));
21267 void Player::ResurectUsingRequestData()
21269 /// Teleport before resurrecting by player, otherwise the player might get attacked from creatures near his corpse
21270 if (m_resurrectGuid
.IsPlayer())
21271 TeleportTo(m_resurrectMap
, m_resurrectX
, m_resurrectY
, m_resurrectZ
, GetOrientation());
21273 // we cannot resurrect player when we triggered far teleport
21274 // player will be resurrected upon teleportation
21275 if (IsBeingTeleportedFar())
21277 ScheduleDelayedOperation(DELAYED_RESURRECT_PLAYER
);
21281 ResurrectPlayer(0.0f
, false);
21283 if (GetMaxHealth() > m_resurrectHealth
)
21284 SetHealth(m_resurrectHealth
);
21286 SetHealth(GetMaxHealth());
21288 if (GetMaxPower(POWER_MANA
) > m_resurrectMana
)
21289 SetPower(POWER_MANA
, m_resurrectMana
);
21291 SetPower(POWER_MANA
, GetMaxPower(POWER_MANA
));
21293 SetPower(POWER_RAGE
, 0);
21295 SetPower(POWER_ENERGY
, GetMaxPower(POWER_ENERGY
));
21297 SpawnCorpseBones();
21300 void Player::SetClientControl(Unit
* target
, uint8 allowMove
)
21302 WorldPacket
data(SMSG_CLIENT_CONTROL_UPDATE
, target
->GetPackGUID().size() + 1);
21303 data
<< target
->GetPackGUID();
21304 data
<< uint8(allowMove
);
21305 GetSession()->SendPacket(&data
);
21308 void Player::UpdateZoneDependentAuras()
21310 // Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update
21311 SpellAreaForAreaMapBounds saBounds
= sSpellMgr
.GetSpellAreaForAreaMapBounds(m_zoneUpdateId
);
21312 for (SpellAreaForAreaMap::const_iterator itr
= saBounds
.first
; itr
!= saBounds
.second
; ++itr
)
21313 if (itr
->second
->autocast
&& itr
->second
->IsFitToRequirements(this, m_zoneUpdateId
, 0))
21314 if (!HasAura(itr
->second
->spellId
, EFFECT_INDEX_0
))
21315 CastSpell(this, itr
->second
->spellId
, true);
21318 void Player::UpdateAreaDependentAuras()
21320 // remove auras from spells with area limitations
21321 for (SpellAuraHolderMap::iterator iter
= m_spellAuraHolders
.begin(); iter
!= m_spellAuraHolders
.end();)
21323 // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date
21324 if (sSpellMgr
.GetSpellAllowedInLocationError(iter
->second
->GetSpellProto(), GetMapId(), m_zoneUpdateId
, m_areaUpdateId
, this) != SPELL_CAST_OK
)
21326 RemoveSpellAuraHolder(iter
->second
);
21327 iter
= m_spellAuraHolders
.begin();
21333 // some auras applied at subzone enter
21334 SpellAreaForAreaMapBounds saBounds
= sSpellMgr
.GetSpellAreaForAreaMapBounds(m_areaUpdateId
);
21335 for (SpellAreaForAreaMap::const_iterator itr
= saBounds
.first
; itr
!= saBounds
.second
; ++itr
)
21336 if (itr
->second
->autocast
&& itr
->second
->IsFitToRequirements(this, m_zoneUpdateId
, m_areaUpdateId
))
21337 if (!HasAura(itr
->second
->spellId
, EFFECT_INDEX_0
))
21338 CastSpell(this, itr
->second
->spellId
, true);
21341 struct UpdateZoneDependentPetsHelper
21343 explicit UpdateZoneDependentPetsHelper(Player
* _owner
, uint32 zone
, uint32 area
) : owner(_owner
), zone_id(zone
), area_id(area
) {}
21344 void operator()(Unit
* unit
) const
21346 if (unit
->GetTypeId() == TYPEID_UNIT
&& ((Creature
*)unit
)->IsPet() && !((Pet
*)unit
)->IsPermanentPetFor(owner
))
21347 if (uint32 spell_id
= unit
->GetUInt32Value(UNIT_CREATED_BY_SPELL
))
21348 if (SpellEntry
const* spellEntry
= sSpellStore
.LookupEntry(spell_id
))
21349 if (sSpellMgr
.GetSpellAllowedInLocationError(spellEntry
, owner
->GetMapId(), zone_id
, area_id
, owner
) != SPELL_CAST_OK
)
21350 ((Pet
*)unit
)->Unsummon(PET_SAVE_AS_DELETED
, owner
);
21357 void Player::UpdateZoneDependentPets()
21359 // check pet (permanent pets ignored), minipet, guardians (including protector)
21360 CallForAllControlledUnits(UpdateZoneDependentPetsHelper(this, m_zoneUpdateId
, m_areaUpdateId
), CONTROLLED_PET
| CONTROLLED_GUARDIANS
| CONTROLLED_MINIPET
);
21363 uint32
Player::GetCorpseReclaimDelay(bool pvp
) const
21365 if ((pvp
&& !sWorld
.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVP
)) ||
21366 (!pvp
&& !sWorld
.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVE
)))
21368 return corpseReclaimDelay
[0];
21371 time_t now
= time(NULL
);
21372 // 0..2 full period
21373 uint32 count
= (now
< m_deathExpireTime
) ? uint32((m_deathExpireTime
- now
) / DEATH_EXPIRE_STEP
) : 0;
21374 return corpseReclaimDelay
[count
];
21377 void Player::UpdateCorpseReclaimDelay()
21379 bool pvp
= m_ExtraFlags
& PLAYER_EXTRA_PVP_DEATH
;
21381 if ((pvp
&& !sWorld
.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVP
)) ||
21382 (!pvp
&& !sWorld
.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVE
)))
21385 time_t now
= time(NULL
);
21386 if (now
< m_deathExpireTime
)
21388 // full and partly periods 1..3
21389 uint32 count
= uint32((m_deathExpireTime
- now
) / DEATH_EXPIRE_STEP
+ 1);
21390 if (count
< MAX_DEATH_COUNT
)
21391 m_deathExpireTime
= now
+ (count
+ 1) * DEATH_EXPIRE_STEP
;
21393 m_deathExpireTime
= now
+ MAX_DEATH_COUNT
* DEATH_EXPIRE_STEP
;
21396 m_deathExpireTime
= now
+ DEATH_EXPIRE_STEP
;
21399 void Player::SendCorpseReclaimDelay(bool load
)
21401 Corpse
* corpse
= GetCorpse();
21408 if (corpse
->GetGhostTime() > m_deathExpireTime
)
21411 bool pvp
= corpse
->GetType() == CORPSE_RESURRECTABLE_PVP
;
21414 if ((pvp
&& sWorld
.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVP
)) ||
21415 (!pvp
&& sWorld
.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVE
)))
21417 count
= uint32(m_deathExpireTime
- corpse
->GetGhostTime()) / DEATH_EXPIRE_STEP
;
21418 if (count
>= MAX_DEATH_COUNT
)
21419 count
= MAX_DEATH_COUNT
- 1;
21424 time_t expected_time
= corpse
->GetGhostTime() + corpseReclaimDelay
[count
];
21426 time_t now
= time(NULL
);
21427 if (now
>= expected_time
)
21430 delay
= uint32(expected_time
- now
);
21433 delay
= GetCorpseReclaimDelay(corpse
->GetType() == CORPSE_RESURRECTABLE_PVP
);
21435 //! corpse reclaim delay 30 * 1000ms or longer at often deaths
21436 WorldPacket
data(SMSG_CORPSE_RECLAIM_DELAY
, 4);
21437 data
<< uint32(delay
* IN_MILLISECONDS
);
21438 GetSession()->SendPacket(&data
);
21441 Player
* Player::GetNextRandomRaidMember(float radius
)
21443 Group
* pGroup
= GetGroup();
21447 std::vector
<Player
*> nearMembers
;
21448 nearMembers
.reserve(pGroup
->GetMembersCount());
21450 for (GroupReference
* itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
21452 Player
* Target
= itr
->getSource();
21454 // IsHostileTo check duel and controlled by enemy
21455 if (Target
&& Target
!= this && IsWithinDistInMap(Target
, radius
) &&
21456 !Target
->HasInvisibilityAura() && !IsHostileTo(Target
))
21457 nearMembers
.push_back(Target
);
21460 if (nearMembers
.empty())
21463 uint32 randTarget
= urand(0, nearMembers
.size() - 1);
21464 return nearMembers
[randTarget
];
21467 PartyResult
Player::CanUninviteFromGroup() const
21469 const Group
* grp
= GetGroup();
21471 return ERR_NOT_IN_GROUP
;
21473 if (!grp
->IsLeader(GetObjectGuid()) && !grp
->IsAssistant(GetObjectGuid()))
21474 return ERR_NOT_LEADER
;
21476 if (InBattleGround())
21477 return ERR_INVITE_RESTRICTED
;
21479 return ERR_PARTY_RESULT_OK
;
21482 void Player::SetBattleGroundRaid(Group
* group
, int8 subgroup
)
21484 // we must move references from m_group to m_originalGroup
21485 SetOriginalGroup(GetGroup(), GetSubGroup());
21488 m_group
.link(group
, this);
21489 m_group
.setSubGroup((uint8
)subgroup
);
21492 void Player::RemoveFromBattleGroundRaid()
21494 // remove existing reference
21496 if (Group
* group
= GetOriginalGroup())
21498 m_group
.link(group
, this);
21499 m_group
.setSubGroup(GetOriginalSubGroup());
21501 SetOriginalGroup(NULL
);
21504 void Player::SetOriginalGroup(Group
* group
, int8 subgroup
)
21507 m_originalGroup
.unlink();
21510 // never use SetOriginalGroup without a subgroup unless you specify NULL for group
21511 MANGOS_ASSERT(subgroup
>= 0);
21512 m_originalGroup
.link(group
, this);
21513 m_originalGroup
.setSubGroup((uint8
)subgroup
);
21517 void Player::UpdateUnderwaterState(Map
* m
, float x
, float y
, float z
)
21519 GridMapLiquidData liquid_status
;
21520 GridMapLiquidStatus res
= m
->GetTerrain()->getLiquidStatus(x
, y
, z
, MAP_ALL_LIQUIDS
, &liquid_status
);
21523 m_MirrorTimerFlags
&= ~(UNDERWATER_INWATER
| UNDERWATER_INLAVA
| UNDERWATER_INSLIME
| UNDERWATER_INDARKWATER
);
21524 // Small hack for enable breath in WMO
21525 /* if (IsInWater())
21526 m_MirrorTimerFlags|=UNDERWATER_INWATER; */
21530 // All liquids type - check under water position
21531 if (liquid_status
.type
& (MAP_LIQUID_TYPE_WATER
| MAP_LIQUID_TYPE_OCEAN
| MAP_LIQUID_TYPE_MAGMA
| MAP_LIQUID_TYPE_SLIME
))
21533 if (res
& LIQUID_MAP_UNDER_WATER
)
21534 m_MirrorTimerFlags
|= UNDERWATER_INWATER
;
21536 m_MirrorTimerFlags
&= ~UNDERWATER_INWATER
;
21539 // Allow travel in dark water on taxi or transport
21540 if ((liquid_status
.type
& MAP_LIQUID_TYPE_DARK_WATER
) && !IsTaxiFlying() && !GetTransport())
21541 m_MirrorTimerFlags
|= UNDERWATER_INDARKWATER
;
21543 m_MirrorTimerFlags
&= ~UNDERWATER_INDARKWATER
;
21545 // in lava check, anywhere in lava level
21546 if (liquid_status
.type
& MAP_LIQUID_TYPE_MAGMA
)
21548 if (res
& (LIQUID_MAP_UNDER_WATER
| LIQUID_MAP_IN_WATER
| LIQUID_MAP_WATER_WALK
))
21549 m_MirrorTimerFlags
|= UNDERWATER_INLAVA
;
21551 m_MirrorTimerFlags
&= ~UNDERWATER_INLAVA
;
21553 // in slime check, anywhere in slime level
21554 if (liquid_status
.type
& MAP_LIQUID_TYPE_SLIME
)
21556 if (res
& (LIQUID_MAP_UNDER_WATER
| LIQUID_MAP_IN_WATER
| LIQUID_MAP_WATER_WALK
))
21557 m_MirrorTimerFlags
|= UNDERWATER_INSLIME
;
21559 m_MirrorTimerFlags
&= ~UNDERWATER_INSLIME
;
21563 void Player::SetCanParry(bool value
)
21565 if (m_canParry
== value
)
21568 m_canParry
= value
;
21569 UpdateParryPercentage();
21572 void Player::SetCanBlock(bool value
)
21574 if (m_canBlock
== value
)
21577 m_canBlock
= value
;
21578 UpdateBlockPercentage();
21579 UpdateShieldBlockDamageValue();
21582 bool ItemPosCount::isContainedIn(ItemPosCountVec
const& vec
) const
21584 for (ItemPosCountVec::const_iterator itr
= vec
.begin(); itr
!= vec
.end(); ++itr
)
21585 if (itr
->pos
== pos
)
21591 bool Player::CanUseBattleGroundObject()
21593 // TODO : some spells gives player ForceReaction to one faction (ReputationMgr::ApplyForceReaction)
21594 // maybe gameobject code should handle that ForceReaction usage
21595 // BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet
21596 return (isAlive() && // living
21597 // the following two are incorrect, because invisible/stealthed players should get visible when they click on flag
21598 !HasStealthAura() && // not stealthed
21599 !HasInvisibilityAura() && // visible
21600 !isTotalImmune() && // vulnerable (not immune)
21601 !HasAura(SPELL_RECENTLY_DROPPED_FLAG
, EFFECT_INDEX_0
));
21604 uint32
Player::GetBarberShopCost(uint8 newhairstyle
, uint8 newhaircolor
, uint8 newfacialhair
, uint32 newskintone
)
21606 uint32 level
= getLevel();
21608 if (level
> GT_MAX_LEVEL
)
21609 level
= GT_MAX_LEVEL
; // max level in this dbc
21611 uint8 hairstyle
= GetByteValue(PLAYER_BYTES
, 2);
21612 uint8 haircolor
= GetByteValue(PLAYER_BYTES
, 3);
21613 uint8 facialhair
= GetByteValue(PLAYER_BYTES_2
, 0);
21614 uint8 skintone
= GetByteValue(PLAYER_BYTES
, 0);
21616 if (hairstyle
== newhairstyle
&& haircolor
== newhaircolor
&& facialhair
== newfacialhair
&&
21617 (skintone
== newskintone
|| newskintone
== -1))
21620 GtBarberShopCostBaseEntry
const* bsc
= sGtBarberShopCostBaseStore
.LookupEntry(level
- 1);
21622 if (!bsc
) // shouldn't happen
21627 if (hairstyle
!= newhairstyle
)
21628 cost
+= bsc
->cost
; // full price
21630 if (haircolor
!= newhaircolor
&& hairstyle
== newhairstyle
)
21631 cost
+= bsc
->cost
* 0.5f
; // +1/2 of price
21633 if (facialhair
!= newfacialhair
)
21634 cost
+= bsc
->cost
* 0.75f
; // +3/4 of price
21636 if (skintone
!= newskintone
&& newskintone
!= -1)
21637 cost
+= bsc
->cost
* 0.5f
; // +1/2 of price
21639 return uint32(cost
);
21642 void Player::InitGlyphsForLevel()
21645 for (uint32 i
= 0; i
< sGlyphSlotStore
.GetNumRows() && slot
< MAX_GLYPH_SLOT_INDEX
; ++i
)
21646 if (GlyphSlotEntry
const* gs
= sGlyphSlotStore
.LookupEntry(i
))
21647 SetGlyphSlot(slot
++, gs
->Id
);
21649 uint32 level
= getLevel();
21652 // 0x3F = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 for 80 level
21654 value
|= 0x01 | 0x02 | 0x40;
21656 value
|= 0x04 | 0x08 | 0x80;
21658 value
|= 0x10 | 0x20 | 0x100;
21660 SetUInt32Value(PLAYER_GLYPHS_ENABLED
, value
);
21663 void Player::ApplyGlyph(uint8 slot
, bool apply
)
21665 if (uint32 glyph
= GetGlyph(slot
))
21667 if (GlyphPropertiesEntry
const* gp
= sGlyphPropertiesStore
.LookupEntry(glyph
))
21671 CastSpell(this, gp
->SpellId
, true);
21672 SetUInt32Value(PLAYER_FIELD_GLYPHS_1
+ slot
, glyph
);
21676 RemoveAurasDueToSpell(gp
->SpellId
);
21677 SetUInt32Value(PLAYER_FIELD_GLYPHS_1
+ slot
, 0);
21683 void Player::ApplyGlyphs(bool apply
)
21685 for (uint8 i
= 0; i
< MAX_GLYPH_SLOT_INDEX
; ++i
)
21686 ApplyGlyph(i
, apply
);
21689 bool Player::isTotalImmune()
21691 AuraList
const& immune
= GetAurasByType(SPELL_AURA_SCHOOL_IMMUNITY
);
21693 uint32 immuneMask
= 0;
21694 for (AuraList::const_iterator itr
= immune
.begin(); itr
!= immune
.end(); ++itr
)
21696 immuneMask
|= (*itr
)->GetModifier()->m_miscvalue
;
21697 if (immuneMask
& SPELL_SCHOOL_MASK_ALL
) // total immunity
21703 bool Player::HasTitle(uint32 bitIndex
) const
21705 if (bitIndex
> MAX_TITLE_INDEX
)
21708 uint32 fieldIndexOffset
= bitIndex
/ 32;
21709 uint32 flag
= 1 << (bitIndex
% 32);
21710 return HasFlag(PLAYER__FIELD_KNOWN_TITLES
+ fieldIndexOffset
, flag
);
21713 void Player::SetTitle(CharTitlesEntry
const* title
, bool lost
)
21715 uint32 fieldIndexOffset
= title
->bit_index
/ 32;
21716 uint32 flag
= 1 << (title
->bit_index
% 32);
21720 if (!HasFlag(PLAYER__FIELD_KNOWN_TITLES
+ fieldIndexOffset
, flag
))
21723 RemoveFlag(PLAYER__FIELD_KNOWN_TITLES
+ fieldIndexOffset
, flag
);
21727 if (HasFlag(PLAYER__FIELD_KNOWN_TITLES
+ fieldIndexOffset
, flag
))
21730 SetFlag(PLAYER__FIELD_KNOWN_TITLES
+ fieldIndexOffset
, flag
);
21733 WorldPacket
data(SMSG_TITLE_EARNED
, 4 + 4);
21734 data
<< uint32(title
->bit_index
);
21735 data
<< uint32(lost
? 0 : 1); // 1 - earned, 0 - lost
21736 GetSession()->SendPacket(&data
);
21739 void Player::ConvertRune(uint8 index
, RuneType newType
)
21741 SetCurrentRune(index
, newType
);
21743 WorldPacket
data(SMSG_CONVERT_RUNE
, 2);
21744 data
<< uint8(index
);
21745 data
<< uint8(newType
);
21746 GetSession()->SendPacket(&data
);
21749 bool Player::ActivateRunes(RuneType type
, uint32 count
)
21751 bool modify
= false;
21752 for (uint32 j
= 0; count
> 0 && j
< MAX_RUNES
; ++j
)
21754 if (GetRuneCooldown(j
) && GetCurrentRune(j
) == type
)
21756 SetRuneCooldown(j
, 0);
21765 void Player::ResyncRunes()
21767 WorldPacket
data(SMSG_RESYNC_RUNES
, 4 + MAX_RUNES
* 2);
21768 data
<< uint32(MAX_RUNES
);
21769 for (uint32 i
= 0; i
< MAX_RUNES
; ++i
)
21771 data
<< uint8(GetCurrentRune(i
)); // rune type
21772 data
<< uint8(255 - ((GetRuneCooldown(i
) / REGEN_TIME_FULL
) * 51)); // passed cooldown time (0-255)
21774 GetSession()->SendPacket(&data
);
21777 void Player::AddRunePower(uint8 index
)
21779 WorldPacket
data(SMSG_ADD_RUNE_POWER
, 4);
21780 data
<< uint32(1 << index
); // mask (0x00-0x3F probably)
21781 GetSession()->SendPacket(&data
);
21784 static RuneType runeSlotTypes
[MAX_RUNES
] =
21794 void Player::InitRunes()
21796 if (getClass() != CLASS_DEATH_KNIGHT
)
21799 m_runes
= new Runes
;
21801 m_runes
->runeState
= 0;
21803 for (uint32 i
= 0; i
< MAX_RUNES
; ++i
)
21805 SetBaseRune(i
, runeSlotTypes
[i
]); // init base types
21806 SetCurrentRune(i
, runeSlotTypes
[i
]); // init current types
21807 SetRuneCooldown(i
, 0); // reset cooldowns
21808 m_runes
->SetRuneState(i
);
21811 for (uint32 i
= 0; i
< NUM_RUNE_TYPES
; ++i
)
21812 SetFloatValue(PLAYER_RUNE_REGEN_1
+ i
, 0.1f
);
21816 bool Player::IsBaseRuneSlotsOnCooldown(RuneType runeType
) const
21818 for (uint32 i
= 0; i
< MAX_RUNES
; ++i
)
21819 if (GetBaseRune(i
) == runeType
&& GetRuneCooldown(i
) == 0)
21825 void Player::AutoStoreLoot(uint32 loot_id
, LootStore
const& store
, bool broadcast
, uint8 bag
, uint8 slot
)
21828 loot
.FillLoot(loot_id
, store
, this, true);
21830 AutoStoreLoot(loot
, broadcast
, bag
, slot
);
21833 void Player::AutoStoreLoot(Loot
& loot
, bool broadcast
, uint8 bag
, uint8 slot
)
21835 uint32 max_slot
= loot
.GetMaxSlotInLootFor(this);
21836 for (uint32 i
= 0; i
< max_slot
; ++i
)
21838 LootItem
* lootItem
= loot
.LootItemInSlot(i
, this);
21840 ItemPosCountVec dest
;
21841 InventoryResult msg
= CanStoreNewItem(bag
, slot
, dest
, lootItem
->itemid
, lootItem
->count
);
21842 if (msg
!= EQUIP_ERR_OK
&& slot
!= NULL_SLOT
)
21843 msg
= CanStoreNewItem(bag
, NULL_SLOT
, dest
, lootItem
->itemid
, lootItem
->count
);
21844 if (msg
!= EQUIP_ERR_OK
&& bag
!= NULL_BAG
)
21845 msg
= CanStoreNewItem(NULL_BAG
, NULL_SLOT
, dest
, lootItem
->itemid
, lootItem
->count
);
21846 if (msg
!= EQUIP_ERR_OK
)
21848 SendEquipError(msg
, NULL
, NULL
, lootItem
->itemid
);
21852 Item
* pItem
= StoreNewItem(dest
, lootItem
->itemid
, true, lootItem
->randomPropertyId
);
21853 SendNewItem(pItem
, lootItem
->count
, false, false, broadcast
);
21857 Item
* Player::ConvertItem(Item
* item
, uint32 newItemId
)
21859 uint16 pos
= item
->GetPos();
21861 Item
* pNewItem
= Item::CreateItem(newItemId
, 1, this);
21865 // copy enchantments
21866 for (uint8 j
= PERM_ENCHANTMENT_SLOT
; j
<= TEMP_ENCHANTMENT_SLOT
; ++j
)
21868 if (item
->GetEnchantmentId(EnchantmentSlot(j
)))
21869 pNewItem
->SetEnchantment(EnchantmentSlot(j
), item
->GetEnchantmentId(EnchantmentSlot(j
)),
21870 item
->GetEnchantmentDuration(EnchantmentSlot(j
)), item
->GetEnchantmentCharges(EnchantmentSlot(j
)));
21874 if (item
->GetUInt32Value(ITEM_FIELD_DURABILITY
) < item
->GetUInt32Value(ITEM_FIELD_MAXDURABILITY
))
21876 double loosePercent
= 1 - item
->GetUInt32Value(ITEM_FIELD_DURABILITY
) / double(item
->GetUInt32Value(ITEM_FIELD_MAXDURABILITY
));
21877 DurabilityLoss(pNewItem
, loosePercent
);
21880 if (IsInventoryPos(pos
))
21882 ItemPosCountVec dest
;
21883 InventoryResult msg
= CanStoreItem(item
->GetBagSlot(), item
->GetSlot(), dest
, pNewItem
, true);
21884 // ignore cast/combat time restriction
21885 if (msg
== EQUIP_ERR_OK
)
21887 DestroyItem(item
->GetBagSlot(), item
->GetSlot(), true);
21888 return StoreItem(dest
, pNewItem
, true);
21891 else if (IsBankPos(pos
))
21893 ItemPosCountVec dest
;
21894 InventoryResult msg
= CanBankItem(item
->GetBagSlot(), item
->GetSlot(), dest
, pNewItem
, true);
21895 // ignore cast/combat time restriction
21896 if (msg
== EQUIP_ERR_OK
)
21898 DestroyItem(item
->GetBagSlot(), item
->GetSlot(), true);
21899 return BankItem(dest
, pNewItem
, true);
21902 else if (IsEquipmentPos(pos
))
21905 InventoryResult msg
= CanEquipItem(item
->GetSlot(), dest
, pNewItem
, true, false);
21906 // ignore cast/combat time restriction
21907 if (msg
== EQUIP_ERR_OK
)
21909 DestroyItem(item
->GetBagSlot(), item
->GetSlot(), true);
21910 pNewItem
= EquipItem(dest
, pNewItem
, true);
21911 AutoUnequipOffhandIfNeed();
21921 uint32
Player::CalculateTalentsPoints() const
21923 // this dbc file has entries only up to level 100
21924 NumTalentsAtLevelEntry
const* count
= sNumTalentsAtLevelStore
.LookupEntry(std::min
<uint32
>(getLevel(), 100));
21928 float baseForLevel
= count
->Talents
;
21930 if (getClass() != CLASS_DEATH_KNIGHT
)
21931 return uint32(baseForLevel
* sWorld
.getConfig(CONFIG_FLOAT_RATE_TALENT
));
21933 // Death Knight starting level
21934 // hardcoded here - number of quest awarded talents is equal to number of talents any other class would have at level 55
21935 if (getLevel() < 55)
21938 NumTalentsAtLevelEntry
const* dkBase
= sNumTalentsAtLevelStore
.LookupEntry(55);
21942 float talentPointsForLevel
= count
->Talents
- dkBase
->Talents
;
21943 talentPointsForLevel
+= float(m_questRewardTalentCount
);
21945 if (talentPointsForLevel
> baseForLevel
)
21946 talentPointsForLevel
= baseForLevel
;
21948 return uint32(talentPointsForLevel
* sWorld
.getConfig(CONFIG_FLOAT_RATE_TALENT
));
21951 bool Player::CanStartFlyInArea(uint32 mapid
, uint32 zone
, uint32 area
) const
21953 if (isGameMaster())
21955 // continent checked in SpellMgr::GetSpellAllowedInLocationError at cast and area update
21956 uint32 v_map
= GetVirtualMapForMapAndZone(mapid
, zone
);
21958 if (v_map
== 571 && !HasSpell(54197)) // Cold Weather Flying
21961 // don't allow flying in Dalaran restricted areas
21962 // (no other zones currently has areas with AREA_FLAG_CANNOT_FLY)
21963 if (AreaTableEntry
const* atEntry
= GetAreaEntryByAreaID(area
))
21964 return (!(atEntry
->flags
& AREA_FLAG_CANNOT_FLY
));
21966 // TODO: disallow mounting in wintergrasp too when battle is in progress
21967 // forced dismount part in Player::UpdateArea()
21971 struct DoPlayerLearnSpell
21973 DoPlayerLearnSpell(Player
& _player
) : player(_player
) {}
21974 void operator()(uint32 spell_id
) { player
.learnSpell(spell_id
, false); }
21978 void Player::learnSpellHighRank(uint32 spellid
)
21980 learnSpell(spellid
, false);
21982 DoPlayerLearnSpell
worker(*this);
21983 sSpellMgr
.doForHighRanks(spellid
, worker
);
21986 void Player::_LoadSkills(QueryResult
* result
)
21989 // SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid));
21992 uint8 professionCount
= 0;
21997 Field
* fields
= result
->Fetch();
21999 uint16 skill
= fields
[0].GetUInt16();
22000 uint16 value
= fields
[1].GetUInt16();
22001 uint16 max
= fields
[2].GetUInt16();
22003 SkillLineEntry
const* pSkill
= sSkillLineStore
.LookupEntry(skill
);
22006 sLog
.outError("Character %u has skill %u that does not exist.", GetGUIDLow(), skill
);
22010 // set fixed skill ranges
22011 switch (GetSkillRangeType(pSkill
, false))
22013 case SKILL_RANGE_LANGUAGE
: // 300..300
22016 case SKILL_RANGE_MONO
: // 1..1, grey monolite bar
22025 sLog
.outError("Character %u has skill %u with value 0. Will be deleted.", GetGUIDLow(), skill
);
22026 CharacterDatabase
.PExecute("DELETE FROM character_skills WHERE guid = '%u' AND skill = '%u' ", GetGUIDLow(), skill
);
22030 uint16 field
= count
/ 2;
22031 uint8 offset
= count
& 1;
22033 SetUInt16Value(PLAYER_SKILL_LINEID_0
+ field
, offset
, skill
);
22036 if (pSkill
->categoryId
== SKILL_CATEGORY_SECONDARY
)
22039 if (pSkill
->categoryId
== SKILL_CATEGORY_PROFESSION
)
22043 if (professionCount
< 2)
22044 SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1
+ professionCount
++, skill
);
22047 SetUInt16Value(PLAYER_SKILL_STEP_0
+ field
, offset
, step
);
22048 SetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
, value
);
22049 SetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
, max
);
22050 SetUInt16Value(PLAYER_SKILL_MODIFIER_0
+ field
, offset
, 0);
22051 SetUInt16Value(PLAYER_SKILL_TALENT_0
+ field
, offset
, 0);
22053 mSkillStatus
.insert(SkillStatusMap::value_type(skill
, SkillStatusData(count
, SKILL_UNCHANGED
)));
22055 learnSkillRewardedSpells(skill
, value
);
22059 if (count
>= PLAYER_MAX_SKILLS
) // client limit
22061 sLog
.outError("Character %u has more than %u skills.", GetGUIDLow(), PLAYER_MAX_SKILLS
);
22065 while (result
->NextRow());
22069 for (; count
< PLAYER_MAX_SKILLS
; ++count
)
22071 uint16 field
= count
/ 2;
22072 uint8 offset
= count
& 1;
22074 SetUInt16Value(PLAYER_SKILL_LINEID_0
+ field
, offset
, 0);
22075 SetUInt16Value(PLAYER_SKILL_STEP_0
+ field
, offset
, 0);
22076 SetUInt16Value(PLAYER_SKILL_RANK_0
+ field
, offset
, 0);
22077 SetUInt16Value(PLAYER_SKILL_MAX_RANK_0
+ field
, offset
, 0);
22078 SetUInt16Value(PLAYER_SKILL_MODIFIER_0
+ field
, offset
, 0);
22079 SetUInt16Value(PLAYER_SKILL_TALENT_0
+ field
, offset
, 0);
22082 // special settings
22083 if (getClass() == CLASS_DEATH_KNIGHT
)
22085 uint32 base_level
= std::min(getLevel(), sWorld
.getConfig(CONFIG_UINT32_START_HEROIC_PLAYER_LEVEL
));
22086 if (base_level
< 1)
22088 uint32 base_skill
= (base_level
- 1) * 5; // 270 at starting level 55
22089 if (base_skill
< 1)
22090 base_skill
= 1; // skill mast be known and then > 0 in any case
22092 if (GetPureSkillValue(SKILL_FIRST_AID
) < base_skill
)
22093 SetSkill(SKILL_FIRST_AID
, base_skill
, base_skill
);
22094 if (GetPureSkillValue(SKILL_AXES
) < base_skill
)
22095 SetSkill(SKILL_AXES
, base_skill
, base_skill
);
22096 if (GetPureSkillValue(SKILL_DEFENSE
) < base_skill
)
22097 SetSkill(SKILL_DEFENSE
, base_skill
, base_skill
);
22098 if (GetPureSkillValue(SKILL_POLEARMS
) < base_skill
)
22099 SetSkill(SKILL_POLEARMS
, base_skill
, base_skill
);
22100 if (GetPureSkillValue(SKILL_SWORDS
) < base_skill
)
22101 SetSkill(SKILL_SWORDS
, base_skill
, base_skill
);
22102 if (GetPureSkillValue(SKILL_2H_AXES
) < base_skill
)
22103 SetSkill(SKILL_2H_AXES
, base_skill
, base_skill
);
22104 if (GetPureSkillValue(SKILL_2H_SWORDS
) < base_skill
)
22105 SetSkill(SKILL_2H_SWORDS
, base_skill
, base_skill
);
22106 if (GetPureSkillValue(SKILL_UNARMED
) < base_skill
)
22107 SetSkill(SKILL_UNARMED
, base_skill
, base_skill
);
22111 uint32
Player::GetPhaseMaskForSpawn() const
22113 uint32 phase
= PHASEMASK_NORMAL
;
22114 if (!isGameMaster())
22115 phase
= GetPhaseMask();
22118 AuraList
const& phases
= GetAurasByType(SPELL_AURA_PHASE
);
22119 if (!phases
.empty())
22120 phase
= phases
.front()->GetMiscValue();
22123 // some aura phases include 1 normal map in addition to phase itself
22124 if (uint32 n_phase
= phase
& ~PHASEMASK_NORMAL
)
22127 return PHASEMASK_NORMAL
;
22130 InventoryResult
Player::CanEquipUniqueItem(Item
* pItem
, uint8 eslot
, uint32 limit_count
) const
22132 ItemPrototype
const* pProto
= pItem
->GetProto();
22134 // proto based limitations
22135 if (InventoryResult res
= CanEquipUniqueItem(pProto
, eslot
, limit_count
))
22138 // check unique-equipped on gems
22139 for (uint32 enchant_slot
= SOCK_ENCHANTMENT_SLOT
; enchant_slot
< SOCK_ENCHANTMENT_SLOT
+ 3; ++enchant_slot
)
22141 uint32 enchant_id
= pItem
->GetEnchantmentId(EnchantmentSlot(enchant_slot
));
22144 SpellItemEnchantmentEntry
const* enchantEntry
= sSpellItemEnchantmentStore
.LookupEntry(enchant_id
);
22148 ItemPrototype
const* pGem
= ObjectMgr::GetItemPrototype(enchantEntry
->GemID
);
22152 // include for check equip another gems with same limit category for not equipped item (and then not counted)
22153 uint32 gem_limit_count
= !pItem
->IsEquipped() && pGem
->ItemLimitCategory
22154 ? pItem
->GetGemCountWithLimitCategory(pGem
->ItemLimitCategory
) : 1;
22156 if (InventoryResult res
= CanEquipUniqueItem(pGem
, eslot
, gem_limit_count
))
22160 return EQUIP_ERR_OK
;
22163 InventoryResult
Player::CanEquipUniqueItem(ItemPrototype
const* itemProto
, uint8 except_slot
, uint32 limit_count
) const
22165 // check unique-equipped on item
22166 if (itemProto
->Flags
& ITEM_FLAG_UNIQUE_EQUIPPED
)
22168 // there is an equip limit on this item
22169 if (HasItemOrGemWithIdEquipped(itemProto
->ItemId
, 1, except_slot
))
22170 return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE
;
22173 // check unique-equipped limit
22174 if (itemProto
->ItemLimitCategory
)
22176 ItemLimitCategoryEntry
const* limitEntry
= sItemLimitCategoryStore
.LookupEntry(itemProto
->ItemLimitCategory
);
22178 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED
;
22180 // NOTE: limitEntry->mode not checked because if item have have-limit then it applied and to equip case
22182 if (limit_count
> limitEntry
->maxCount
)
22183 return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS
;
22185 // there is an equip limit on this item
22186 if (HasItemOrGemWithLimitCategoryEquipped(itemProto
->ItemLimitCategory
, limitEntry
->maxCount
- limit_count
+ 1, except_slot
))
22187 return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS
;
22190 return EQUIP_ERR_OK
;
22193 void Player::HandleFall(MovementInfo
const& movementInfo
)
22195 // calculate total z distance of the fall
22196 float z_diff
= m_lastFallZ
- movementInfo
.GetPos()->z
;
22197 DEBUG_LOG("zDiff = %f", z_diff
);
22199 // Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored
22200 // 14.57 can be calculated by resolving damageperc formula below to 0
22201 if (z_diff
>= 14.57f
&& !isDead() && !isGameMaster() &&
22202 !HasAuraType(SPELL_AURA_HOVER
) && !HasAuraType(SPELL_AURA_FEATHER_FALL
) &&
22203 !HasAuraType(SPELL_AURA_FLY
) && !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL
))
22205 // Safe fall, fall height reduction
22206 int32 safe_fall
= GetTotalAuraModifier(SPELL_AURA_SAFE_FALL
);
22208 float damageperc
= 0.018f
* (z_diff
- safe_fall
) - 0.2426f
;
22210 if (damageperc
> 0)
22212 uint32 damage
= (uint32
)(damageperc
* GetMaxHealth() * sWorld
.getConfig(CONFIG_FLOAT_RATE_DAMAGE_FALL
));
22214 float height
= movementInfo
.GetPos()->z
;
22215 UpdateAllowedPositionZ(movementInfo
.GetPos()->x
, movementInfo
.GetPos()->y
, height
);
22219 // Prevent fall damage from being more than the player maximum health
22220 if (damage
> GetMaxHealth())
22221 damage
= GetMaxHealth();
22224 if (GetDummyAura(43621))
22225 damage
= GetMaxHealth() / 2;
22227 uint32 original_health
= GetHealth();
22228 uint32 final_damage
= EnvironmentalDamage(DAMAGE_FALL
, damage
);
22230 // recheck alive, might have died of EnvironmentalDamage, avoid cases when player die in fact like Spirit of Redemption case
22231 if (isAlive() && final_damage
< original_health
)
22232 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
, uint32(z_diff
* 100));
22235 // Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction
22236 DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo
.GetPos()->z
, height
, GetPositionZ(), movementInfo
.GetFallTime(), height
, damage
, safe_fall
);
22241 void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type
, uint32 miscvalue1
/*=0*/, uint32 miscvalue2
/*=0*/, Unit
* unit
/*=NULL*/, uint32 time
/*=0*/)
22243 GetAchievementMgr().UpdateAchievementCriteria(type
, miscvalue1
, miscvalue2
, unit
, time
);
22246 void Player::StartTimedAchievementCriteria(AchievementCriteriaTypes type
, uint32 timedRequirementId
, time_t startTime
/*= 0*/)
22248 GetAchievementMgr().StartTimedAchievementCriteria(type
, timedRequirementId
, startTime
);
22251 PlayerTalent
const* Player::GetKnownTalentById(int32 talentId
) const
22253 PlayerTalentMap::const_iterator itr
= m_talents
[m_activeSpec
].find(talentId
);
22254 if (itr
!= m_talents
[m_activeSpec
].end() && itr
->second
.state
!= PLAYERSPELL_REMOVED
)
22255 return &itr
->second
;
22260 SpellEntry
const* Player::GetKnownTalentRankById(int32 talentId
) const
22262 if (PlayerTalent
const* talent
= GetKnownTalentById(talentId
))
22263 return sSpellStore
.LookupEntry(talent
->talentEntry
->RankID
[talent
->currentRank
]);
22268 bool Player::LearnTalent(uint32 talentId
, uint32 talentRank
)
22270 uint32 CurTalentPoints
= GetFreeTalentPoints();
22272 if (CurTalentPoints
== 0)
22275 if (talentRank
>= MAX_TALENT_RANK
)
22278 TalentEntry
const* talentInfo
= sTalentStore
.LookupEntry(talentId
);
22283 TalentTabEntry
const* talentTabInfo
= sTalentTabStore
.LookupEntry(talentInfo
->TalentTab
);
22285 if (!talentTabInfo
)
22288 // prevent learn talent for different class (cheating)
22289 if ((getClassMask() & talentTabInfo
->ClassMask
) == 0)
22292 // find current max talent rank
22293 uint32 curtalent_maxrank
= 0;
22294 if (PlayerTalent
const* talent
= GetKnownTalentById(talentId
))
22295 curtalent_maxrank
= talent
->currentRank
+ 1;
22297 // we already have same or higher talent rank learned
22298 if (curtalent_maxrank
>= (talentRank
+ 1))
22301 // check if we have enough talent points
22302 if (CurTalentPoints
< (talentRank
- curtalent_maxrank
+ 1))
22305 // Check if it requires another talent
22306 if (talentInfo
->DependsOn
> 0)
22308 if (TalentEntry
const* depTalentInfo
= sTalentStore
.LookupEntry(talentInfo
->DependsOn
))
22310 bool hasEnoughRank
= false;
22311 PlayerTalentMap::iterator dependsOnTalent
= m_talents
[m_activeSpec
].find(depTalentInfo
->TalentID
);
22312 if (dependsOnTalent
!= m_talents
[m_activeSpec
].end() && dependsOnTalent
->second
.state
!= PLAYERSPELL_REMOVED
)
22314 PlayerTalent depTalent
= (*dependsOnTalent
).second
;
22315 if (depTalent
.currentRank
>= talentInfo
->DependsOnRank
)
22316 hasEnoughRank
= true;
22319 if (!hasEnoughRank
)
22324 // Find out how many points we have in this field
22325 uint32 spentPoints
= 0;
22327 uint32 primaryTreeTalents
= 0;
22328 uint32 tTab
= talentInfo
->TalentTab
;
22329 bool isMainTree
= m_talentsPrimaryTree
[m_activeSpec
] == tTab
|| !m_talentsPrimaryTree
[m_activeSpec
];
22331 if (talentInfo
->Row
> 0 || !isMainTree
)
22333 for (uint32 i
= 0; i
< sTalentStore
.GetNumRows(); i
++) // Loop through all talents.
22335 if (TalentEntry
const* tmpTalent
= sTalentStore
.LookupEntry(i
)) // Someday, someone needs to revamp the way talents are tracked
22337 for (uint8 rank
= 0; rank
< MAX_TALENT_RANK
; rank
++)
22339 if (tmpTalent
->RankID
[rank
] != 0)
22341 if (HasSpell(tmpTalent
->RankID
[rank
]))
22343 if (tmpTalent
->TalentTab
== tTab
)
22344 spentPoints
+= (rank
+ 1);
22345 if (tmpTalent
->TalentTab
== m_talentsPrimaryTree
[m_activeSpec
])
22346 primaryTreeTalents
+= (rank
+ 1);
22354 // not have required min points spent in talent tree
22355 if (spentPoints
< (talentInfo
->Row
* MAX_TALENT_RANK
))
22358 // player has not spent 31 talents in main tree before attempting to learn other tree's talents
22359 if (!isMainTree
&& primaryTreeTalents
< REQ_PRIMARY_TREE_TALENTS
)
22362 // spell not set in talent.dbc
22363 uint32 spellid
= talentInfo
->RankID
[talentRank
];
22366 sLog
.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId
, talentRank
);
22371 if (HasSpell(spellid
))
22374 // learn! (other talent ranks will unlearned at learning)
22375 learnSpell(spellid
, false);
22376 DETAIL_LOG("TalentID: %u Rank: %u Spell: %u\n", talentId
, talentRank
, spellid
);
22378 // set talent tree for player
22379 if (!m_talentsPrimaryTree
[m_activeSpec
])
22381 m_talentsPrimaryTree
[m_activeSpec
] = talentInfo
->TalentTab
;
22382 if (std::vector
<uint32
> const* specSpells
= GetTalentTreeMasterySpells(talentInfo
->TalentTab
))
22383 for (size_t i
= 0; i
< specSpells
->size(); ++i
)
22384 learnSpell(specSpells
->at(i
), false);
22386 if (std::vector
<uint32
> const* specSpells
= GetTalentTreePrimarySpells(talentInfo
->TalentTab
))
22387 for (size_t i
= 0; i
< specSpells
->size(); ++i
)
22388 learnSpell(specSpells
->at(i
), false);
22390 // Update talent tree role-dependent mana regen
22392 UpdateArmorSpecializations();
22398 void Player::LearnPetTalent(ObjectGuid petGuid
, uint32 talentId
, uint32 talentRank
)
22400 Pet
* pet
= GetPet();
22404 if (petGuid
!= pet
->GetObjectGuid())
22407 uint32 CurTalentPoints
= pet
->GetFreeTalentPoints();
22409 if (CurTalentPoints
== 0)
22412 if (talentRank
>= MAX_PET_TALENT_RANK
)
22415 TalentEntry
const* talentInfo
= sTalentStore
.LookupEntry(talentId
);
22420 TalentTabEntry
const* talentTabInfo
= sTalentTabStore
.LookupEntry(talentInfo
->TalentTab
);
22422 if (!talentTabInfo
)
22425 CreatureInfo
const* ci
= pet
->GetCreatureInfo();
22430 CreatureFamilyEntry
const* pet_family
= sCreatureFamilyStore
.LookupEntry(ci
->family
);
22435 if (pet_family
->petTalentType
< 0) // not hunter pet
22438 // prevent learn talent for different family (cheating)
22439 if (!((1 << pet_family
->petTalentType
) & talentTabInfo
->petTalentMask
))
22442 // find current max talent rank
22443 int32 curtalent_maxrank
= 0;
22444 for (int32 k
= MAX_TALENT_RANK
- 1; k
> -1; --k
)
22446 if (talentInfo
->RankID
[k
] && pet
->HasSpell(talentInfo
->RankID
[k
]))
22448 curtalent_maxrank
= k
+ 1;
22453 // we already have same or higher talent rank learned
22454 if (curtalent_maxrank
>= int32(talentRank
+ 1))
22457 // check if we have enough talent points
22458 if (CurTalentPoints
< (talentRank
- curtalent_maxrank
+ 1))
22461 // Check if it requires another talent
22462 if (talentInfo
->DependsOn
> 0)
22464 if (TalentEntry
const* depTalentInfo
= sTalentStore
.LookupEntry(talentInfo
->DependsOn
))
22466 bool hasEnoughRank
= false;
22467 for (int i
= talentInfo
->DependsOnRank
; i
< MAX_TALENT_RANK
; ++i
)
22469 if (depTalentInfo
->RankID
[i
] != 0)
22470 if (pet
->HasSpell(depTalentInfo
->RankID
[i
]))
22471 hasEnoughRank
= true;
22473 if (!hasEnoughRank
)
22478 // Find out how many points we have in this field
22479 uint32 spentPoints
= 0;
22481 uint32 tTab
= talentInfo
->TalentTab
;
22482 if (talentInfo
->Row
> 0)
22484 unsigned int numRows
= sTalentStore
.GetNumRows();
22485 for (unsigned int i
= 0; i
< numRows
; ++i
) // Loop through all talents.
22487 // Someday, someone needs to revamp
22488 const TalentEntry
* tmpTalent
= sTalentStore
.LookupEntry(i
);
22489 if (tmpTalent
) // the way talents are tracked
22491 if (tmpTalent
->TalentTab
== tTab
)
22493 for (int j
= 0; j
< MAX_TALENT_RANK
; ++j
)
22495 if (tmpTalent
->RankID
[j
] != 0)
22497 if (pet
->HasSpell(tmpTalent
->RankID
[j
]))
22499 spentPoints
+= j
+ 1;
22508 // not have required min points spent in talent tree
22509 if (spentPoints
< (talentInfo
->Row
* MAX_PET_TALENT_RANK
))
22512 // spell not set in talent.dbc
22513 uint32 spellid
= talentInfo
->RankID
[talentRank
];
22516 sLog
.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId
, talentRank
);
22521 if (pet
->HasSpell(spellid
))
22524 // learn! (other talent ranks will unlearned at learning)
22525 pet
->learnSpell(spellid
);
22526 DETAIL_LOG("PetTalentID: %u Rank: %u Spell: %u\n", talentId
, talentRank
, spellid
);
22529 void Player::UpdateFallInformationIfNeed(MovementInfo
const& minfo
, uint16 opcode
)
22531 if (m_lastFallTime
>= minfo
.GetFallTime() || m_lastFallZ
<= minfo
.GetPos()->z
|| opcode
== CMSG_MOVE_FALL_LAND
)
22532 SetFallInformation(minfo
.GetFallTime(), minfo
.GetPos()->z
);
22535 void Player::UnsummonPetTemporaryIfAny()
22537 Pet
* pet
= GetPet();
22541 if (!m_temporaryUnsummonedPetNumber
&& pet
->isControlled() && !pet
->isTemporarySummoned())
22542 m_temporaryUnsummonedPetNumber
= pet
->GetCharmInfo()->GetPetNumber();
22544 pet
->Unsummon(PET_SAVE_AS_CURRENT
, this);
22547 void Player::ResummonPetTemporaryUnSummonedIfAny()
22549 if (!m_temporaryUnsummonedPetNumber
)
22552 // not resummon in not appropriate state
22553 if (IsPetNeedBeTemporaryUnsummoned())
22559 Pet
* NewPet
= new Pet
;
22560 if (!NewPet
->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber
, true))
22563 m_temporaryUnsummonedPetNumber
= 0;
22566 bool Player::canSeeSpellClickOn(Creature
const* c
) const
22568 if (!c
->HasFlag(UNIT_NPC_FLAGS
, UNIT_NPC_FLAG_SPELLCLICK
))
22571 SpellClickInfoMapBounds clickPair
= sObjectMgr
.GetSpellClickInfoMapBounds(c
->GetEntry());
22572 for (SpellClickInfoMap::const_iterator itr
= clickPair
.first
; itr
!= clickPair
.second
; ++itr
)
22573 if (itr
->second
.IsFitToRequirements(this))
22579 void Player::BuildPlayerTalentsInfoData(WorldPacket
* data
)
22581 *data
<< uint32(GetFreeTalentPoints()); // unspentTalentPoints
22582 *data
<< uint8(m_specsCount
); // talent group count (0, 1 or 2)
22583 *data
<< uint8(m_activeSpec
); // talent group index (0 or 1)
22587 if (m_specsCount
> MAX_TALENT_SPEC_COUNT
)
22588 m_specsCount
= MAX_TALENT_SPEC_COUNT
;
22590 // loop through all specs (only 1 for now)
22591 for (uint32 specIdx
= 0; specIdx
< m_specsCount
; ++specIdx
)
22593 *data
<< uint32(m_talentsPrimaryTree
[specIdx
]);
22594 uint8 talentIdCount
= 0;
22595 size_t pos
= data
->wpos();
22596 *data
<< uint8(talentIdCount
); // [PH], talentIdCount
22598 // find class talent tabs (all players have 3 talent tabs)
22599 uint32
const* talentTabIds
= GetTalentTabPages(getClass());
22601 for (uint32 i
= 0; i
< MAX_TALENT_TABS
; ++i
)
22603 uint32 talentTabId
= talentTabIds
[i
];
22604 for (PlayerTalentMap::iterator iter
= m_talents
[specIdx
].begin(); iter
!= m_talents
[specIdx
].end(); ++iter
)
22606 PlayerTalent talent
= (*iter
).second
;
22608 if (talent
.state
== PLAYERSPELL_REMOVED
)
22611 // skip another tab talents
22612 if (talent
.talentEntry
->TalentTab
!= talentTabId
)
22615 *data
<< uint32(talent
.talentEntry
->TalentID
); // Talent.dbc
22616 *data
<< uint8(talent
.currentRank
); // talentMaxRank (0-4)
22622 data
->put
<uint8
>(pos
, talentIdCount
); // put real count
22624 *data
<< uint8(MAX_GLYPH_SLOT_INDEX
); // glyphs count
22626 // GlyphProperties.dbc
22627 for (uint8 i
= 0; i
< MAX_GLYPH_SLOT_INDEX
; ++i
)
22628 *data
<< uint16(m_glyphs
[specIdx
][i
].GetId());
22633 void Player::BuildPetTalentsInfoData(WorldPacket
* data
)
22635 uint32 unspentTalentPoints
= 0;
22636 size_t pointsPos
= data
->wpos();
22637 *data
<< uint32(unspentTalentPoints
); // [PH], unspentTalentPoints
22639 uint8 talentIdCount
= 0;
22640 size_t countPos
= data
->wpos();
22641 *data
<< uint8(talentIdCount
); // [PH], talentIdCount
22643 Pet
* pet
= GetPet();
22647 unspentTalentPoints
= pet
->GetFreeTalentPoints();
22649 data
->put
<uint32
>(pointsPos
, unspentTalentPoints
); // put real points
22651 CreatureInfo
const* ci
= pet
->GetCreatureInfo();
22655 CreatureFamilyEntry
const* pet_family
= sCreatureFamilyStore
.LookupEntry(ci
->family
);
22656 if (!pet_family
|| pet_family
->petTalentType
< 0)
22659 for (uint32 talentTabId
= 1; talentTabId
< sTalentTabStore
.GetNumRows(); ++talentTabId
)
22661 TalentTabEntry
const* talentTabInfo
= sTalentTabStore
.LookupEntry(talentTabId
);
22662 if (!talentTabInfo
)
22665 if (!((1 << pet_family
->petTalentType
) & talentTabInfo
->petTalentMask
))
22668 for (uint32 talentId
= 0; talentId
< sTalentStore
.GetNumRows(); ++talentId
)
22670 TalentEntry
const* talentInfo
= sTalentStore
.LookupEntry(talentId
);
22674 // skip another tab talents
22675 if (talentInfo
->TalentTab
!= talentTabId
)
22678 // find max talent rank
22679 int32 curtalent_maxrank
= -1;
22680 for (int32 k
= 4; k
> -1; --k
)
22682 if (talentInfo
->RankID
[k
] && pet
->HasSpell(talentInfo
->RankID
[k
]))
22684 curtalent_maxrank
= k
;
22689 // not learned talent
22690 if (curtalent_maxrank
< 0)
22693 *data
<< uint32(talentInfo
->TalentID
); // Talent.dbc
22694 *data
<< uint8(curtalent_maxrank
); // talentMaxRank (0-4)
22699 data
->put
<uint8
>(countPos
, talentIdCount
); // put real count
22705 void Player::SendTalentsInfoData(bool pet
)
22707 WorldPacket
data(SMSG_TALENT_UPDATE
, 50);
22708 data
<< uint8(pet
? 1 : 0);
22710 BuildPetTalentsInfoData(&data
);
22712 BuildPlayerTalentsInfoData(&data
);
22713 GetSession()->SendPacket(&data
);
22716 void Player::BuildEnchantmentsInfoData(WorldPacket
* data
)
22718 uint32 slotUsedMask
= 0;
22719 size_t slotUsedMaskPos
= data
->wpos();
22720 *data
<< uint32(slotUsedMask
); // slotUsedMask < 0x80000
22722 for (uint32 i
= 0; i
< EQUIPMENT_SLOT_END
; ++i
)
22724 Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
22729 slotUsedMask
|= (1 << i
);
22731 *data
<< uint32(item
->GetEntry()); // item entry
22733 uint16 enchantmentMask
= 0;
22734 size_t enchantmentMaskPos
= data
->wpos();
22735 *data
<< uint16(enchantmentMask
); // enchantmentMask < 0x1000
22737 for (uint32 j
= 0; j
< MAX_ENCHANTMENT_SLOT
; ++j
)
22739 uint32 enchId
= item
->GetEnchantmentId(EnchantmentSlot(j
));
22744 enchantmentMask
|= (1 << j
);
22746 *data
<< uint16(enchId
); // enchantmentId?
22749 data
->put
<uint16
>(enchantmentMaskPos
, enchantmentMask
);
22751 *data
<< uint16(item
->GetItemRandomPropertyId());
22752 *data
<< item
->GetGuidValue(ITEM_FIELD_CREATOR
).WriteAsPacked();
22753 *data
<< uint32(item
->GetItemSuffixFactor());
22756 data
->put
<uint32
>(slotUsedMaskPos
, slotUsedMask
);
22759 void Player::SendEquipmentSetList()
22762 WorldPacket
data(SMSG_LOAD_EQUIPMENT_SET
, 4);
22763 size_t count_pos
= data
.wpos();
22764 data
<< uint32(count
); // count placeholder
22765 for (EquipmentSets::iterator itr
= m_EquipmentSets
.begin(); itr
!= m_EquipmentSets
.end(); ++itr
)
22767 if (itr
->second
.state
== EQUIPMENT_SET_DELETED
)
22769 data
.appendPackGUID(itr
->second
.Guid
);
22770 data
<< uint32(itr
->first
);
22771 data
<< itr
->second
.Name
;
22772 data
<< itr
->second
.IconName
;
22773 for (uint32 i
= 0; i
< EQUIPMENT_SLOT_END
; ++i
)
22775 // ignored slots stored in IgnoreMask, client wants "1" as raw GUID, so no HIGHGUID_ITEM
22776 if (itr
->second
.IgnoreMask
& (1 << i
))
22777 data
<< ObjectGuid(uint64(1)).WriteAsPacked();
22779 data
<< ObjectGuid(HIGHGUID_ITEM
, itr
->second
.Items
[i
]).WriteAsPacked();
22782 ++count
; // client have limit but it checked at loading and set
22784 data
.put
<uint32
>(count_pos
, count
);
22785 GetSession()->SendPacket(&data
);
22788 void Player::SetEquipmentSet(uint32 index
, EquipmentSet eqset
)
22790 if (eqset
.Guid
!= 0)
22792 bool found
= false;
22794 for (EquipmentSets::iterator itr
= m_EquipmentSets
.begin(); itr
!= m_EquipmentSets
.end(); ++itr
)
22796 if ((itr
->second
.Guid
== eqset
.Guid
) && (itr
->first
== index
))
22803 if (!found
) // something wrong...
22805 sLog
.outError("Player %s tried to save equipment set " UI64FMTD
" (index %u), but that equipment set not found!", GetName(), eqset
.Guid
, index
);
22810 EquipmentSet
& eqslot
= m_EquipmentSets
[index
];
22812 EquipmentSetUpdateState old_state
= eqslot
.state
;
22816 if (eqset
.Guid
== 0)
22818 eqslot
.Guid
= sObjectMgr
.GenerateEquipmentSetGuid();
22820 WorldPacket
data(SMSG_EQUIPMENT_SET_ID
, 4 + 1);
22821 data
<< uint32(index
);
22822 data
.appendPackGUID(eqslot
.Guid
);
22823 GetSession()->SendPacket(&data
);
22826 eqslot
.state
= old_state
== EQUIPMENT_SET_NEW
? EQUIPMENT_SET_NEW
: EQUIPMENT_SET_CHANGED
;
22829 void Player::_SaveEquipmentSets()
22831 static SqlStatementID updSets
;
22832 static SqlStatementID insSets
;
22833 static SqlStatementID delSets
;
22835 for (EquipmentSets::iterator itr
= m_EquipmentSets
.begin(); itr
!= m_EquipmentSets
.end();)
22837 uint32 index
= itr
->first
;
22838 EquipmentSet
& eqset
= itr
->second
;
22839 switch (eqset
.state
)
22841 case EQUIPMENT_SET_UNCHANGED
:
22843 break; // nothing do
22844 case EQUIPMENT_SET_CHANGED
:
22846 SqlStatement stmt
= CharacterDatabase
.CreateStatement(updSets
, "UPDATE character_equipmentsets SET name=?, iconname=?, ignore_mask=?, item0=?, item1=?, item2=?, item3=?, item4=?, "
22847 "item5=?, item6=?, item7=?, item8=?, item9=?, item10=?, item11=?, item12=?, item13=?, item14=?, "
22848 "item15=?, item16=?, item17=?, item18=? WHERE guid=? AND setguid=? AND setindex=?");
22850 stmt
.addString(eqset
.Name
);
22851 stmt
.addString(eqset
.IconName
);
22852 stmt
.addUInt32(eqset
.IgnoreMask
);
22854 for (int i
= 0; i
< EQUIPMENT_SLOT_END
; ++i
)
22855 stmt
.addUInt32(eqset
.Items
[i
]);
22857 stmt
.addUInt32(GetGUIDLow());
22858 stmt
.addUInt64(eqset
.Guid
);
22859 stmt
.addUInt32(index
);
22863 eqset
.state
= EQUIPMENT_SET_UNCHANGED
;
22867 case EQUIPMENT_SET_NEW
:
22869 SqlStatement stmt
= CharacterDatabase
.CreateStatement(insSets
, "INSERT INTO character_equipmentsets VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
22870 stmt
.addUInt32(GetGUIDLow());
22871 stmt
.addUInt64(eqset
.Guid
);
22872 stmt
.addUInt32(index
);
22873 stmt
.addString(eqset
.Name
);
22874 stmt
.addString(eqset
.IconName
);
22875 stmt
.addUInt32(eqset
.IgnoreMask
);
22877 for (int i
= 0; i
< EQUIPMENT_SLOT_END
; ++i
)
22878 stmt
.addUInt32(eqset
.Items
[i
]);
22882 eqset
.state
= EQUIPMENT_SET_UNCHANGED
;
22886 case EQUIPMENT_SET_DELETED
:
22888 SqlStatement stmt
= CharacterDatabase
.CreateStatement(delSets
, "DELETE FROM character_equipmentsets WHERE setguid = ?");
22889 stmt
.PExecute(eqset
.Guid
);
22890 m_EquipmentSets
.erase(itr
++);
22897 void Player::_SaveBGData()
22900 if (!m_bgData
.m_needSave
)
22903 static SqlStatementID delBGData
;
22904 static SqlStatementID insBGData
;
22906 SqlStatement stmt
= CharacterDatabase
.CreateStatement(delBGData
, "DELETE FROM character_battleground_data WHERE guid = ?");
22908 stmt
.PExecute(GetGUIDLow());
22910 if (m_bgData
.bgInstanceID
)
22912 stmt
= CharacterDatabase
.CreateStatement(insBGData
, "INSERT INTO character_battleground_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
22913 /* guid, bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */
22914 stmt
.addUInt32(GetGUIDLow());
22915 stmt
.addUInt32(m_bgData
.bgInstanceID
);
22916 stmt
.addUInt32(uint32(m_bgData
.bgTeam
));
22917 stmt
.addFloat(m_bgData
.joinPos
.coord_x
);
22918 stmt
.addFloat(m_bgData
.joinPos
.coord_y
);
22919 stmt
.addFloat(m_bgData
.joinPos
.coord_z
);
22920 stmt
.addFloat(m_bgData
.joinPos
.orientation
);
22921 stmt
.addUInt32(m_bgData
.joinPos
.mapid
);
22922 stmt
.addUInt32(m_bgData
.taxiPath
[0]);
22923 stmt
.addUInt32(m_bgData
.taxiPath
[1]);
22924 stmt
.addUInt32(m_bgData
.mountSpell
);
22929 m_bgData
.m_needSave
= false;
22932 void Player::DeleteEquipmentSet(uint64 setGuid
)
22934 for (EquipmentSets::iterator itr
= m_EquipmentSets
.begin(); itr
!= m_EquipmentSets
.end(); ++itr
)
22936 if (itr
->second
.Guid
== setGuid
)
22938 if (itr
->second
.state
== EQUIPMENT_SET_NEW
)
22939 m_EquipmentSets
.erase(itr
);
22941 itr
->second
.state
= EQUIPMENT_SET_DELETED
;
22947 void Player::ActivateSpec(uint8 specNum
)
22949 if (GetActiveSpec() == specNum
)
22952 if (specNum
>= GetSpecsCount())
22955 UnsummonPetTemporaryIfAny();
22957 // prevent deletion of action buttons by client at spell unlearn or by player while spec change in progress
22958 SendLockActionButtons();
22960 // Remove spec specific spells
22961 for (uint32 i
= 0; i
< MAX_TALENT_TABS
; ++i
)
22963 if (std::vector
<uint32
> const* specSpells
= GetTalentTreeMasterySpells(GetTalentTabPages(getClass())[i
]))
22964 for (size_t i
= 0; i
< specSpells
->size(); ++i
)
22965 removeSpell(specSpells
->at(i
), true);
22967 if (std::vector
<uint32
> const* specSpells
= GetTalentTreePrimarySpells(GetTalentTabPages(getClass())[i
]))
22968 for (size_t i
= 0; i
< specSpells
->size(); ++i
)
22969 removeSpell(specSpells
->at(i
), true);
22972 ApplyGlyphs(false);
22974 // copy of new talent spec (we will use it as model for converting current tlanet state to new)
22975 PlayerTalentMap tempSpec
= m_talents
[specNum
];
22977 // copy old spec talents to new one, must be before spec switch to have previous spec num(as m_activeSpec)
22978 m_talents
[specNum
] = m_talents
[m_activeSpec
];
22980 SetActiveSpec(specNum
);
22982 // remove all talent spells that don't exist in next spec but exist in old
22983 for (PlayerTalentMap::iterator specIter
= m_talents
[m_activeSpec
].begin(); specIter
!= m_talents
[m_activeSpec
].end();)
22985 PlayerTalent
& talent
= specIter
->second
;
22987 if (talent
.state
== PLAYERSPELL_REMOVED
)
22993 PlayerTalentMap::iterator iterTempSpec
= tempSpec
.find(specIter
->first
);
22995 // remove any talent rank if talent not listed in temp spec
22996 if (iterTempSpec
== tempSpec
.end() || iterTempSpec
->second
.state
== PLAYERSPELL_REMOVED
)
22998 TalentEntry
const* talentInfo
= talent
.talentEntry
;
23000 for (int r
= 0; r
< MAX_TALENT_RANK
; ++r
)
23001 if (talentInfo
->RankID
[r
])
23002 removeSpell(talentInfo
->RankID
[r
], !IsPassiveSpell(talentInfo
->RankID
[r
]), false);
23004 specIter
= m_talents
[m_activeSpec
].begin();
23010 // now new spec data have only talents (maybe different rank) as in temp spec data, sync ranks then.
23011 for (PlayerTalentMap::const_iterator tempIter
= tempSpec
.begin(); tempIter
!= tempSpec
.end(); ++tempIter
)
23013 PlayerTalent
const& talent
= tempIter
->second
;
23015 // removed state talent already unlearned in prev. loop
23016 // but we need restore it if it deleted for finish removed-marked data in DB
23017 if (talent
.state
== PLAYERSPELL_REMOVED
)
23019 m_talents
[m_activeSpec
][tempIter
->first
] = talent
;
23023 uint32 talentSpellId
= talent
.talentEntry
->RankID
[talent
.currentRank
];
23025 // learn talent spells if they not in new spec (old spec copy)
23026 // and if they have different rank
23027 if (PlayerTalent
const* cur_talent
= GetKnownTalentById(tempIter
->first
))
23029 if (cur_talent
->currentRank
!= talent
.currentRank
)
23030 learnSpell(talentSpellId
, false);
23033 learnSpell(talentSpellId
, false);
23035 // sync states - original state is changed in addSpell that learnSpell calls
23036 PlayerTalentMap::iterator specIter
= m_talents
[m_activeSpec
].find(tempIter
->first
);
23037 if (specIter
!= m_talents
[m_activeSpec
].end())
23038 specIter
->second
.state
= talent
.state
;
23041 sLog
.outError("ActivateSpec: Talent spell %u expected to learned at spec switch but not listed in talents at final check!", talentSpellId
);
23043 // attempt resync DB state (deleted lost spell from DB)
23044 if (talent
.state
!= PLAYERSPELL_NEW
)
23046 PlayerTalent
& talentNew
= m_talents
[m_activeSpec
][tempIter
->first
];
23047 talentNew
= talent
;
23048 talentNew
.state
= PLAYERSPELL_REMOVED
;
23053 InitTalentForLevel();
23055 // recheck action buttons (not checked at loading/spec copy)
23056 ActionButtonList
const& currentActionButtonList
= m_actionButtons
[m_activeSpec
];
23057 for (ActionButtonList::const_iterator itr
= currentActionButtonList
.begin(); itr
!= currentActionButtonList
.end();)
23059 if (itr
->second
.uState
!= ACTIONBUTTON_DELETED
)
23061 // remove broken without any output (it can be not correct because talents not copied at spec creating)
23062 if (!IsActionButtonDataValid(itr
->first
, itr
->second
.GetAction(), itr
->second
.GetType(), this, false))
23064 removeActionButton(m_activeSpec
, itr
->first
);
23065 itr
= currentActionButtonList
.begin();
23072 ResummonPetTemporaryUnSummonedIfAny();
23074 if (std::vector
<uint32
> const* specSpells
= GetTalentTreeMasterySpells(m_talentsPrimaryTree
[m_activeSpec
]))
23075 for (size_t i
= 0; i
< specSpells
->size(); ++i
)
23076 learnSpell(specSpells
->at(i
), false);
23078 if (std::vector
<uint32
> const* specSpells
= GetTalentTreePrimarySpells(m_talentsPrimaryTree
[m_activeSpec
]))
23079 for (size_t i
= 0; i
< specSpells
->size(); ++i
)
23080 learnSpell(specSpells
->at(i
), false);
23084 SendInitialActionButtons();
23086 Powers pw
= getPowerType();
23087 if (pw
!= POWER_MANA
)
23088 SetPower(POWER_MANA
, 0);
23092 if (m_talentsPrimaryTree
[m_activeSpec
] && !sTalentTabStore
.LookupEntry(m_talentsPrimaryTree
[m_activeSpec
]))
23093 resetTalents(true);
23095 // Update talent tree role-dependent mana regen
23098 UpdateArmorSpecializations();
23101 void Player::UpdateSpecCount(uint8 count
)
23103 uint8 curCount
= GetSpecsCount();
23104 if (curCount
== count
)
23107 // maybe current spec data must be copied to 0 spec?
23108 if (m_activeSpec
>= count
)
23111 // copy spec data from new specs
23112 if (count
> curCount
)
23114 // copy action buttons from active spec (more easy in this case iterate first by button)
23115 ActionButtonList
const& currentActionButtonList
= m_actionButtons
[m_activeSpec
];
23117 for (ActionButtonList::const_iterator itr
= currentActionButtonList
.begin(); itr
!= currentActionButtonList
.end(); ++itr
)
23119 if (itr
->second
.uState
!= ACTIONBUTTON_DELETED
)
23121 for (uint8 spec
= curCount
; spec
< count
; ++spec
)
23122 addActionButton(spec
, itr
->first
, itr
->second
.GetAction(), itr
->second
.GetType());
23126 // delete spec data for removed specs
23127 else if (count
< curCount
)
23129 // delete action buttons for removed spec
23130 for (uint8 spec
= count
; spec
< curCount
; ++spec
)
23132 // delete action buttons for removed spec
23133 for (uint8 button
= 0; button
< MAX_ACTION_BUTTONS
; ++button
)
23134 removeActionButton(spec
, button
);
23138 SetSpecsCount(count
);
23140 SendTalentsInfoData(false);
23143 void Player::RemoveAtLoginFlag(AtLoginFlags f
, bool in_db_also
/*= false*/)
23145 m_atLoginFlags
&= ~f
;
23148 CharacterDatabase
.PExecute("UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'", uint32(f
), GetGUIDLow());
23151 void Player::SendClearCooldown(uint32 spell_id
, Unit
* target
)
23153 ObjectGuid guid
= target
->GetObjectGuid();
23155 WorldPacket
data(SMSG_CLEAR_COOLDOWNS
, 1 + 8 + 4);
23156 data
.WriteGuidMask
<1, 3, 6>(guid
);
23157 data
.WriteBits(1, 24); // cooldown count
23158 data
.WriteGuidMask
<7, 5, 2, 4, 0>(guid
);
23160 data
.WriteGuidBytes
<7, 2, 4, 5, 1, 3>(guid
);
23161 data
<< uint32(spell_id
);
23162 data
.WriteGuidBytes
<0, 6>(guid
);
23164 SendDirectMessage(&data
);
23167 bool Player::HasMovementFlag(MovementFlags f
) const
23169 return m_movementInfo
.HasMovementFlag(f
);
23172 void Player::ResetTimeSync()
23174 m_timeSyncCounter
= 0;
23175 m_timeSyncTimer
= 0;
23176 m_timeSyncClient
= 0;
23177 m_timeSyncServer
= WorldTimer::getMSTime();
23180 void Player::SendTimeSync()
23182 WorldPacket
data(SMSG_TIME_SYNC_REQ
, 4);
23183 data
<< uint32(m_timeSyncCounter
++);
23184 GetSession()->SendPacket(&data
);
23186 // Schedule next sync in 10 sec
23187 m_timeSyncTimer
= 10000;
23188 m_timeSyncServer
= WorldTimer::getMSTime();
23191 void Player::SendDuelCountdown(uint32 counter
)
23193 WorldPacket
data(SMSG_DUEL_COUNTDOWN
, 4);
23194 data
<< uint32(counter
); // seconds
23195 GetSession()->SendPacket(&data
);
23198 bool Player::IsImmuneToSpellEffect(SpellEntry
const* spellInfo
, SpellEffectIndex index
, bool castOnSelf
) const
23200 SpellEffectEntry
const* spellEffect
= spellInfo
->GetSpellEffect(index
);
23203 switch(spellEffect
->Effect
)
23205 case SPELL_EFFECT_ATTACK_ME
:
23210 switch(spellEffect
->EffectApplyAuraName
)
23212 case SPELL_AURA_MOD_TAUNT
:
23219 return Unit::IsImmuneToSpellEffect(spellInfo
, index
, castOnSelf
);
23222 void Player::SetHomebindToLocation(WorldLocation
const& loc
, uint32 area_id
)
23224 m_homebindMapId
= loc
.mapid
;
23225 m_homebindAreaId
= area_id
;
23226 m_homebindX
= loc
.coord_x
;
23227 m_homebindY
= loc
.coord_y
;
23228 m_homebindZ
= loc
.coord_z
;
23230 // update sql homebind
23231 CharacterDatabase
.PExecute("UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'",
23232 m_homebindMapId
, m_homebindAreaId
, m_homebindX
, m_homebindY
, m_homebindZ
, GetGUIDLow());
23235 Object
* Player::GetObjectByTypeMask(ObjectGuid guid
, TypeMask typemask
)
23237 switch (guid
.GetHigh())
23239 case HIGHGUID_ITEM
:
23240 if (typemask
& TYPEMASK_ITEM
)
23241 return GetItemByGuid(guid
);
23243 case HIGHGUID_PLAYER
:
23244 if (GetObjectGuid() == guid
)
23246 if ((typemask
& TYPEMASK_PLAYER
) && IsInWorld())
23247 return ObjectAccessor::FindPlayer(guid
);
23249 case HIGHGUID_GAMEOBJECT
:
23250 if ((typemask
& TYPEMASK_GAMEOBJECT
) && IsInWorld())
23251 return GetMap()->GetGameObject(guid
);
23253 case HIGHGUID_UNIT
:
23254 case HIGHGUID_VEHICLE
:
23255 if ((typemask
& TYPEMASK_UNIT
) && IsInWorld())
23256 return GetMap()->GetCreature(guid
);
23259 if ((typemask
& TYPEMASK_UNIT
) && IsInWorld())
23260 return GetMap()->GetPet(guid
);
23262 case HIGHGUID_DYNAMICOBJECT
:
23263 if ((typemask
& TYPEMASK_DYNAMICOBJECT
) && IsInWorld())
23264 return GetMap()->GetDynamicObject(guid
);
23266 case HIGHGUID_TRANSPORT
:
23267 case HIGHGUID_CORPSE
:
23268 case HIGHGUID_MO_TRANSPORT
:
23269 case HIGHGUID_INSTANCE
:
23270 case HIGHGUID_GROUP
:
23278 void Player::SetRestType(RestType n_r_type
, uint32 areaTriggerId
/*= 0*/)
23280 rest_type
= n_r_type
;
23282 if (rest_type
== REST_TYPE_NO
)
23284 RemoveFlag(PLAYER_FLAGS
, PLAYER_FLAGS_RESTING
);
23286 // Set player to FFA PVP when not in rested environment.
23287 if (sWorld
.IsFFAPvPRealm())
23292 SetFlag(PLAYER_FLAGS
, PLAYER_FLAGS_RESTING
);
23294 inn_trigger_id
= areaTriggerId
;
23295 time_inn_enter
= time(NULL
);
23297 if (sWorld
.IsFFAPvPRealm())
23302 uint32
Player::GetEquipGearScore(bool withBags
, bool withBank
)
23304 if (withBags
&& withBank
&& m_cachedGS
> 0)
23307 GearScoreVec
gearScore(EQUIPMENT_SLOT_END
);
23308 uint32 twoHandScore
= 0;
23310 for (uint8 i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_END
; ++i
)
23312 if (Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
23313 _fillGearScoreData(item
, &gearScore
, twoHandScore
);
23319 for (int i
= INVENTORY_SLOT_ITEM_START
; i
< INVENTORY_SLOT_ITEM_END
; ++i
)
23321 if (Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
23322 _fillGearScoreData(item
, &gearScore
, twoHandScore
);
23326 for (int i
= INVENTORY_SLOT_BAG_START
; i
< INVENTORY_SLOT_BAG_END
; ++i
)
23328 if (Bag
* pBag
= (Bag
*)GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
23330 for (uint32 j
= 0; j
< pBag
->GetBagSize(); ++j
)
23332 if (Item
* item2
= pBag
->GetItemByPos(j
))
23333 _fillGearScoreData(item2
, &gearScore
, twoHandScore
);
23341 for (uint8 i
= BANK_SLOT_ITEM_START
; i
< BANK_SLOT_ITEM_END
; ++i
)
23343 if (Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
23344 _fillGearScoreData(item
, &gearScore
, twoHandScore
);
23347 for (uint8 i
= BANK_SLOT_BAG_START
; i
< BANK_SLOT_BAG_END
; ++i
)
23349 if (Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
))
23353 Bag
* bag
= (Bag
*)item
;
23354 for (uint8 j
= 0; j
< bag
->GetBagSize(); ++j
)
23356 if (Item
* item2
= bag
->GetItemByPos(j
))
23357 _fillGearScoreData(item2
, &gearScore
, twoHandScore
);
23364 uint8 count
= EQUIPMENT_SLOT_END
- 2; // ignore body and tabard slots
23367 // check if 2h hand is higher level than main hand + off hand
23368 if (gearScore
[EQUIPMENT_SLOT_MAINHAND
] + gearScore
[EQUIPMENT_SLOT_OFFHAND
] < twoHandScore
* 2)
23370 gearScore
[EQUIPMENT_SLOT_OFFHAND
] = 0; // off hand is ignored in calculations if 2h weapon has higher score
23372 gearScore
[EQUIPMENT_SLOT_MAINHAND
] = twoHandScore
;
23375 for (uint8 i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_END
; ++i
)
23377 sum
+= gearScore
[i
];
23382 uint32 res
= uint32(sum
/ count
);
23383 DEBUG_LOG("Player: calculating gear score for %u. Result is %u", GetObjectGuid().GetCounter(), res
);
23385 if (withBags
&& withBank
)
23394 void Player::_fillGearScoreData(Item
* item
, GearScoreVec
* gearScore
, uint32
& twoHandScore
)
23399 if (CanUseItem(item
->GetProto()) != EQUIP_ERR_OK
)
23402 uint8 type
= item
->GetProto()->InventoryType
;
23403 uint32 level
= item
->GetProto()->ItemLevel
;
23407 case INVTYPE_2HWEAPON
:
23408 twoHandScore
= std::max(twoHandScore
, level
);
23410 case INVTYPE_WEAPON
:
23411 case INVTYPE_WEAPONMAINHAND
:
23412 (*gearScore
)[EQUIPMENT_SLOT_MAINHAND
] = std::max((*gearScore
)[EQUIPMENT_SLOT_MAINHAND
], level
);
23414 case INVTYPE_SHIELD
:
23415 case INVTYPE_WEAPONOFFHAND
:
23416 (*gearScore
)[EQUIPMENT_SLOT_OFFHAND
] = std::max((*gearScore
)[EQUIPMENT_SLOT_OFFHAND
], level
);
23418 case INVTYPE_THROWN
:
23419 case INVTYPE_RANGEDRIGHT
:
23420 case INVTYPE_RANGED
:
23421 case INVTYPE_QUIVER
:
23422 case INVTYPE_RELIC
:
23423 (*gearScore
)[EQUIPMENT_SLOT_RANGED
] = std::max((*gearScore
)[EQUIPMENT_SLOT_RANGED
], level
);
23426 (*gearScore
)[EQUIPMENT_SLOT_HEAD
] = std::max((*gearScore
)[EQUIPMENT_SLOT_HEAD
], level
);
23429 (*gearScore
)[EQUIPMENT_SLOT_NECK
] = std::max((*gearScore
)[EQUIPMENT_SLOT_NECK
], level
);
23431 case INVTYPE_SHOULDERS
:
23432 (*gearScore
)[EQUIPMENT_SLOT_SHOULDERS
] = std::max((*gearScore
)[EQUIPMENT_SLOT_SHOULDERS
], level
);
23435 (*gearScore
)[EQUIPMENT_SLOT_BODY
] = std::max((*gearScore
)[EQUIPMENT_SLOT_BODY
], level
);
23437 case INVTYPE_CHEST
:
23438 (*gearScore
)[EQUIPMENT_SLOT_CHEST
] = std::max((*gearScore
)[EQUIPMENT_SLOT_CHEST
], level
);
23440 case INVTYPE_WAIST
:
23441 (*gearScore
)[EQUIPMENT_SLOT_WAIST
] = std::max((*gearScore
)[EQUIPMENT_SLOT_WAIST
], level
);
23444 (*gearScore
)[EQUIPMENT_SLOT_LEGS
] = std::max((*gearScore
)[EQUIPMENT_SLOT_LEGS
], level
);
23447 (*gearScore
)[EQUIPMENT_SLOT_FEET
] = std::max((*gearScore
)[EQUIPMENT_SLOT_FEET
], level
);
23449 case INVTYPE_WRISTS
:
23450 (*gearScore
)[EQUIPMENT_SLOT_WRISTS
] = std::max((*gearScore
)[EQUIPMENT_SLOT_WRISTS
], level
);
23452 case INVTYPE_HANDS
:
23453 (*gearScore
)[EQUIPMENT_SLOT_HEAD
] = std::max((*gearScore
)[EQUIPMENT_SLOT_HEAD
], level
);
23455 // equipped gear score check uses both rings and trinkets for calculation, assume that for bags/banks it is the same
23456 // with keeping second highest score at second slot
23457 case INVTYPE_FINGER
:
23459 if ((*gearScore
)[EQUIPMENT_SLOT_FINGER1
] < level
)
23461 (*gearScore
)[EQUIPMENT_SLOT_FINGER2
] = (*gearScore
)[EQUIPMENT_SLOT_FINGER1
];
23462 (*gearScore
)[EQUIPMENT_SLOT_FINGER1
] = level
;
23464 else if ((*gearScore
)[EQUIPMENT_SLOT_FINGER2
] < level
)
23465 (*gearScore
)[EQUIPMENT_SLOT_FINGER2
] = level
;
23468 case INVTYPE_TRINKET
:
23470 if ((*gearScore
)[EQUIPMENT_SLOT_TRINKET1
] < level
)
23472 (*gearScore
)[EQUIPMENT_SLOT_TRINKET2
] = (*gearScore
)[EQUIPMENT_SLOT_TRINKET1
];
23473 (*gearScore
)[EQUIPMENT_SLOT_TRINKET1
] = level
;
23475 else if ((*gearScore
)[EQUIPMENT_SLOT_TRINKET2
] < level
)
23476 (*gearScore
)[EQUIPMENT_SLOT_TRINKET2
] = level
;
23479 case INVTYPE_CLOAK
:
23480 (*gearScore
)[EQUIPMENT_SLOT_BACK
] = std::max((*gearScore
)[EQUIPMENT_SLOT_BACK
], level
);
23487 void Player::SendCurrencies() const
23489 WorldPacket
data(SMSG_SEND_CURRENCIES
, m_currencies
.size() * 4);
23490 data
.WriteBits(m_currencies
.size(), 23);
23492 for (PlayerCurrenciesMap::const_iterator itr
= m_currencies
.begin(); itr
!= m_currencies
.end(); ++itr
)
23494 uint32 weekCap
= GetCurrencyWeekCap(itr
->second
.currencyEntry
);
23495 data
.WriteBit(weekCap
&& itr
->second
.weekCount
);
23496 data
.WriteBits(itr
->second
.flags
, 4);
23497 data
.WriteBit(weekCap
);
23498 data
.WriteBit(itr
->second
.currencyEntry
->HasSeasonCount());
23501 for (PlayerCurrenciesMap::const_iterator itr
= m_currencies
.begin(); itr
!= m_currencies
.end(); ++itr
)
23503 data
<< uint32(floor(itr
->second
.totalCount
/ itr
->second
.currencyEntry
->GetPrecision()));
23505 uint32 weekCap
= GetCurrencyWeekCap(itr
->second
.currencyEntry
);
23507 data
<< uint32(floor(weekCap
/ itr
->second
.currencyEntry
->GetPrecision()));
23508 if (itr
->second
.currencyEntry
->HasSeasonCount())
23509 data
<< uint32(floor(itr
->second
.seasonCount
/ itr
->second
.currencyEntry
->GetPrecision()));
23510 data
<< uint32(itr
->first
);
23511 if (weekCap
&& itr
->second
.weekCount
)
23512 data
<< uint32(floor(itr
->second
.weekCount
/ itr
->second
.currencyEntry
->GetPrecision()));
23515 GetSession()->SendPacket(&data
);
23518 uint32
Player::GetCurrencyWeekCap(CurrencyTypesEntry
const * currency
) const
23520 uint32 cap
= currency
->WeekCap
;
23521 switch (currency
->ID
)
23523 case CURRENCY_CONQUEST_POINTS
:
23524 cap
= sWorld
.getConfig(CONFIG_UINT32_CURRENCY_CONQUEST_POINTS_DEFAULT_WEEK_CAP
);
23531 void Player::SendCurrencyWeekCap(uint32 id
) const
23533 SendCurrencyWeekCap(sCurrencyTypesStore
.LookupEntry(id
));
23536 void Player::SendCurrencyWeekCap(CurrencyTypesEntry
const * currency
) const
23538 if (!currency
|| !IsInWorld() || GetSession()->PlayerLoading())
23541 uint32 cap
= GetCurrencyWeekCap(currency
);
23545 WorldPacket
packet(SMSG_SET_CURRENCY_WEEK_LIMIT
, 8);
23546 packet
<< uint32(floor(cap
/ currency
->GetPrecision()));
23547 packet
<< uint32(currency
->ID
);
23548 GetSession()->SendPacket(&packet
);
23551 uint32
Player::GetCurrencyTotalCap(CurrencyTypesEntry
const* currency
) const
23553 uint32 cap
= currency
->TotalCap
;
23558 uint32
Player::GetCurrencyCount(uint32 id
) const
23560 PlayerCurrenciesMap::const_iterator itr
= m_currencies
.find(id
);
23561 return itr
!= m_currencies
.end() ? itr
->second
.totalCount
: 0;
23564 uint32
Player::GetCurrencySeasonCount(uint32 id
) const
23566 PlayerCurrenciesMap::const_iterator itr
= m_currencies
.find(id
);
23567 return itr
!= m_currencies
.end() ? itr
->second
.seasonCount
: 0;
23570 uint32
Player::GetCurrencyWeekCount(uint32 id
) const
23572 PlayerCurrenciesMap::const_iterator itr
= m_currencies
.find(id
);
23573 return itr
!= m_currencies
.end() ? itr
->second
.weekCount
: 0;
23576 void Player::ModifyCurrencyCount(uint32 id
, int32 count
, bool modifyWeek
, bool modifySeason
)
23581 CurrencyTypesEntry
const * currency
= NULL
;
23583 int32 oldTotalCount
= 0;
23584 int32 oldWeekCount
= 0;
23585 PlayerCurrenciesMap::iterator itr
= m_currencies
.find(id
);
23587 bool initWeek
= false;
23588 if (itr
== m_currencies
.end())
23590 currency
= sCurrencyTypesStore
.LookupEntry(id
);
23591 MANGOS_ASSERT(currency
);
23593 PlayerCurrency cur
;
23594 cur
.state
= PLAYERCURRENCY_NEW
;
23595 cur
.totalCount
= 0;
23597 cur
.seasonCount
= 0;
23599 cur
.currencyEntry
= currency
;
23600 m_currencies
[id
] = cur
;
23602 itr
= m_currencies
.find(id
);
23606 oldTotalCount
= itr
->second
.totalCount
;
23607 oldWeekCount
= itr
->second
.weekCount
;
23608 currency
= itr
->second
.currencyEntry
;
23611 int32 newTotalCount
= oldTotalCount
+ count
;
23612 if (newTotalCount
< 0)
23615 int32 newWeekCount
= oldWeekCount
+ (modifyWeek
&& count
> 0 ? count
: 0);
23616 if (newWeekCount
< 0)
23619 int32 totalCap
= GetCurrencyTotalCap(currency
);
23620 if (totalCap
&& int32(totalCap
) < newTotalCount
)
23622 int32 delta
= newTotalCount
- totalCap
;
23623 newTotalCount
= totalCap
;
23624 newWeekCount
-= delta
;
23627 int32 weekCap
= GetCurrencyWeekCap(currency
);
23628 if (modifyWeek
&& weekCap
&& newWeekCount
> weekCap
)
23630 int32 delta
= newWeekCount
- weekCap
;
23631 newWeekCount
= weekCap
;
23632 newTotalCount
-= delta
;
23634 initWeek
&= weekCap
!= currency
->WeekCap
;
23636 if (newTotalCount
!= oldTotalCount
)
23638 if (itr
->second
.state
!= PLAYERCURRENCY_NEW
)
23639 itr
->second
.state
= PLAYERCURRENCY_CHANGED
;
23641 itr
->second
.totalCount
= newTotalCount
;
23642 itr
->second
.weekCount
= newWeekCount
;
23644 int32 diff
= newTotalCount
- oldTotalCount
;
23645 if (diff
> 0 && modifySeason
)
23646 itr
->second
.seasonCount
+= diff
;
23648 // probably excessive checks
23649 if (IsInWorld() && !GetSession()->PlayerLoading())
23652 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CURRENCY_EARNED
, id
, newTotalCount
);
23654 WorldPacket
packet(SMSG_SET_CURRENCY
, 13);
23655 bool bit0
= modifyWeek
&& weekCap
&& diff
> 0;
23656 bool bit1
= currency
->HasSeasonCount();
23657 bool bit2
= currency
->ID
== CURRENCY_CONQUEST_ARENA_META
|| currency
->ID
== CURRENCY_CONQUEST_BG_META
; // hides message in client when set
23658 packet
.WriteBit(bit0
);
23659 packet
.WriteBit(bit1
);
23660 packet
.WriteBit(bit2
);
23663 packet
<< uint32(floor(itr
->second
.seasonCount
/ currency
->GetPrecision()));
23664 packet
<< uint32(floor(newTotalCount
/ currency
->GetPrecision()));
23665 packet
<< uint32(id
);
23667 packet
<< uint32(floor(newWeekCount
/ currency
->GetPrecision()));
23668 GetSession()->SendPacket(&packet
);
23670 // init currency week limit for new currencies
23672 SendCurrencyWeekCap(currency
);
23675 CurrencyAddedQuestCheck(id
);
23677 CurrencyRemovedQuestCheck(id
);
23680 if (itr
->first
== CURRENCY_CONQUEST_ARENA_META
|| itr
->first
== CURRENCY_CONQUEST_BG_META
)
23681 ModifyCurrencyCount(CURRENCY_CONQUEST_POINTS
, diff
, modifyWeek
);
23685 void Player::SetCurrencyCount(uint32 id
, uint32 count
)
23687 ModifyCurrencyCount(id
, int32(count
) - GetCurrencyCount(id
));
23690 void Player::_LoadCurrencies(QueryResult
* result
)
23693 // "SELECT id, totalCount, weekCount, seasonCount, flags FROM character_currencies WHERE guid = '%u'"
23699 Field
* fields
= result
->Fetch();
23701 uint32 currency_id
= fields
[0].GetUInt16();
23702 uint32 totalCount
= fields
[1].GetUInt32();
23703 uint32 weekCount
= fields
[2].GetUInt32();
23704 uint32 seasonCount
= fields
[3].GetUInt32();
23705 uint8 flags
= fields
[4].GetUInt8();
23707 CurrencyTypesEntry
const * entry
= sCurrencyTypesStore
.LookupEntry(currency_id
);
23710 sLog
.outError("Player::_LoadCurrencies: %s has not existing currency id %u, removing.", GetGuidStr().c_str(), currency_id
);
23711 CharacterDatabase
.PExecute("DELETE FROM character_currencies WHERE id = '%u'", currency_id
);
23715 uint32 weekCap
= GetCurrencyWeekCap(entry
);
23716 uint32 totalCap
= GetCurrencyTotalCap(entry
);
23718 PlayerCurrency cur
;
23720 cur
.state
= PLAYERCURRENCY_UNCHANGED
;
23722 if (totalCap
&& totalCount
> totalCap
)
23723 cur
.totalCount
= totalCap
;
23725 cur
.totalCount
= totalCount
;
23727 if (weekCap
&& weekCount
> weekCap
)
23728 cur
.weekCount
= weekCap
;
23730 cur
.weekCount
= weekCount
;
23732 cur
.seasonCount
= seasonCount
;
23734 cur
.flags
= flags
& PLAYERCURRENCY_MASK_USED_BY_CLIENT
;
23735 cur
.currencyEntry
= entry
;
23737 m_currencies
[currency_id
] = cur
;
23739 while (result
->NextRow());
23743 void Player::_SaveCurrencies()
23745 for (PlayerCurrenciesMap::iterator itr
= m_currencies
.begin(); itr
!= m_currencies
.end();)
23747 if (itr
->second
.state
== PLAYERCURRENCY_CHANGED
)
23748 CharacterDatabase
.PExecute("UPDATE `character_currencies` SET `totalCount` = '%u', `weekCount` = '%u', `seasonCount` = '%u', `flags` = '%u' WHERE `guid` = '%u' AND `id` = '%u'", itr
->second
.totalCount
, itr
->second
.weekCount
, itr
->second
.seasonCount
, itr
->second
.flags
, GetGUIDLow(), itr
->first
);
23749 else if (itr
->second
.state
== PLAYERCURRENCY_NEW
)
23750 CharacterDatabase
.PExecute("INSERT INTO `character_currencies` (`guid`, `id`, `totalCount`, `weekCount`, `seasonCount`, `flags`) VALUES ('%u', '%u', '%u', '%u', '%u', '%u')", GetGUIDLow(), itr
->first
, itr
->second
.totalCount
, itr
->second
.weekCount
, itr
->second
.seasonCount
, itr
->second
.flags
);
23752 if (itr
->second
.state
== PLAYERCURRENCY_REMOVED
)
23753 m_currencies
.erase(itr
++);
23756 itr
->second
.state
= PLAYERCURRENCY_UNCHANGED
;
23762 void Player::SetCurrencyFlags(uint32 currencyId
, uint8 flags
)
23764 PlayerCurrenciesMap::iterator itr
= m_currencies
.find(currencyId
);
23765 if (itr
== m_currencies
.end())
23768 itr
->second
.flags
= flags
;
23769 itr
->second
.state
= PLAYERCURRENCY_CHANGED
;
23772 void Player::ResetCurrencyWeekCounts()
23774 for (PlayerCurrenciesMap::iterator itr
= m_currencies
.begin(); itr
!= m_currencies
.end(); ++itr
)
23776 itr
->second
.weekCount
= 0;
23777 itr
->second
.state
= PLAYERCURRENCY_CHANGED
;
23780 WorldPacket
data(SMSG_WEEKLY_RESET_CURRENCIES
, 0);
23781 SendDirectMessage(&data
);
23784 void Player::SendPvPRewards()
23788 WorldPacket
data(SMSG_PVP_REWARDS
, 6 * 4);
23789 data
<< uint32(1650); // rbg conquest cap
23790 data
<< uint32(0); // total conquest earned
23791 data
<< uint32(1350); // arena conquest cap
23792 data
<< uint32(0); // rbg conquest earned
23793 data
<< uint32(0); // arena conquest earned
23794 data
<< uint32(1650); // total conquest cap
23796 SendDirectMessage(&data
);
23799 void Player::SendRatedBGStats()
23803 WorldPacket
data(SMSG_RATED_BG_STATS
, 18 * 4);
23804 for (int i
= 0; i
< 18; ++i
)
23807 SendDirectMessage(&data
);
23810 AreaLockStatus
Player::GetAreaTriggerLockStatus(AreaTrigger
const* at
, Difficulty difficulty
, uint32
& miscRequirement
)
23812 miscRequirement
= 0;
23815 return AREA_LOCKSTATUS_UNKNOWN_ERROR
;
23817 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(at
->target_mapId
);
23819 return AREA_LOCKSTATUS_UNKNOWN_ERROR
;
23821 bool isRegularTargetMap
= !mapEntry
->IsDungeon() || GetDifficulty(mapEntry
->IsRaid()) == REGULAR_DIFFICULTY
;
23823 MapDifficultyEntry
const* mapDiff
= GetMapDifficultyData(at
->target_mapId
, difficulty
);
23824 if (mapEntry
->IsDungeon() && !mapDiff
)
23825 return AREA_LOCKSTATUS_MISSING_DIFFICULTY
;
23827 // Expansion requirement
23828 if (GetSession()->Expansion() < mapEntry
->Expansion())
23830 miscRequirement
= mapEntry
->Expansion();
23831 return AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION
;
23834 // Gamemaster can always enter
23835 if (isGameMaster())
23836 return AREA_LOCKSTATUS_OK
;
23838 // Level Requirements
23839 if (getLevel() < at
->requiredLevel
&& !sWorld
.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_LEVEL
))
23841 miscRequirement
= at
->requiredLevel
;
23842 return AREA_LOCKSTATUS_TOO_LOW_LEVEL
;
23844 if (!isRegularTargetMap
&& !sWorld
.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_LEVEL
) && getLevel() < uint32(maxLevelForExpansion
[mapEntry
->Expansion()]))
23846 miscRequirement
= maxLevelForExpansion
[mapEntry
->Expansion()];
23847 return AREA_LOCKSTATUS_TOO_LOW_LEVEL
;
23850 // Raid Requirements
23851 if (mapEntry
->IsRaid() && !sWorld
.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_RAID
))
23852 if (!GetGroup() || !GetGroup()->isRaidGroup())
23853 return AREA_LOCKSTATUS_RAID_LOCKED
;
23855 // Item Requirements: must have requiredItem OR requiredItem2, report the first one that's missing
23856 if (at
->requiredItem
)
23858 if (!HasItemCount(at
->requiredItem
, 1) &&
23859 (!at
->requiredItem2
|| !HasItemCount(at
->requiredItem2
, 1)))
23861 miscRequirement
= at
->requiredItem
;
23862 return AREA_LOCKSTATUS_MISSING_ITEM
;
23865 else if (at
->requiredItem2
&& !HasItemCount(at
->requiredItem2
, 1))
23867 miscRequirement
= at
->requiredItem2
;
23868 return AREA_LOCKSTATUS_MISSING_ITEM
;
23870 // Heroic item requirements
23871 if (!isRegularTargetMap
&& at
->heroicKey
)
23873 if (!HasItemCount(at
->heroicKey
, 1) && (!at
->heroicKey2
|| !HasItemCount(at
->heroicKey2
, 1)))
23875 miscRequirement
= at
->heroicKey
;
23876 return AREA_LOCKSTATUS_MISSING_ITEM
;
23879 else if (!isRegularTargetMap
&& at
->heroicKey2
&& !HasItemCount(at
->heroicKey2
, 1))
23881 miscRequirement
= at
->heroicKey2
;
23882 return AREA_LOCKSTATUS_MISSING_ITEM
;
23885 // Quest Requirements
23886 if (isRegularTargetMap
&& at
->requiredQuest
&& !GetQuestRewardStatus(at
->requiredQuest
))
23888 miscRequirement
= at
->requiredQuest
;
23889 return AREA_LOCKSTATUS_QUEST_NOT_COMPLETED
;
23891 if (!isRegularTargetMap
&& at
->requiredQuestHeroic
&& !GetQuestRewardStatus(at
->requiredQuestHeroic
))
23893 miscRequirement
= at
->requiredQuestHeroic
;
23894 return AREA_LOCKSTATUS_QUEST_NOT_COMPLETED
;
23897 // If the map is not created, assume it is possible to enter it.
23898 DungeonPersistentState
* state
= GetBoundInstanceSaveForSelfOrGroup(at
->target_mapId
);
23899 Map
* map
= sMapMgr
.FindMap(at
->target_mapId
, state
? state
->GetInstanceId() : 0);
23901 // ToDo add achievement check
23903 // Map's state check
23904 if (map
&& map
->IsDungeon())
23906 // cannot enter if the instance is full (player cap), GMs don't count
23907 if (((DungeonMap
*)map
)->GetPlayersCountExceptGMs() >= ((DungeonMap
*)map
)->GetMaxPlayers())
23908 return AREA_LOCKSTATUS_INSTANCE_IS_FULL
;
23911 if (map
&& map
->GetInstanceData() && map
->GetInstanceData()->IsEncounterInProgress())
23912 return AREA_LOCKSTATUS_ZONE_IN_COMBAT
;
23915 InstancePlayerBind
* pBind
= GetBoundInstance(at
->target_mapId
, GetDifficulty(mapEntry
->IsRaid()));
23916 if (pBind
&& pBind
->perm
&& pBind
->state
!= state
)
23917 return AREA_LOCKSTATUS_HAS_BIND
;
23918 if (pBind
&& pBind
->perm
&& pBind
->state
!= map
->GetPersistentState())
23919 return AREA_LOCKSTATUS_HAS_BIND
;
23922 return AREA_LOCKSTATUS_OK
;
23925 const uint32 armorSpecToClass
[MAX_CLASSES
] =
23928 86526, // CLASS_WARRIOR
23929 86525, // CLASS_PALADIN
23930 86528, // CLASS_HUNTER
23931 86531, // CLASS_ROGUE
23933 86524, // CLASS_DEATH_KNIGHT
23934 86529, // CLASS_SHAMAN
23936 0, // CLASS_WARLOCK
23938 86530, // CLASS_DRUID
23941 #define MAX_ARMOR_SPECIALIZATION_SPELLS 18
23942 struct armorSpecToTabInfo
23949 const armorSpecToTabInfo armorSpecToTab
[MAX_ARMOR_SPECIALIZATION_SPELLS
] =
23951 { 86537, CLASS_DEATH_KNIGHT
, 398 }, // blood
23952 { 86113, CLASS_DEATH_KNIGHT
, 399 }, // frost
23953 { 86536, CLASS_DEATH_KNIGHT
, 400 }, // unholy
23954 { 86093, CLASS_DRUID
, 752 }, // balance
23955 { 86096, CLASS_DRUID
, 750 }, // feral
23956 { 86097, CLASS_DRUID
, 750 }, // feral
23957 { 86104, CLASS_DRUID
, 748 }, // resto
23958 { 86538, CLASS_HUNTER
, 0 }, //
23959 { 86103, CLASS_PALADIN
, 831 }, // holy
23960 { 86102, CLASS_PALADIN
, 839 }, // prot
23961 { 86539, CLASS_PALADIN
, 855 }, // retro
23962 { 86092, CLASS_ROGUE
, 0 }, //
23963 { 86100, CLASS_SHAMAN
, 261 }, // elem
23964 { 86099, CLASS_SHAMAN
, 263 }, // ench
23965 { 86108, CLASS_SHAMAN
, 262 }, // restor
23966 { 86101, CLASS_WARRIOR
, 746 }, // arms
23967 { 86110, CLASS_WARRIOR
, 815 }, // fury
23968 { 86535, CLASS_WARRIOR
, 845 }, // prot
23971 void Player::UpdateArmorSpecializations()
23973 uint32 specPassive
= armorSpecToClass
[getClass()];
23974 // return class has no armor specialization
23978 for (int i
= 0; i
< MAX_ARMOR_SPECIALIZATION_SPELLS
; ++i
)
23980 if (armorSpecToTab
[i
].Class
!= getClass())
23983 SpellEntry
const * spellProto
= sSpellStore
.LookupEntry(armorSpecToTab
[i
].spellId
);
23984 if (!spellProto
|| !spellProto
->HasAttribute(SPELL_ATTR_EX8_ARMOR_SPECIALIZATION
))
23986 sLog
.outError("Player::UpdateArmorSpecializations: unexistent or wrong spell %u for class %u",
23987 armorSpecToTab
[i
].spellId
, armorSpecToTab
[i
].Class
);
23991 // remove if base passive is unlearned
23992 if (!HasSpell(specPassive
))
23994 RemoveAurasDueToSpell(spellProto
->Id
);
23998 SpellAuraHolder
* holder
= GetSpellAuraHolder(spellProto
->Id
);
24001 // cast absent spells that may be missing due to shapeshift form dependency
24002 CastSpell(this, spellProto
->Id
, true);
24006 Aura
* aura
= holder
->GetAuraByEffectIndex(EFFECT_INDEX_0
);
24010 // recalculate modifier depending on current tree
24011 aura
->ApplyModifier(false, false);
24012 aura
->GetModifier()->m_amount
= CalculateSpellDamage(this, spellProto
, EFFECT_INDEX_0
);
24013 aura
->ApplyModifier(true, false);
24017 bool Player::FitArmorSpecializationRules(SpellEntry
const * spellProto
) const
24019 if (!spellProto
|| !spellProto
->HasAttribute(SPELL_ATTR_EX8_ARMOR_SPECIALIZATION
))
24023 for (; i
< MAX_ARMOR_SPECIALIZATION_SPELLS
; ++i
)
24025 if (spellProto
->Id
== armorSpecToTab
[i
].spellId
)
24027 if (!armorSpecToTab
[i
].tab
&& m_talentsPrimaryTree
[m_activeSpec
] == 0 ||
24028 armorSpecToTab
[i
].tab
&& armorSpecToTab
[i
].tab
!= m_talentsPrimaryTree
[m_activeSpec
])
24035 if (i
== MAX_ARMOR_SPECIALIZATION_SPELLS
)
24038 if (!HasSpell(armorSpecToClass
[getClass()]))
24041 if (SpellEquippedItemsEntry
const * itemsEntry
= spellProto
->GetSpellEquippedItems())
24043 // there spells check items with inventory types which are in EquippedItemInventoryTypeMask
24044 uint32 inventoryTypeMask
= itemsEntry
->EquippedItemInventoryTypeMask
;
24045 // get slots that should be check for item presence and SpellEquippedItemsEntry match
24046 uint32 slotMask
= 0;
24048 for (int i
= 0; i
< MAX_INVTYPE
; ++i
)
24050 if (inventoryTypeMask
& (1 << i
))
24052 if (!GetSlotsForInventoryType(i
, slots
))
24054 for (int j
= 0; j
< 4; ++j
)
24055 if (slots
[j
] != NULL_SLOT
)
24056 slotMask
|= 1 << slots
[j
];
24060 for (int i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_END
; ++i
)
24062 if (slotMask
& (1 << i
))
24064 Item
* item
= GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
24065 // item must be present for specialization to work
24069 if (item
->GetProto()->Class
!= itemsEntry
->EquippedItemClass
)
24072 if (((1 << item
->GetProto()->SubClass
) & itemsEntry
->EquippedItemSubClassMask
) == 0)
24081 void Player::SendPetitionSignResult(ObjectGuid petitionGuid
, Player
* player
, uint32 result
)
24083 WorldPacket
data(SMSG_PETITION_SIGN_RESULTS
, 8 + 8 + 4);
24084 data
<< petitionGuid
;
24085 data
<< player
->GetObjectGuid();
24086 data
<< uint32(result
);
24087 GetSession()->SendPacket(&data
);
24090 void Player::SendPetitionTurnInResult(uint32 result
)
24092 WorldPacket
data(SMSG_TURN_IN_PETITION_RESULTS
, 4);
24093 data
<< uint32(result
);
24094 GetSession()->SendPacket(&data
);