[10016] Remove unused RewardSinglePlayerAtKill result.
[getmangos.git] / src / game / Player.cpp
blobbccc778ae18fbc222d2e90c6a1bbb14a8e0208cc
1 /*
2 * Copyright (C) 2005-2010 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
19 #include "Common.h"
20 #include "Language.h"
21 #include "Database/DatabaseEnv.h"
22 #include "Log.h"
23 #include "Opcodes.h"
24 #include "SpellMgr.h"
25 #include "World.h"
26 #include "WorldPacket.h"
27 #include "WorldSession.h"
28 #include "UpdateMask.h"
29 #include "Player.h"
30 #include "Vehicle.h"
31 #include "SkillDiscovery.h"
32 #include "QuestDef.h"
33 #include "GossipDef.h"
34 #include "UpdateData.h"
35 #include "Channel.h"
36 #include "ChannelMgr.h"
37 #include "MapManager.h"
38 #include "MapInstanced.h"
39 #include "InstanceSaveMgr.h"
40 #include "GridNotifiers.h"
41 #include "GridNotifiersImpl.h"
42 #include "CellImpl.h"
43 #include "ObjectMgr.h"
44 #include "ObjectAccessor.h"
45 #include "ObjectGuid.h"
46 #include "CreatureAI.h"
47 #include "Formulas.h"
48 #include "Group.h"
49 #include "Guild.h"
50 #include "Pet.h"
51 #include "Util.h"
52 #include "Transports.h"
53 #include "Weather.h"
54 #include "BattleGround.h"
55 #include "BattleGroundAV.h"
56 #include "BattleGroundMgr.h"
57 #include "ArenaTeam.h"
58 #include "Chat.h"
59 #include "Database/DatabaseImpl.h"
60 #include "Spell.h"
61 #include "SocialMgr.h"
62 #include "AchievementMgr.h"
63 #include "Mail.h"
65 #include <cmath>
67 #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)
69 #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
70 #define PLAYER_SKILL_VALUE_INDEX(x) (PLAYER_SKILL_INDEX(x)+1)
71 #define PLAYER_SKILL_BONUS_INDEX(x) (PLAYER_SKILL_INDEX(x)+2)
73 #define SKILL_VALUE(x) PAIR32_LOPART(x)
74 #define SKILL_MAX(x) PAIR32_HIPART(x)
75 #define MAKE_SKILL_VALUE(v, m) MAKE_PAIR32(v,m)
77 #define SKILL_TEMP_BONUS(x) int16(PAIR32_LOPART(x))
78 #define SKILL_PERM_BONUS(x) int16(PAIR32_HIPART(x))
79 #define MAKE_SKILL_BONUS(t, p) MAKE_PAIR32(t,p)
81 enum CharacterFlags
83 CHARACTER_FLAG_NONE = 0x00000000,
84 CHARACTER_FLAG_UNK1 = 0x00000001,
85 CHARACTER_FLAG_UNK2 = 0x00000002,
86 CHARACTER_LOCKED_FOR_TRANSFER = 0x00000004,
87 CHARACTER_FLAG_UNK4 = 0x00000008,
88 CHARACTER_FLAG_UNK5 = 0x00000010,
89 CHARACTER_FLAG_UNK6 = 0x00000020,
90 CHARACTER_FLAG_UNK7 = 0x00000040,
91 CHARACTER_FLAG_UNK8 = 0x00000080,
92 CHARACTER_FLAG_UNK9 = 0x00000100,
93 CHARACTER_FLAG_UNK10 = 0x00000200,
94 CHARACTER_FLAG_HIDE_HELM = 0x00000400,
95 CHARACTER_FLAG_HIDE_CLOAK = 0x00000800,
96 CHARACTER_FLAG_UNK13 = 0x00001000,
97 CHARACTER_FLAG_GHOST = 0x00002000,
98 CHARACTER_FLAG_RENAME = 0x00004000,
99 CHARACTER_FLAG_UNK16 = 0x00008000,
100 CHARACTER_FLAG_UNK17 = 0x00010000,
101 CHARACTER_FLAG_UNK18 = 0x00020000,
102 CHARACTER_FLAG_UNK19 = 0x00040000,
103 CHARACTER_FLAG_UNK20 = 0x00080000,
104 CHARACTER_FLAG_UNK21 = 0x00100000,
105 CHARACTER_FLAG_UNK22 = 0x00200000,
106 CHARACTER_FLAG_UNK23 = 0x00400000,
107 CHARACTER_FLAG_UNK24 = 0x00800000,
108 CHARACTER_FLAG_LOCKED_BY_BILLING = 0x01000000,
109 CHARACTER_FLAG_DECLINED = 0x02000000,
110 CHARACTER_FLAG_UNK27 = 0x04000000,
111 CHARACTER_FLAG_UNK28 = 0x08000000,
112 CHARACTER_FLAG_UNK29 = 0x10000000,
113 CHARACTER_FLAG_UNK30 = 0x20000000,
114 CHARACTER_FLAG_UNK31 = 0x40000000,
115 CHARACTER_FLAG_UNK32 = 0x80000000
118 enum CharacterCustomizeFlags
120 CHAR_CUSTOMIZE_FLAG_NONE = 0x00000000,
121 CHAR_CUSTOMIZE_FLAG_CUSTOMIZE = 0x00000001, // name, gender, etc...
122 CHAR_CUSTOMIZE_FLAG_FACTION = 0x00010000, // name, gender, faction, etc...
123 CHAR_CUSTOMIZE_FLAG_RACE = 0x00100000 // name, gender, race, etc...
126 // corpse reclaim times
127 #define DEATH_EXPIRE_STEP (5*MINUTE)
128 #define MAX_DEATH_COUNT 3
130 static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
132 //== PlayerTaxi ================================================
134 PlayerTaxi::PlayerTaxi()
136 // Taxi nodes
137 memset(m_taximask, 0, sizeof(m_taximask));
140 void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint32 level)
142 // class specific initial known nodes
143 switch(chrClass)
145 case CLASS_DEATH_KNIGHT:
147 for(int i = 0; i < TaxiMaskSize; ++i)
148 m_taximask[i] |= sOldContinentsNodesMask[i];
149 break;
153 // race specific initial known nodes: capital and taxi hub masks
154 switch(race)
156 case RACE_HUMAN: SetTaximaskNode(2); break; // Human
157 case RACE_ORC: SetTaximaskNode(23); break; // Orc
158 case RACE_DWARF: SetTaximaskNode(6); break; // Dwarf
159 case RACE_NIGHTELF: SetTaximaskNode(26);
160 SetTaximaskNode(27); break; // Night Elf
161 case RACE_UNDEAD_PLAYER: SetTaximaskNode(11); break;// Undead
162 case RACE_TAUREN: SetTaximaskNode(22); break; // Tauren
163 case RACE_GNOME: SetTaximaskNode(6); break; // Gnome
164 case RACE_TROLL: SetTaximaskNode(23); break; // Troll
165 case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf
166 case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei
169 // new continent starting masks (It will be accessible only at new map)
170 switch(Player::TeamForRace(race))
172 case ALLIANCE: SetTaximaskNode(100); break;
173 case HORDE: SetTaximaskNode(99); break;
175 // level dependent taxi hubs
176 if(level>=68)
177 SetTaximaskNode(213); //Shattered Sun Staging Area
180 void PlayerTaxi::LoadTaxiMask(const char* data)
182 Tokens tokens = StrSplit(data, " ");
184 int index;
185 Tokens::iterator iter;
186 for (iter = tokens.begin(), index = 0;
187 (index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index)
189 // load and set bits only for existed taxi nodes
190 m_taximask[index] = sTaxiNodesMask[index] & uint32(atol((*iter).c_str()));
194 void PlayerTaxi::AppendTaximaskTo( ByteBuffer& data, bool all )
196 if(all)
198 for (uint8 i=0; i<TaxiMaskSize; ++i)
199 data << uint32(sTaxiNodesMask[i]); // all existed nodes
201 else
203 for (uint8 i=0; i<TaxiMaskSize; ++i)
204 data << uint32(m_taximask[i]); // known nodes
208 bool PlayerTaxi::LoadTaxiDestinationsFromString( const std::string& values, uint32 team )
210 ClearTaxiDestinations();
212 Tokens tokens = StrSplit(values," ");
214 for(Tokens::iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
216 uint32 node = uint32(atol(iter->c_str()));
217 AddTaxiDestination(node);
220 if(m_TaxiDestinations.empty())
221 return true;
223 // Check integrity
224 if(m_TaxiDestinations.size() < 2)
225 return false;
227 for(size_t i = 1; i < m_TaxiDestinations.size(); ++i)
229 uint32 cost;
230 uint32 path;
231 sObjectMgr.GetTaxiPath(m_TaxiDestinations[i-1],m_TaxiDestinations[i],path,cost);
232 if(!path)
233 return false;
236 // can't load taxi path without mount set (quest taxi path?)
237 if(!sObjectMgr.GetTaxiMountDisplayId(GetTaxiSource(),team,true))
238 return false;
240 return true;
243 std::string PlayerTaxi::SaveTaxiDestinationsToString()
245 if(m_TaxiDestinations.empty())
246 return "";
248 std::ostringstream ss;
250 for(size_t i=0; i < m_TaxiDestinations.size(); ++i)
251 ss << m_TaxiDestinations[i] << " ";
253 return ss.str();
256 uint32 PlayerTaxi::GetCurrentTaxiPath() const
258 if(m_TaxiDestinations.size() < 2)
259 return 0;
261 uint32 path;
262 uint32 cost;
264 sObjectMgr.GetTaxiPath(m_TaxiDestinations[0],m_TaxiDestinations[1],path,cost);
266 return path;
269 std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
271 ss << "'";
272 for(int i = 0; i < TaxiMaskSize; ++i)
273 ss << taxi.m_taximask[i] << " ";
274 ss << "'";
275 return ss;
278 SpellModifier::SpellModifier( SpellModOp _op, SpellModType _type, int32 _value, SpellEntry const* spellEntry, SpellEffectIndex eff, int16 _charges /*= 0*/ ) : op(_op), type(_type), charges(_charges), value(_value), spellId(spellEntry->Id), lastAffected(NULL)
280 uint32 const* ptr = spellEntry->GetEffectSpellClassMask(eff);
281 mask = uint64(ptr[0]) | (uint64(ptr[1]) << 32);
282 mask2= ptr[2];
285 SpellModifier::SpellModifier( SpellModOp _op, SpellModType _type, int32 _value, Aura const* aura, int16 _charges /*= 0*/ ) : op(_op), type(_type), charges(_charges), value(_value), spellId(aura->GetId()), lastAffected(NULL)
287 uint32 const* ptr = aura->getAuraSpellClassMask();
288 mask = uint64(ptr[0]) | (uint64(ptr[1]) << 32);
289 mask2= ptr[2];
292 bool SpellModifier::isAffectedOnSpell( SpellEntry const *spell ) const
294 SpellEntry const *affect_spell = sSpellStore.LookupEntry(spellId);
295 // False if affect_spell == NULL or spellFamily not equal
296 if (!affect_spell || affect_spell->SpellFamilyName != spell->SpellFamilyName)
297 return false;
298 if (mask & spell->SpellFamilyFlags)
299 return true;
300 if (mask2 & spell->SpellFamilyFlags2)
301 return true;
302 return false;
305 //== Player ====================================================
307 UpdateMask Player::updateVisualBits;
309 Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputationMgr(this)
311 m_transport = 0;
313 m_speakTime = 0;
314 m_speakCount = 0;
316 m_objectType |= TYPEMASK_PLAYER;
317 m_objectTypeId = TYPEID_PLAYER;
319 m_valuesCount = PLAYER_END;
321 m_session = session;
323 m_divider = 0;
325 m_ExtraFlags = 0;
326 if(GetSession()->GetSecurity() >= SEC_GAMEMASTER)
327 SetAcceptTicket(true);
329 // players always accept
330 if(GetSession()->GetSecurity() == SEC_PLAYER)
331 SetAcceptWhispers(true);
333 m_curSelection = 0;
334 m_lootGuid = 0;
336 m_comboTarget = 0;
337 m_comboPoints = 0;
339 m_usedTalentCount = 0;
340 m_questRewardTalentCount = 0;
342 m_regenTimer = 0;
343 m_weaponChangeTimer = 0;
345 m_zoneUpdateId = 0;
346 m_zoneUpdateTimer = 0;
348 m_areaUpdateId = 0;
350 m_nextSave = sWorld.getConfig(CONFIG_UINT32_INTERVAL_SAVE);
352 // randomize first save time in range [CONFIG_UINT32_INTERVAL_SAVE] around [CONFIG_UINT32_INTERVAL_SAVE]
353 // this must help in case next save after mass player load after server startup
354 m_nextSave = urand(m_nextSave/2,m_nextSave*3/2);
356 clearResurrectRequestData();
358 m_SpellModRemoveCount = 0;
360 memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT);
362 m_social = NULL;
364 // group is initialized in the reference constructor
365 SetGroupInvite(NULL);
366 m_groupUpdateMask = 0;
367 m_auraUpdateMask = 0;
369 duel = NULL;
371 m_GuildIdInvited = 0;
372 m_ArenaTeamIdInvited = 0;
374 m_atLoginFlags = AT_LOGIN_NONE;
376 mSemaphoreTeleport_Near = false;
377 mSemaphoreTeleport_Far = false;
379 m_DelayedOperations = 0;
380 m_bCanDelayTeleport = false;
381 m_bHasDelayedTeleport = false;
382 m_teleport_options = 0;
384 pTrader = 0;
385 ClearTrade();
387 m_cinematic = 0;
389 PlayerTalkClass = new PlayerMenu( GetSession() );
390 m_currentBuybackSlot = BUYBACK_SLOT_START;
392 m_DailyQuestChanged = false;
393 m_WeeklyQuestChanged = false;
395 for (int i=0; i<MAX_TIMERS; ++i)
396 m_MirrorTimer[i] = DISABLED_MIRROR_TIMER;
398 m_MirrorTimerFlags = UNDERWATER_NONE;
399 m_MirrorTimerFlagsLast = UNDERWATER_NONE;
400 m_isInWater = false;
401 m_drunkTimer = 0;
402 m_drunk = 0;
403 m_restTime = 0;
404 m_deathTimer = 0;
405 m_deathExpireTime = 0;
407 m_swingErrorMsg = 0;
409 m_DetectInvTimer = 1*IN_MILLISECONDS;
411 for (int j=0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j)
413 m_bgBattleGroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE;
414 m_bgBattleGroundQueueID[j].invitedToInstance = 0;
417 m_logintime = time(NULL);
418 m_Last_tick = m_logintime;
419 m_WeaponProficiency = 0;
420 m_ArmorProficiency = 0;
421 m_canParry = false;
422 m_canBlock = false;
423 m_canDualWield = false;
424 m_canTitanGrip = false;
425 m_ammoDPS = 0.0f;
427 m_temporaryUnsummonedPetNumber = 0;
428 //cache for UNIT_CREATED_BY_SPELL to allow
429 //returning reagents for temporarily removed pets
430 //when dying/logging out
431 m_oldpetspell = 0;
433 ////////////////////Rest System/////////////////////
434 time_inn_enter=0;
435 inn_trigger_id=0;
436 m_rest_bonus=0;
437 rest_type=REST_TYPE_NO;
438 ////////////////////Rest System/////////////////////
440 m_mailsUpdated = false;
441 unReadMails = 0;
442 m_nextMailDelivereTime = 0;
444 m_resetTalentsCost = 0;
445 m_resetTalentsTime = 0;
446 m_itemUpdateQueueBlocked = false;
448 for (int i = 0; i < MAX_MOVE_TYPE; ++i)
449 m_forced_speed_changes[i] = 0;
451 m_stableSlots = 0;
453 /////////////////// Instance System /////////////////////
455 m_HomebindTimer = 0;
456 m_InstanceValid = true;
457 m_dungeonDifficulty = DUNGEON_DIFFICULTY_NORMAL;
458 m_raidDifficulty = RAID_DIFFICULTY_10MAN_NORMAL;
460 m_lastPotionId = 0;
462 m_activeSpec = 0;
463 m_specsCount = 1;
465 for (int i = 0; i < BASEMOD_END; ++i)
467 m_auraBaseMod[i][FLAT_MOD] = 0.0f;
468 m_auraBaseMod[i][PCT_MOD] = 1.0f;
471 for (int i = 0; i < MAX_COMBAT_RATING; ++i)
472 m_baseRatingValue[i] = 0;
474 m_baseSpellPower = 0;
475 m_baseFeralAP = 0;
476 m_baseManaRegen = 0;
477 m_armorPenetrationPct = 0.0f;
479 // Honor System
480 m_lastHonorUpdateTime = time(NULL);
482 // Player summoning
483 m_summon_expire = 0;
484 m_summon_mapid = 0;
485 m_summon_x = 0.0f;
486 m_summon_y = 0.0f;
487 m_summon_z = 0.0f;
489 m_mover = this;
491 m_miniPet = 0;
492 m_contestedPvPTimer = 0;
494 m_declinedname = NULL;
495 m_runes = NULL;
497 m_lastFallTime = 0;
498 m_lastFallZ = 0;
501 Player::~Player ()
503 CleanupsBeforeDelete();
505 // it must be unloaded already in PlayerLogout and accessed only for loggined player
506 //m_social = NULL;
508 // Note: buy back item already deleted from DB when player was saved
509 for(int i = 0; i < PLAYER_SLOTS_COUNT; ++i)
511 if(m_items[i])
512 delete m_items[i];
514 CleanupChannels();
516 //all mailed items should be deleted, also all mail should be deallocated
517 for (PlayerMails::const_iterator itr = m_mail.begin(); itr != m_mail.end();++itr)
518 delete *itr;
520 for (ItemMap::const_iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
521 delete iter->second; //if item is duplicated... then server may crash ... but that item should be deallocated
523 delete PlayerTalkClass;
525 if (m_transport)
527 m_transport->RemovePassenger(this);
530 for(size_t x = 0; x < ItemSetEff.size(); x++)
531 if(ItemSetEff[x])
532 delete ItemSetEff[x];
534 // clean up player-instance binds, may unload some instance saves
535 for(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
536 for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
537 itr->second.save->RemovePlayer(this);
539 delete m_declinedname;
540 delete m_runes;
543 void Player::CleanupsBeforeDelete()
545 if(m_uint32Values) // only for fully created Object
547 TradeCancel(false);
548 DuelComplete(DUEL_INTERUPTED);
550 Unit::CleanupsBeforeDelete();
553 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 */)
555 //FIXME: outfitId not used in player creating
557 Object::_Create(guidlow, 0, HIGHGUID_PLAYER);
559 m_name = name;
561 PlayerInfo const* info = sObjectMgr.GetPlayerInfo(race, class_);
562 if(!info)
564 sLog.outError("Player have incorrect race/class pair. Can't be loaded.");
565 return false;
568 for (int i = 0; i < PLAYER_SLOTS_COUNT; ++i)
569 m_items[i] = NULL;
571 SetLocationMapId(info->mapId);
572 Relocate(info->positionX,info->positionY,info->positionZ);
574 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(class_);
575 if(!cEntry)
577 sLog.outError("Class %u not found in DBC (Wrong DBC files?)",class_);
578 return false;
581 SetMap(sMapMgr.CreateMap(info->mapId, this));
583 uint8 powertype = cEntry->powerType;
585 SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE);
586 SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f);
588 setFactionForRace(race);
590 uint32 RaceClassGender = ( race ) | ( class_ << 8 ) | ( gender << 16 );
592 SetUInt32Value(UNIT_FIELD_BYTES_0, ( RaceClassGender | ( powertype << 24 ) ) );
593 InitDisplayIds();
594 SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP );
595 SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE );
596 SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER);
597 SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client
598 SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // default for players in 3.0.3
600 // -1 is default value
601 SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, -1);
603 SetUInt32Value(PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24)));
604 SetUInt32Value(PLAYER_BYTES_2, (facialHair | (0x00 << 8) | (0x00 << 16) | (0x02 << 24)));
605 SetByteValue(PLAYER_BYTES_3, 0, gender);
606 SetByteValue(PLAYER_BYTES_3, 3, 0); // BattlefieldArenaFaction (0 or 1)
608 SetUInt32Value( PLAYER_GUILDID, 0 );
609 SetUInt32Value( PLAYER_GUILDRANK, 0 );
610 SetUInt32Value( PLAYER_GUILD_TIMESTAMP, 0 );
612 for(int i = 0; i < KNOWN_TITLES_SIZE; ++i)
613 SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES + i, 0); // 0=disabled
614 SetUInt32Value( PLAYER_CHOSEN_TITLE, 0 );
616 SetUInt32Value( PLAYER_FIELD_KILLS, 0 );
617 SetUInt32Value( PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0 );
618 SetUInt32Value( PLAYER_FIELD_TODAY_CONTRIBUTION, 0 );
619 SetUInt32Value( PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0 );
621 // set starting level
622 uint32 start_level = getClass() != CLASS_DEATH_KNIGHT
623 ? sWorld.getConfig(CONFIG_UINT32_START_PLAYER_LEVEL)
624 : sWorld.getConfig(CONFIG_UINT32_START_HEROIC_PLAYER_LEVEL);
626 if (GetSession()->GetSecurity() >= SEC_MODERATOR)
628 uint32 gm_level = sWorld.getConfig(CONFIG_UINT32_START_GM_LEVEL);
629 if(gm_level > start_level)
630 start_level = gm_level;
633 SetUInt32Value(UNIT_FIELD_LEVEL, start_level);
635 InitRunes();
637 SetUInt32Value (PLAYER_FIELD_COINAGE, sWorld.getConfig(CONFIG_UINT32_START_PLAYER_MONEY));
638 SetUInt32Value (PLAYER_FIELD_HONOR_CURRENCY, sWorld.getConfig(CONFIG_UINT32_START_HONOR_POINTS));
639 SetUInt32Value (PLAYER_FIELD_ARENA_CURRENCY, sWorld.getConfig(CONFIG_UINT32_START_ARENA_POINTS));
641 // Played time
642 m_Last_tick = time(NULL);
643 m_Played_time[PLAYED_TIME_TOTAL] = 0;
644 m_Played_time[PLAYED_TIME_LEVEL] = 0;
646 // base stats and related field values
647 InitStatsForLevel();
648 InitTaxiNodesForLevel();
649 InitGlyphsForLevel();
650 InitTalentForLevel();
651 InitPrimaryProfessions(); // to max set before any spell added
653 // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
654 UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
655 SetHealth(GetMaxHealth());
656 if (getPowerType()==POWER_MANA)
658 UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intellect)
659 SetPower(POWER_MANA,GetMaxPower(POWER_MANA));
662 if(getPowerType() == POWER_RUNIC_POWER)
664 SetPower(POWER_RUNE, 8);
665 SetMaxPower(POWER_RUNE, 8);
666 SetPower(POWER_RUNIC_POWER, 0);
667 SetMaxPower(POWER_RUNIC_POWER, 1000);
670 // original spells
671 learnDefaultSpells();
673 // original action bar
674 for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr)
675 addActionButton(0, action_itr->button,action_itr->action,action_itr->type);
677 // original items
678 CharStartOutfitEntry const* oEntry = NULL;
679 for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
681 if(CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i))
683 if(entry->RaceClassGender == RaceClassGender)
685 oEntry = entry;
686 break;
691 if(oEntry)
693 for(int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
695 if(oEntry->ItemId[j] <= 0)
696 continue;
698 uint32 item_id = oEntry->ItemId[j];
700 // just skip, reported in ObjectMgr::LoadItemPrototypes
701 ItemPrototype const* iProto = ObjectMgr::GetItemPrototype(item_id);
702 if(!iProto)
703 continue;
705 // BuyCount by default
706 int32 count = iProto->BuyCount;
708 // special amount for foor/drink
709 if(iProto->Class==ITEM_CLASS_CONSUMABLE && iProto->SubClass==ITEM_SUBCLASS_FOOD)
711 switch(iProto->Spells[0].SpellCategory)
713 case 11: // food
714 count = getClass()==CLASS_DEATH_KNIGHT ? 10 : 4;
715 break;
716 case 59: // drink
717 count = 2;
718 break;
720 if(iProto->Stackable < count)
721 count = iProto->Stackable;
724 StoreNewItemInBestSlots(item_id, count);
728 for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr!=info->item.end(); ++item_id_itr++)
729 StoreNewItemInBestSlots(item_id_itr->item_id, item_id_itr->item_amount);
731 // bags and main-hand weapon must equipped at this moment
732 // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon)
733 // or ammo not equipped in special bag
734 for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
736 if(Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
738 uint16 eDest;
739 // equip offhand weapon/shield if it attempt equipped before main-hand weapon
740 uint8 msg = CanEquipItem( NULL_SLOT, eDest, pItem, false );
741 if( msg == EQUIP_ERR_OK )
743 RemoveItem(INVENTORY_SLOT_BAG_0, i,true);
744 EquipItem( eDest, pItem, true);
746 // move other items to more appropriate slots (ammo not equipped in special bag)
747 else
749 ItemPosCountVec sDest;
750 msg = CanStoreItem( NULL_BAG, NULL_SLOT, sDest, pItem, false );
751 if( msg == EQUIP_ERR_OK )
753 RemoveItem(INVENTORY_SLOT_BAG_0, i,true);
754 pItem = StoreItem( sDest, pItem, true);
757 // if this is ammo then use it
758 msg = CanUseAmmo( pItem->GetEntry() );
759 if( msg == EQUIP_ERR_OK )
760 SetAmmo( pItem->GetEntry() );
764 // all item positions resolved
766 return true;
769 bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount)
771 DEBUG_LOG("STORAGE: Creating initial item, itemId = %u, count = %u",titem_id, titem_amount);
773 // attempt equip by one
774 while(titem_amount > 0)
776 uint16 eDest;
777 uint8 msg = CanEquipNewItem( NULL_SLOT, eDest, titem_id, false );
778 if( msg != EQUIP_ERR_OK )
779 break;
781 EquipNewItem( eDest, titem_id, true);
782 AutoUnequipOffhandIfNeed();
783 --titem_amount;
786 if(titem_amount == 0)
787 return true; // equipped
789 // attempt store
790 ItemPosCountVec sDest;
791 // store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
792 uint8 msg = CanStoreNewItem( INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount );
793 if( msg == EQUIP_ERR_OK )
795 StoreNewItem( sDest, titem_id, true, Item::GenerateItemRandomPropertyId(titem_id) );
796 return true; // stored
799 // item can't be added
800 sLog.outError("STORAGE: Can't equip or store initial item %u for race %u class %u , error msg = %u",titem_id,getRace(),getClass(),msg);
801 return false;
804 // helper function, mainly for script side, but can be used for simple task in mangos also.
805 Item* Player::StoreNewItemInInventorySlot(uint32 itemEntry, uint32 amount)
807 ItemPosCountVec vDest;
809 uint8 msg = CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, vDest, itemEntry, amount);
811 if (msg == EQUIP_ERR_OK)
813 if (Item* pItem = StoreNewItem(vDest, itemEntry, true, Item::GenerateItemRandomPropertyId(itemEntry)))
814 return pItem;
817 return NULL;
820 void Player::SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, int32 Regen)
822 if (int(MaxValue) == DISABLED_MIRROR_TIMER)
824 if (int(CurrentValue) != DISABLED_MIRROR_TIMER)
825 StopMirrorTimer(Type);
826 return;
828 WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
829 data << (uint32)Type;
830 data << CurrentValue;
831 data << MaxValue;
832 data << Regen;
833 data << (uint8)0;
834 data << (uint32)0; // spell id
835 GetSession()->SendPacket( &data );
838 void Player::StopMirrorTimer(MirrorTimerType Type)
840 m_MirrorTimer[Type] = DISABLED_MIRROR_TIMER;
841 WorldPacket data(SMSG_STOP_MIRROR_TIMER, 4);
842 data << (uint32)Type;
843 GetSession()->SendPacket( &data );
846 uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage)
848 if(!isAlive() || isGameMaster())
849 return 0;
851 // Absorb, resist some environmental damage type
852 uint32 absorb = 0;
853 uint32 resist = 0;
854 if (type == DAMAGE_LAVA)
855 CalculateAbsorbAndResist(this, SPELL_SCHOOL_MASK_FIRE, DIRECT_DAMAGE, damage, &absorb, &resist);
856 else if (type == DAMAGE_SLIME)
857 CalculateAbsorbAndResist(this, SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, damage, &absorb, &resist);
859 damage-=absorb+resist;
861 DealDamageMods(this,damage,&absorb);
863 WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21));
864 data << uint64(GetGUID());
865 data << uint8(type!=DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL);
866 data << uint32(damage);
867 data << uint32(absorb);
868 data << uint32(resist);
869 SendMessageToSet(&data, true);
871 uint32 final_damage = DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
873 if(!isAlive())
875 if(type==DAMAGE_FALL) // DealDamage not apply item durability loss at self damage
877 DEBUG_LOG("We are fall to death, loosing 10 percents durability");
878 DurabilityLossAll(0.10f,false);
879 // durability lost message
880 WorldPacket data2(SMSG_DURABILITY_DAMAGE_DEATH, 0);
881 GetSession()->SendPacket(&data2);
884 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM, 1, type);
887 return final_damage;
890 int32 Player::getMaxTimer(MirrorTimerType timer)
892 switch (timer)
894 case FATIGUE_TIMER:
895 if (GetSession()->GetSecurity() >= (AccountTypes)sWorld.getConfig(CONFIG_UINT32_TIMERBAR_FATIGUE_GMLEVEL))
896 return DISABLED_MIRROR_TIMER;
897 return sWorld.getConfig(CONFIG_UINT32_TIMERBAR_FATIGUE_MAX)*IN_MILLISECONDS;
898 case BREATH_TIMER:
900 if (!isAlive() || HasAuraType(SPELL_AURA_WATER_BREATHING) ||
901 GetSession()->GetSecurity() >= (AccountTypes)sWorld.getConfig(CONFIG_UINT32_TIMERBAR_BREATH_GMLEVEL))
902 return DISABLED_MIRROR_TIMER;
903 int32 UnderWaterTime = sWorld.getConfig(CONFIG_UINT32_TIMERBAR_BREATH_MAX)*IN_MILLISECONDS;
904 AuraList const& mModWaterBreathing = GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING);
905 for(AuraList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i)
906 UnderWaterTime = uint32(UnderWaterTime * (100.0f + (*i)->GetModifier()->m_amount) / 100.0f);
907 return UnderWaterTime;
909 case FIRE_TIMER:
911 if (!isAlive() || GetSession()->GetSecurity() >= (AccountTypes)sWorld.getConfig(CONFIG_UINT32_TIMERBAR_FIRE_GMLEVEL))
912 return DISABLED_MIRROR_TIMER;
913 return sWorld.getConfig(CONFIG_UINT32_TIMERBAR_FIRE_MAX)*IN_MILLISECONDS;
915 default:
916 return 0;
918 return 0;
921 void Player::UpdateMirrorTimers()
923 // Desync flags for update on next HandleDrowning
924 if (m_MirrorTimerFlags)
925 m_MirrorTimerFlagsLast = ~m_MirrorTimerFlags;
928 void Player::HandleDrowning(uint32 time_diff)
930 if (!m_MirrorTimerFlags)
931 return;
933 // In water
934 if (m_MirrorTimerFlags & UNDERWATER_INWATER)
936 // Breath timer not activated - activate it
937 if (m_MirrorTimer[BREATH_TIMER] == DISABLED_MIRROR_TIMER)
939 m_MirrorTimer[BREATH_TIMER] = getMaxTimer(BREATH_TIMER);
940 SendMirrorTimer(BREATH_TIMER, m_MirrorTimer[BREATH_TIMER], m_MirrorTimer[BREATH_TIMER], -1);
942 else // If activated - do tick
944 m_MirrorTimer[BREATH_TIMER]-=time_diff;
945 // Timer limit - need deal damage
946 if (m_MirrorTimer[BREATH_TIMER] < 0)
948 m_MirrorTimer[BREATH_TIMER]+= 1*IN_MILLISECONDS;
949 // Calculate and deal damage
950 // TODO: Check this formula
951 uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1);
952 EnvironmentalDamage(DAMAGE_DROWNING, damage);
954 else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INWATER)) // Update time in client if need
955 SendMirrorTimer(BREATH_TIMER, getMaxTimer(BREATH_TIMER), m_MirrorTimer[BREATH_TIMER], -1);
958 else if (m_MirrorTimer[BREATH_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
960 int32 UnderWaterTime = getMaxTimer(BREATH_TIMER);
961 // Need breath regen
962 m_MirrorTimer[BREATH_TIMER]+=10*time_diff;
963 if (m_MirrorTimer[BREATH_TIMER] >= UnderWaterTime || !isAlive())
964 StopMirrorTimer(BREATH_TIMER);
965 else if (m_MirrorTimerFlagsLast & UNDERWATER_INWATER)
966 SendMirrorTimer(BREATH_TIMER, UnderWaterTime, m_MirrorTimer[BREATH_TIMER], 10);
969 // In dark water
970 if (m_MirrorTimerFlags & UNDERWATER_INDARKWATER)
972 // Fatigue timer not activated - activate it
973 if (m_MirrorTimer[FATIGUE_TIMER] == DISABLED_MIRROR_TIMER)
975 m_MirrorTimer[FATIGUE_TIMER] = getMaxTimer(FATIGUE_TIMER);
976 SendMirrorTimer(FATIGUE_TIMER, m_MirrorTimer[FATIGUE_TIMER], m_MirrorTimer[FATIGUE_TIMER], -1);
978 else
980 m_MirrorTimer[FATIGUE_TIMER]-=time_diff;
981 // Timer limit - need deal damage or teleport ghost to graveyard
982 if (m_MirrorTimer[FATIGUE_TIMER] < 0)
984 m_MirrorTimer[FATIGUE_TIMER]+= 1*IN_MILLISECONDS;
985 if (isAlive()) // Calculate and deal damage
987 uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1);
988 EnvironmentalDamage(DAMAGE_EXHAUSTED, damage);
990 else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) // Teleport ghost to graveyard
991 RepopAtGraveyard();
993 else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER))
994 SendMirrorTimer(FATIGUE_TIMER, getMaxTimer(FATIGUE_TIMER), m_MirrorTimer[FATIGUE_TIMER], -1);
997 else if (m_MirrorTimer[FATIGUE_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
999 int32 DarkWaterTime = getMaxTimer(FATIGUE_TIMER);
1000 m_MirrorTimer[FATIGUE_TIMER]+=10*time_diff;
1001 if (m_MirrorTimer[FATIGUE_TIMER] >= DarkWaterTime || !isAlive())
1002 StopMirrorTimer(FATIGUE_TIMER);
1003 else if (m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER)
1004 SendMirrorTimer(FATIGUE_TIMER, DarkWaterTime, m_MirrorTimer[FATIGUE_TIMER], 10);
1007 if (m_MirrorTimerFlags & (UNDERWATER_INLAVA|UNDERWATER_INSLIME))
1009 // Breath timer not activated - activate it
1010 if (m_MirrorTimer[FIRE_TIMER] == DISABLED_MIRROR_TIMER)
1011 m_MirrorTimer[FIRE_TIMER] = getMaxTimer(FIRE_TIMER);
1012 else
1014 m_MirrorTimer[FIRE_TIMER]-=time_diff;
1015 if (m_MirrorTimer[FIRE_TIMER] < 0)
1017 m_MirrorTimer[FIRE_TIMER]+= 1*IN_MILLISECONDS;
1018 // Calculate and deal damage
1019 // TODO: Check this formula
1020 uint32 damage = urand(600, 700);
1021 if (m_MirrorTimerFlags&UNDERWATER_INLAVA)
1022 EnvironmentalDamage(DAMAGE_LAVA, damage);
1023 else
1024 EnvironmentalDamage(DAMAGE_SLIME, damage);
1028 else
1029 m_MirrorTimer[FIRE_TIMER] = DISABLED_MIRROR_TIMER;
1031 // Recheck timers flag
1032 m_MirrorTimerFlags&=~UNDERWATER_EXIST_TIMERS;
1033 for (int i = 0; i< MAX_TIMERS; ++i)
1034 if (m_MirrorTimer[i]!=DISABLED_MIRROR_TIMER)
1036 m_MirrorTimerFlags|=UNDERWATER_EXIST_TIMERS;
1037 break;
1039 m_MirrorTimerFlagsLast = m_MirrorTimerFlags;
1042 ///The player sobers by 256 every 10 seconds
1043 void Player::HandleSobering()
1045 m_drunkTimer = 0;
1047 uint32 drunk = (m_drunk <= 256) ? 0 : (m_drunk - 256);
1048 SetDrunkValue(drunk);
1051 DrunkenState Player::GetDrunkenstateByValue(uint16 value)
1053 if(value >= 23000)
1054 return DRUNKEN_SMASHED;
1055 if(value >= 12800)
1056 return DRUNKEN_DRUNK;
1057 if(value & 0xFFFE)
1058 return DRUNKEN_TIPSY;
1059 return DRUNKEN_SOBER;
1062 void Player::SetDrunkValue(uint16 newDrunkenValue, uint32 itemId)
1064 uint32 oldDrunkenState = Player::GetDrunkenstateByValue(m_drunk);
1066 m_drunk = newDrunkenValue;
1067 SetUInt32Value(PLAYER_BYTES_3,(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFF0001) | (m_drunk & 0xFFFE));
1069 uint32 newDrunkenState = Player::GetDrunkenstateByValue(m_drunk);
1071 // special drunk invisibility detection
1072 if(newDrunkenState >= DRUNKEN_DRUNK)
1073 m_detectInvisibilityMask |= (1<<6);
1074 else
1075 m_detectInvisibilityMask &= ~(1<<6);
1077 if(newDrunkenState == oldDrunkenState)
1078 return;
1080 WorldPacket data(SMSG_CROSSED_INEBRIATION_THRESHOLD, (8+4+4));
1081 data << uint64(GetGUID());
1082 data << uint32(newDrunkenState);
1083 data << uint32(itemId);
1085 SendMessageToSet(&data, true);
1088 void Player::Update( uint32 p_time )
1090 if(!IsInWorld())
1091 return;
1093 // undelivered mail
1094 if(m_nextMailDelivereTime && m_nextMailDelivereTime <= time(NULL))
1096 SendNewMail();
1097 ++unReadMails;
1099 // It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated)
1100 m_nextMailDelivereTime = 0;
1103 //used to implement delayed far teleports
1104 SetCanDelayTeleport(true);
1105 Unit::Update( p_time );
1106 SetCanDelayTeleport(false);
1108 // update player only attacks
1109 if(uint32 ranged_att = getAttackTimer(RANGED_ATTACK))
1111 setAttackTimer(RANGED_ATTACK, (p_time >= ranged_att ? 0 : ranged_att - p_time) );
1114 if(uint32 off_att = getAttackTimer(OFF_ATTACK))
1116 setAttackTimer(OFF_ATTACK, (p_time >= off_att ? 0 : off_att - p_time) );
1119 time_t now = time (NULL);
1121 UpdatePvPFlag(now);
1123 UpdateContestedPvP(p_time);
1125 UpdateDuelFlag(now);
1127 CheckDuelDistance(now);
1129 UpdateAfkReport(now);
1131 // Update items that have just a limited lifetime
1132 if (now>m_Last_tick)
1133 UpdateItemDuration(uint32(now- m_Last_tick));
1135 if (!m_timedquests.empty())
1137 QuestSet::iterator iter = m_timedquests.begin();
1138 while (iter != m_timedquests.end())
1140 QuestStatusData& q_status = mQuestStatus[*iter];
1141 if( q_status.m_timer <= p_time )
1143 uint32 quest_id = *iter;
1144 ++iter; // current iter will be removed in FailQuest
1145 FailQuest(quest_id);
1147 else
1149 q_status.m_timer -= p_time;
1150 if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
1151 ++iter;
1156 if (hasUnitState(UNIT_STAT_MELEE_ATTACKING))
1158 Unit *pVictim = getVictim();
1159 if (pVictim && !IsNonMeleeSpellCasted(false))
1161 // default combat reach 10
1162 // TODO add weapon,skill check
1164 float pldistance = ATTACK_DISTANCE;
1166 if (isAttackReady(BASE_ATTACK))
1168 if(!IsWithinDistInMap(pVictim, pldistance))
1170 setAttackTimer(BASE_ATTACK,100);
1171 if(m_swingErrorMsg != 1) // send single time (client auto repeat)
1173 SendAttackSwingNotInRange();
1174 m_swingErrorMsg = 1;
1177 //120 degrees of radiant range
1178 else if( !HasInArc( 2*M_PI_F/3, pVictim ))
1180 setAttackTimer(BASE_ATTACK,100);
1181 if(m_swingErrorMsg != 2) // send single time (client auto repeat)
1183 SendAttackSwingBadFacingAttack();
1184 m_swingErrorMsg = 2;
1187 else
1189 m_swingErrorMsg = 0; // reset swing error state
1191 // prevent base and off attack in same time, delay attack at 0.2 sec
1192 if(haveOffhandWeapon())
1194 uint32 off_att = getAttackTimer(OFF_ATTACK);
1195 if(off_att < ATTACK_DISPLAY_DELAY)
1196 setAttackTimer(OFF_ATTACK,ATTACK_DISPLAY_DELAY);
1198 AttackerStateUpdate(pVictim, BASE_ATTACK);
1199 resetAttackTimer(BASE_ATTACK);
1203 if ( haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
1205 if(!IsWithinDistInMap(pVictim, pldistance))
1207 setAttackTimer(OFF_ATTACK,100);
1209 else if( !HasInArc( 2*M_PI_F/3, pVictim ))
1211 setAttackTimer(OFF_ATTACK,100);
1213 else
1215 // prevent base and off attack in same time, delay attack at 0.2 sec
1216 uint32 base_att = getAttackTimer(BASE_ATTACK);
1217 if(base_att < ATTACK_DISPLAY_DELAY)
1218 setAttackTimer(BASE_ATTACK,ATTACK_DISPLAY_DELAY);
1219 // do attack
1220 AttackerStateUpdate(pVictim, OFF_ATTACK);
1221 resetAttackTimer(OFF_ATTACK);
1225 Unit *owner = pVictim->GetOwner();
1226 Unit *u = owner ? owner : pVictim;
1227 if(u->IsPvP() && (!duel || duel->opponent != u))
1229 UpdatePvP(true);
1230 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
1235 if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
1237 if(roll_chance_i(3) && GetTimeInnEnter() > 0) //freeze update
1239 time_t time_inn = time(NULL)-GetTimeInnEnter();
1240 if (time_inn >= 10) //freeze update
1242 float bubble = 0.125f*sWorld.getConfig(CONFIG_FLOAT_RATE_REST_INGAME);
1243 //speed collect rest bonus (section/in hour)
1244 SetRestBonus( float(GetRestBonus()+ time_inn*(GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble ));
1245 UpdateInnerTime(time(NULL));
1250 if (m_regenTimer)
1252 if(p_time >= m_regenTimer)
1253 m_regenTimer = 0;
1254 else
1255 m_regenTimer -= p_time;
1258 if (m_weaponChangeTimer > 0)
1260 if(p_time >= m_weaponChangeTimer)
1261 m_weaponChangeTimer = 0;
1262 else
1263 m_weaponChangeTimer -= p_time;
1266 if (m_zoneUpdateTimer > 0)
1268 if(p_time >= m_zoneUpdateTimer)
1270 uint32 newzone, newarea;
1271 GetZoneAndAreaId(newzone,newarea);
1273 if( m_zoneUpdateId != newzone )
1274 UpdateZone(newzone,newarea); // also update area
1275 else
1277 // use area updates as well
1278 // needed for free far all arenas for example
1279 if( m_areaUpdateId != newarea )
1280 UpdateArea(newarea);
1282 m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
1285 else
1286 m_zoneUpdateTimer -= p_time;
1289 if (m_timeSyncTimer > 0)
1291 if(p_time >= m_timeSyncTimer)
1292 SendTimeSync();
1293 else
1294 m_timeSyncTimer -= p_time;
1297 if (isAlive())
1299 // if no longer casting, set regen power as soon as it is up.
1300 if (!IsUnderLastManaUseEffect())
1301 SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER);
1303 if (!m_regenTimer)
1304 RegenerateAll();
1307 if (m_deathState == JUST_DIED)
1308 KillPlayer();
1310 if(m_nextSave > 0)
1312 if(p_time >= m_nextSave)
1314 // m_nextSave reseted in SaveToDB call
1315 SaveToDB();
1316 DETAIL_LOG("Player '%s' (GUID: %u) saved", GetName(), GetGUIDLow());
1318 else
1319 m_nextSave -= p_time;
1322 //Handle Water/drowning
1323 HandleDrowning(p_time);
1325 //Handle detect stealth players
1326 if (m_DetectInvTimer > 0)
1328 if (p_time >= m_DetectInvTimer)
1330 HandleStealthedUnitsDetection();
1331 m_DetectInvTimer = 3000;
1333 else
1334 m_DetectInvTimer -= p_time;
1337 // Played time
1338 if (now > m_Last_tick)
1340 uint32 elapsed = uint32(now - m_Last_tick);
1341 m_Played_time[PLAYED_TIME_TOTAL] += elapsed; // Total played time
1342 m_Played_time[PLAYED_TIME_LEVEL] += elapsed; // Level played time
1343 m_Last_tick = now;
1346 if (m_drunk)
1348 m_drunkTimer += p_time;
1350 if (m_drunkTimer > 10*IN_MILLISECONDS)
1351 HandleSobering();
1354 // not auto-free ghost from body in instances
1355 if(m_deathTimer > 0 && !GetBaseMap()->Instanceable())
1357 if(p_time >= m_deathTimer)
1359 m_deathTimer = 0;
1360 BuildPlayerRepop();
1361 RepopAtGraveyard();
1363 else
1364 m_deathTimer -= p_time;
1367 UpdateEnchantTime(p_time);
1368 UpdateHomebindTime(p_time);
1370 // group update
1371 SendUpdateToOutOfRangeGroupMembers();
1373 Pet* pet = GetPet();
1374 if(pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID())))
1376 RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
1379 //we should execute delayed teleports only for alive(!) players
1380 //because we don't want player's ghost teleported from graveyard
1381 if(IsHasDelayedTeleport() && isAlive())
1382 TeleportTo(m_teleport_dest, m_teleport_options);
1385 void Player::setDeathState(DeathState s)
1387 uint32 ressSpellId = 0;
1389 bool cur = isAlive();
1391 if(s == JUST_DIED && cur)
1393 // drunken state is cleared on death
1394 SetDrunkValue(0);
1395 // lost combo points at any target (targeted combo points clear in Unit::setDeathState)
1396 ClearComboPoints();
1398 clearResurrectRequestData();
1400 // remove form before other mods to prevent incorrect stats calculation
1401 RemoveAurasDueToSpell(m_ShapeShiftFormSpellId);
1403 //FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
1404 RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
1406 // remove uncontrolled pets
1407 RemoveMiniPet();
1409 // save value before aura remove in Unit::setDeathState
1410 ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL);
1412 // passive spell
1413 if(!ressSpellId)
1414 ressSpellId = GetResurrectionSpellId();
1415 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP, 1);
1416 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH, 1);
1417 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON, 1);
1419 Unit::setDeathState(s);
1421 // restore resurrection spell id for player after aura remove
1422 if(s == JUST_DIED && cur && ressSpellId)
1423 SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId);
1425 if(isAlive() && !cur)
1427 //clear aura case after resurrection by another way (spells will be applied before next death)
1428 SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
1430 // restore default warrior stance
1431 if(getClass()== CLASS_WARRIOR)
1432 CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true);
1436 bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
1438 // 0 1 2 3 4 5 6 7
1439 // "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
1440 // 8 9 10 11 12 13 14
1441 // "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
1442 // 15 16 17 18 19 20
1443 // "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.equipmentCache, character_declinedname.genitive "
1445 Field *fields = result->Fetch();
1447 uint32 guid = fields[0].GetUInt32();
1448 uint8 pRace = fields[2].GetUInt8();
1449 uint8 pClass = fields[3].GetUInt8();
1451 PlayerInfo const *info = sObjectMgr.GetPlayerInfo(pRace, pClass);
1452 if(!info)
1454 sLog.outError("Player %u has incorrect race/class pair. Don't build enum.", guid);
1455 return false;
1458 *p_data << uint64(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
1459 *p_data << fields[1].GetString(); // name
1460 *p_data << uint8(pRace); // race
1461 *p_data << uint8(pClass); // class
1462 *p_data << uint8(fields[4].GetUInt8()); // gender
1464 uint32 playerBytes = fields[5].GetUInt32();
1465 *p_data << uint8(playerBytes); // skin
1466 *p_data << uint8(playerBytes >> 8); // face
1467 *p_data << uint8(playerBytes >> 16); // hair style
1468 *p_data << uint8(playerBytes >> 24); // hair color
1470 uint32 playerBytes2 = fields[6].GetUInt32();
1471 *p_data << uint8(playerBytes2 & 0xFF); // facial hair
1473 *p_data << uint8(fields[7].GetUInt8()); // level
1474 *p_data << uint32(fields[8].GetUInt32()); // zone
1475 *p_data << uint32(fields[9].GetUInt32()); // map
1477 *p_data << fields[10].GetFloat(); // x
1478 *p_data << fields[11].GetFloat(); // y
1479 *p_data << fields[12].GetFloat(); // z
1481 *p_data << uint32(fields[13].GetUInt32()); // guild id
1483 uint32 char_flags = 0;
1484 uint32 playerFlags = fields[14].GetUInt32();
1485 uint32 atLoginFlags = fields[15].GetUInt32();
1486 if(playerFlags & PLAYER_FLAGS_HIDE_HELM)
1487 char_flags |= CHARACTER_FLAG_HIDE_HELM;
1488 if(playerFlags & PLAYER_FLAGS_HIDE_CLOAK)
1489 char_flags |= CHARACTER_FLAG_HIDE_CLOAK;
1490 if(playerFlags & PLAYER_FLAGS_GHOST)
1491 char_flags |= CHARACTER_FLAG_GHOST;
1492 if(atLoginFlags & AT_LOGIN_RENAME)
1493 char_flags |= CHARACTER_FLAG_RENAME;
1494 if(sWorld.getConfig(CONFIG_BOOL_DECLINED_NAMES_USED))
1496 if(!fields[20].GetCppString().empty())
1497 char_flags |= CHARACTER_FLAG_DECLINED;
1499 else
1500 char_flags |= CHARACTER_FLAG_DECLINED;
1502 *p_data << uint32(char_flags); // character flags
1503 // character customize flags
1504 *p_data << uint32(atLoginFlags & AT_LOGIN_CUSTOMIZE ? CHAR_CUSTOMIZE_FLAG_CUSTOMIZE : CHAR_CUSTOMIZE_FLAG_NONE);
1505 // First login
1506 *p_data << uint8(atLoginFlags & AT_LOGIN_FIRST ? 1 : 0);
1508 // Pets info
1510 uint32 petDisplayId = 0;
1511 uint32 petLevel = 0;
1512 uint32 petFamily = 0;
1514 // show pet at selection character in character list only for non-ghost character
1515 if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER || pClass == CLASS_DEATH_KNIGHT))
1517 uint32 entry = fields[16].GetUInt32();
1518 CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(entry);
1519 if(cInfo)
1521 petDisplayId = fields[17].GetUInt32();
1522 petLevel = fields[18].GetUInt32();
1523 petFamily = cInfo->family;
1527 *p_data << uint32(petDisplayId);
1528 *p_data << uint32(petLevel);
1529 *p_data << uint32(petFamily);
1533 Tokens data = StrSplit(fields[19].GetCppString(), " ");
1534 for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++)
1536 uint32 visualbase = slot * 2;
1537 uint32 item_id = GetUInt32ValueFromArray(data, visualbase);
1538 const ItemPrototype * proto = ObjectMgr::GetItemPrototype(item_id);
1539 if(!proto)
1541 *p_data << uint32(0);
1542 *p_data << uint8(0);
1543 *p_data << uint32(0);
1544 continue;
1547 SpellItemEnchantmentEntry const *enchant = NULL;
1549 uint32 enchants = GetUInt32ValueFromArray(data, visualbase + 1);
1550 for(uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot)
1552 // values stored in 2 uint16
1553 uint32 enchantId = 0x0000FFFF & (enchants >> enchantSlot*16);
1554 if(!enchantId)
1555 continue;
1557 if ((enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId)))
1558 break;
1561 *p_data << uint32(proto->DisplayInfoID);
1562 *p_data << uint8(proto->InventoryType);
1563 *p_data << uint32(enchant ? enchant->aura_id : 0);
1566 *p_data << uint32(0); // bag 1 display id
1567 *p_data << uint8(0); // bag 1 inventory type
1568 *p_data << uint32(0); // enchant?
1569 *p_data << uint32(0); // bag 2 display id
1570 *p_data << uint8(0); // bag 2 inventory type
1571 *p_data << uint32(0); // enchant?
1572 *p_data << uint32(0); // bag 3 display id
1573 *p_data << uint8(0); // bag 3 inventory type
1574 *p_data << uint32(0); // enchant?
1575 *p_data << uint32(0); // bag 4 display id
1576 *p_data << uint8(0); // bag 4 inventory type
1577 *p_data << uint32(0); // enchant?
1579 return true;
1582 bool Player::ToggleAFK()
1584 ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);
1586 bool state = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);
1588 // afk player not allowed in battleground
1589 if (state && InBattleGround() && !InArena())
1590 LeaveBattleground();
1592 return state;
1595 bool Player::ToggleDND()
1597 ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);
1599 return HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);
1602 uint8 Player::chatTag() const
1604 // it's bitmask
1605 // 0x8 - ??
1606 // 0x4 - gm
1607 // 0x2 - dnd
1608 // 0x1 - afk
1609 if(isGMChat())
1610 return 4;
1611 else if(isDND())
1612 return 3;
1613 if(isAFK())
1614 return 1;
1615 else
1616 return 0;
1619 bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options)
1621 if(!MapManager::IsValidMapCoord(mapid, x, y, z, orientation))
1623 sLog.outError("TeleportTo: invalid map %d or absent instance template.", mapid);
1624 return false;
1627 // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
1628 Pet* pet = GetPet();
1630 MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
1632 // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)...
1633 // don't let gm level > 1 either
1634 if(!InBattleGround() && mEntry->IsBattleGroundOrArena())
1635 return false;
1637 // client without expansion support
1638 if(GetSession()->Expansion() < mEntry->Expansion())
1640 DEBUG_LOG("Player %s using client without required expansion tried teleport to non accessible map %u", GetName(), mapid);
1642 if(GetTransport())
1643 RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :)
1645 SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL, mEntry->Expansion());
1647 return false; // normal client can't teleport to this map...
1649 else
1651 DEBUG_LOG("Player %s is being teleported to map %u", GetName(), mapid);
1654 // if we were on a transport, leave
1655 if (!(options & TELE_TO_NOT_LEAVE_TRANSPORT) && m_transport)
1657 m_transport->RemovePassenger(this);
1658 m_transport = NULL;
1659 m_movementInfo.ClearTransportData();
1662 // The player was ported to another map and looses the duel immediately.
1663 // We have to perform this check before the teleport, otherwise the
1664 // ObjectAccessor won't find the flag.
1665 if (duel && GetMapId() != mapid)
1667 GameObject* obj = GetMap()->GetGameObject(GetUInt64Value(PLAYER_DUEL_ARBITER));
1668 if (obj)
1669 DuelComplete(DUEL_FLED);
1672 // reset movement flags at teleport, because player will continue move with these flags after teleport
1673 m_movementInfo.SetMovementFlags(MOVEFLAG_NONE);
1675 if ((GetMapId() == mapid) && (!m_transport))
1677 //lets reset far teleport flag if it wasn't reset during chained teleports
1678 SetSemaphoreTeleportFar(false);
1679 //setup delayed teleport flag
1680 SetDelayedTeleportFlag(IsCanDelayTeleport());
1681 //if teleport spell is casted in Unit::Update() func
1682 //then we need to delay it until update process will be finished
1683 if(IsHasDelayedTeleport())
1685 SetSemaphoreTeleportNear(true);
1686 //lets save teleport destination for player
1687 m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
1688 m_teleport_options = options;
1689 return true;
1692 if (!(options & TELE_TO_NOT_UNSUMMON_PET))
1694 //same map, only remove pet if out of range for new position
1695 if(pet && !pet->IsWithinDist3d(x, y, z, GetMap()->GetVisibilityDistance()))
1696 UnsummonPetTemporaryIfAny();
1699 if(!(options & TELE_TO_NOT_LEAVE_COMBAT))
1700 CombatStop();
1702 // this will be used instead of the current location in SaveToDB
1703 m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
1704 SetFallInformation(0, z);
1706 // code for finish transfer called in WorldSession::HandleMovementOpcodes()
1707 // at client packet MSG_MOVE_TELEPORT_ACK
1708 SetSemaphoreTeleportNear(true);
1709 // near teleport, triggering send MSG_MOVE_TELEPORT_ACK from client at landing
1710 if(!GetSession()->PlayerLogout())
1712 WorldPacket data;
1713 BuildTeleportAckMsg(&data, x, y, z, orientation);
1714 GetSession()->SendPacket(&data);
1717 else
1719 // far teleport to another map
1720 Map* oldmap = IsInWorld() ? GetMap() : NULL;
1721 // check if we can enter before stopping combat / removing pet / totems / interrupting spells
1723 // Check enter rights before map getting to avoid creating instance copy for player
1724 // this check not dependent from map instance copy and same for all instance copies of selected map
1725 if (!sMapMgr.CanPlayerEnter(mapid, this))
1726 return false;
1728 // If the map is not created, assume it is possible to enter it.
1729 // It will be created in the WorldPortAck.
1730 Map *map = sMapMgr.FindMap(mapid);
1731 if (!map || map->CanEnter(this))
1733 //lets reset near teleport flag if it wasn't reset during chained teleports
1734 SetSemaphoreTeleportNear(false);
1735 //setup delayed teleport flag
1736 SetDelayedTeleportFlag(IsCanDelayTeleport());
1737 //if teleport spell is casted in Unit::Update() func
1738 //then we need to delay it until update process will be finished
1739 if(IsHasDelayedTeleport())
1741 SetSemaphoreTeleportFar(true);
1742 //lets save teleport destination for player
1743 m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
1744 m_teleport_options = options;
1745 return true;
1748 SetSelection(0);
1750 CombatStop();
1752 ResetContestedPvP();
1754 // remove player from battleground on far teleport (when changing maps)
1755 if(BattleGround const* bg = GetBattleGround())
1757 // Note: at battleground join battleground id set before teleport
1758 // and we already will found "current" battleground
1759 // just need check that this is targeted map or leave
1760 if(bg->GetMapId() != mapid)
1761 LeaveBattleground(false); // don't teleport to entry point
1764 // remove pet on map change
1765 if (pet)
1766 UnsummonPetTemporaryIfAny();
1768 // remove all dyn objects
1769 RemoveAllDynObjects();
1771 // stop spellcasting
1772 // not attempt interrupt teleportation spell at caster teleport
1773 if(!(options & TELE_TO_SPELL))
1774 if(IsNonMeleeSpellCasted(true))
1775 InterruptNonMeleeSpells(true);
1777 //remove auras before removing from map...
1778 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING);
1780 if(!GetSession()->PlayerLogout())
1782 // send transfer packets
1783 WorldPacket data(SMSG_TRANSFER_PENDING, (4+4+4));
1784 data << uint32(mapid);
1785 if (m_transport)
1787 data << uint32(m_transport->GetEntry());
1788 data << uint32(GetMapId());
1790 GetSession()->SendPacket(&data);
1792 data.Initialize(SMSG_NEW_WORLD, (20));
1793 if (m_transport)
1795 data << uint32(mapid);
1796 data << float(m_movementInfo.GetTransportPos()->x);
1797 data << float(m_movementInfo.GetTransportPos()->y);
1798 data << float(m_movementInfo.GetTransportPos()->z);
1799 data << float(m_movementInfo.GetTransportPos()->o);
1801 else
1803 data << uint32(mapid);
1804 data << float(x);
1805 data << float(y);
1806 data << float(z);
1807 data << float(orientation);
1809 GetSession()->SendPacket( &data );
1810 SendSavedInstances();
1813 // remove from old map now
1814 if(oldmap)
1815 oldmap->Remove(this, false);
1817 // new final coordinates
1818 float final_x = x;
1819 float final_y = y;
1820 float final_z = z;
1821 float final_o = orientation;
1823 if(m_transport)
1825 final_x += m_movementInfo.GetTransportPos()->x;
1826 final_y += m_movementInfo.GetTransportPos()->y;
1827 final_z += m_movementInfo.GetTransportPos()->z;
1828 final_o += m_movementInfo.GetTransportPos()->o;
1831 m_teleport_dest = WorldLocation(mapid, final_x, final_y, final_z, final_o);
1832 SetFallInformation(0, final_z);
1833 // if the player is saved before worldport ack (at logout for example)
1834 // this will be used instead of the current location in SaveToDB
1836 // move packet sent by client always after far teleport
1837 // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
1838 SetSemaphoreTeleportFar(true);
1840 else
1841 return false;
1843 return true;
1846 bool Player::TeleportToBGEntryPoint()
1848 ScheduleDelayedOperation(DELAYED_BG_MOUNT_RESTORE);
1849 ScheduleDelayedOperation(DELAYED_BG_TAXI_RESTORE);
1850 return TeleportTo(m_bgData.joinPos);
1853 void Player::ProcessDelayedOperations()
1855 if(m_DelayedOperations == 0)
1856 return;
1858 if(m_DelayedOperations & DELAYED_RESURRECT_PLAYER)
1860 ResurrectPlayer(0.0f, false);
1862 if(GetMaxHealth() > m_resurrectHealth)
1863 SetHealth( m_resurrectHealth );
1864 else
1865 SetHealth( GetMaxHealth() );
1867 if(GetMaxPower(POWER_MANA) > m_resurrectMana)
1868 SetPower(POWER_MANA, m_resurrectMana );
1869 else
1870 SetPower(POWER_MANA, GetMaxPower(POWER_MANA) );
1872 SetPower(POWER_RAGE, 0 );
1873 SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY) );
1875 SpawnCorpseBones();
1878 if(m_DelayedOperations & DELAYED_SAVE_PLAYER)
1880 SaveToDB();
1883 if(m_DelayedOperations & DELAYED_SPELL_CAST_DESERTER)
1885 CastSpell(this, 26013, true); // Deserter
1888 if (m_DelayedOperations & DELAYED_BG_MOUNT_RESTORE)
1890 if (m_bgData.mountSpell)
1892 CastSpell(this, m_bgData.mountSpell, true);
1893 m_bgData.mountSpell = 0;
1897 if (m_DelayedOperations & DELAYED_BG_TAXI_RESTORE)
1899 if (m_bgData.HasTaxiPath())
1901 m_taxi.AddTaxiDestination(m_bgData.taxiPath[0]);
1902 m_taxi.AddTaxiDestination(m_bgData.taxiPath[1]);
1903 m_bgData.ClearTaxiPath();
1905 ContinueTaxiFlight();
1909 //we have executed ALL delayed ops, so clear the flag
1910 m_DelayedOperations = 0;
1913 void Player::AddToWorld()
1915 ///- Do not add/remove the player from the object storage
1916 ///- It will crash when updating the ObjectAccessor
1917 ///- The player should only be added when logging in
1918 Unit::AddToWorld();
1920 for(int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
1922 if(m_items[i])
1923 m_items[i]->AddToWorld();
1927 void Player::RemoveFromWorld()
1929 // cleanup
1930 if(IsInWorld())
1932 ///- Release charmed creatures, unsummon totems and remove pets/guardians
1933 UnsummonAllTotems();
1934 RemoveMiniPet();
1937 for(int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
1939 if(m_items[i])
1940 m_items[i]->RemoveFromWorld();
1943 ///- Do not add/remove the player from the object storage
1944 ///- It will crash when updating the ObjectAccessor
1945 ///- The player should only be removed when logging out
1946 Unit::RemoveFromWorld();
1949 void Player::RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker )
1951 float addRage;
1953 float rageconversion = float((0.0091107836 * getLevel()*getLevel())+3.225598133*getLevel())+4.2652911f;
1955 if(attacker)
1957 addRage = ((damage/rageconversion*7.5f + weaponSpeedHitFactor)/2.0f);
1959 // talent who gave more rage on attack
1960 addRage *= 1.0f + GetTotalAuraModifier(SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT) / 100.0f;
1962 else
1964 addRage = damage/rageconversion*2.5f;
1966 // Berserker Rage effect
1967 if (HasAura(18499, EFFECT_INDEX_0))
1968 addRage *= 1.3f;
1971 addRage *= sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_RAGE_INCOME);
1973 ModifyPower(POWER_RAGE, uint32(addRage*10));
1976 void Player::RegenerateAll(uint32 diff)
1978 // Not in combat or they have regeneration
1979 if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) ||
1980 HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT) || IsPolymorphed() )
1982 RegenerateHealth(diff);
1983 if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN))
1985 Regenerate(POWER_RAGE, diff);
1986 if(getClass() == CLASS_DEATH_KNIGHT)
1987 Regenerate(POWER_RUNIC_POWER, diff);
1991 Regenerate(POWER_ENERGY, diff);
1993 Regenerate(POWER_MANA, diff);
1995 if (getClass() == CLASS_DEATH_KNIGHT)
1996 Regenerate(POWER_RUNE, diff);
1998 m_regenTimer = REGEN_TIME_FULL;
2001 // diff contains the time in milliseconds since last regen.
2002 void Player::Regenerate(Powers power, uint32 diff)
2004 uint32 curValue = GetPower(power);
2005 uint32 maxValue = GetMaxPower(power);
2007 float addvalue = 0.0f;
2009 switch (power)
2011 case POWER_MANA:
2013 bool recentCast = IsUnderLastManaUseEffect();
2014 float ManaIncreaseRate = sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_MANA);
2015 if (recentCast)
2017 // Mangos Updates Mana in intervals of 2s, which is correct
2018 addvalue = GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER) * ManaIncreaseRate * 2.00f;
2020 else
2022 addvalue = GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER) * ManaIncreaseRate * 2.00f;
2024 } break;
2025 case POWER_RAGE: // Regenerate rage
2027 float RageDecreaseRate = sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_RAGE_LOSS);
2028 addvalue = 20 * RageDecreaseRate; // 2 rage by tick (= 2 seconds => 1 rage/sec)
2029 } break;
2030 case POWER_ENERGY: // Regenerate energy (rogue)
2031 addvalue = 20;
2032 break;
2033 case POWER_RUNIC_POWER:
2035 float RunicPowerDecreaseRate = sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_RUNICPOWER_LOSS);
2036 addvalue = 30 * RunicPowerDecreaseRate; // 3 RunicPower by tick
2037 } break;
2038 case POWER_RUNE:
2040 if (getClass() != CLASS_DEATH_KNIGHT)
2041 break;
2043 for(uint32 rune = 0; rune < MAX_RUNES; ++rune)
2045 if(uint16 cd = GetRuneCooldown(rune)) // if we have cooldown, reduce it...
2047 uint32 cd_diff = diff;
2048 AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
2049 for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
2050 if ((*i)->GetModifier()->m_miscvalue == power && (*i)->GetMiscBValue()==GetCurrentRune(rune))
2051 cd_diff = cd_diff * ((*i)->GetModifier()->m_amount + 100) / 100;
2053 SetRuneCooldown(rune, (cd < cd_diff) ? 0 : cd - cd_diff);
2056 } break;
2057 case POWER_FOCUS:
2058 case POWER_HAPPINESS:
2059 case POWER_HEALTH:
2060 break;
2063 // Mana regen calculated in Player::UpdateManaRegen()
2064 // Exist only for POWER_MANA, POWER_ENERGY, POWER_FOCUS auras
2065 if(power != POWER_MANA)
2067 AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
2068 for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
2069 if ((*i)->GetModifier()->m_miscvalue == power)
2070 addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f;
2073 // addvalue computed on a 2sec basis. => update to diff time
2074 addvalue *= float(diff) / REGEN_TIME_FULL;
2076 if (power != POWER_RAGE && power != POWER_RUNIC_POWER)
2078 curValue += uint32(addvalue);
2079 if (curValue > maxValue)
2080 curValue = maxValue;
2082 else
2084 if(curValue <= uint32(addvalue))
2085 curValue = 0;
2086 else
2087 curValue -= uint32(addvalue);
2089 SetPower(power, curValue);
2092 void Player::RegenerateHealth(uint32 diff)
2094 uint32 curValue = GetHealth();
2095 uint32 maxValue = GetMaxHealth();
2097 if (curValue >= maxValue) return;
2099 float HealthIncreaseRate = sWorld.getConfig(CONFIG_FLOAT_RATE_HEALTH);
2101 float addvalue = 0.0f;
2103 // polymorphed case
2104 if ( IsPolymorphed() )
2105 addvalue = (float)GetMaxHealth()/3;
2106 // normal regen case (maybe partly in combat case)
2107 else if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) )
2109 addvalue = OCTRegenHPPerSpirit()* HealthIncreaseRate;
2110 if (!isInCombat())
2112 AuraList const& mModHealthRegenPct = GetAurasByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
2113 for(AuraList::const_iterator i = mModHealthRegenPct.begin(); i != mModHealthRegenPct.end(); ++i)
2114 addvalue *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f;
2116 else if(HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT))
2117 addvalue *= GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT) / 100.0f;
2119 if(!IsStandState())
2120 addvalue *= 1.5;
2123 // always regeneration bonus (including combat)
2124 addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT);
2126 if(addvalue < 0)
2127 addvalue = 0;
2129 addvalue *= (float)diff / REGEN_TIME_FULL;
2131 ModifyHealth(int32(addvalue));
2134 Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask)
2136 // some basic checks
2137 if (guid.IsEmpty() || !IsInWorld() || isInFlight())
2138 return NULL;
2140 // not in interactive state
2141 if (hasUnitState(UNIT_STAT_CAN_NOT_REACT))
2142 return NULL;
2144 // exist (we need look pets also for some interaction (quest/etc)
2145 Creature *unit = GetMap()->GetCreatureOrPetOrVehicle(guid);
2146 if (!unit)
2147 return NULL;
2149 // appropriate npc type
2150 if (npcflagmask && !unit->HasFlag( UNIT_NPC_FLAGS, npcflagmask ))
2151 return NULL;
2153 if (npcflagmask == UNIT_NPC_FLAG_STABLEMASTER)
2155 if (getClass() != CLASS_HUNTER)
2156 return NULL;
2159 // if a dead unit should be able to talk - the creature must be alive and have special flags
2160 if (!unit->isAlive())
2161 return NULL;
2163 if (isAlive() && unit->isInvisibleForAlive())
2164 return NULL;
2166 // not allow interaction under control, but allow with own pets
2167 if (unit->GetCharmerGUID())
2168 return NULL;
2170 // not enemy
2171 if (unit->IsHostileTo(this))
2172 return NULL;
2174 // not unfriendly
2175 if(FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(unit->getFaction()))
2176 if(factionTemplate->faction)
2177 if(FactionEntry const* faction = sFactionStore.LookupEntry(factionTemplate->faction))
2178 if(faction->reputationListID >= 0 && GetReputationMgr().GetRank(faction) <= REP_UNFRIENDLY)
2179 return NULL;
2181 // not too far
2182 if(!unit->IsWithinDistInMap(this,INTERACTION_DISTANCE))
2183 return NULL;
2185 return unit;
2188 GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, uint32 gameobject_type) const
2190 // some basic checks
2191 if (guid.IsEmpty() || !IsInWorld() || isInFlight())
2192 return NULL;
2194 // not in interactive state
2195 if (hasUnitState(UNIT_STAT_CAN_NOT_REACT))
2196 return NULL;
2198 if (GameObject *go = GetMap()->GetGameObject(guid))
2200 if (uint32(go->GetGoType()) == gameobject_type || gameobject_type == MAX_GAMEOBJECT_TYPE)
2202 float maxdist;
2203 switch(go->GetGoType())
2205 // TODO: find out how the client calculates the maximal usage distance to spellless working
2206 // gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary choosen number
2207 case GAMEOBJECT_TYPE_GUILD_BANK:
2208 case GAMEOBJECT_TYPE_MAILBOX:
2209 maxdist = 10.0f;
2210 break;
2211 case GAMEOBJECT_TYPE_FISHINGHOLE:
2212 maxdist = 20.0f+CONTACT_DISTANCE; // max spell range
2213 break;
2214 default:
2215 maxdist = INTERACTION_DISTANCE;
2216 break;
2219 if (go->IsWithinDistInMap(this, maxdist) && go->isSpawned())
2220 return go;
2222 sLog.outError("GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal 10 is allowed)", go->GetGOInfo()->name,
2223 go->GetGUIDLow(), GetName(), GetGUIDLow(), go->GetDistance(this));
2226 return NULL;
2229 bool Player::IsUnderWater() const
2231 return IsInWater() &&
2232 GetPositionZ() < (GetBaseMap()->GetWaterLevel(GetPositionX(),GetPositionY())-2);
2235 void Player::SetInWater(bool apply)
2237 if(m_isInWater==apply)
2238 return;
2240 //define player in water by opcodes
2241 //move player's guid into HateOfflineList of those mobs
2242 //which can't swim and move guid back into ThreatList when
2243 //on surface.
2244 //TODO: exist also swimming mobs, and function must be symmetric to enter/leave water
2245 m_isInWater = apply;
2247 // remove auras that need water/land
2248 RemoveAurasWithInterruptFlags(apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
2250 getHostileRefManager().updateThreatTables();
2253 struct SetGameMasterOnHelper
2255 explicit SetGameMasterOnHelper() {}
2256 void operator()(Unit* unit) const
2258 unit->setFaction(35);
2259 unit->getHostileRefManager().setOnlineOfflineState(false);
2263 struct SetGameMasterOffHelper
2265 explicit SetGameMasterOffHelper(uint32 _faction) : faction(_faction) {}
2266 void operator()(Unit* unit) const
2268 unit->setFaction(faction);
2269 unit->getHostileRefManager().setOnlineOfflineState(true);
2271 uint32 faction;
2274 void Player::SetGameMaster(bool on)
2276 if(on)
2278 m_ExtraFlags |= PLAYER_EXTRA_GM_ON;
2279 setFaction(35);
2280 SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
2282 CallForAllControlledUnits(SetGameMasterOnHelper(),true,true,true,false);
2284 SetFFAPvP(false);
2285 ResetContestedPvP();
2287 getHostileRefManager().setOnlineOfflineState(false);
2288 CombatStopWithPets();
2290 SetPhaseMask(PHASEMASK_ANYWHERE,false); // see and visible in all phases
2292 else
2294 // restore phase
2295 AuraList const& phases = GetAurasByType(SPELL_AURA_PHASE);
2296 SetPhaseMask(!phases.empty() ? phases.front()->GetMiscValue() : PHASEMASK_NORMAL,false);
2298 m_ExtraFlags &= ~ PLAYER_EXTRA_GM_ON;
2299 setFactionForRace(getRace());
2300 RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
2302 CallForAllControlledUnits(SetGameMasterOffHelper(getFaction()),true,true,true,false);
2304 // restore FFA PvP Server state
2305 if(sWorld.IsFFAPvPRealm())
2306 SetFFAPvP(true);
2308 // restore FFA PvP area state, remove not allowed for GM mounts
2309 UpdateArea(m_areaUpdateId);
2311 getHostileRefManager().setOnlineOfflineState(true);
2314 UpdateVisibilityForPlayer();
2317 void Player::SetGMVisible(bool on)
2319 if(on)
2321 m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; //remove flag
2323 // Reapply stealth/invisibility if active or show if not any
2324 if(HasAuraType(SPELL_AURA_MOD_STEALTH))
2325 SetVisibility(VISIBILITY_GROUP_STEALTH);
2326 else if(HasAuraType(SPELL_AURA_MOD_INVISIBILITY))
2327 SetVisibility(VISIBILITY_GROUP_INVISIBILITY);
2328 else
2329 SetVisibility(VISIBILITY_ON);
2331 else
2333 m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE; //add flag
2335 SetAcceptWhispers(false);
2336 SetGameMaster(true);
2338 SetVisibility(VISIBILITY_OFF);
2342 bool Player::IsGroupVisibleFor(Player* p) const
2344 switch(sWorld.getConfig(CONFIG_UINT32_GROUP_VISIBILITY))
2346 default: return IsInSameGroupWith(p);
2347 case 1: return IsInSameRaidWith(p);
2348 case 2: return GetTeam()==p->GetTeam();
2352 bool Player::IsInSameGroupWith(Player const* p) const
2354 return (p==this || (GetGroup() != NULL &&
2355 GetGroup()->SameSubGroup((Player*)this, (Player*)p)));
2358 ///- If the player is invited, remove him. If the group if then only 1 person, disband the group.
2359 /// \todo Shouldn't we also check if there is no other invitees before disbanding the group?
2360 void Player::UninviteFromGroup()
2362 Group* group = GetGroupInvite();
2363 if(!group)
2364 return;
2366 group->RemoveInvite(this);
2368 if(group->GetMembersCount() <= 1) // group has just 1 member => disband
2370 if(group->IsCreated())
2372 group->Disband(true);
2373 sObjectMgr.RemoveGroup(group);
2375 else
2376 group->RemoveAllInvites();
2378 delete group;
2382 void Player::RemoveFromGroup(Group* group, uint64 guid)
2384 if(group)
2386 if (group->RemoveMember(guid, 0) <= 1)
2388 // group->Disband(); already disbanded in RemoveMember
2389 sObjectMgr.RemoveGroup(group);
2390 delete group;
2391 // removemember sets the player's group pointer to NULL
2396 void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 RestXP)
2398 WorldPacket data(SMSG_LOG_XPGAIN, 21);
2399 data << uint64(victim ? victim->GetGUID() : 0); // guid
2400 data << uint32(GivenXP+RestXP); // given experience
2401 data << uint8(victim ? 0 : 1); // 00-kill_xp type, 01-non_kill_xp type
2402 if(victim)
2404 data << uint32(GivenXP); // experience without rested bonus
2405 data << float(1); // 1 - none 0 - 100% group bonus output
2407 data << uint8(0); // new 2.4.0
2408 GetSession()->SendPacket(&data);
2411 void Player::GiveXP(uint32 xp, Unit* victim)
2413 if ( xp < 1 )
2414 return;
2416 if(!isAlive())
2417 return;
2419 uint32 level = getLevel();
2421 // XP to money conversion processed in Player::RewardQuest
2422 if(level >= sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
2423 return;
2425 if(victim)
2427 // handle SPELL_AURA_MOD_KILL_XP_PCT auras
2428 Unit::AuraList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_KILL_XP_PCT);
2429 for(Unit::AuraList::const_iterator i = ModXPPctAuras.begin();i != ModXPPctAuras.end(); ++i)
2430 xp = uint32(xp*(1.0f + (*i)->GetModifier()->m_amount / 100.0f));
2432 else
2434 // handle SPELL_AURA_MOD_QUEST_XP_PCT auras
2435 Unit::AuraList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_QUEST_XP_PCT);
2436 for(Unit::AuraList::const_iterator i = ModXPPctAuras.begin();i != ModXPPctAuras.end(); ++i)
2437 xp = uint32(xp*(1.0f + (*i)->GetModifier()->m_amount / 100.0f));
2440 // XP resting bonus for kill
2441 uint32 rested_bonus_xp = victim ? GetXPRestBonus(xp) : 0;
2443 SendLogXPGain(xp,victim,rested_bonus_xp);
2445 uint32 curXP = GetUInt32Value(PLAYER_XP);
2446 uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
2447 uint32 newXP = curXP + xp + rested_bonus_xp;
2449 while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL) )
2451 newXP -= nextLvlXP;
2453 if ( level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL) )
2454 GiveLevel(level + 1);
2456 level = getLevel();
2457 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
2460 SetUInt32Value(PLAYER_XP, newXP);
2463 // Update player to next level
2464 // Current player experience not update (must be update by caller)
2465 void Player::GiveLevel(uint32 level)
2467 if ( level == getLevel() )
2468 return;
2470 PlayerLevelInfo info;
2471 sObjectMgr.GetPlayerLevelInfo(getRace(),getClass(),level,&info);
2473 PlayerClassLevelInfo classInfo;
2474 sObjectMgr.GetPlayerClassLevelInfo(getClass(),level,&classInfo);
2476 // send levelup info to client
2477 WorldPacket data(SMSG_LEVELUP_INFO, (4+4+MAX_POWERS*4+MAX_STATS*4));
2478 data << uint32(level);
2479 data << uint32(int32(classInfo.basehealth) - int32(GetCreateHealth()));
2480 // for(int i = 0; i < MAX_POWERS; ++i) // Powers loop (0-6)
2481 data << uint32(int32(classInfo.basemana) - int32(GetCreateMana()));
2482 data << uint32(0);
2483 data << uint32(0);
2484 data << uint32(0);
2485 data << uint32(0);
2486 data << uint32(0);
2487 data << uint32(0);
2488 // end for
2489 for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) // Stats loop (0-4)
2490 data << uint32(int32(info.stats[i]) - GetCreateStat(Stats(i)));
2492 GetSession()->SendPacket(&data);
2494 SetUInt32Value(PLAYER_NEXT_LEVEL_XP, sObjectMgr.GetXPForLevel(level));
2496 //update level, max level of skills
2497 m_Played_time[PLAYED_TIME_LEVEL] = 0; // Level Played Time reset
2499 _ApplyAllLevelScaleItemMods(false);
2501 SetLevel(level);
2503 UpdateSkillsForLevel ();
2505 // save base values (bonuses already included in stored stats
2506 for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
2507 SetCreateStat(Stats(i), info.stats[i]);
2509 SetCreateHealth(classInfo.basehealth);
2510 SetCreateMana(classInfo.basemana);
2512 InitTalentForLevel();
2513 InitTaxiNodesForLevel();
2514 InitGlyphsForLevel();
2516 UpdateAllStats();
2518 // set current level health and mana/energy to maximum after applying all mods.
2519 SetHealth(GetMaxHealth());
2520 SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
2521 SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
2522 if(GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
2523 SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
2524 SetPower(POWER_FOCUS, 0);
2525 SetPower(POWER_HAPPINESS, 0);
2527 _ApplyAllLevelScaleItemMods(true);
2529 // update level to hunter/summon pet
2530 if (Pet* pet = GetPet())
2531 pet->SynchronizeLevelWithOwner();
2533 if (MailLevelReward const* mailReward = sObjectMgr.GetMailLevelReward(level,getRaceMask()))
2534 MailDraft(mailReward->mailTemplateId).SendMailTo(this,MailSender(MAIL_CREATURE,mailReward->senderEntry));
2536 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL);
2539 void Player::UpdateFreeTalentPoints(bool resetIfNeed)
2541 uint32 level = getLevel();
2542 // talents base at level diff ( talents = level - 9 but some can be used already)
2543 if (level < 10)
2545 // Remove all talent points
2546 if (m_usedTalentCount > 0) // Free any used talents
2548 if (resetIfNeed)
2549 resetTalents(true);
2550 SetFreeTalentPoints(0);
2553 else
2555 uint32 talentPointsForLevel = CalculateTalentsPoints();
2557 // if used more that have then reset
2558 if (m_usedTalentCount > talentPointsForLevel)
2560 if (resetIfNeed && GetSession()->GetSecurity() < SEC_ADMINISTRATOR)
2561 resetTalents(true);
2562 else
2563 SetFreeTalentPoints(0);
2565 // else update amount of free points
2566 else
2567 SetFreeTalentPoints(talentPointsForLevel-m_usedTalentCount);
2571 void Player::InitTalentForLevel()
2573 UpdateFreeTalentPoints();
2575 if (!GetSession()->PlayerLoading())
2576 SendTalentsInfoData(false); // update at client
2579 void Player::InitStatsForLevel(bool reapplyMods)
2581 if(reapplyMods) //reapply stats values only on .reset stats (level) command
2582 _RemoveAllStatBonuses();
2584 PlayerClassLevelInfo classInfo;
2585 sObjectMgr.GetPlayerClassLevelInfo(getClass(),getLevel(),&classInfo);
2587 PlayerLevelInfo info;
2588 sObjectMgr.GetPlayerLevelInfo(getRace(),getClass(),getLevel(),&info);
2590 SetUInt32Value(PLAYER_FIELD_MAX_LEVEL, sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL) );
2591 SetUInt32Value(PLAYER_NEXT_LEVEL_XP, sObjectMgr.GetXPForLevel(getLevel()));
2593 // reset before any aura state sources (health set/aura apply)
2594 SetUInt32Value(UNIT_FIELD_AURASTATE, 0);
2596 UpdateSkillsForLevel ();
2598 // set default cast time multiplier
2599 SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
2601 // reset size before reapply auras
2602 SetFloatValue(OBJECT_FIELD_SCALE_X,1.0f);
2604 // save base values (bonuses already included in stored stats
2605 for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
2606 SetCreateStat(Stats(i), info.stats[i]);
2608 for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
2609 SetStat(Stats(i), info.stats[i]);
2611 SetCreateHealth(classInfo.basehealth);
2613 //set create powers
2614 SetCreateMana(classInfo.basemana);
2616 SetArmor(int32(m_createStats[STAT_AGILITY]*2));
2618 InitStatBuffMods();
2620 //reset rating fields values
2621 for(uint16 index = PLAYER_FIELD_COMBAT_RATING_1; index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index)
2622 SetUInt32Value(index, 0);
2624 SetUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS,0);
2625 for (int i = 0; i < MAX_SPELL_SCHOOL; ++i)
2627 SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i, 0);
2628 SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, 0);
2629 SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+i, 1.00f);
2632 //reset attack power, damage and attack speed fields
2633 SetFloatValue(UNIT_FIELD_BASEATTACKTIME, 2000.0f );
2634 SetFloatValue(UNIT_FIELD_BASEATTACKTIME + 1, 2000.0f ); // offhand attack time
2635 SetFloatValue(UNIT_FIELD_RANGEDATTACKTIME, 2000.0f );
2637 SetFloatValue(UNIT_FIELD_MINDAMAGE, 0.0f );
2638 SetFloatValue(UNIT_FIELD_MAXDAMAGE, 0.0f );
2639 SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, 0.0f );
2640 SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, 0.0f );
2641 SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, 0.0f );
2642 SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, 0.0f );
2644 SetInt32Value(UNIT_FIELD_ATTACK_POWER, 0 );
2645 SetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, 0 );
2646 SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER,0.0f);
2647 SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0 );
2648 SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MODS,0 );
2649 SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER,0.0f);
2651 // Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
2652 SetFloatValue(PLAYER_CRIT_PERCENTAGE,0.0f);
2653 SetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE,0.0f);
2654 SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE,0.0f);
2656 // Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
2657 for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
2658 SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1+i, 0.0f);
2660 SetFloatValue(PLAYER_PARRY_PERCENTAGE, 0.0f);
2661 SetFloatValue(PLAYER_BLOCK_PERCENTAGE, 0.0f);
2662 SetUInt32Value(PLAYER_SHIELD_BLOCK, 0);
2664 // Dodge percentage
2665 SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f);
2667 // set armor (resistance 0) to original value (create_agility*2)
2668 SetArmor(int32(m_createStats[STAT_AGILITY]*2));
2669 SetResistanceBuffMods(SpellSchools(0), true, 0.0f);
2670 SetResistanceBuffMods(SpellSchools(0), false, 0.0f);
2671 // set other resistance to original value (0)
2672 for (int i = 1; i < MAX_SPELL_SCHOOL; ++i)
2674 SetResistance(SpellSchools(i), 0);
2675 SetResistanceBuffMods(SpellSchools(i), true, 0.0f);
2676 SetResistanceBuffMods(SpellSchools(i), false, 0.0f);
2679 SetUInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,0);
2680 SetUInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,0);
2681 for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
2683 SetUInt32Value(UNIT_FIELD_POWER_COST_MODIFIER+i,0);
2684 SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,0.0f);
2686 // Reset no reagent cost field
2687 for(int i = 0; i < 3; ++i)
2688 SetUInt32Value(PLAYER_NO_REAGENT_COST_1 + i, 0);
2689 // Init data for form but skip reapply item mods for form
2690 InitDataForForm(reapplyMods);
2692 // save new stats
2693 for (int i = POWER_MANA; i < MAX_POWERS; ++i)
2694 SetMaxPower(Powers(i), GetCreatePowers(Powers(i)));
2696 SetMaxHealth(classInfo.basehealth); // stamina bonus will applied later
2698 // cleanup mounted state (it will set correctly at aura loading if player saved at mount.
2699 SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
2701 // cleanup unit flags (will be re-applied if need at aura load).
2702 RemoveFlag( UNIT_FIELD_FLAGS,
2703 UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 |
2704 UNIT_FLAG_OOC_NOT_ATTACKABLE | UNIT_FLAG_PASSIVE | UNIT_FLAG_LOOTING |
2705 UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED |
2706 UNIT_FLAG_STUNNED | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED |
2707 UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_NOT_SELECTABLE |
2708 UNIT_FLAG_SKINNABLE | UNIT_FLAG_MOUNT | UNIT_FLAG_TAXI_FLIGHT );
2709 SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE ); // must be set
2711 SetFlag(UNIT_FIELD_FLAGS_2,UNIT_FLAG2_REGENERATE_POWER);// must be set
2713 // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example.
2714 RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST);
2716 RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes
2717 RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SANCTUARY);
2719 // restore if need some important flags
2720 SetUInt32Value(PLAYER_FIELD_BYTES2, 0 ); // flags empty by default
2722 if(reapplyMods) //reapply stats values only on .reset stats (level) command
2723 _ApplyAllStatBonuses();
2725 // set current level health and mana/energy to maximum after applying all mods.
2726 SetHealth(GetMaxHealth());
2727 SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
2728 SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
2729 if(GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
2730 SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
2731 SetPower(POWER_FOCUS, 0);
2732 SetPower(POWER_HAPPINESS, 0);
2733 SetPower(POWER_RUNIC_POWER, 0);
2735 // update level to hunter/summon pet
2736 if (Pet* pet = GetPet())
2737 pet->SynchronizeLevelWithOwner();
2740 void Player::SendInitialSpells()
2742 time_t curTime = time(NULL);
2743 time_t infTime = curTime + infinityCooldownDelayCheck;
2745 uint16 spellCount = 0;
2747 WorldPacket data(SMSG_INITIAL_SPELLS, (1+2+4*m_spells.size()+2+m_spellCooldowns.size()*(2+2+2+4+4)));
2748 data << uint8(0);
2750 size_t countPos = data.wpos();
2751 data << uint16(spellCount); // spell count placeholder
2753 for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
2755 if(itr->second.state == PLAYERSPELL_REMOVED)
2756 continue;
2758 if(!itr->second.active || itr->second.disabled)
2759 continue;
2761 data << uint32(itr->first);
2762 data << uint16(0); // it's not slot id
2764 spellCount +=1;
2767 data.put<uint16>(countPos,spellCount); // write real count value
2769 uint16 spellCooldowns = m_spellCooldowns.size();
2770 data << uint16(spellCooldowns);
2771 for(SpellCooldowns::const_iterator itr=m_spellCooldowns.begin(); itr!=m_spellCooldowns.end(); ++itr)
2773 SpellEntry const *sEntry = sSpellStore.LookupEntry(itr->first);
2774 if(!sEntry)
2775 continue;
2777 data << uint32(itr->first);
2779 data << uint16(itr->second.itemid); // cast item id
2780 data << uint16(sEntry->Category); // spell category
2782 // send infinity cooldown in special format
2783 if(itr->second.end >= infTime)
2785 data << uint32(1); // cooldown
2786 data << uint32(0x80000000); // category cooldown
2787 continue;
2790 time_t cooldown = itr->second.end > curTime ? (itr->second.end-curTime)*IN_MILLISECONDS : 0;
2792 if(sEntry->Category) // may be wrong, but anyway better than nothing...
2794 data << uint32(0); // cooldown
2795 data << uint32(cooldown); // category cooldown
2797 else
2799 data << uint32(cooldown); // cooldown
2800 data << uint32(0); // category cooldown
2804 GetSession()->SendPacket(&data);
2806 DETAIL_LOG( "CHARACTER: Sent Initial Spells" );
2809 void Player::RemoveMail(uint32 id)
2811 for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();++itr)
2813 if ((*itr)->messageID == id)
2815 //do not delete item, because Player::removeMail() is called when returning mail to sender.
2816 m_mail.erase(itr);
2817 return;
2822 void Player::SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError, uint32 item_guid, uint32 item_count)
2824 WorldPacket data(SMSG_SEND_MAIL_RESULT, (4+4+4+(mailError == MAIL_ERR_EQUIP_ERROR?4:(mailAction == MAIL_ITEM_TAKEN?4+4:0))));
2825 data << (uint32) mailId;
2826 data << (uint32) mailAction;
2827 data << (uint32) mailError;
2828 if ( mailError == MAIL_ERR_EQUIP_ERROR )
2829 data << (uint32) equipError;
2830 else if( mailAction == MAIL_ITEM_TAKEN )
2832 data << (uint32) item_guid; // item guid low?
2833 data << (uint32) item_count; // item count?
2835 GetSession()->SendPacket(&data);
2838 void Player::SendNewMail()
2840 // deliver undelivered mail
2841 WorldPacket data(SMSG_RECEIVED_MAIL, 4);
2842 data << (uint32) 0;
2843 GetSession()->SendPacket(&data);
2846 void Player::UpdateNextMailTimeAndUnreads()
2848 // calculate next delivery time (min. from non-delivered mails
2849 // and recalculate unReadMail
2850 time_t cTime = time(NULL);
2851 m_nextMailDelivereTime = 0;
2852 unReadMails = 0;
2853 for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
2855 if((*itr)->deliver_time > cTime)
2857 if(!m_nextMailDelivereTime || m_nextMailDelivereTime > (*itr)->deliver_time)
2858 m_nextMailDelivereTime = (*itr)->deliver_time;
2860 else if(((*itr)->checked & MAIL_CHECK_MASK_READ) == 0)
2861 ++unReadMails;
2865 void Player::AddNewMailDeliverTime(time_t deliver_time)
2867 if(deliver_time <= time(NULL)) // ready now
2869 ++unReadMails;
2870 SendNewMail();
2872 else // not ready and no have ready mails
2874 if(!m_nextMailDelivereTime || m_nextMailDelivereTime > deliver_time)
2875 m_nextMailDelivereTime = deliver_time;
2879 bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled)
2881 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
2882 if (!spellInfo)
2884 // do character spell book cleanup (all characters)
2885 if(!IsInWorld() && !learning) // spell load case
2887 sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.",spell_id);
2888 CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id);
2890 else
2891 sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
2893 return false;
2896 if(!SpellMgr::IsSpellValid(spellInfo,this,false))
2898 // do character spell book cleanup (all characters)
2899 if(!IsInWorld() && !learning) // spell load case
2901 sLog.outError("Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.",spell_id);
2902 CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id);
2904 else
2905 sLog.outError("Player::addSpell: Broken spell #%u learning not allowed.",spell_id);
2907 return false;
2910 PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
2912 bool dependent_set = false;
2913 bool disabled_case = false;
2914 bool superceded_old = false;
2916 PlayerSpellMap::iterator itr = m_spells.find(spell_id);
2917 if (itr != m_spells.end())
2919 uint32 next_active_spell_id = 0;
2920 // fix activate state for non-stackable low rank (and find next spell for !active case)
2921 if(!SpellMgr::canStackSpellRanks(spellInfo) && sSpellMgr.GetSpellRank(spellInfo->Id) != 0)
2923 SpellChainMapNext const& nextMap = sSpellMgr.GetSpellChainNext();
2924 for(SpellChainMapNext::const_iterator next_itr = nextMap.lower_bound(spell_id); next_itr != nextMap.upper_bound(spell_id); ++next_itr)
2926 if(HasSpell(next_itr->second))
2928 // high rank already known so this must !active
2929 active = false;
2930 next_active_spell_id = next_itr->second;
2931 break;
2936 // not do anything if already known in expected state
2937 if(itr->second.state != PLAYERSPELL_REMOVED && itr->second.active == active &&
2938 itr->second.dependent == dependent && itr->second.disabled == disabled)
2940 if(!IsInWorld() && !learning) // explicitly load from DB and then exist in it already and set correctly
2941 itr->second.state = PLAYERSPELL_UNCHANGED;
2943 return false;
2946 // dependent spell known as not dependent, overwrite state
2947 if (itr->second.state != PLAYERSPELL_REMOVED && !itr->second.dependent && dependent)
2949 itr->second.dependent = dependent;
2950 if (itr->second.state != PLAYERSPELL_NEW)
2951 itr->second.state = PLAYERSPELL_CHANGED;
2952 dependent_set = true;
2955 // update active state for known spell
2956 if(itr->second.active != active && itr->second.state != PLAYERSPELL_REMOVED && !itr->second.disabled)
2958 itr->second.active = active;
2960 if(!IsInWorld() && !learning && !dependent_set) // explicitly load from DB and then exist in it already and set correctly
2961 itr->second.state = PLAYERSPELL_UNCHANGED;
2962 else if(itr->second.state != PLAYERSPELL_NEW)
2963 itr->second.state = PLAYERSPELL_CHANGED;
2965 if(active)
2967 if (IsPassiveSpell(spell_id) && IsNeedCastPassiveSpellAtLearn(spellInfo))
2968 CastSpell (this, spell_id, true);
2970 else if(IsInWorld())
2972 if(next_active_spell_id)
2974 // update spell ranks in spellbook and action bar
2975 WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
2976 data << uint32(spell_id);
2977 data << uint32(next_active_spell_id);
2978 GetSession()->SendPacket( &data );
2980 else
2982 WorldPacket data(SMSG_REMOVED_SPELL, 4);
2983 data << uint32(spell_id);
2984 GetSession()->SendPacket(&data);
2988 return active; // learn (show in spell book if active now)
2991 if(itr->second.disabled != disabled && itr->second.state != PLAYERSPELL_REMOVED)
2993 if(itr->second.state != PLAYERSPELL_NEW)
2994 itr->second.state = PLAYERSPELL_CHANGED;
2995 itr->second.disabled = disabled;
2997 if(disabled)
2998 return false;
3000 disabled_case = true;
3002 else switch(itr->second.state)
3004 case PLAYERSPELL_UNCHANGED: // known saved spell
3005 return false;
3006 case PLAYERSPELL_REMOVED: // re-learning removed not saved spell
3008 m_spells.erase(itr);
3009 state = PLAYERSPELL_CHANGED;
3010 break; // need re-add
3012 default: // known not saved yet spell (new or modified)
3014 // can be in case spell loading but learned at some previous spell loading
3015 if(!IsInWorld() && !learning && !dependent_set)
3016 itr->second.state = PLAYERSPELL_UNCHANGED;
3018 return false;
3023 TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id);
3025 if(!disabled_case) // skip new spell adding if spell already known (disabled spells case)
3027 // talent: unlearn all other talent ranks (high and low)
3028 if (talentPos)
3030 if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
3032 for(int i=0; i < MAX_TALENT_RANK; ++i)
3034 // skip learning spell and no rank spell case
3035 uint32 rankSpellId = talentInfo->RankID[i];
3036 if(!rankSpellId || rankSpellId == spell_id)
3037 continue;
3039 removeSpell(rankSpellId, false, false);
3043 // non talent spell: learn low ranks (recursive call)
3044 else if(uint32 prev_spell = sSpellMgr.GetPrevSpellInChain(spell_id))
3046 if(!IsInWorld() || disabled) // at spells loading, no output, but allow save
3047 addSpell(prev_spell, active, true, true, disabled);
3048 else // at normal learning
3049 learnSpell(prev_spell, true);
3052 PlayerSpell newspell;
3053 newspell.state = state;
3054 newspell.active = active;
3055 newspell.dependent = dependent;
3056 newspell.disabled = disabled;
3058 // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
3059 if(newspell.active && !newspell.disabled && !SpellMgr::canStackSpellRanks(spellInfo) && sSpellMgr.GetSpellRank(spellInfo->Id) != 0)
3061 for( PlayerSpellMap::iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2 )
3063 if(itr2->second.state == PLAYERSPELL_REMOVED) continue;
3064 SpellEntry const *i_spellInfo = sSpellStore.LookupEntry(itr2->first);
3065 if(!i_spellInfo) continue;
3067 if( sSpellMgr.IsRankSpellDueToSpell(spellInfo, itr2->first) )
3069 if(itr2->second.active)
3071 if(sSpellMgr.IsHighRankOfSpell(spell_id,itr2->first))
3073 if(IsInWorld()) // not send spell (re-/over-)learn packets at loading
3075 WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
3076 data << uint32(itr2->first);
3077 data << uint32(spell_id);
3078 GetSession()->SendPacket( &data );
3081 // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
3082 itr2->second.active = false;
3083 if(itr2->second.state != PLAYERSPELL_NEW)
3084 itr2->second.state = PLAYERSPELL_CHANGED;
3085 superceded_old = true; // new spell replace old in action bars and spell book.
3087 else if(sSpellMgr.IsHighRankOfSpell(itr2->first,spell_id))
3089 if(IsInWorld()) // not send spell (re-/over-)learn packets at loading
3091 WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
3092 data << uint32(spell_id);
3093 data << uint32(itr2->first);
3094 GetSession()->SendPacket( &data );
3097 // mark new spell as disable (not learned yet for client and will not learned)
3098 newspell.active = false;
3099 if(newspell.state != PLAYERSPELL_NEW)
3100 newspell.state = PLAYERSPELL_CHANGED;
3107 m_spells[spell_id] = newspell;
3109 // return false if spell disabled
3110 if (newspell.disabled)
3111 return false;
3114 if (talentPos)
3116 // update talent map
3117 PlayerTalentMap::iterator iter = m_talents[m_activeSpec].find(talentPos->talent_id);
3118 if (iter != m_talents[m_activeSpec].end())
3120 // check if ranks different or removed
3121 if ((*iter).second.state == PLAYERSPELL_REMOVED || talentPos->rank != (*iter).second.currentRank)
3123 (*iter).second.currentRank = talentPos->rank;
3125 if ((*iter).second.state != PLAYERSPELL_NEW)
3126 (*iter).second.state = PLAYERSPELL_CHANGED;
3129 else
3131 PlayerTalent talent;
3132 talent.currentRank = talentPos->rank;
3133 talent.m_talentEntry = sTalentStore.LookupEntry(talentPos->talent_id);
3134 talent.state = IsInWorld() ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
3135 m_talents[m_activeSpec][talentPos->talent_id] = talent;
3138 // update used talent points count
3139 m_usedTalentCount += GetTalentSpellCost(talentPos);
3140 UpdateFreeTalentPoints(false);
3143 // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
3144 if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
3146 if(sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id))
3147 SetFreePrimaryProfessions(freeProfs-1);
3150 // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
3151 // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
3152 if (talentPos && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_LEARN_SPELL))
3154 // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
3155 CastSpell(this, spell_id, true);
3157 // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
3158 else if (IsPassiveSpell(spell_id))
3160 if (IsNeedCastPassiveSpellAtLearn(spellInfo))
3161 CastSpell(this, spell_id, true);
3163 else if (IsSpellHaveEffect(spellInfo,SPELL_EFFECT_SKILL_STEP))
3165 CastSpell(this, spell_id, true);
3166 return false;
3169 // add dependent skills
3170 uint16 maxskill = GetMaxSkillValueForLevel();
3172 SpellLearnSkillNode const* spellLearnSkill = sSpellMgr.GetSpellLearnSkill(spell_id);
3174 SkillLineAbilityMapBounds skill_bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spell_id);
3176 if (spellLearnSkill)
3178 uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
3179 uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
3181 if (skill_value < spellLearnSkill->value)
3182 skill_value = spellLearnSkill->value;
3184 uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue;
3186 if (skill_max_value < new_skill_max_value)
3187 skill_max_value = new_skill_max_value;
3189 SetSkill(spellLearnSkill->skill, skill_value, skill_max_value, spellLearnSkill->step);
3191 else
3193 // not ranked skills
3194 for(SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
3196 SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
3197 if (!pSkill)
3198 continue;
3200 if (HasSkill(pSkill->id))
3201 continue;
3203 if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
3204 // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
3205 ((pSkill->id==SKILL_LOCKPICKING || pSkill->id==SKILL_RUNEFORGING) && _spell_idx->second->max_value==0))
3207 switch(GetSkillRangeType(pSkill, _spell_idx->second->racemask != 0))
3209 case SKILL_RANGE_LANGUAGE:
3210 SetSkill(pSkill->id, 300, 300 );
3211 break;
3212 case SKILL_RANGE_LEVEL:
3213 SetSkill(pSkill->id, 1, GetMaxSkillValueForLevel() );
3214 break;
3215 case SKILL_RANGE_MONO:
3216 SetSkill(pSkill->id, 1, 1 );
3217 break;
3218 default:
3219 break;
3225 // learn dependent spells
3226 SpellLearnSpellMapBounds spell_bounds = sSpellMgr.GetSpellLearnSpellMapBounds(spell_id);
3228 for(SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
3230 if (!itr2->second.autoLearned)
3232 if (!IsInWorld() || !itr2->second.active) // at spells loading, no output, but allow save
3233 addSpell(itr2->second.spell,itr2->second.active,true,true,false);
3234 else // at normal learning
3235 learnSpell(itr2->second.spell, true);
3239 if (!GetSession()->PlayerLoading())
3241 // not ranked skills
3242 for(SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
3244 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE,_spell_idx->second->skillId);
3245 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS,_spell_idx->second->skillId);
3248 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL,spell_id);
3251 // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell
3252 return active && !disabled && !superceded_old;
3255 bool Player::IsNeedCastPassiveSpellAtLearn(SpellEntry const* spellInfo) const
3257 // note: form passives activated with shapeshift spells be implemented by HandleShapeshiftBoosts instead of spell_learn_spell
3258 // talent dependent passives activated at form apply have proper stance data
3259 bool need_cast = (!spellInfo->Stances || (m_form != 0 && (spellInfo->Stances & (1<<(m_form-1)))));
3261 //Check CasterAuraStates
3262 return need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState)));
3265 void Player::learnSpell(uint32 spell_id, bool dependent)
3267 PlayerSpellMap::iterator itr = m_spells.find(spell_id);
3269 bool disabled = (itr != m_spells.end()) ? itr->second.disabled : false;
3270 bool active = disabled ? itr->second.active : true;
3272 bool learning = addSpell(spell_id, active, true, dependent, false);
3274 // learn all disabled higher ranks (recursive)
3275 if(disabled)
3277 SpellChainMapNext const& nextMap = sSpellMgr.GetSpellChainNext();
3278 for(SpellChainMapNext::const_iterator i = nextMap.lower_bound(spell_id); i != nextMap.upper_bound(spell_id); ++i)
3280 PlayerSpellMap::iterator iter = m_spells.find(i->second);
3281 if (iter != m_spells.end() && iter->second.disabled)
3282 learnSpell(i->second, false);
3286 // prevent duplicated entires in spell book, also not send if not in world (loading)
3287 if(!learning || !IsInWorld ())
3288 return;
3290 WorldPacket data(SMSG_LEARNED_SPELL, 6);
3291 data << uint32(spell_id);
3292 data << uint16(0); // 3.3.3 unk
3293 GetSession()->SendPacket(&data);
3296 void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bool sendUpdate)
3298 PlayerSpellMap::iterator itr = m_spells.find(spell_id);
3299 if (itr == m_spells.end())
3300 return;
3302 if (itr->second.state == PLAYERSPELL_REMOVED || (disabled && itr->second.disabled))
3303 return;
3305 // unlearn non talent higher ranks (recursive)
3306 SpellChainMapNext const& nextMap = sSpellMgr.GetSpellChainNext();
3307 for(SpellChainMapNext::const_iterator itr2 = nextMap.lower_bound(spell_id); itr2 != nextMap.upper_bound(spell_id); ++itr2)
3308 if(HasSpell(itr2->second) && !GetTalentSpellPos(itr2->second))
3309 removeSpell(itr2->second, disabled, false);
3311 // re-search, it can be corrupted in prev loop
3312 itr = m_spells.find(spell_id);
3313 if (itr == m_spells.end() || itr->second.state == PLAYERSPELL_REMOVED)
3314 return; // already unleared
3316 bool cur_active = itr->second.active;
3317 bool cur_dependent = itr->second.dependent;
3319 if (disabled)
3321 itr->second.disabled = disabled;
3322 if(itr->second.state != PLAYERSPELL_NEW)
3323 itr->second.state = PLAYERSPELL_CHANGED;
3325 else
3327 if(itr->second.state == PLAYERSPELL_NEW)
3328 m_spells.erase(itr);
3329 else
3330 itr->second.state = PLAYERSPELL_REMOVED;
3333 RemoveAurasDueToSpell(spell_id);
3335 // remove pet auras
3336 for(int i = 0; i < MAX_EFFECT_INDEX; ++i)
3337 if(PetAura const* petSpell = sSpellMgr.GetPetAura(spell_id, SpellEffectIndex(i)))
3338 RemovePetAura(petSpell);
3340 TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id);
3341 if (talentPos)
3343 // update talent map
3344 PlayerTalentMap::iterator iter = m_talents[m_activeSpec].find(talentPos->talent_id);
3345 if (iter != m_talents[m_activeSpec].end())
3347 if ((*iter).second.state != PLAYERSPELL_NEW)
3348 (*iter).second.state = PLAYERSPELL_REMOVED;
3349 else
3350 m_talents[m_activeSpec].erase(iter);
3352 else
3353 sLog.outError("removeSpell: Player (GUID: %u) has talent spell (id: %u) but doesn't have talent",GetGUIDLow(), spell_id );
3355 // free talent points
3356 uint32 talentCosts = GetTalentSpellCost(talentPos);
3358 if(talentCosts < m_usedTalentCount)
3359 m_usedTalentCount -= talentCosts;
3360 else
3361 m_usedTalentCount = 0;
3363 UpdateFreeTalentPoints(false);
3366 // update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
3367 if(sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id))
3369 uint32 freeProfs = GetFreePrimaryProfessionPoints()+1;
3370 if(freeProfs <= sWorld.getConfig(CONFIG_UINT32_MAX_PRIMARY_TRADE_SKILL))
3371 SetFreePrimaryProfessions(freeProfs);
3374 // remove dependent skill
3375 SpellLearnSkillNode const* spellLearnSkill = sSpellMgr.GetSpellLearnSkill(spell_id);
3376 if(spellLearnSkill)
3378 uint32 prev_spell = sSpellMgr.GetPrevSpellInChain(spell_id);
3379 if(!prev_spell) // first rank, remove skill
3380 SetSkill(spellLearnSkill->skill, 0, 0);
3381 else
3383 // search prev. skill setting by spell ranks chain
3384 SpellLearnSkillNode const* prevSkill = sSpellMgr.GetSpellLearnSkill(prev_spell);
3385 while(!prevSkill && prev_spell)
3387 prev_spell = sSpellMgr.GetPrevSpellInChain(prev_spell);
3388 prevSkill = sSpellMgr.GetSpellLearnSkill(sSpellMgr.GetFirstSpellInChain(prev_spell));
3391 if (!prevSkill) // not found prev skill setting, remove skill
3392 SetSkill(spellLearnSkill->skill, 0, 0);
3393 else // set to prev. skill setting values
3395 uint32 skill_value = GetPureSkillValue(prevSkill->skill);
3396 uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill);
3398 if (skill_value > prevSkill->value)
3399 skill_value = prevSkill->value;
3401 uint32 new_skill_max_value = prevSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : prevSkill->maxvalue;
3403 if (skill_max_value > new_skill_max_value)
3404 skill_max_value = new_skill_max_value;
3406 SetSkill(prevSkill->skill, skill_value, skill_max_value, prevSkill->step);
3411 else
3413 // not ranked skills
3414 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spell_id);
3416 for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
3418 SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
3419 if (!pSkill)
3420 continue;
3422 if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL &&
3423 pSkill->categoryId != SKILL_CATEGORY_CLASS ||// not unlearn class skills (spellbook/talent pages)
3424 // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
3425 ((pSkill->id == SKILL_LOCKPICKING || pSkill->id == SKILL_RUNEFORGING) && _spell_idx->second->max_value == 0))
3427 // not reset skills for professions and racial abilities
3428 if ((pSkill->categoryId == SKILL_CATEGORY_SECONDARY || pSkill->categoryId == SKILL_CATEGORY_PROFESSION) &&
3429 (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask != 0))
3430 continue;
3432 SetSkill(pSkill->id, 0, 0);
3437 // remove dependent spells
3438 SpellLearnSpellMapBounds spell_bounds = sSpellMgr.GetSpellLearnSpellMapBounds(spell_id);
3440 for(SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
3441 removeSpell(itr2->second.spell, disabled);
3443 // activate lesser rank in spellbook/action bar, and cast it if need
3444 bool prev_activate = false;
3446 if (uint32 prev_id = sSpellMgr.GetPrevSpellInChain (spell_id))
3448 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
3450 // if talent then lesser rank also talent and need learn
3451 if (talentPos)
3453 if(learn_low_rank)
3454 learnSpell(prev_id, false);
3456 // if ranked non-stackable spell: need activate lesser rank and update dendence state
3457 else if (cur_active && !SpellMgr::canStackSpellRanks(spellInfo) && sSpellMgr.GetSpellRank(spellInfo->Id) != 0)
3459 // need manually update dependence state (learn spell ignore like attempts)
3460 PlayerSpellMap::iterator prev_itr = m_spells.find(prev_id);
3461 if (prev_itr != m_spells.end())
3463 if (prev_itr->second.dependent != cur_dependent)
3465 prev_itr->second.dependent = cur_dependent;
3466 if (prev_itr->second.state != PLAYERSPELL_NEW)
3467 prev_itr->second.state = PLAYERSPELL_CHANGED;
3470 // now re-learn if need re-activate
3471 if (cur_active && !prev_itr->second.active && learn_low_rank)
3473 if (addSpell(prev_id, true, false, prev_itr->second.dependent, prev_itr->second.disabled))
3475 // downgrade spell ranks in spellbook and action bar
3476 WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
3477 data << uint32(spell_id);
3478 data << uint32(prev_id);
3479 GetSession()->SendPacket( &data );
3480 prev_activate = true;
3487 if (m_canTitanGrip)
3489 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
3490 if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_TITAN_GRIP))
3492 m_canTitanGrip = false;
3493 if(sWorld.getConfig(CONFIG_BOOL_OFFHAND_CHECK_AT_TALENTS_RESET))
3494 AutoUnequipOffhandIfNeed();
3498 // remove from spell book if not replaced by lesser rank
3499 if (!prev_activate && sendUpdate)
3501 WorldPacket data(SMSG_REMOVED_SPELL, 4);
3502 data << uint32(spell_id);
3503 GetSession()->SendPacket(&data);
3507 void Player::RemoveSpellCooldown( uint32 spell_id, bool update /* = false */ )
3509 m_spellCooldowns.erase(spell_id);
3511 if(update)
3512 SendClearCooldown(spell_id, this);
3515 void Player::RemoveSpellCategoryCooldown(uint32 cat, bool update /* = false */)
3517 SpellCategoryStore::const_iterator ct = sSpellCategoryStore.find(cat);
3518 if (ct == sSpellCategoryStore.end())
3519 return;
3521 const SpellCategorySet& ct_set = ct->second;
3522 for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end();)
3524 if (ct_set.find(i->first) != ct_set.end())
3525 RemoveSpellCooldown((i++)->first, update);
3526 else
3527 ++i;
3531 void Player::RemoveArenaSpellCooldowns()
3533 // remove cooldowns on spells that has < 15 min CD
3534 SpellCooldowns::iterator itr, next;
3535 // iterate spell cooldowns
3536 for(itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); itr = next)
3538 next = itr;
3539 ++next;
3540 SpellEntry const * entry = sSpellStore.LookupEntry(itr->first);
3541 // check if spellentry is present and if the cooldown is less than 15 mins
3542 if( entry &&
3543 entry->RecoveryTime <= 15 * MINUTE * IN_MILLISECONDS &&
3544 entry->CategoryRecoveryTime <= 15 * MINUTE * IN_MILLISECONDS )
3546 // remove & notify
3547 RemoveSpellCooldown(itr->first, true);
3552 void Player::RemoveAllSpellCooldown()
3554 if(!m_spellCooldowns.empty())
3556 for(SpellCooldowns::const_iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); ++itr)
3557 SendClearCooldown(itr->first, this);
3559 m_spellCooldowns.clear();
3563 void Player::_LoadSpellCooldowns(QueryResult *result)
3565 // some cooldowns can be already set at aura loading...
3567 //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'",GetGUIDLow());
3569 if(result)
3571 time_t curTime = time(NULL);
3575 Field *fields = result->Fetch();
3577 uint32 spell_id = fields[0].GetUInt32();
3578 uint32 item_id = fields[1].GetUInt32();
3579 time_t db_time = (time_t)fields[2].GetUInt64();
3581 if(!sSpellStore.LookupEntry(spell_id))
3583 sLog.outError("Player %u has unknown spell %u in `character_spell_cooldown`, skipping.",GetGUIDLow(),spell_id);
3584 continue;
3587 // skip outdated cooldown
3588 if(db_time <= curTime)
3589 continue;
3591 AddSpellCooldown(spell_id, item_id, db_time);
3593 DEBUG_LOG("Player (GUID: %u) spell %u, item %u cooldown loaded (%u secs).", GetGUIDLow(), spell_id, item_id, uint32(db_time-curTime));
3595 while( result->NextRow() );
3597 delete result;
3601 void Player::_SaveSpellCooldowns()
3603 CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow());
3605 time_t curTime = time(NULL);
3606 time_t infTime = curTime + infinityCooldownDelayCheck;
3608 /* copied following sql-code partly from achievementmgr */
3609 bool first_round = true;
3610 std::ostringstream ss;
3612 // remove outdated and save active
3613 for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();)
3615 if(itr->second.end <= curTime)
3616 m_spellCooldowns.erase(itr++);
3617 else if(itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload
3619 if (first_round)
3621 ss << "INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES ";
3622 first_round = false;
3624 // next new/changed record prefix
3625 else
3626 ss << ", ";
3627 ss << "(" << GetGUIDLow() << "," << itr->first << "," << itr->second.itemid << "," << uint64(itr->second.end) << ")";
3628 ++itr;
3630 else
3631 ++itr;
3634 // if something changed execute
3635 if (!first_round)
3636 CharacterDatabase.Execute( ss.str().c_str() );
3639 uint32 Player::resetTalentsCost() const
3641 // The first time reset costs 1 gold
3642 if(m_resetTalentsCost < 1*GOLD)
3643 return 1*GOLD;
3644 // then 5 gold
3645 else if(m_resetTalentsCost < 5*GOLD)
3646 return 5*GOLD;
3647 // After that it increases in increments of 5 gold
3648 else if(m_resetTalentsCost < 10*GOLD)
3649 return 10*GOLD;
3650 else
3652 time_t months = (sWorld.GetGameTime() - m_resetTalentsTime)/MONTH;
3653 if(months > 0)
3655 // This cost will be reduced by a rate of 5 gold per month
3656 int32 new_cost = int32((m_resetTalentsCost) - 5*GOLD*months);
3657 // to a minimum of 10 gold.
3658 return uint32(new_cost < 10*GOLD ? 10*GOLD : new_cost);
3660 else
3662 // After that it increases in increments of 5 gold
3663 int32 new_cost = m_resetTalentsCost + 5*GOLD;
3664 // until it hits a cap of 50 gold.
3665 if(new_cost > 50*GOLD)
3666 new_cost = 50*GOLD;
3667 return new_cost;
3672 bool Player::resetTalents(bool no_cost, bool all_specs)
3674 // not need after this call
3675 if(HasAtLoginFlag(AT_LOGIN_RESET_TALENTS) && all_specs)
3676 RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS,true);
3678 if (m_usedTalentCount == 0 && !all_specs)
3680 UpdateFreeTalentPoints(false); // for fix if need counter
3681 return false;
3684 uint32 cost = 0;
3686 if(!no_cost)
3688 cost = resetTalentsCost();
3690 if (GetMoney() < cost)
3692 SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
3693 return false;
3697 for (PlayerTalentMap::iterator iter = m_talents[m_activeSpec].begin(); iter != m_talents[m_activeSpec].end();)
3699 if (iter->second.state == PLAYERSPELL_REMOVED)
3701 ++iter;
3702 continue;
3705 TalentEntry const *talentInfo = (*iter).second.m_talentEntry;
3706 if (!talentInfo)
3708 m_talents[m_activeSpec].erase(iter++);
3709 continue;
3712 TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
3714 if (!talentTabInfo)
3716 m_talents[m_activeSpec].erase(iter++);
3717 continue;
3720 // unlearn only talents for character class
3721 // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
3722 // to prevent unexpected lost normal learned spell skip another class talents
3723 if ((getClassMask() & talentTabInfo->ClassMask) == 0)
3725 ++iter;
3726 continue;
3729 for (int j = 0; j < MAX_TALENT_RANK; ++j)
3730 if (talentInfo->RankID[j])
3731 removeSpell(talentInfo->RankID[j],!IsPassiveSpell(talentInfo->RankID[j]),false);
3733 iter = m_talents[m_activeSpec].begin();
3736 // for not current spec just mark removed all saved to DB case and drop not saved
3737 if (all_specs)
3739 for (uint8 spec = 0; spec < MAX_TALENT_SPEC_COUNT; ++spec)
3741 if (spec == m_activeSpec)
3742 continue;
3744 for (PlayerTalentMap::iterator iter = m_talents[spec].begin(); iter != m_talents[spec].end();)
3746 switch (iter->second.state)
3748 case PLAYERSPELL_REMOVED:
3749 ++iter;
3750 break;
3751 case PLAYERSPELL_NEW:
3752 m_talents[spec].erase(iter++);
3753 break;
3754 default:
3755 iter->second.state = PLAYERSPELL_REMOVED;
3756 ++iter;
3757 break;
3763 UpdateFreeTalentPoints(false);
3765 if(!no_cost)
3767 ModifyMoney(-(int32)cost);
3768 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost);
3769 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS, 1);
3771 m_resetTalentsCost = cost;
3772 m_resetTalentsTime = time(NULL);
3775 //FIXME: remove pet before or after unlearn spells? for now after unlearn to allow removing of talent related, pet affecting auras
3776 RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
3777 /* when prev line will dropped use next line
3778 if(Pet* pet = GetPet())
3780 if(pet->getPetType()==HUNTER_PET && !pet->GetCreatureInfo()->isTameable(CanTameExoticPets()))
3781 RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
3784 return true;
3787 Mail* Player::GetMail(uint32 id)
3789 for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
3791 if ((*itr)->messageID == id)
3793 return (*itr);
3796 return NULL;
3799 void Player::_SetCreateBits(UpdateMask *updateMask, Player *target) const
3801 if(target == this)
3803 Object::_SetCreateBits(updateMask, target);
3805 else
3807 for(uint16 index = 0; index < m_valuesCount; index++)
3809 if(GetUInt32Value(index) != 0 && updateVisualBits.GetBit(index))
3810 updateMask->SetBit(index);
3815 void Player::_SetUpdateBits(UpdateMask *updateMask, Player *target) const
3817 if(target == this)
3819 Object::_SetUpdateBits(updateMask, target);
3821 else
3823 Object::_SetUpdateBits(updateMask, target);
3824 *updateMask &= updateVisualBits;
3828 void Player::InitVisibleBits()
3830 updateVisualBits.SetCount(PLAYER_END);
3832 updateVisualBits.SetBit(OBJECT_FIELD_GUID);
3833 updateVisualBits.SetBit(OBJECT_FIELD_TYPE);
3834 updateVisualBits.SetBit(OBJECT_FIELD_ENTRY);
3835 updateVisualBits.SetBit(OBJECT_FIELD_SCALE_X);
3836 updateVisualBits.SetBit(UNIT_FIELD_CHARM + 0);
3837 updateVisualBits.SetBit(UNIT_FIELD_CHARM + 1);
3838 updateVisualBits.SetBit(UNIT_FIELD_SUMMON + 0);
3839 updateVisualBits.SetBit(UNIT_FIELD_SUMMON + 1);
3840 updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY + 0);
3841 updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY + 1);
3842 updateVisualBits.SetBit(UNIT_FIELD_TARGET + 0);
3843 updateVisualBits.SetBit(UNIT_FIELD_TARGET + 1);
3844 updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT + 0);
3845 updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT + 1);
3846 updateVisualBits.SetBit(UNIT_FIELD_BYTES_0);
3847 updateVisualBits.SetBit(UNIT_FIELD_HEALTH);
3848 updateVisualBits.SetBit(UNIT_FIELD_POWER1);
3849 updateVisualBits.SetBit(UNIT_FIELD_POWER2);
3850 updateVisualBits.SetBit(UNIT_FIELD_POWER3);
3851 updateVisualBits.SetBit(UNIT_FIELD_POWER4);
3852 updateVisualBits.SetBit(UNIT_FIELD_POWER5);
3853 updateVisualBits.SetBit(UNIT_FIELD_POWER6);
3854 updateVisualBits.SetBit(UNIT_FIELD_POWER7);
3855 updateVisualBits.SetBit(UNIT_FIELD_MAXHEALTH);
3856 updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER1);
3857 updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER2);
3858 updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER3);
3859 updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER4);
3860 updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER5);
3861 updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER6);
3862 updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER7);
3863 updateVisualBits.SetBit(UNIT_FIELD_LEVEL);
3864 updateVisualBits.SetBit(UNIT_FIELD_FACTIONTEMPLATE);
3865 updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 0);
3866 updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 1);
3867 updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 2);
3868 updateVisualBits.SetBit(UNIT_FIELD_FLAGS);
3869 updateVisualBits.SetBit(UNIT_FIELD_FLAGS_2);
3870 updateVisualBits.SetBit(UNIT_FIELD_AURASTATE);
3871 updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 0);
3872 updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 1);
3873 updateVisualBits.SetBit(UNIT_FIELD_BOUNDINGRADIUS);
3874 updateVisualBits.SetBit(UNIT_FIELD_COMBATREACH);
3875 updateVisualBits.SetBit(UNIT_FIELD_DISPLAYID);
3876 updateVisualBits.SetBit(UNIT_FIELD_NATIVEDISPLAYID);
3877 updateVisualBits.SetBit(UNIT_FIELD_MOUNTDISPLAYID);
3878 updateVisualBits.SetBit(UNIT_FIELD_BYTES_1);
3879 updateVisualBits.SetBit(UNIT_FIELD_PETNUMBER);
3880 updateVisualBits.SetBit(UNIT_FIELD_PET_NAME_TIMESTAMP);
3881 updateVisualBits.SetBit(UNIT_DYNAMIC_FLAGS);
3882 updateVisualBits.SetBit(UNIT_CHANNEL_SPELL);
3883 updateVisualBits.SetBit(UNIT_MOD_CAST_SPEED);
3884 updateVisualBits.SetBit(UNIT_FIELD_BASE_MANA);
3885 updateVisualBits.SetBit(UNIT_FIELD_BYTES_2);
3886 updateVisualBits.SetBit(UNIT_FIELD_HOVERHEIGHT);
3888 updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 0);
3889 updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 1);
3890 updateVisualBits.SetBit(PLAYER_FLAGS);
3891 updateVisualBits.SetBit(PLAYER_GUILDID);
3892 updateVisualBits.SetBit(PLAYER_GUILDRANK);
3893 updateVisualBits.SetBit(PLAYER_BYTES);
3894 updateVisualBits.SetBit(PLAYER_BYTES_2);
3895 updateVisualBits.SetBit(PLAYER_BYTES_3);
3896 updateVisualBits.SetBit(PLAYER_DUEL_TEAM);
3897 updateVisualBits.SetBit(PLAYER_GUILD_TIMESTAMP);
3899 // PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)...
3900 for(uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i += MAX_QUEST_OFFSET)
3901 updateVisualBits.SetBit(i);
3903 // Players visible items are not inventory stuff
3904 for(uint16 i = 0; i < EQUIPMENT_SLOT_END; ++i)
3906 uint32 offset = i * 2;
3908 // item entry
3909 updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_ENTRYID + offset);
3910 // enchant
3911 updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + offset);
3914 updateVisualBits.SetBit(PLAYER_CHOSEN_TITLE);
3917 void Player::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const
3919 if(target == this)
3921 for(int i = 0; i < EQUIPMENT_SLOT_END; ++i)
3923 if(m_items[i] == NULL)
3924 continue;
3926 m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
3928 for(int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
3930 if(m_items[i] == NULL)
3931 continue;
3933 m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
3935 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
3937 if(m_items[i] == NULL)
3938 continue;
3940 m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
3944 Unit::BuildCreateUpdateBlockForPlayer( data, target );
3947 void Player::DestroyForPlayer( Player *target, bool anim ) const
3949 Unit::DestroyForPlayer( target, anim );
3951 for(int i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
3953 if(m_items[i] == NULL)
3954 continue;
3956 m_items[i]->DestroyForPlayer( target );
3959 if(target == this)
3961 for(int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
3963 if(m_items[i] == NULL)
3964 continue;
3966 m_items[i]->DestroyForPlayer( target );
3968 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
3970 if(m_items[i] == NULL)
3971 continue;
3973 m_items[i]->DestroyForPlayer( target );
3978 bool Player::HasSpell(uint32 spell) const
3980 PlayerSpellMap::const_iterator itr = m_spells.find(spell);
3981 return (itr != m_spells.end() && itr->second.state != PLAYERSPELL_REMOVED &&
3982 !itr->second.disabled);
3985 bool Player::HasActiveSpell(uint32 spell) const
3987 PlayerSpellMap::const_iterator itr = m_spells.find(spell);
3988 return (itr != m_spells.end() && itr->second.state != PLAYERSPELL_REMOVED &&
3989 itr->second.active && !itr->second.disabled);
3992 TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell) const
3994 if (!trainer_spell)
3995 return TRAINER_SPELL_RED;
3997 if (!trainer_spell->learnedSpell)
3998 return TRAINER_SPELL_RED;
4000 // known spell
4001 if(HasSpell(trainer_spell->learnedSpell))
4002 return TRAINER_SPELL_GRAY;
4004 // check race/class requirement
4005 if(!IsSpellFitByClassAndRace(trainer_spell->learnedSpell))
4006 return TRAINER_SPELL_RED;
4008 // check level requirement
4009 if(getLevel() < trainer_spell->reqLevel)
4010 return TRAINER_SPELL_RED;
4012 if(SpellChainNode const* spell_chain = sSpellMgr.GetSpellChainNode(trainer_spell->learnedSpell))
4014 // check prev.rank requirement
4015 if(spell_chain->prev && !HasSpell(spell_chain->prev))
4016 return TRAINER_SPELL_RED;
4018 // check additional spell requirement
4019 if(spell_chain->req && !HasSpell(spell_chain->req))
4020 return TRAINER_SPELL_RED;
4023 // check skill requirement
4024 if(trainer_spell->reqSkill && GetBaseSkillValue(trainer_spell->reqSkill) < trainer_spell->reqSkillValue)
4025 return TRAINER_SPELL_RED;
4027 // exist, already checked at loading
4028 SpellEntry const* spell = sSpellStore.LookupEntry(trainer_spell->learnedSpell);
4030 // secondary prof. or not prof. spell
4031 uint32 skill = spell->EffectMiscValue[1];
4033 if(spell->Effect[1] != SPELL_EFFECT_SKILL || !IsPrimaryProfessionSkill(skill))
4034 return TRAINER_SPELL_GREEN;
4036 // check primary prof. limit
4037 if(sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell->Id) && GetFreePrimaryProfessionPoints() == 0)
4038 return TRAINER_SPELL_GREEN_DISABLED;
4040 return TRAINER_SPELL_GREEN;
4044 * Deletes a character from the database
4046 * The way, how the characters will be deleted is decided based on the config option.
4048 * @see Player::DeleteOldCharacters
4050 * @param playerguid the low-GUID from the player which should be deleted
4051 * @param accountId the account id from the player
4052 * @param updateRealmChars when this flag is set, the amount of characters on that realm will be updated in the realmlist
4053 * @param deleteFinally if this flag is set, the config option will be ignored and the character will be permanently removed from the database
4055 void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars, bool deleteFinally)
4057 // for not existed account avoid update realm
4058 if (accountId == 0)
4059 updateRealmChars = false;
4061 uint32 charDelete_method = sWorld.getConfig(CONFIG_UINT32_CHARDELETE_METHOD);
4062 uint32 charDelete_minLvl = sWorld.getConfig(CONFIG_UINT32_CHARDELETE_MIN_LEVEL);
4064 // if we want to finally delete the character or the character does not meet the level requirement, we set it to mode 0
4065 if (deleteFinally || Player::GetLevelFromDB(playerguid) < charDelete_minLvl)
4066 charDelete_method = 0;
4068 uint32 guid = GUID_LOPART(playerguid);
4070 // convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry)
4071 // bones will be deleted by corpse/bones deleting thread shortly
4072 sObjectAccessor.ConvertCorpseForPlayer(playerguid);
4074 // remove from guild
4075 if (uint32 guildId = GetGuildIdFromDB(playerguid))
4076 if (Guild* guild = sObjectMgr.GetGuildById(guildId))
4077 guild->DelMember(guid);
4079 // remove from arena teams
4080 LeaveAllArenaTeams(playerguid);
4082 // the player was uninvited already on logout so just remove from group
4083 QueryResult *resultGroup = CharacterDatabase.PQuery("SELECT groupId FROM group_member WHERE memberGuid='%u'", guid);
4084 if (resultGroup)
4086 uint32 groupId = (*resultGroup)[0].GetUInt32();
4087 delete resultGroup;
4088 if (Group* group = sObjectMgr.GetGroupById(groupId))
4089 RemoveFromGroup(group, playerguid);
4092 // remove signs from petitions (also remove petitions if owner);
4093 RemovePetitionsAndSigns(playerguid, 10);
4095 switch(charDelete_method)
4097 // completely remove from the database
4098 case 0:
4100 // return back all mails with COD and Item 0 1 2 3 4 5 6 7
4101 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", guid);
4102 if (resultMail)
4106 Field *fields = resultMail->Fetch();
4108 uint32 mail_id = fields[0].GetUInt32();
4109 uint16 mailType = fields[1].GetUInt16();
4110 uint16 mailTemplateId= fields[2].GetUInt16();
4111 uint32 sender = fields[3].GetUInt32();
4112 std::string subject = fields[4].GetCppString();
4113 std::string body = fields[5].GetCppString();
4114 uint32 money = fields[6].GetUInt32();
4115 bool has_items = fields[7].GetBool();
4117 //we can return mail now
4118 //so firstly delete the old one
4119 CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id);
4121 // mail not from player
4122 if (mailType != MAIL_NORMAL)
4124 if(has_items)
4125 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
4126 continue;
4129 MailDraft draft(subject, body);
4130 if (mailTemplateId)
4131 draft = MailDraft(mailTemplateId, false); // items already included
4133 if (has_items)
4135 // data needs to be at first place for Item::LoadFromDB
4136 // 0 1 2 3
4137 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);
4138 if (resultItems)
4142 Field *fields2 = resultItems->Fetch();
4144 uint32 item_guidlow = fields2[2].GetUInt32();
4145 uint32 item_template = fields2[3].GetUInt32();
4147 ItemPrototype const* itemProto = ObjectMgr::GetItemPrototype(item_template);
4148 if (!itemProto)
4150 CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guidlow);
4151 continue;
4154 Item *pItem = NewItemOrBag(itemProto);
4155 if (!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER),resultItems))
4157 pItem->FSetState(ITEM_REMOVED);
4158 pItem->SaveToDB(); // it also deletes item object !
4159 continue;
4162 draft.AddItem(pItem);
4164 while (resultItems->NextRow());
4166 delete resultItems;
4170 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
4172 uint32 pl_account = sObjectMgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
4174 draft.AddMoney(money).SendReturnToSender(pl_account, guid, sender);
4176 while (resultMail->NextRow());
4178 delete resultMail;
4181 // unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
4182 // Get guids of character's pets, will deleted in transaction
4183 QueryResult *resultPets = CharacterDatabase.PQuery("SELECT id FROM character_pet WHERE owner = '%u'",guid);
4185 // NOW we can finally clear other DB data related to character
4186 CharacterDatabase.BeginTransaction();
4187 if (resultPets)
4191 Field *fields3 = resultPets->Fetch();
4192 uint32 petguidlow = fields3[0].GetUInt32();
4193 Pet::DeleteFromDB(petguidlow);
4194 } while (resultPets->NextRow());
4195 delete resultPets;
4198 CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",guid);
4199 CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid = '%u'",guid);
4200 CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'",guid);
4201 CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u'",guid);
4202 CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",guid);
4203 CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE guid = '%u'",guid);
4204 CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE guid = '%u'",guid);
4205 CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'",guid);
4206 CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u'",guid);
4207 CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u'",guid);
4208 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE guid = '%u'",guid);
4209 CharacterDatabase.PExecute("DELETE FROM character_queststatus WHERE guid = '%u'",guid);
4210 CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'",guid);
4211 CharacterDatabase.PExecute("DELETE FROM character_queststatus_weekly WHERE guid = '%u'",guid);
4212 CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u'",guid);
4213 CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u'",guid);
4214 CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u'",guid);
4215 CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'",guid);
4216 CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u'",guid);
4217 CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u'",guid);
4218 CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'",guid);
4219 CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'",guid,guid);
4220 CharacterDatabase.PExecute("DELETE FROM mail WHERE receiver = '%u'",guid);
4221 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE receiver = '%u'",guid);
4222 CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u'",guid);
4223 CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'",guid);
4224 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = '%u'",guid);
4225 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = '%u'",guid);
4226 CharacterDatabase.PExecute("DELETE FROM character_equipmentsets WHERE guid = '%u'",guid);
4227 CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE PlayerGuid1 = '%u' OR PlayerGuid2 = '%u'",guid, guid);
4228 CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE PlayerGuid = '%u'",guid);
4229 CharacterDatabase.CommitTransaction();
4230 break;
4232 // The character gets unlinked from the account, the name gets freed up and appears as deleted ingame
4233 case 1:
4234 CharacterDatabase.PExecute("UPDATE characters SET deleteInfos_Name=name, deleteInfos_Account=account, deleteDate='" UI64FMTD "', name='', account=0 WHERE guid=%u", uint64(time(NULL)), guid);
4235 break;
4236 default:
4237 sLog.outError("Player::DeleteFromDB: Unsupported delete method: %u.", charDelete_method);
4240 if (updateRealmChars)
4241 sWorld.UpdateRealmCharCount(accountId);
4245 * 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.
4247 * @see Player::DeleteFromDB
4249 void Player::DeleteOldCharacters()
4251 uint32 keepDays = sWorld.getConfig(CONFIG_UINT32_CHARDELETE_KEEP_DAYS);
4252 if (!keepDays)
4253 return;
4255 Player::DeleteOldCharacters(keepDays);
4259 * Characters which were kept back in the database after being deleted and are older than the specified amount of days, will be completely deleted.
4261 * @see Player::DeleteFromDB
4263 * @param keepDays overrite the config option by another amount of days
4265 void Player::DeleteOldCharacters(uint32 keepDays)
4267 sLog.outString("Player::DeleteOldChars: Deleting all characters which have been deleted %u days before...", keepDays);
4269 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)));
4270 if (resultChars)
4272 sLog.outString("Player::DeleteOldChars: Found %u character(s) to delete",uint32(resultChars->GetRowCount()));
4275 Field *charFields = resultChars->Fetch();
4276 Player::DeleteFromDB(charFields[0].GetUInt64(), charFields[1].GetUInt32(), true, true);
4277 } while(resultChars->NextRow());
4278 delete resultChars;
4282 void Player::SetMovement(PlayerMovementType pType)
4284 WorldPacket data;
4285 switch(pType)
4287 case MOVE_ROOT: data.Initialize(SMSG_FORCE_MOVE_ROOT, GetPackGUID().size()+4); break;
4288 case MOVE_UNROOT: data.Initialize(SMSG_FORCE_MOVE_UNROOT, GetPackGUID().size()+4); break;
4289 case MOVE_WATER_WALK: data.Initialize(SMSG_MOVE_WATER_WALK, GetPackGUID().size()+4); break;
4290 case MOVE_LAND_WALK: data.Initialize(SMSG_MOVE_LAND_WALK, GetPackGUID().size()+4); break;
4291 default:
4292 sLog.outError("Player::SetMovement: Unsupported move type (%d), data not sent to client.",pType);
4293 return;
4295 data << GetPackGUID();
4296 data << uint32(0);
4297 GetSession()->SendPacket( &data );
4300 /* Preconditions:
4301 - a resurrectable corpse must not be loaded for the player (only bones)
4302 - the player must be in world
4304 void Player::BuildPlayerRepop()
4306 WorldPacket data(SMSG_PRE_RESURRECT, GetPackGUID().size());
4307 data << GetPackGUID();
4308 GetSession()->SendPacket(&data);
4310 if(getRace() == RACE_NIGHTELF)
4311 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)
4312 CastSpell(this, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
4314 // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK
4315 // there must be SMSG.STOP_MIRROR_TIMER
4316 // there we must send 888 opcode
4318 // the player cannot have a corpse already, only bones which are not returned by GetCorpse
4319 if(GetCorpse())
4321 sLog.outError("BuildPlayerRepop: player %s(%d) already has a corpse", GetName(), GetGUIDLow());
4322 ASSERT(false);
4325 // create a corpse and place it at the player's location
4326 Corpse *corpse = CreateCorpse();
4327 if(!corpse)
4329 sLog.outError("Error creating corpse for Player %s [%u]", GetName(), GetGUIDLow());
4330 return;
4332 GetMap()->Add(corpse);
4334 // convert player body to ghost
4335 SetHealth( 1 );
4337 SetMovement(MOVE_WATER_WALK);
4338 if(!GetSession()->isLogingOut())
4339 SetMovement(MOVE_UNROOT);
4341 // BG - remove insignia related
4342 RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
4344 SendCorpseReclaimDelay();
4346 // to prevent cheating
4347 corpse->ResetGhostTime();
4349 StopMirrorTimers(); //disable timers(bars)
4351 SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (float)1.0); //see radius of death player?
4353 // set and clear other
4354 SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND);
4357 void Player::ResurrectPlayer(float restore_percent, bool applySickness)
4359 WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // remove spirit healer position
4360 data << uint32(-1);
4361 data << float(0);
4362 data << float(0);
4363 data << float(0);
4364 GetSession()->SendPacket(&data);
4366 // speed change, land walk
4368 // remove death flag + set aura
4369 SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00);
4370 if(getRace() == RACE_NIGHTELF)
4371 RemoveAurasDueToSpell(20584); // speed bonuses
4372 RemoveAurasDueToSpell(8326); // SPELL_AURA_GHOST
4374 setDeathState(ALIVE);
4376 SetMovement(MOVE_LAND_WALK);
4377 SetMovement(MOVE_UNROOT);
4379 m_deathTimer = 0;
4381 // set health/powers (0- will be set in caller)
4382 if(restore_percent>0.0f)
4384 SetHealth(uint32(GetMaxHealth()*restore_percent));
4385 SetPower(POWER_MANA, uint32(GetMaxPower(POWER_MANA)*restore_percent));
4386 SetPower(POWER_RAGE, 0);
4387 SetPower(POWER_ENERGY, uint32(GetMaxPower(POWER_ENERGY)*restore_percent));
4390 // trigger update zone for alive state zone updates
4391 uint32 newzone, newarea;
4392 GetZoneAndAreaId(newzone,newarea);
4393 UpdateZone(newzone,newarea);
4395 // update visibility
4396 UpdateVisibilityForPlayer();
4398 if(!applySickness)
4399 return;
4401 //Characters from level 1-10 are not affected by resurrection sickness.
4402 //Characters from level 11-19 will suffer from one minute of sickness
4403 //for each level they are above 10.
4404 //Characters level 20 and up suffer from ten minutes of sickness.
4405 int32 startLevel = sWorld.getConfig(CONFIG_INT32_DEATH_SICKNESS_LEVEL);
4407 if(int32(getLevel()) >= startLevel)
4409 // set resurrection sickness
4410 CastSpell(this,SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,true);
4412 // not full duration
4413 if(int32(getLevel()) < startLevel+9)
4415 int32 delta = (int32(getLevel()) - startLevel + 1)*MINUTE;
4417 for(int i = 0; i < MAX_EFFECT_INDEX; ++i)
4419 if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,SpellEffectIndex(i)))
4421 Aur->SetAuraDuration(delta*IN_MILLISECONDS);
4422 Aur->SendAuraUpdate(false);
4429 void Player::KillPlayer()
4431 SetMovement(MOVE_ROOT);
4433 StopMirrorTimers(); //disable timers(bars)
4435 setDeathState(CORPSE);
4436 //SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_IN_PVP );
4438 SetFlag(UNIT_DYNAMIC_FLAGS, 0x00);
4439 ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(GetMapId())->Instanceable());
4441 // 6 minutes until repop at graveyard
4442 m_deathTimer = 6*MINUTE*IN_MILLISECONDS;
4444 UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill
4446 // don't create corpse at this moment, player might be falling
4448 // update visibility
4449 UpdateObjectVisibility();
4452 Corpse* Player::CreateCorpse()
4454 // prevent existence 2 corpse for player
4455 SpawnCorpseBones();
4457 uint32 _uf, _pb, _pb2, _cfb1, _cfb2;
4459 Corpse *corpse = new Corpse( (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE );
4460 SetPvPDeath(false);
4462 if (!corpse->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_CORPSE), this))
4464 delete corpse;
4465 return NULL;
4468 _uf = GetUInt32Value(UNIT_FIELD_BYTES_0);
4469 _pb = GetUInt32Value(PLAYER_BYTES);
4470 _pb2 = GetUInt32Value(PLAYER_BYTES_2);
4472 uint8 race = (uint8)(_uf);
4473 uint8 skin = (uint8)(_pb);
4474 uint8 face = (uint8)(_pb >> 8);
4475 uint8 hairstyle = (uint8)(_pb >> 16);
4476 uint8 haircolor = (uint8)(_pb >> 24);
4477 uint8 facialhair = (uint8)(_pb2);
4479 _cfb1 = ((0x00) | (race << 8) | (getGender() << 16) | (skin << 24));
4480 _cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));
4482 corpse->SetUInt32Value( CORPSE_FIELD_BYTES_1, _cfb1 );
4483 corpse->SetUInt32Value( CORPSE_FIELD_BYTES_2, _cfb2 );
4485 uint32 flags = CORPSE_FLAG_UNK2;
4486 if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
4487 flags |= CORPSE_FLAG_HIDE_HELM;
4488 if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
4489 flags |= CORPSE_FLAG_HIDE_CLOAK;
4490 if (InBattleGround() && !InArena())
4491 flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia
4492 corpse->SetUInt32Value( CORPSE_FIELD_FLAGS, flags );
4494 corpse->SetUInt32Value( CORPSE_FIELD_DISPLAY_ID, GetNativeDisplayId() );
4496 corpse->SetUInt32Value( CORPSE_FIELD_GUILD, GetGuildId() );
4498 uint32 iDisplayID;
4499 uint32 iIventoryType;
4500 uint32 _cfi;
4501 for (int i = 0; i < EQUIPMENT_SLOT_END; ++i)
4503 if (m_items[i])
4505 iDisplayID = m_items[i]->GetProto()->DisplayInfoID;
4506 iIventoryType = m_items[i]->GetProto()->InventoryType;
4508 _cfi = iDisplayID | (iIventoryType << 24);
4509 corpse->SetUInt32Value(CORPSE_FIELD_ITEM + i, _cfi);
4513 // we not need saved corpses for BG/arenas
4514 if (!GetMap()->IsBattleGroundOrArena())
4515 corpse->SaveToDB();
4517 // register for player, but not show
4518 sObjectAccessor.AddCorpse(corpse);
4519 return corpse;
4522 void Player::SpawnCorpseBones()
4524 if(sObjectAccessor.ConvertCorpseForPlayer(GetGUID()))
4525 if (!GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player
4526 SaveToDB(); // prevent loading as ghost without corpse
4529 Corpse* Player::GetCorpse() const
4531 return sObjectAccessor.GetCorpseForPlayerGUID(GetGUID());
4534 void Player::DurabilityLossAll(double percent, bool inventory)
4536 for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
4537 if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
4538 DurabilityLoss(pItem,percent);
4540 if(inventory)
4542 // bags not have durability
4543 // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
4545 for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
4546 if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
4547 DurabilityLoss(pItem,percent);
4549 // keys not have durability
4550 //for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
4552 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
4553 if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
4554 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
4555 if(Item* pItem = GetItemByPos( i, j ))
4556 DurabilityLoss(pItem,percent);
4560 void Player::DurabilityLoss(Item* item, double percent)
4562 if(!item )
4563 return;
4565 uint32 pMaxDurability = item ->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
4567 if(!pMaxDurability)
4568 return;
4570 uint32 pDurabilityLoss = uint32(pMaxDurability*percent);
4572 if(pDurabilityLoss < 1 )
4573 pDurabilityLoss = 1;
4575 DurabilityPointsLoss(item,pDurabilityLoss);
4578 void Player::DurabilityPointsLossAll(int32 points, bool inventory)
4580 for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
4581 if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
4582 DurabilityPointsLoss(pItem,points);
4584 if(inventory)
4586 // bags not have durability
4587 // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
4589 for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
4590 if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
4591 DurabilityPointsLoss(pItem,points);
4593 // keys not have durability
4594 //for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
4596 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
4597 if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
4598 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
4599 if(Item* pItem = GetItemByPos( i, j ))
4600 DurabilityPointsLoss(pItem,points);
4604 void Player::DurabilityPointsLoss(Item* item, int32 points)
4606 int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
4607 int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
4608 int32 pNewDurability = pOldDurability - points;
4610 if (pNewDurability < 0)
4611 pNewDurability = 0;
4612 else if (pNewDurability > pMaxDurability)
4613 pNewDurability = pMaxDurability;
4615 if (pOldDurability != pNewDurability)
4617 // modify item stats _before_ Durability set to 0 to pass _ApplyItemMods internal check
4618 if ( pNewDurability == 0 && pOldDurability > 0 && item->IsEquipped())
4619 _ApplyItemMods(item,item->GetSlot(), false);
4621 item->SetUInt32Value(ITEM_FIELD_DURABILITY, pNewDurability);
4623 // modify item stats _after_ restore durability to pass _ApplyItemMods internal check
4624 if ( pNewDurability > 0 && pOldDurability == 0 && item->IsEquipped())
4625 _ApplyItemMods(item,item->GetSlot(), true);
4627 item->SetState(ITEM_CHANGED, this);
4631 void Player::DurabilityPointLossForEquipSlot(EquipmentSlots slot)
4633 if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot ))
4634 DurabilityPointsLoss(pItem,1);
4637 uint32 Player::DurabilityRepairAll(bool cost, float discountMod, bool guildBank)
4639 uint32 TotalCost = 0;
4640 // equipped, backpack, bags itself
4641 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
4642 TotalCost += DurabilityRepair(( (INVENTORY_SLOT_BAG_0 << 8) | i ),cost,discountMod, guildBank);
4644 // bank, buyback and keys not repaired
4646 // items in inventory bags
4647 for(int j = INVENTORY_SLOT_BAG_START; j < INVENTORY_SLOT_BAG_END; ++j)
4648 for(int i = 0; i < MAX_BAG_SIZE; ++i)
4649 TotalCost += DurabilityRepair(( (j << 8) | i ),cost,discountMod, guildBank);
4650 return TotalCost;
4653 uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank)
4655 Item* item = GetItemByPos(pos);
4657 uint32 TotalCost = 0;
4658 if(!item)
4659 return TotalCost;
4661 uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
4662 if(!maxDurability)
4663 return TotalCost;
4665 uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
4667 if(cost)
4669 uint32 LostDurability = maxDurability - curDurability;
4670 if(LostDurability>0)
4672 ItemPrototype const *ditemProto = item->GetProto();
4674 DurabilityCostsEntry const *dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
4675 if(!dcost)
4677 sLog.outError("RepairDurability: Wrong item lvl %u", ditemProto->ItemLevel);
4678 return TotalCost;
4681 uint32 dQualitymodEntryId = (ditemProto->Quality+1)*2;
4682 DurabilityQualityEntry const *dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId);
4683 if(!dQualitymodEntry)
4685 sLog.outError("RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntryId);
4686 return TotalCost;
4689 uint32 dmultiplier = dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class,ditemProto->SubClass)];
4690 uint32 costs = uint32(LostDurability*dmultiplier*double(dQualitymodEntry->quality_mod));
4692 costs = uint32(costs * discountMod);
4694 if (costs==0) //fix for ITEM_QUALITY_ARTIFACT
4695 costs = 1;
4697 if (guildBank)
4699 if (GetGuildId()==0)
4701 DEBUG_LOG("You are not member of a guild");
4702 return TotalCost;
4705 Guild *pGuild = sObjectMgr.GetGuildById(GetGuildId());
4706 if (!pGuild)
4707 return TotalCost;
4709 if (!pGuild->HasRankRight(GetRank(), GR_RIGHT_WITHDRAW_REPAIR))
4711 DEBUG_LOG("You do not have rights to withdraw for repairs");
4712 return TotalCost;
4715 if (pGuild->GetMemberMoneyWithdrawRem(GetGUIDLow()) < costs)
4717 DEBUG_LOG("You do not have enough money withdraw amount remaining");
4718 return TotalCost;
4721 if (pGuild->GetGuildBankMoney() < costs)
4723 DEBUG_LOG("There is not enough money in bank");
4724 return TotalCost;
4727 pGuild->MemberMoneyWithdraw(costs, GetGUIDLow());
4728 TotalCost = costs;
4730 else if (GetMoney() < costs)
4732 DEBUG_LOG("You do not have enough money");
4733 return TotalCost;
4735 else
4736 ModifyMoney( -int32(costs) );
4740 item->SetUInt32Value(ITEM_FIELD_DURABILITY, maxDurability);
4741 item->SetState(ITEM_CHANGED, this);
4743 // reapply mods for total broken and repaired item if equipped
4744 if(IsEquipmentPos(pos) && !curDurability)
4745 _ApplyItemMods(item,pos & 255, true);
4746 return TotalCost;
4749 void Player::RepopAtGraveyard()
4751 // note: this can be called also when the player is alive
4752 // for example from WorldSession::HandleMovementOpcodes
4754 AreaTableEntry const *zone = GetAreaEntryByAreaID(GetAreaId());
4756 // Such zones are considered unreachable as a ghost and the player must be automatically revived
4757 if ((!isAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY) || GetTransport())
4759 ResurrectPlayer(0.5f);
4760 SpawnCorpseBones();
4763 WorldSafeLocsEntry const *ClosestGrave = NULL;
4765 // Special handle for battleground maps
4766 if( BattleGround *bg = GetBattleGround() )
4767 ClosestGrave = bg->GetClosestGraveYard(this);
4768 else
4769 ClosestGrave = sObjectMgr.GetClosestGraveYard( GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam() );
4771 // stop countdown until repop
4772 m_deathTimer = 0;
4774 // if no grave found, stay at the current location
4775 // and don't show spirit healer location
4776 if(ClosestGrave)
4778 TeleportTo(ClosestGrave->map_id, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, GetOrientation());
4779 if(isDead()) // not send if alive, because it used in TeleportTo()
4781 WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // show spirit healer position on minimap
4782 data << ClosestGrave->map_id;
4783 data << ClosestGrave->x;
4784 data << ClosestGrave->y;
4785 data << ClosestGrave->z;
4786 GetSession()->SendPacket(&data);
4791 void Player::JoinedChannel(Channel *c)
4793 m_channels.push_back(c);
4796 void Player::LeftChannel(Channel *c)
4798 m_channels.remove(c);
4801 void Player::CleanupChannels()
4803 while(!m_channels.empty())
4805 Channel* ch = *m_channels.begin();
4806 m_channels.erase(m_channels.begin()); // remove from player's channel list
4807 ch->Leave(GetGUID(), false); // not send to client, not remove from player's channel list
4808 if (ChannelMgr* cMgr = channelMgr(GetTeam()))
4809 cMgr->LeftChannel(ch->GetName()); // deleted channel if empty
4812 DEBUG_LOG("Player: channels cleaned up!");
4815 void Player::UpdateLocalChannels(uint32 newZone )
4817 if(m_channels.empty())
4818 return;
4820 AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone);
4821 if(!current_zone)
4822 return;
4824 ChannelMgr* cMgr = channelMgr(GetTeam());
4825 if(!cMgr)
4826 return;
4828 std::string current_zone_name = current_zone->area_name[GetSession()->GetSessionDbcLocale()];
4830 for(JoinedChannelsList::iterator i = m_channels.begin(), next; i != m_channels.end(); i = next)
4832 next = i; ++next;
4834 // skip non built-in channels
4835 if(!(*i)->IsConstant())
4836 continue;
4838 ChatChannelsEntry const* ch = GetChannelEntryFor((*i)->GetChannelId());
4839 if(!ch)
4840 continue;
4842 if((ch->flags & 4) == 4) // global channel without zone name in pattern
4843 continue;
4845 // new channel
4846 char new_channel_name_buf[100];
4847 snprintf(new_channel_name_buf,100,ch->pattern[m_session->GetSessionDbcLocale()],current_zone_name.c_str());
4848 Channel* new_channel = cMgr->GetJoinChannel(new_channel_name_buf,ch->ChannelID);
4850 if((*i)!=new_channel)
4852 new_channel->Join(GetGUID(),""); // will output Changed Channel: N. Name
4854 // leave old channel
4855 (*i)->Leave(GetGUID(),false); // not send leave channel, it already replaced at client
4856 std::string name = (*i)->GetName(); // store name, (*i)erase in LeftChannel
4857 LeftChannel(*i); // remove from player's channel list
4858 cMgr->LeftChannel(name); // delete if empty
4861 DEBUG_LOG("Player: channels cleaned up!");
4864 void Player::LeaveLFGChannel()
4866 for(JoinedChannelsList::iterator i = m_channels.begin(); i != m_channels.end(); ++i )
4868 if((*i)->IsLFG())
4870 (*i)->Leave(GetGUID());
4871 break;
4876 void Player::UpdateDefense()
4878 uint32 defense_skill_gain = sWorld.getConfig(CONFIG_UINT32_SKILL_GAIN_DEFENSE);
4880 if(UpdateSkill(SKILL_DEFENSE,defense_skill_gain))
4882 // update dependent from defense skill part
4883 UpdateDefenseBonusesMod();
4887 void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply)
4889 if(modGroup >= BASEMOD_END || modType >= MOD_END)
4891 sLog.outError("ERROR in HandleBaseModValue(): non existed BaseModGroup of wrong BaseModType!");
4892 return;
4895 float val = 1.0f;
4897 switch(modType)
4899 case FLAT_MOD:
4900 m_auraBaseMod[modGroup][modType] += apply ? amount : -amount;
4901 break;
4902 case PCT_MOD:
4903 if(amount <= -100.0f)
4904 amount = -200.0f;
4906 val = (100.0f + amount) / 100.0f;
4907 m_auraBaseMod[modGroup][modType] *= apply ? val : (1.0f/val);
4908 break;
4911 if(!CanModifyStats())
4912 return;
4914 switch(modGroup)
4916 case CRIT_PERCENTAGE: UpdateCritPercentage(BASE_ATTACK); break;
4917 case RANGED_CRIT_PERCENTAGE: UpdateCritPercentage(RANGED_ATTACK); break;
4918 case OFFHAND_CRIT_PERCENTAGE: UpdateCritPercentage(OFF_ATTACK); break;
4919 case SHIELD_BLOCK_VALUE: UpdateShieldBlockValue(); break;
4920 default: break;
4924 float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const
4926 if(modGroup >= BASEMOD_END || modType > MOD_END)
4928 sLog.outError("trial to access non existed BaseModGroup or wrong BaseModType!");
4929 return 0.0f;
4932 if(modType == PCT_MOD && m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
4933 return 0.0f;
4935 return m_auraBaseMod[modGroup][modType];
4938 float Player::GetTotalBaseModValue(BaseModGroup modGroup) const
4940 if(modGroup >= BASEMOD_END)
4942 sLog.outError("wrong BaseModGroup in GetTotalBaseModValue()!");
4943 return 0.0f;
4946 if(m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
4947 return 0.0f;
4949 return m_auraBaseMod[modGroup][FLAT_MOD] * m_auraBaseMod[modGroup][PCT_MOD];
4952 uint32 Player::GetShieldBlockValue() const
4954 float value = (m_auraBaseMod[SHIELD_BLOCK_VALUE][FLAT_MOD] + GetStat(STAT_STRENGTH) * 0.5f - 10)*m_auraBaseMod[SHIELD_BLOCK_VALUE][PCT_MOD];
4956 value = (value < 0) ? 0 : value;
4958 return uint32(value);
4961 float Player::GetMeleeCritFromAgility()
4963 uint32 level = getLevel();
4964 uint32 pclass = getClass();
4966 if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
4968 GtChanceToMeleeCritBaseEntry const *critBase = sGtChanceToMeleeCritBaseStore.LookupEntry(pclass-1);
4969 GtChanceToMeleeCritEntry const *critRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
4970 if (critBase==NULL || critRatio==NULL)
4971 return 0.0f;
4973 float crit=critBase->base + GetStat(STAT_AGILITY)*critRatio->ratio;
4974 return crit*100.0f;
4977 float Player::GetDodgeFromAgility()
4979 // Table for base dodge values
4980 float dodge_base[MAX_CLASSES] = {
4981 0.0075f, // Warrior
4982 0.00652f, // Paladin
4983 -0.0545f, // Hunter
4984 -0.0059f, // Rogue
4985 0.03183f, // Priest
4986 0.0114f, // DK
4987 0.0167f, // Shaman
4988 0.034575f, // Mage
4989 0.02011f, // Warlock
4990 0.0f, // ??
4991 -0.0187f // Druid
4993 // Crit/agility to dodge/agility coefficient multipliers
4994 float crit_to_dodge[MAX_CLASSES] = {
4995 1.1f, // Warrior
4996 1.0f, // Paladin
4997 1.6f, // Hunter
4998 2.0f, // Rogue
4999 1.0f, // Priest
5000 1.0f, // DK?
5001 1.0f, // Shaman
5002 1.0f, // Mage
5003 1.0f, // Warlock
5004 0.0f, // ??
5005 1.7f // Druid
5008 uint32 level = getLevel();
5009 uint32 pclass = getClass();
5011 if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
5013 // Dodge per agility for most classes equal crit per agility (but for some classes need apply some multiplier)
5014 GtChanceToMeleeCritEntry const *dodgeRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
5015 if (dodgeRatio==NULL || pclass > MAX_CLASSES)
5016 return 0.0f;
5018 float dodge=dodge_base[pclass-1] + GetStat(STAT_AGILITY) * dodgeRatio->ratio * crit_to_dodge[pclass-1];
5019 return dodge*100.0f;
5022 float Player::GetSpellCritFromIntellect()
5024 uint32 level = getLevel();
5025 uint32 pclass = getClass();
5027 if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
5029 GtChanceToSpellCritBaseEntry const *critBase = sGtChanceToSpellCritBaseStore.LookupEntry(pclass-1);
5030 GtChanceToSpellCritEntry const *critRatio = sGtChanceToSpellCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
5031 if (critBase==NULL || critRatio==NULL)
5032 return 0.0f;
5034 float crit=critBase->base + GetStat(STAT_INTELLECT)*critRatio->ratio;
5035 return crit*100.0f;
5038 float Player::GetRatingCoefficient(CombatRating cr) const
5040 uint32 level = getLevel();
5042 if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
5044 GtCombatRatingsEntry const *Rating = sGtCombatRatingsStore.LookupEntry(cr*GT_MAX_LEVEL+level-1);
5045 if (Rating == NULL)
5046 return 1.0f; // By default use minimum coefficient (not must be called)
5048 return Rating->ratio;
5051 float Player::GetRatingBonusValue(CombatRating cr) const
5053 return float(GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr)) / GetRatingCoefficient(cr);
5056 float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const
5058 switch (attType)
5060 case BASE_ATTACK:
5061 return GetUInt32Value(PLAYER_EXPERTISE) / 4.0f;
5062 case OFF_ATTACK:
5063 return GetUInt32Value(PLAYER_OFFHAND_EXPERTISE) / 4.0f;
5064 default:
5065 break;
5067 return 0.0f;
5070 float Player::OCTRegenHPPerSpirit()
5072 uint32 level = getLevel();
5073 uint32 pclass = getClass();
5075 if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
5077 GtOCTRegenHPEntry const *baseRatio = sGtOCTRegenHPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
5078 GtRegenHPPerSptEntry const *moreRatio = sGtRegenHPPerSptStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
5079 if (baseRatio==NULL || moreRatio==NULL)
5080 return 0.0f;
5082 // Formula from PaperDollFrame script
5083 float spirit = GetStat(STAT_SPIRIT);
5084 float baseSpirit = spirit;
5085 if (baseSpirit>50) baseSpirit = 50;
5086 float moreSpirit = spirit - baseSpirit;
5087 float regen = baseSpirit * baseRatio->ratio + moreSpirit * moreRatio->ratio;
5088 return regen;
5091 float Player::OCTRegenMPPerSpirit()
5093 uint32 level = getLevel();
5094 uint32 pclass = getClass();
5096 if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
5098 // GtOCTRegenMPEntry const *baseRatio = sGtOCTRegenMPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
5099 GtRegenMPPerSptEntry const *moreRatio = sGtRegenMPPerSptStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
5100 if (moreRatio==NULL)
5101 return 0.0f;
5103 // Formula get from PaperDollFrame script
5104 float spirit = GetStat(STAT_SPIRIT);
5105 float regen = spirit * moreRatio->ratio;
5106 return regen;
5109 void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply)
5111 m_baseRatingValue[cr]+=(apply ? value : -value);
5113 // explicit affected values
5114 switch (cr)
5116 case CR_HASTE_MELEE:
5118 float RatingChange = value / GetRatingCoefficient(cr);
5119 ApplyAttackTimePercentMod(BASE_ATTACK,RatingChange,apply);
5120 ApplyAttackTimePercentMod(OFF_ATTACK,RatingChange,apply);
5121 break;
5123 case CR_HASTE_RANGED:
5125 float RatingChange = value / GetRatingCoefficient(cr);
5126 ApplyAttackTimePercentMod(RANGED_ATTACK, RatingChange, apply);
5127 break;
5129 case CR_HASTE_SPELL:
5131 float RatingChange = value / GetRatingCoefficient(cr);
5132 ApplyCastTimePercentMod(RatingChange,apply);
5133 break;
5137 UpdateRating(cr);
5140 void Player::UpdateRating(CombatRating cr)
5142 int32 amount = m_baseRatingValue[cr];
5143 // Apply bonus from SPELL_AURA_MOD_RATING_FROM_STAT
5144 // stat used stored in miscValueB for this aura
5145 AuraList const& modRatingFromStat = GetAurasByType(SPELL_AURA_MOD_RATING_FROM_STAT);
5146 for(AuraList::const_iterator i = modRatingFromStat.begin();i != modRatingFromStat.end(); ++i)
5147 if ((*i)->GetMiscValue() & (1<<cr))
5148 amount += int32(GetStat(Stats((*i)->GetMiscBValue())) * (*i)->GetModifier()->m_amount / 100.0f);
5149 if (amount < 0)
5150 amount = 0;
5151 SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, uint32(amount));
5153 bool affectStats = CanModifyStats();
5155 switch (cr)
5157 case CR_WEAPON_SKILL: // Implemented in Unit::RollMeleeOutcomeAgainst
5158 case CR_DEFENSE_SKILL:
5159 UpdateDefenseBonusesMod();
5160 break;
5161 case CR_DODGE:
5162 UpdateDodgePercentage();
5163 break;
5164 case CR_PARRY:
5165 UpdateParryPercentage();
5166 break;
5167 case CR_BLOCK:
5168 UpdateBlockPercentage();
5169 break;
5170 case CR_HIT_MELEE:
5171 UpdateMeleeHitChances();
5172 break;
5173 case CR_HIT_RANGED:
5174 UpdateRangedHitChances();
5175 break;
5176 case CR_HIT_SPELL:
5177 UpdateSpellHitChances();
5178 break;
5179 case CR_CRIT_MELEE:
5180 if(affectStats)
5182 UpdateCritPercentage(BASE_ATTACK);
5183 UpdateCritPercentage(OFF_ATTACK);
5185 break;
5186 case CR_CRIT_RANGED:
5187 if(affectStats)
5188 UpdateCritPercentage(RANGED_ATTACK);
5189 break;
5190 case CR_CRIT_SPELL:
5191 if(affectStats)
5192 UpdateAllSpellCritChances();
5193 break;
5194 case CR_HIT_TAKEN_MELEE: // Implemented in Unit::MeleeMissChanceCalc
5195 case CR_HIT_TAKEN_RANGED:
5196 break;
5197 case CR_HIT_TAKEN_SPELL: // Implemented in Unit::MagicSpellHitResult
5198 break;
5199 case CR_CRIT_TAKEN_MELEE: // Implemented in Unit::RollMeleeOutcomeAgainst (only for chance to crit)
5200 case CR_CRIT_TAKEN_RANGED:
5201 break;
5202 case CR_CRIT_TAKEN_SPELL: // Implemented in Unit::SpellCriticalBonus (only for chance to crit)
5203 break;
5204 case CR_HASTE_MELEE: // Implemented in Player::ApplyRatingMod
5205 case CR_HASTE_RANGED:
5206 case CR_HASTE_SPELL:
5207 break;
5208 case CR_WEAPON_SKILL_MAINHAND: // Implemented in Unit::RollMeleeOutcomeAgainst
5209 case CR_WEAPON_SKILL_OFFHAND:
5210 case CR_WEAPON_SKILL_RANGED:
5211 break;
5212 case CR_EXPERTISE:
5213 if(affectStats)
5215 UpdateExpertise(BASE_ATTACK);
5216 UpdateExpertise(OFF_ATTACK);
5218 break;
5219 case CR_ARMOR_PENETRATION:
5220 if(affectStats)
5221 UpdateArmorPenetration();
5222 break;
5226 void Player::UpdateAllRatings()
5228 for(int cr = 0; cr < MAX_COMBAT_RATING; ++cr)
5229 UpdateRating(CombatRating(cr));
5232 void Player::SetRegularAttackTime()
5234 for(int i = 0; i < MAX_ATTACK; ++i)
5236 Item *tmpitem = GetWeaponForAttack(WeaponAttackType(i),true,false);
5237 if (tmpitem)
5239 ItemPrototype const *proto = tmpitem->GetProto();
5240 if(proto->Delay)
5241 SetAttackTime(WeaponAttackType(i), proto->Delay);
5242 else
5243 SetAttackTime(WeaponAttackType(i), BASE_ATTACK_TIME);
5248 //skill+step, checking for max value
5249 bool Player::UpdateSkill(uint32 skill_id, uint32 step)
5251 if(!skill_id)
5252 return false;
5254 SkillStatusMap::iterator itr = mSkillStatus.find(skill_id);
5255 if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
5256 return false;
5258 uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);
5259 uint32 data = GetUInt32Value(valueIndex);
5260 uint32 value = SKILL_VALUE(data);
5261 uint32 max = SKILL_MAX(data);
5263 if ((!max) || (!value) || (value >= max))
5264 return false;
5266 if (value*512 < max*urand(0,512))
5268 uint32 new_value = value+step;
5269 if(new_value > max)
5270 new_value = max;
5272 SetUInt32Value(valueIndex,MAKE_SKILL_VALUE(new_value,max));
5273 if(itr->second.uState != SKILL_NEW)
5274 itr->second.uState = SKILL_CHANGED;
5275 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,skill_id);
5276 return true;
5279 return false;
5282 inline int SkillGainChance(uint32 SkillValue, uint32 GrayLevel, uint32 GreenLevel, uint32 YellowLevel)
5284 if ( SkillValue >= GrayLevel )
5285 return sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_GREY)*10;
5286 if ( SkillValue >= GreenLevel )
5287 return sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_GREEN)*10;
5288 if ( SkillValue >= YellowLevel )
5289 return sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_YELLOW)*10;
5290 return sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_ORANGE)*10;
5293 bool Player::UpdateCraftSkill(uint32 spellid)
5295 DEBUG_LOG("UpdateCraftSkill spellid %d", spellid);
5297 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spellid);
5299 for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
5301 if (_spell_idx->second->skillId)
5303 uint32 SkillValue = GetPureSkillValue(_spell_idx->second->skillId);
5305 // Alchemy Discoveries here
5306 SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellid);
5307 if (spellEntry && spellEntry->Mechanic == MECHANIC_DISCOVERY)
5309 if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this))
5310 learnSpell(discoveredSpell, false);
5313 uint32 craft_skill_gain = sWorld.getConfig(CONFIG_UINT32_SKILL_GAIN_CRAFTING);
5315 return UpdateSkillPro(_spell_idx->second->skillId, SkillGainChance(SkillValue,
5316 _spell_idx->second->max_value,
5317 (_spell_idx->second->max_value + _spell_idx->second->min_value)/2,
5318 _spell_idx->second->min_value),
5319 craft_skill_gain);
5322 return false;
5325 bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator )
5327 DEBUG_LOG("UpdateGatherSkill(SkillId %d SkillLevel %d RedLevel %d)", SkillId, SkillValue, RedLevel);
5329 uint32 gathering_skill_gain = sWorld.getConfig(CONFIG_UINT32_SKILL_GAIN_GATHERING);
5331 // For skinning and Mining chance decrease with level. 1-74 - no decrease, 75-149 - 2 times, 225-299 - 8 times
5332 switch (SkillId)
5334 case SKILL_HERBALISM:
5335 case SKILL_LOCKPICKING:
5336 case SKILL_JEWELCRAFTING:
5337 case SKILL_INSCRIPTION:
5338 return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
5339 case SKILL_SKINNING:
5340 if( sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_SKINNING_STEPS)==0)
5341 return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
5342 else
5343 return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator) >> (SkillValue/sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_SKINNING_STEPS)), gathering_skill_gain);
5344 case SKILL_MINING:
5345 if (sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_MINING_STEPS)==0)
5346 return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
5347 else
5348 return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator) >> (SkillValue/sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_MINING_STEPS)),gathering_skill_gain);
5350 return false;
5353 bool Player::UpdateFishingSkill()
5355 DEBUG_LOG("UpdateFishingSkill");
5357 uint32 SkillValue = GetPureSkillValue(SKILL_FISHING);
5359 int32 chance = SkillValue < 75 ? 100 : 2500/(SkillValue-50);
5361 uint32 gathering_skill_gain = sWorld.getConfig(CONFIG_UINT32_SKILL_GAIN_GATHERING);
5363 return UpdateSkillPro(SKILL_FISHING,chance*10,gathering_skill_gain);
5366 // levels sync. with spell requirement for skill levels to learn
5367 // bonus abilities in sSkillLineAbilityStore
5368 // Used only to avoid scan DBC at each skill grow
5369 static uint32 bonusSkillLevels[] = {75,150,225,300,375,450};
5371 bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step)
5373 DEBUG_LOG("UpdateSkillPro(SkillId %d, Chance %3.1f%%)", SkillId, Chance/10.0);
5374 if ( !SkillId )
5375 return false;
5377 if(Chance <= 0) // speedup in 0 chance case
5379 DEBUG_LOG("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance/10.0);
5380 return false;
5383 SkillStatusMap::iterator itr = mSkillStatus.find(SkillId);
5384 if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
5385 return false;
5387 uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);
5389 uint32 data = GetUInt32Value(valueIndex);
5390 uint16 SkillValue = SKILL_VALUE(data);
5391 uint16 MaxValue = SKILL_MAX(data);
5393 if ( !MaxValue || !SkillValue || SkillValue >= MaxValue )
5394 return false;
5396 int32 Roll = irand(1,1000);
5398 if ( Roll <= Chance )
5400 uint32 new_value = SkillValue+step;
5401 if(new_value > MaxValue)
5402 new_value = MaxValue;
5404 SetUInt32Value(valueIndex,MAKE_SKILL_VALUE(new_value,MaxValue));
5405 if(itr->second.uState != SKILL_NEW)
5406 itr->second.uState = SKILL_CHANGED;
5407 for(uint32* bsl = &bonusSkillLevels[0]; *bsl; ++bsl)
5409 if((SkillValue < *bsl && new_value >= *bsl))
5411 learnSkillRewardedSpells( SkillId, new_value);
5412 break;
5415 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,SkillId);
5416 DEBUG_LOG("Player::UpdateSkillPro Chance=%3.1f%% taken", Chance/10.0);
5417 return true;
5420 DEBUG_LOG("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance/10.0);
5421 return false;
5424 void Player::UpdateWeaponSkill (WeaponAttackType attType)
5426 // no skill gain in pvp
5427 Unit *pVictim = getVictim();
5428 if(pVictim && pVictim->IsCharmerOrOwnerPlayerOrPlayerItself())
5429 return;
5431 if(IsInFeralForm())
5432 return; // always maximized SKILL_FERAL_COMBAT in fact
5434 if(m_form == FORM_TREE)
5435 return; // use weapon but not skill up
5437 uint32 weapon_skill_gain = sWorld.getConfig(CONFIG_UINT32_SKILL_GAIN_WEAPON);
5439 switch(attType)
5441 case BASE_ATTACK:
5443 Item *tmpitem = GetWeaponForAttack(attType,true,true);
5445 if (!tmpitem)
5446 UpdateSkill(SKILL_UNARMED,weapon_skill_gain);
5447 else if(tmpitem->GetProto()->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE)
5448 UpdateSkill(tmpitem->GetSkill(),weapon_skill_gain);
5449 break;
5451 case OFF_ATTACK:
5452 case RANGED_ATTACK:
5454 Item *tmpitem = GetWeaponForAttack(attType,true,true);
5455 if (tmpitem)
5456 UpdateSkill(tmpitem->GetSkill(),weapon_skill_gain);
5457 break;
5460 UpdateAllCritPercentages();
5463 void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, bool defence)
5465 uint32 plevel = getLevel(); // if defense than pVictim == attacker
5466 uint32 greylevel = MaNGOS::XP::GetGrayLevel(plevel);
5467 uint32 moblevel = pVictim->getLevelForTarget(this);
5468 if(moblevel < greylevel)
5469 return;
5471 if (moblevel > plevel + 5)
5472 moblevel = plevel + 5;
5474 uint32 lvldif = moblevel - greylevel;
5475 if(lvldif < 3)
5476 lvldif = 3;
5478 uint32 skilldif = 5 * plevel - (defence ? GetBaseDefenseSkillValue() : GetBaseWeaponSkillValue(attType));
5479 if(skilldif <= 0)
5480 return;
5482 float chance = float(3 * lvldif * skilldif) / plevel;
5483 if(!defence)
5485 if(getClass() == CLASS_WARRIOR || getClass() == CLASS_ROGUE)
5486 chance *= 0.1f * GetStat(STAT_INTELLECT);
5489 chance = chance < 1.0f ? 1.0f : chance; //minimum chance to increase skill is 1%
5491 if(roll_chance_f(chance))
5493 if(defence)
5494 UpdateDefense();
5495 else
5496 UpdateWeaponSkill(attType);
5498 else
5499 return;
5502 void Player::ModifySkillBonus(uint32 skillid,int32 val, bool talent)
5504 SkillStatusMap::const_iterator itr = mSkillStatus.find(skillid);
5505 if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
5506 return;
5508 uint32 bonusIndex = PLAYER_SKILL_BONUS_INDEX(itr->second.pos);
5510 uint32 bonus_val = GetUInt32Value(bonusIndex);
5511 int16 temp_bonus = SKILL_TEMP_BONUS(bonus_val);
5512 int16 perm_bonus = SKILL_PERM_BONUS(bonus_val);
5514 if(talent) // permanent bonus stored in high part
5515 SetUInt32Value(bonusIndex,MAKE_SKILL_BONUS(temp_bonus,perm_bonus+val));
5516 else // temporary/item bonus stored in low part
5517 SetUInt32Value(bonusIndex,MAKE_SKILL_BONUS(temp_bonus+val,perm_bonus));
5520 void Player::UpdateSkillsForLevel()
5522 uint16 maxconfskill = sWorld.GetConfigMaxSkillValue();
5523 uint32 maxSkill = GetMaxSkillValueForLevel();
5525 bool alwaysMaxSkill = sWorld.getConfig(CONFIG_BOOL_ALWAYS_MAX_SKILL_FOR_LEVEL);
5527 for(SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr)
5529 if(itr->second.uState == SKILL_DELETED)
5530 continue;
5532 uint32 pskill = itr->first;
5534 SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(pskill);
5535 if(!pSkill)
5536 continue;
5538 if(GetSkillRangeType(pSkill,false) != SKILL_RANGE_LEVEL)
5539 continue;
5541 uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);
5542 uint32 data = GetUInt32Value(valueIndex);
5543 uint32 max = SKILL_MAX(data);
5544 uint32 val = SKILL_VALUE(data);
5546 /// update only level dependent max skill values
5547 if(max!=1)
5549 /// maximize skill always
5550 if(alwaysMaxSkill)
5552 SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(maxSkill,maxSkill));
5553 if(itr->second.uState != SKILL_NEW)
5554 itr->second.uState = SKILL_CHANGED;
5556 else if(max != maxconfskill) /// update max skill value if current max skill not maximized
5558 SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(val,maxSkill));
5559 if(itr->second.uState != SKILL_NEW)
5560 itr->second.uState = SKILL_CHANGED;
5566 void Player::UpdateSkillsToMaxSkillsForLevel()
5568 for(SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr)
5570 if(itr->second.uState == SKILL_DELETED)
5571 continue;
5573 uint32 pskill = itr->first;
5574 if( IsProfessionOrRidingSkill(pskill))
5575 continue;
5576 uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);
5577 uint32 data = GetUInt32Value(valueIndex);
5579 uint32 max = SKILL_MAX(data);
5581 if(max > 1)
5583 SetUInt32Value(valueIndex,MAKE_SKILL_VALUE(max,max));
5584 if(itr->second.uState != SKILL_NEW)
5585 itr->second.uState = SKILL_CHANGED;
5588 if(pskill == SKILL_DEFENSE)
5589 UpdateDefenseBonusesMod();
5593 // This functions sets a skill line value (and adds if doesn't exist yet)
5594 // To "remove" a skill line, set it's values to zero
5595 void Player::SetSkill(uint16 id, uint16 currVal, uint16 maxVal, uint16 step /*=0*/)
5597 if(!id)
5598 return;
5600 SkillStatusMap::iterator itr = mSkillStatus.find(id);
5602 // has skill
5603 if(itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED)
5605 if(currVal)
5607 if (step) // need update step
5608 SetUInt32Value(PLAYER_SKILL_INDEX(itr->second.pos), MAKE_PAIR32(id, step));
5609 // update value
5610 SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos), MAKE_SKILL_VALUE(currVal, maxVal));
5611 if(itr->second.uState != SKILL_NEW)
5612 itr->second.uState = SKILL_CHANGED;
5613 learnSkillRewardedSpells(id, currVal);
5614 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, id);
5615 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL, id);
5617 else //remove
5619 // clear skill fields
5620 SetUInt32Value(PLAYER_SKILL_INDEX(itr->second.pos), 0);
5621 SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos), 0);
5622 SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos), 0);
5624 // mark as deleted or simply remove from map if not saved yet
5625 if(itr->second.uState != SKILL_NEW)
5626 itr->second.uState = SKILL_DELETED;
5627 else
5628 mSkillStatus.erase(itr);
5630 // remove all spells that related to this skill
5631 for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
5632 if(SkillLineAbilityEntry const *pAbility = sSkillLineAbilityStore.LookupEntry(j))
5633 if (pAbility->skillId == id)
5634 removeSpell(sSpellMgr.GetFirstSpellInChain(pAbility->spellId));
5637 else if(currVal) // add
5639 for (int i = 0; i < PLAYER_MAX_SKILLS; ++i)
5641 if (!GetUInt32Value(PLAYER_SKILL_INDEX(i)))
5643 SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id);
5644 if(!pSkill)
5646 sLog.outError("Skill not found in SkillLineStore: skill #%u", id);
5647 return;
5650 SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id, step));
5651 SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal, maxVal));
5652 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, id);
5653 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL, id);
5655 // insert new entry or update if not deleted old entry yet
5656 if(itr != mSkillStatus.end())
5658 itr->second.pos = i;
5659 itr->second.uState = SKILL_CHANGED;
5661 else
5662 mSkillStatus.insert(SkillStatusMap::value_type(id, SkillStatusData(i, SKILL_NEW)));
5664 // apply skill bonuses
5665 SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i), 0);
5667 // temporary bonuses
5668 AuraList const& mModSkill = GetAurasByType(SPELL_AURA_MOD_SKILL);
5669 for(AuraList::const_iterator j = mModSkill.begin(); j != mModSkill.end(); ++j)
5670 if ((*j)->GetModifier()->m_miscvalue == int32(id))
5671 (*j)->ApplyModifier(true);
5673 // permanent bonuses
5674 AuraList const& mModSkillTalent = GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT);
5675 for(AuraList::const_iterator j = mModSkillTalent.begin(); j != mModSkillTalent.end(); ++j)
5676 if ((*j)->GetModifier()->m_miscvalue == int32(id))
5677 (*j)->ApplyModifier(true);
5679 // Learn all spells for skill
5680 learnSkillRewardedSpells(id, currVal);
5681 return;
5687 bool Player::HasSkill(uint32 skill) const
5689 if(!skill)
5690 return false;
5692 SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
5693 return (itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED);
5696 uint16 Player::GetSkillValue(uint32 skill) const
5698 if(!skill)
5699 return 0;
5701 SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
5702 if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
5703 return 0;
5705 uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos));
5707 int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))));
5708 result += SKILL_TEMP_BONUS(bonus);
5709 result += SKILL_PERM_BONUS(bonus);
5710 return result < 0 ? 0 : result;
5713 uint16 Player::GetMaxSkillValue(uint32 skill) const
5715 if(!skill)
5716 return 0;
5718 SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
5719 if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
5720 return 0;
5722 uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos));
5724 int32 result = int32(SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))));
5725 result += SKILL_TEMP_BONUS(bonus);
5726 result += SKILL_PERM_BONUS(bonus);
5727 return result < 0 ? 0 : result;
5730 uint16 Player::GetPureMaxSkillValue(uint32 skill) const
5732 if(!skill)
5733 return 0;
5735 SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
5736 if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
5737 return 0;
5739 return SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos)));
5742 uint16 Player::GetBaseSkillValue(uint32 skill) const
5744 if(!skill)
5745 return 0;
5747 SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
5748 if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
5749 return 0;
5751 int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))));
5752 result += SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos)));
5753 return result < 0 ? 0 : result;
5756 uint16 Player::GetPureSkillValue(uint32 skill) const
5758 if(!skill)
5759 return 0;
5761 SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
5762 if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
5763 return 0;
5765 return SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos)));
5768 int16 Player::GetSkillPermBonusValue(uint32 skill) const
5770 if(!skill)
5771 return 0;
5773 SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
5774 if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
5775 return 0;
5777 return SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos)));
5780 int16 Player::GetSkillTempBonusValue(uint32 skill) const
5782 if(!skill)
5783 return 0;
5785 SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
5786 if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
5787 return 0;
5789 return SKILL_TEMP_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos)));
5792 void Player::SendInitialActionButtons() const
5794 DETAIL_LOG( "Initializing Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec);
5796 WorldPacket data(SMSG_ACTION_BUTTONS, 1+(MAX_ACTION_BUTTONS*4));
5797 data << uint8(1); // talent spec amount (in packet)
5798 ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec];
5799 for(uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button)
5801 ActionButtonList::const_iterator itr = currentActionButtonList.find(button);
5802 if(itr != currentActionButtonList.end() && itr->second.uState != ACTIONBUTTON_DELETED)
5803 data << uint32(itr->second.packedData);
5804 else
5805 data << uint32(0);
5808 GetSession()->SendPacket( &data );
5809 DETAIL_LOG( "Action Buttons for '%u' spec '%u' Initialized", GetGUIDLow(), m_activeSpec );
5812 bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type, Player* player, bool msg)
5814 if(button >= MAX_ACTION_BUTTONS)
5816 if (msg)
5818 if (player)
5819 sLog.outError( "Action %u not added into button %u for player %s: button must be < %u", action, button, player->GetName(), MAX_ACTION_BUTTONS );
5820 else
5821 sLog.outError( "Table `playercreateinfo_action` have action %u into button %u : button must be < %u", action, button, MAX_ACTION_BUTTONS );
5824 return false;
5827 if(action >= MAX_ACTION_BUTTON_ACTION_VALUE)
5829 if (msg)
5831 if (player)
5832 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 );
5833 else
5834 sLog.outError( "Table `playercreateinfo_action` have action %u into button %u : action must be < %u", action, button, MAX_ACTION_BUTTON_ACTION_VALUE );
5836 return false;
5839 switch(type)
5841 case ACTION_BUTTON_SPELL:
5842 if(!sSpellStore.LookupEntry(action))
5844 if (msg)
5846 if (player)
5847 sLog.outError( "Spell action %u not added into button %u for player %s: spell not exist", action, button, player->GetName() );
5848 else
5849 sLog.outError( "Table `playercreateinfo_action` have spell action %u into button %u: spell not exist", action, button );
5851 return false;
5854 if(player && !player->HasSpell(action))
5856 if (msg)
5857 sLog.outError( "Spell action %u not added into button %u for player %s: player don't known this spell", action, button, player->GetName() );
5858 return false;
5860 break;
5861 case ACTION_BUTTON_ITEM:
5862 if(!ObjectMgr::GetItemPrototype(action))
5864 if (msg)
5866 if (player)
5867 sLog.outError( "Item action %u not added into button %u for player %s: item not exist", action, button, player->GetName() );
5868 else
5869 sLog.outError( "Table `playercreateinfo_action` have item action %u into button %u: item not exist", action, button );
5871 return false;
5873 break;
5874 default:
5875 break; // other cases not checked at this moment
5878 return true;
5881 ActionButton* Player::addActionButton(uint8 spec, uint8 button, uint32 action, uint8 type)
5883 // check action only for active spec (so not check at copy/load passive spec)
5884 if (spec == GetActiveSpec() && !IsActionButtonDataValid(button,action,type,this))
5885 return NULL;
5887 // it create new button (NEW state) if need or return existed
5888 ActionButton& ab = m_actionButtons[spec][button];
5890 // set data and update to CHANGED if not NEW
5891 ab.SetActionAndType(action,ActionButtonType(type));
5893 DETAIL_LOG("Player '%u' Added Action '%u' (type %u) to Button '%u' for spec %u", GetGUIDLow(), action, uint32(type), button, spec);
5894 return &ab;
5897 void Player::removeActionButton(uint8 spec, uint8 button)
5899 ActionButtonList& currentActionButtonList = m_actionButtons[spec];
5900 ActionButtonList::iterator buttonItr = currentActionButtonList.find(button);
5901 if (buttonItr == currentActionButtonList.end() || buttonItr->second.uState == ACTIONBUTTON_DELETED)
5902 return;
5904 if (buttonItr->second.uState == ACTIONBUTTON_NEW)
5905 currentActionButtonList.erase(buttonItr); // new and not saved
5906 else
5907 buttonItr->second.uState = ACTIONBUTTON_DELETED; // saved, will deleted at next save
5909 DETAIL_LOG("Action Button '%u' Removed from Player '%u' for spec %u", button, GetGUIDLow(), spec);
5912 ActionButton const* Player::GetActionButton(uint8 button)
5914 ActionButtonList& currentActionButtonList = m_actionButtons[m_activeSpec];
5915 ActionButtonList::iterator buttonItr = currentActionButtonList.find(button);
5916 if (buttonItr==currentActionButtonList.end() || buttonItr->second.uState == ACTIONBUTTON_DELETED)
5917 return NULL;
5919 return &buttonItr->second;
5922 bool Player::SetPosition(float x, float y, float z, float orientation, bool teleport)
5924 // prevent crash when a bad coord is sent by the client
5925 if(!MaNGOS::IsValidMapCoord(x,y,z,orientation))
5927 DEBUG_LOG("Player::SetPosition(%f, %f, %f, %f, %d) .. bad coordinates for player %d!",x,y,z,orientation,teleport,GetGUIDLow());
5928 return false;
5931 Map *m = GetMap();
5933 const float old_x = GetPositionX();
5934 const float old_y = GetPositionY();
5935 const float old_z = GetPositionZ();
5936 const float old_r = GetOrientation();
5938 if( teleport || old_x != x || old_y != y || old_z != z || old_r != orientation )
5940 if (teleport || old_x != x || old_y != y || old_z != z)
5941 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING);
5942 else
5943 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);
5945 // move and update visible state if need
5946 m->PlayerRelocation(this, x, y, z, orientation);
5948 // reread after Map::Relocation
5949 m = GetMap();
5950 x = GetPositionX();
5951 y = GetPositionY();
5952 z = GetPositionZ();
5954 // group update
5955 if(GetGroup() && (old_x != x || old_y != y))
5956 SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
5959 // code block for underwater state update
5960 UpdateUnderwaterState(m, x, y, z);
5962 CheckExploreSystem();
5964 return true;
5967 void Player::SaveRecallPosition()
5969 m_recallMap = GetMapId();
5970 m_recallX = GetPositionX();
5971 m_recallY = GetPositionY();
5972 m_recallZ = GetPositionZ();
5973 m_recallO = GetOrientation();
5976 void Player::SendMessageToSet(WorldPacket *data, bool self)
5978 Map * _map = IsInWorld() ? GetMap() : sMapMgr.FindMap(GetMapId(), GetInstanceId());
5979 if(_map)
5981 _map->MessageBroadcast(this, data, self);
5982 return;
5985 //if player is not in world and map in not created/already destroyed
5986 //no need to create one, just send packet for itself!
5987 if(self)
5988 GetSession()->SendPacket(data);
5991 void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self)
5993 Map * _map = IsInWorld() ? GetMap() : sMapMgr.FindMap(GetMapId(), GetInstanceId());
5994 if(_map)
5996 _map->MessageDistBroadcast(this, data, dist, self);
5997 return;
6000 if(self)
6001 GetSession()->SendPacket(data);
6004 void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only)
6006 Map * _map = IsInWorld() ? GetMap() : sMapMgr.FindMap(GetMapId(), GetInstanceId());
6007 if(_map)
6009 _map->MessageDistBroadcast(this, data, dist, self, own_team_only);
6010 return;
6013 if(self)
6014 GetSession()->SendPacket(data);
6017 void Player::SendDirectMessage(WorldPacket *data)
6019 GetSession()->SendPacket(data);
6022 void Player::SendCinematicStart(uint32 CinematicSequenceId)
6024 WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
6025 data << uint32(CinematicSequenceId);
6026 SendDirectMessage(&data);
6029 void Player::SendMovieStart(uint32 MovieId)
6031 WorldPacket data(SMSG_TRIGGER_MOVIE, 4);
6032 data << uint32(MovieId);
6033 SendDirectMessage(&data);
6036 void Player::CheckExploreSystem()
6038 if (!isAlive())
6039 return;
6041 if (isInFlight())
6042 return;
6044 uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(),GetPositionY(),GetPositionZ());
6045 if(areaFlag==0xffff)
6046 return;
6047 int offset = areaFlag / 32;
6049 if(offset >= PLAYER_EXPLORED_ZONES_SIZE)
6051 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);
6052 return;
6055 uint32 val = (uint32)(1 << (areaFlag % 32));
6056 uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
6058 if( !(currFields & val) )
6060 SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val));
6062 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA);
6064 AreaTableEntry const *p = GetAreaEntryByAreaFlagAndMap(areaFlag,GetMapId());
6065 if(!p)
6067 sLog.outError("PLAYER: Player %u discovered unknown area (x: %f y: %f map: %u", GetGUIDLow(), GetPositionX(),GetPositionY(),GetMapId());
6069 else if(p->area_level > 0)
6071 uint32 area = p->ID;
6072 if (getLevel() >= sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
6074 SendExplorationExperience(area,0);
6076 else
6078 int32 diff = int32(getLevel()) - p->area_level;
6079 uint32 XP = 0;
6080 if (diff < -5)
6082 XP = uint32(sObjectMgr.GetBaseXP(getLevel()+5)*sWorld.getConfig(CONFIG_FLOAT_RATE_XP_EXPLORE));
6084 else if (diff > 5)
6086 int32 exploration_percent = (100-((diff-5)*5));
6087 if (exploration_percent > 100)
6088 exploration_percent = 100;
6089 else if (exploration_percent < 0)
6090 exploration_percent = 0;
6092 XP = uint32(sObjectMgr.GetBaseXP(p->area_level)*exploration_percent/100*sWorld.getConfig(CONFIG_FLOAT_RATE_XP_EXPLORE));
6094 else
6096 XP = uint32(sObjectMgr.GetBaseXP(p->area_level)*sWorld.getConfig(CONFIG_FLOAT_RATE_XP_EXPLORE));
6099 GiveXP( XP, NULL );
6100 SendExplorationExperience(area,XP);
6102 DETAIL_LOG("PLAYER: Player %u discovered a new area: %u", GetGUIDLow(), area);
6107 uint32 Player::TeamForRace(uint8 race)
6109 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
6110 if(!rEntry)
6112 sLog.outError("Race %u not found in DBC: wrong DBC files?",uint32(race));
6113 return ALLIANCE;
6116 switch(rEntry->TeamID)
6118 case 7: return ALLIANCE;
6119 case 1: return HORDE;
6122 sLog.outError("Race %u have wrong teamid %u in DBC: wrong DBC files?",uint32(race),rEntry->TeamID);
6123 return ALLIANCE;
6126 uint32 Player::getFactionForRace(uint8 race)
6128 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
6129 if(!rEntry)
6131 sLog.outError("Race %u not found in DBC: wrong DBC files?",uint32(race));
6132 return 0;
6135 return rEntry->FactionID;
6138 void Player::setFactionForRace(uint8 race)
6140 m_team = TeamForRace(race);
6141 setFaction( getFactionForRace(race) );
6144 ReputationRank Player::GetReputationRank(uint32 faction) const
6146 FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction);
6147 return GetReputationMgr().GetRank(factionEntry);
6150 //Calculate total reputation percent player gain with quest/creature level
6151 int32 Player::CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, int32 faction, bool for_quest)
6153 float percent = 100.0f;
6155 float rate = for_quest ? sWorld.getConfig(CONFIG_FLOAT_RATE_REPUTATION_LOWLEVEL_QUEST) : sWorld.getConfig(CONFIG_FLOAT_RATE_REPUTATION_LOWLEVEL_KILL);
6157 if (rate != 1.0f && creatureOrQuestLevel <= MaNGOS::XP::GetGrayLevel(getLevel()))
6158 percent *= rate;
6160 float repMod = (float)GetTotalAuraModifier(SPELL_AURA_MOD_REPUTATION_GAIN);
6162 if (!for_quest)
6163 repMod += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_FACTION_REPUTATION_GAIN, faction);
6165 percent += rep > 0 ? repMod : -repMod;
6167 if (percent <= 0.0f)
6168 return 0;
6170 return int32(sWorld.getConfig(CONFIG_FLOAT_RATE_REPUTATION_GAIN)*rep*percent/100.0f);
6173 //Calculates how many reputation points player gains in victim's enemy factions
6174 void Player::RewardReputation(Unit *pVictim, float rate)
6176 if(!pVictim || pVictim->GetTypeId() == TYPEID_PLAYER)
6177 return;
6179 ReputationOnKillEntry const* Rep = sObjectMgr.GetReputationOnKilEntry(((Creature*)pVictim)->GetCreatureInfo()->Entry);
6181 if(!Rep)
6182 return;
6184 if(Rep->repfaction1 && (!Rep->team_dependent || GetTeam()==ALLIANCE))
6186 int32 donerep1 = CalculateReputationGain(pVictim->getLevel(), Rep->repvalue1, Rep->repfaction1, false);
6187 donerep1 = int32(donerep1*rate);
6188 FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(Rep->repfaction1);
6189 uint32 current_reputation_rank1 = GetReputationMgr().GetRank(factionEntry1);
6190 if (factionEntry1 && current_reputation_rank1 <= Rep->reputation_max_cap1)
6191 GetReputationMgr().ModifyReputation(factionEntry1, donerep1);
6193 // Wiki: Team factions value divided by 2
6194 if (factionEntry1 && Rep->is_teamaward1)
6196 FactionEntry const *team1_factionEntry = sFactionStore.LookupEntry(factionEntry1->team);
6197 if(team1_factionEntry)
6198 GetReputationMgr().ModifyReputation(team1_factionEntry, donerep1 / 2);
6202 if(Rep->repfaction2 && (!Rep->team_dependent || GetTeam()==HORDE))
6204 int32 donerep2 = CalculateReputationGain(pVictim->getLevel(), Rep->repvalue2, Rep->repfaction2, false);
6205 donerep2 = int32(donerep2*rate);
6206 FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(Rep->repfaction2);
6207 uint32 current_reputation_rank2 = GetReputationMgr().GetRank(factionEntry2);
6208 if (factionEntry2 && current_reputation_rank2 <= Rep->reputation_max_cap2)
6209 GetReputationMgr().ModifyReputation(factionEntry2, donerep2);
6211 // Wiki: Team factions value divided by 2
6212 if (factionEntry2 && Rep->is_teamaward2)
6214 FactionEntry const *team2_factionEntry = sFactionStore.LookupEntry(factionEntry2->team);
6215 if(team2_factionEntry)
6216 GetReputationMgr().ModifyReputation(team2_factionEntry, donerep2 / 2);
6221 //Calculate how many reputation points player gain with the quest
6222 void Player::RewardReputation(Quest const *pQuest)
6224 // quest reputation reward/loss
6225 for(int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
6227 if (!pQuest->RewRepFaction[i])
6228 continue;
6230 // For future, this row should be used as "override". Example quests are 10298 and 10870.
6231 // Typically, no diplomacy mod must apply to the final value (flat). Note the formula must be (finalValue = DBvalue/100)
6232 if (pQuest->RewRepValue[i])
6234 int32 rep = CalculateReputationGain(GetQuestLevelForPlayer(pQuest), pQuest->RewRepValue[i], pQuest->RewRepFaction[i], true);
6236 if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i]))
6237 GetReputationMgr().ModifyReputation(factionEntry, rep);
6239 else
6241 uint32 row = ((pQuest->RewRepValueId[i] < 0) ? 1 : 0) + 1;
6242 uint32 field = abs(pQuest->RewRepValueId[i]);
6244 if (const QuestFactionRewardEntry *pRow = sQuestFactionRewardStore.LookupEntry(row))
6246 int32 repPoints = pRow->rewardValue[field];
6248 if (!repPoints)
6249 continue;
6251 repPoints = CalculateReputationGain(GetQuestLevelForPlayer(pQuest), repPoints, pQuest->RewRepFaction[i], true);
6253 if (const FactionEntry* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i]))
6254 GetReputationMgr().ModifyReputation(factionEntry, repPoints);
6259 // TODO: implement reputation spillover
6262 void Player::UpdateArenaFields(void)
6264 /* arena calcs go here */
6267 void Player::UpdateHonorFields()
6269 /// called when rewarding honor and at each save
6270 time_t now = time(NULL);
6271 time_t today = (time(NULL) / DAY) * DAY;
6273 if(m_lastHonorUpdateTime < today)
6275 time_t yesterday = today - DAY;
6277 uint16 kills_today = PAIR32_LOPART(GetUInt32Value(PLAYER_FIELD_KILLS));
6279 // update yesterday's contribution
6280 if(m_lastHonorUpdateTime >= yesterday )
6282 SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));
6284 // this is the first update today, reset today's contribution
6285 SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0);
6286 SetUInt32Value(PLAYER_FIELD_KILLS, MAKE_PAIR32(0,kills_today));
6288 else
6290 // no honor/kills yesterday or today, reset
6291 SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0);
6292 SetUInt32Value(PLAYER_FIELD_KILLS, 0);
6296 m_lastHonorUpdateTime = now;
6299 ///Calculate the amount of honor gained based on the victim
6300 ///and the size of the group for which the honor is divided
6301 ///An exact honor value can also be given (overriding the calcs)
6302 bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor)
6304 // do not reward honor in arenas, but enable onkill spellproc
6305 if(InArena())
6307 if(!uVictim || uVictim == this || uVictim->GetTypeId() != TYPEID_PLAYER)
6308 return false;
6310 if( GetBGTeam() == ((Player*)uVictim)->GetBGTeam() )
6311 return false;
6313 return true;
6316 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
6317 if(GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
6318 return false;
6320 uint64 victim_guid = 0;
6321 uint32 victim_rank = 0;
6323 // need call before fields update to have chance move yesterday data to appropriate fields before today data change.
6324 UpdateHonorFields();
6326 if(honor <= 0)
6328 if(!uVictim || uVictim == this || uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
6329 return false;
6331 victim_guid = uVictim->GetGUID();
6333 if( uVictim->GetTypeId() == TYPEID_PLAYER )
6335 Player *pVictim = (Player *)uVictim;
6337 if( GetTeam() == pVictim->GetTeam() && !sWorld.IsFFAPvPRealm() )
6338 return false;
6340 float f = 1; //need for total kills (?? need more info)
6341 uint32 k_grey = 0;
6342 uint32 k_level = getLevel();
6343 uint32 v_level = pVictim->getLevel();
6346 // PLAYER_CHOSEN_TITLE VALUES DESCRIPTION
6347 // [0] Just name
6348 // [1..14] Alliance honor titles and player name
6349 // [15..28] Horde honor titles and player name
6350 // [29..38] Other title and player name
6351 // [39+] Nothing
6352 uint32 victim_title = pVictim->GetUInt32Value(PLAYER_CHOSEN_TITLE);
6353 // Get Killer titles, CharTitlesEntry::bit_index
6354 // Ranks:
6355 // title[1..14] -> rank[5..18]
6356 // title[15..28] -> rank[5..18]
6357 // title[other] -> 0
6358 if (victim_title == 0)
6359 victim_guid = 0; // Don't show HK: <rank> message, only log.
6360 else if (victim_title < 15)
6361 victim_rank = victim_title + 4;
6362 else if (victim_title < 29)
6363 victim_rank = victim_title - 14 + 4;
6364 else
6365 victim_guid = 0; // Don't show HK: <rank> message, only log.
6368 k_grey = MaNGOS::XP::GetGrayLevel(k_level);
6370 if(v_level<=k_grey)
6371 return false;
6373 float diff_level = (k_level == k_grey) ? 1 : ((float(v_level) - float(k_grey)) / (float(k_level) - float(k_grey)));
6375 int32 v_rank =1; //need more info
6377 honor = ((f * diff_level * (190 + v_rank*10))/6);
6378 honor *= ((float)k_level) / 70.0f; //factor of dependence on levels of the killer
6380 // count the number of playerkills in one day
6381 ApplyModUInt32Value(PLAYER_FIELD_KILLS, 1, true);
6382 // and those in a lifetime
6383 ApplyModUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 1, true);
6384 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL);
6385 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS, pVictim->getClass());
6386 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_RACE, pVictim->getRace());
6388 else
6390 Creature *cVictim = (Creature *)uVictim;
6392 if (!cVictim->isRacialLeader())
6393 return false;
6395 honor = 100; // ??? need more info
6396 victim_rank = 19; // HK: Leader
6400 if (uVictim != NULL)
6402 honor *= sWorld.getConfig(CONFIG_FLOAT_RATE_HONOR);
6403 honor *= (GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HONOR_GAIN) + 100.0f)/100.0f;
6405 if(groupsize > 1)
6406 honor /= groupsize;
6408 honor *= (((float)urand(8,12))/10); // approx honor: 80% - 120% of real honor
6411 // honor - for show honor points in log
6412 // victim_guid - for show victim name in log
6413 // victim_rank [1..4] HK: <dishonored rank>
6414 // victim_rank [5..19] HK: <alliance\horde rank>
6415 // victim_rank [0,20+] HK: <>
6416 WorldPacket data(SMSG_PVP_CREDIT,4+8+4);
6417 data << (uint32) honor;
6418 data << (uint64) victim_guid;
6419 data << (uint32) victim_rank;
6421 GetSession()->SendPacket(&data);
6423 // add honor points
6424 ModifyHonorPoints(int32(honor));
6426 ApplyModUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, uint32(honor), true);
6427 return true;
6430 void Player::ModifyHonorPoints( int32 value )
6432 if(value < 0)
6434 if (GetHonorPoints() > sWorld.getConfig(CONFIG_UINT32_MAX_HONOR_POINTS))
6435 SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, sWorld.getConfig(CONFIG_UINT32_MAX_HONOR_POINTS) + value);
6436 else
6437 SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, GetHonorPoints() > uint32(-value) ? GetHonorPoints() + value : 0);
6439 else
6440 SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, GetHonorPoints() < sWorld.getConfig(CONFIG_UINT32_MAX_HONOR_POINTS) - value ? GetHonorPoints() + value : sWorld.getConfig(CONFIG_UINT32_MAX_HONOR_POINTS));
6443 void Player::ModifyArenaPoints( int32 value )
6445 if(value < 0)
6447 if (GetArenaPoints() > sWorld.getConfig(CONFIG_UINT32_MAX_ARENA_POINTS))
6448 SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, sWorld.getConfig(CONFIG_UINT32_MAX_ARENA_POINTS) + value);
6449 else
6450 SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, GetArenaPoints() > uint32(-value) ? GetArenaPoints() + value : 0);
6452 else
6453 SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, GetArenaPoints() < sWorld.getConfig(CONFIG_UINT32_MAX_ARENA_POINTS) - value ? GetArenaPoints() + value : sWorld.getConfig(CONFIG_UINT32_MAX_ARENA_POINTS));
6456 uint32 Player::GetGuildIdFromDB(uint64 guid)
6458 QueryResult* result = CharacterDatabase.PQuery("SELECT guildid FROM guild_member WHERE guid='%u'", GUID_LOPART(guid));
6459 if(!result)
6460 return 0;
6462 uint32 id = result->Fetch()[0].GetUInt32();
6463 delete result;
6464 return id;
6467 uint32 Player::GetRankFromDB(uint64 guid)
6469 QueryResult *result = CharacterDatabase.PQuery( "SELECT rank FROM guild_member WHERE guid='%u'", GUID_LOPART(guid) );
6470 if( result )
6472 uint32 v = result->Fetch()[0].GetUInt32();
6473 delete result;
6474 return v;
6476 else
6477 return 0;
6480 uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type)
6482 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_LOPART(guid), type);
6483 if(!result)
6484 return 0;
6486 uint32 id = (*result)[0].GetUInt32();
6487 delete result;
6488 return id;
6491 uint32 Player::GetZoneIdFromDB(uint64 guid)
6493 uint32 guidLow = GUID_LOPART(guid);
6494 QueryResult *result = CharacterDatabase.PQuery( "SELECT zone FROM characters WHERE guid='%u'", guidLow );
6495 if (!result)
6496 return 0;
6497 Field* fields = result->Fetch();
6498 uint32 zone = fields[0].GetUInt32();
6499 delete result;
6501 if (!zone)
6503 // stored zone is zero, use generic and slow zone detection
6504 result = CharacterDatabase.PQuery("SELECT map,position_x,position_y,position_z FROM characters WHERE guid='%u'", guidLow);
6505 if( !result )
6506 return 0;
6507 fields = result->Fetch();
6508 uint32 map = fields[0].GetUInt32();
6509 float posx = fields[1].GetFloat();
6510 float posy = fields[2].GetFloat();
6511 float posz = fields[3].GetFloat();
6512 delete result;
6514 zone = sMapMgr.GetZoneId(map,posx,posy,posz);
6516 if (zone > 0)
6517 CharacterDatabase.PExecute("UPDATE characters SET zone='%u' WHERE guid='%u'", zone, guidLow);
6520 return zone;
6523 uint32 Player::GetLevelFromDB(uint64 guid)
6525 QueryResult *result = CharacterDatabase.PQuery( "SELECT level FROM characters WHERE guid='%u'", GUID_LOPART(guid) );
6526 if (!result)
6527 return 0;
6529 Field* fields = result->Fetch();
6530 uint32 level = fields[0].GetUInt32();
6531 delete result;
6533 return level;
6536 void Player::UpdateArea(uint32 newArea)
6538 // FFA_PVP flags are area and not zone id dependent
6539 // so apply them accordingly
6540 m_areaUpdateId = newArea;
6542 AreaTableEntry const* area = GetAreaEntryByAreaID(newArea);
6544 if(area && (area->flags & AREA_FLAG_ARENA))
6546 if(!isGameMaster())
6547 SetFFAPvP(true);
6549 else
6551 // remove ffa flag only if not ffapvp realm
6552 // removal in sanctuaries and capitals is handled in zone update
6553 if(IsFFAPvP() && !sWorld.IsFFAPvPRealm())
6554 SetFFAPvP(false);
6557 UpdateAreaDependentAuras(newArea);
6560 void Player::UpdateZone(uint32 newZone, uint32 newArea)
6562 AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone);
6563 if(!zone)
6564 return;
6566 if(m_zoneUpdateId != newZone)
6568 SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange...
6570 if (sWorld.getConfig(CONFIG_BOOL_WEATHER))
6572 if(Weather *wth = sWorld.FindWeather(zone->ID))
6573 wth->SendWeatherUpdateToPlayer(this);
6574 else if(!sWorld.AddWeather(zone->ID))
6576 // send fine weather packet to remove old zone's weather
6577 Weather::SendFineWeatherUpdateToPlayer(this);
6582 m_zoneUpdateId = newZone;
6583 m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
6585 // zone changed, so area changed as well, update it
6586 UpdateArea(newArea);
6588 // in PvP, any not controlled zone (except zone->team == 6, default case)
6589 // in PvE, only opposition team capital
6590 switch(zone->team)
6592 case AREATEAM_ALLY:
6593 pvpInfo.inHostileArea = GetTeam() != ALLIANCE && (sWorld.IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
6594 break;
6595 case AREATEAM_HORDE:
6596 pvpInfo.inHostileArea = GetTeam() != HORDE && (sWorld.IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
6597 break;
6598 case AREATEAM_NONE:
6599 // overwrite for battlegrounds, maybe batter some zone flags but current known not 100% fit to this
6600 pvpInfo.inHostileArea = sWorld.IsPvPRealm() || InBattleGround();
6601 break;
6602 default: // 6 in fact
6603 pvpInfo.inHostileArea = false;
6604 break;
6607 if(pvpInfo.inHostileArea) // in hostile area
6609 if(!IsPvP() || pvpInfo.endTimer != 0)
6610 UpdatePvP(true, true);
6612 else // in friendly area
6614 if(IsPvP() && !HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_IN_PVP) && pvpInfo.endTimer == 0)
6615 pvpInfo.endTimer = time(0); // start toggle-off
6618 if(zone->flags & AREA_FLAG_SANCTUARY) // in sanctuary
6620 SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
6621 if(sWorld.IsFFAPvPRealm())
6622 SetFFAPvP(false);
6624 else
6626 RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
6629 if(zone->flags & AREA_FLAG_CAPITAL) // in capital city
6630 SetRestType(REST_TYPE_IN_CITY);
6631 else // anywhere else
6633 if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) // but resting (walk from city or maybe in tavern or leave tavern recently)
6635 if (GetRestType()==REST_TYPE_IN_TAVERN) // has been in tavern. Is still in?
6637 AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(inn_trigger_id);
6638 if (!at || !IsPointInAreaTriggerZone(at, GetMapId(), GetPositionX(), GetPositionY(), GetPositionY()))
6639 SetRestType(REST_TYPE_NO);
6641 else // not in tavern (leave city then)
6642 SetRestType(REST_TYPE_NO);
6646 // remove items with area/map limitations (delete only for alive player to allow back in ghost mode)
6647 // if player resurrected at teleport this will be applied in resurrect code
6648 if(isAlive())
6649 DestroyZoneLimitedItem( true, newZone );
6651 // check some item equip limitations (in result lost CanTitanGrip at talent reset, for example)
6652 AutoUnequipOffhandIfNeed();
6654 // recent client version not send leave/join channel packets for built-in local channels
6655 UpdateLocalChannels( newZone );
6657 // group update
6658 if(GetGroup())
6659 SetGroupUpdateFlag(GROUP_UPDATE_FLAG_ZONE);
6661 UpdateZoneDependentAuras(newZone);
6664 //If players are too far way of duel flag... then player loose the duel
6665 void Player::CheckDuelDistance(time_t currTime)
6667 if(!duel)
6668 return;
6670 uint64 duelFlagGUID = GetUInt64Value(PLAYER_DUEL_ARBITER);
6671 GameObject* obj = GetMap()->GetGameObject(duelFlagGUID);
6672 if(!obj)
6673 return;
6675 if(duel->outOfBound == 0)
6677 if(!IsWithinDistInMap(obj, 50))
6679 duel->outOfBound = currTime;
6681 WorldPacket data(SMSG_DUEL_OUTOFBOUNDS, 0);
6682 GetSession()->SendPacket(&data);
6685 else
6687 if(IsWithinDistInMap(obj, 40))
6689 duel->outOfBound = 0;
6691 WorldPacket data(SMSG_DUEL_INBOUNDS, 0);
6692 GetSession()->SendPacket(&data);
6694 else if(currTime >= (duel->outOfBound+10))
6696 DuelComplete(DUEL_FLED);
6701 void Player::DuelComplete(DuelCompleteType type)
6703 // duel not requested
6704 if(!duel)
6705 return;
6707 WorldPacket data(SMSG_DUEL_COMPLETE, (1));
6708 data << (uint8)((type != DUEL_INTERUPTED) ? 1 : 0);
6709 GetSession()->SendPacket(&data);
6710 duel->opponent->GetSession()->SendPacket(&data);
6712 if(type != DUEL_INTERUPTED)
6714 data.Initialize(SMSG_DUEL_WINNER, (1+20)); // we guess size
6715 data << (uint8)((type==DUEL_WON) ? 0 : 1); // 0 = just won; 1 = fled
6716 data << duel->opponent->GetName();
6717 data << GetName();
6718 SendMessageToSet(&data,true);
6721 if (type == DUEL_WON)
6723 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL, 1);
6724 if (duel->opponent)
6725 duel->opponent->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1);
6728 //Remove Duel Flag object
6729 GameObject* obj = GetMap()->GetGameObject(GetUInt64Value(PLAYER_DUEL_ARBITER));
6730 if(obj)
6731 duel->initiator->RemoveGameObject(obj,true);
6733 /* remove auras */
6734 std::vector<uint32> auras2remove;
6735 AuraMap const& vAuras = duel->opponent->GetAuras();
6736 for (AuraMap::const_iterator i = vAuras.begin(); i != vAuras.end(); ++i)
6738 if (!i->second->IsPositive() && i->second->GetCasterGUID() == GetGUID() && i->second->GetAuraApplyTime() >= duel->startTime)
6739 auras2remove.push_back(i->second->GetId());
6742 for(size_t i=0; i<auras2remove.size(); ++i)
6743 duel->opponent->RemoveAurasDueToSpell(auras2remove[i]);
6745 auras2remove.clear();
6746 AuraMap const& auras = GetAuras();
6747 for (AuraMap::const_iterator i = auras.begin(); i != auras.end(); ++i)
6749 if (!i->second->IsPositive() && i->second->GetCasterGUID() == duel->opponent->GetGUID() && i->second->GetAuraApplyTime() >= duel->startTime)
6750 auras2remove.push_back(i->second->GetId());
6752 for(size_t i=0; i<auras2remove.size(); ++i)
6753 RemoveAurasDueToSpell(auras2remove[i]);
6755 // cleanup combo points
6756 if(GetComboTarget()==duel->opponent->GetGUID())
6757 ClearComboPoints();
6758 else if(GetComboTarget()==duel->opponent->GetPetGUID())
6759 ClearComboPoints();
6761 if(duel->opponent->GetComboTarget()==GetGUID())
6762 duel->opponent->ClearComboPoints();
6763 else if(duel->opponent->GetComboTarget()==GetPetGUID())
6764 duel->opponent->ClearComboPoints();
6766 //cleanups
6767 SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
6768 SetUInt32Value(PLAYER_DUEL_TEAM, 0);
6769 duel->opponent->SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
6770 duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0);
6772 delete duel->opponent->duel;
6773 duel->opponent->duel = NULL;
6774 delete duel;
6775 duel = NULL;
6778 //---------------------------------------------------------//
6780 void Player::_ApplyItemMods(Item *item, uint8 slot,bool apply)
6782 if(slot >= INVENTORY_SLOT_BAG_END || !item)
6783 return;
6785 // not apply/remove mods for broken item
6786 if(item->IsBroken())
6787 return;
6789 ItemPrototype const *proto = item->GetProto();
6791 if(!proto)
6792 return;
6794 DETAIL_LOG("applying mods for item %u ",item->GetGUIDLow());
6796 uint32 attacktype = Player::GetAttackBySlot(slot);
6797 if(attacktype < MAX_ATTACK)
6798 _ApplyWeaponDependentAuraMods(item,WeaponAttackType(attacktype),apply);
6800 _ApplyItemBonuses(proto,slot,apply);
6802 if( slot==EQUIPMENT_SLOT_RANGED )
6803 _ApplyAmmoBonuses();
6805 ApplyItemEquipSpell(item,apply);
6806 ApplyEnchantment(item, apply);
6808 if(proto->Socket[0].Color) //only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items
6809 CorrectMetaGemEnchants(slot, apply);
6811 DEBUG_LOG("_ApplyItemMods complete.");
6814 void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool apply, bool only_level_scale /*= false*/)
6816 if (slot >= INVENTORY_SLOT_BAG_END || !proto)
6817 return;
6819 ScalingStatDistributionEntry const *ssd = proto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(proto->ScalingStatDistribution) : NULL;
6820 if (only_level_scale && !ssd)
6821 return;
6823 // req. check at equip, but allow use for extended range if range limit max level, set proper level
6824 uint32 ssd_level = getLevel();
6825 if (ssd && ssd_level > ssd->MaxLevel)
6826 ssd_level = ssd->MaxLevel;
6828 ScalingStatValuesEntry const *ssv = proto->ScalingStatValue ? sScalingStatValuesStore.LookupEntry(ssd_level) : NULL;
6829 if (only_level_scale && !ssv)
6830 return;
6832 for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
6834 uint32 statType = 0;
6835 int32 val = 0;
6836 // If set ScalingStatDistribution need get stats and values from it
6837 if (ssd && ssv)
6839 if (ssd->StatMod[i] < 0)
6840 continue;
6841 statType = ssd->StatMod[i];
6842 val = (ssv->getssdMultiplier(proto->ScalingStatValue) * ssd->Modifier[i]) / 10000;
6844 else
6846 if (i >= proto->StatsCount)
6847 continue;
6848 statType = proto->ItemStat[i].ItemStatType;
6849 val = proto->ItemStat[i].ItemStatValue;
6852 if(val == 0)
6853 continue;
6855 switch (statType)
6857 case ITEM_MOD_MANA:
6858 HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply);
6859 break;
6860 case ITEM_MOD_HEALTH: // modify HP
6861 HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply);
6862 break;
6863 case ITEM_MOD_AGILITY: // modify agility
6864 HandleStatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply);
6865 ApplyStatBuffMod(STAT_AGILITY, float(val), apply);
6866 break;
6867 case ITEM_MOD_STRENGTH: //modify strength
6868 HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply);
6869 ApplyStatBuffMod(STAT_STRENGTH, float(val), apply);
6870 break;
6871 case ITEM_MOD_INTELLECT: //modify intellect
6872 HandleStatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply);
6873 ApplyStatBuffMod(STAT_INTELLECT, float(val), apply);
6874 break;
6875 case ITEM_MOD_SPIRIT: //modify spirit
6876 HandleStatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply);
6877 ApplyStatBuffMod(STAT_SPIRIT, float(val), apply);
6878 break;
6879 case ITEM_MOD_STAMINA: //modify stamina
6880 HandleStatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply);
6881 ApplyStatBuffMod(STAT_STAMINA, float(val), apply);
6882 break;
6883 case ITEM_MOD_DEFENSE_SKILL_RATING:
6884 ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply);
6885 break;
6886 case ITEM_MOD_DODGE_RATING:
6887 ApplyRatingMod(CR_DODGE, int32(val), apply);
6888 break;
6889 case ITEM_MOD_PARRY_RATING:
6890 ApplyRatingMod(CR_PARRY, int32(val), apply);
6891 break;
6892 case ITEM_MOD_BLOCK_RATING:
6893 ApplyRatingMod(CR_BLOCK, int32(val), apply);
6894 break;
6895 case ITEM_MOD_HIT_MELEE_RATING:
6896 ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
6897 break;
6898 case ITEM_MOD_HIT_RANGED_RATING:
6899 ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
6900 break;
6901 case ITEM_MOD_HIT_SPELL_RATING:
6902 ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
6903 break;
6904 case ITEM_MOD_CRIT_MELEE_RATING:
6905 ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
6906 break;
6907 case ITEM_MOD_CRIT_RANGED_RATING:
6908 ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
6909 break;
6910 case ITEM_MOD_CRIT_SPELL_RATING:
6911 ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
6912 break;
6913 case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
6914 ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
6915 break;
6916 case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
6917 ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
6918 break;
6919 case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
6920 ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
6921 break;
6922 case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
6923 ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
6924 break;
6925 case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
6926 ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
6927 break;
6928 case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
6929 ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
6930 break;
6931 case ITEM_MOD_HASTE_MELEE_RATING:
6932 ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
6933 break;
6934 case ITEM_MOD_HASTE_RANGED_RATING:
6935 ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
6936 break;
6937 case ITEM_MOD_HASTE_SPELL_RATING:
6938 ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
6939 break;
6940 case ITEM_MOD_HIT_RATING:
6941 ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
6942 ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
6943 ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
6944 break;
6945 case ITEM_MOD_CRIT_RATING:
6946 ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
6947 ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
6948 ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
6949 break;
6950 case ITEM_MOD_HIT_TAKEN_RATING:
6951 ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
6952 ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
6953 ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
6954 break;
6955 case ITEM_MOD_CRIT_TAKEN_RATING:
6956 ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
6957 ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
6958 ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
6959 break;
6960 case ITEM_MOD_RESILIENCE_RATING:
6961 ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
6962 ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
6963 ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
6964 break;
6965 case ITEM_MOD_HASTE_RATING:
6966 ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
6967 ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
6968 ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
6969 break;
6970 case ITEM_MOD_EXPERTISE_RATING:
6971 ApplyRatingMod(CR_EXPERTISE, int32(val), apply);
6972 break;
6973 case ITEM_MOD_ATTACK_POWER:
6974 HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(val), apply);
6975 HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply);
6976 break;
6977 case ITEM_MOD_RANGED_ATTACK_POWER:
6978 HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply);
6979 break;
6980 case ITEM_MOD_MANA_REGENERATION:
6981 ApplyManaRegenBonus(int32(val), apply);
6982 break;
6983 case ITEM_MOD_ARMOR_PENETRATION_RATING:
6984 ApplyRatingMod(CR_ARMOR_PENETRATION, int32(val), apply);
6985 break;
6986 case ITEM_MOD_SPELL_POWER:
6987 ApplySpellPowerBonus(int32(val), apply);
6988 break;
6989 case ITEM_MOD_BLOCK_VALUE:
6990 HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(val), apply);
6991 break;
6992 // depricated item mods
6993 case ITEM_MOD_FERAL_ATTACK_POWER:
6994 case ITEM_MOD_SPELL_HEALING_DONE:
6995 case ITEM_MOD_SPELL_DAMAGE_DONE:
6996 break;
7000 // Apply Spell Power from ScalingStatValue if set
7001 if(ssv)
7003 if (int32 spellbonus = ssv->getSpellBonus(proto->ScalingStatValue))
7004 ApplySpellPowerBonus(spellbonus, apply);
7007 // If set ScalingStatValue armor get it or use item armor
7008 uint32 armor = proto->Armor;
7009 if (ssv)
7011 if (uint32 ssvarmor = ssv->getArmorMod(proto->ScalingStatValue))
7012 armor = ssvarmor;
7014 // Add armor bonus from ArmorDamageModifier if > 0
7015 if (proto->ArmorDamageModifier > 0)
7016 armor += uint32(proto->ArmorDamageModifier);
7018 if (armor)
7019 HandleStatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(armor), apply);
7021 if (proto->Block)
7022 HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(proto->Block), apply);
7024 if (proto->HolyRes)
7025 HandleStatModifier(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(proto->HolyRes), apply);
7027 if (proto->FireRes)
7028 HandleStatModifier(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(proto->FireRes), apply);
7030 if (proto->NatureRes)
7031 HandleStatModifier(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(proto->NatureRes), apply);
7033 if (proto->FrostRes)
7034 HandleStatModifier(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(proto->FrostRes), apply);
7036 if (proto->ShadowRes)
7037 HandleStatModifier(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(proto->ShadowRes), apply);
7039 if (proto->ArcaneRes)
7040 HandleStatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(proto->ArcaneRes), apply);
7042 WeaponAttackType attType = BASE_ATTACK;
7043 float damage = 0.0f;
7045 if( slot == EQUIPMENT_SLOT_RANGED && (
7046 proto->InventoryType == INVTYPE_RANGED || proto->InventoryType == INVTYPE_THROWN ||
7047 proto->InventoryType == INVTYPE_RANGEDRIGHT ))
7049 attType = RANGED_ATTACK;
7051 else if(slot==EQUIPMENT_SLOT_OFFHAND)
7053 attType = OFF_ATTACK;
7056 float minDamage = proto->Damage[0].DamageMin;
7057 float maxDamage = proto->Damage[0].DamageMax;
7058 int32 extraDPS = 0;
7059 // If set dpsMod in ScalingStatValue use it for min (70% from average), max (130% from average) damage
7060 if (ssv)
7062 if ((extraDPS = ssv->getDPSMod(proto->ScalingStatValue)))
7064 float average = extraDPS * proto->Delay / 1000.0f;
7065 minDamage = 0.7f * average;
7066 maxDamage = 1.3f * average;
7069 if (minDamage > 0 )
7071 damage = apply ? minDamage : BASE_MINDAMAGE;
7072 SetBaseWeaponDamage(attType, MINDAMAGE, damage);
7073 //sLog.outError("applying mindam: assigning %f to weapon mindamage, now is: %f", damage, GetWeaponDamageRange(attType, MINDAMAGE));
7076 if (maxDamage > 0 )
7078 damage = apply ? maxDamage : BASE_MAXDAMAGE;
7079 SetBaseWeaponDamage(attType, MAXDAMAGE, damage);
7082 // Apply feral bonus from ScalingStatValue if set
7083 if (ssv)
7085 if (int32 feral_bonus = ssv->getFeralBonus(proto->ScalingStatValue))
7086 ApplyFeralAPBonus(feral_bonus, apply);
7088 // Druids get feral AP bonus from weapon dps (lso use DPS from ScalingStatValue)
7089 if(getClass() == CLASS_DRUID)
7091 int32 feral_bonus = proto->getFeralBonus(extraDPS);
7092 if (feral_bonus > 0)
7093 ApplyFeralAPBonus(feral_bonus, apply);
7096 if(!IsUseEquipedWeapon(slot==EQUIPMENT_SLOT_MAINHAND))
7097 return;
7099 if (proto->Delay)
7101 if(slot == EQUIPMENT_SLOT_RANGED)
7102 SetAttackTime(RANGED_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
7103 else if(slot==EQUIPMENT_SLOT_MAINHAND)
7104 SetAttackTime(BASE_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
7105 else if(slot==EQUIPMENT_SLOT_OFFHAND)
7106 SetAttackTime(OFF_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
7109 if(CanModifyStats() && (damage || proto->Delay))
7110 UpdateDamagePhysical(attType);
7113 void Player::_ApplyWeaponDependentAuraMods(Item *item,WeaponAttackType attackType,bool apply)
7115 AuraList const& auraCritList = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT);
7116 for(AuraList::const_iterator itr = auraCritList.begin(); itr!=auraCritList.end();++itr)
7117 _ApplyWeaponDependentAuraCritMod(item,attackType,*itr,apply);
7119 AuraList const& auraDamageFlatList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE);
7120 for(AuraList::const_iterator itr = auraDamageFlatList.begin(); itr!=auraDamageFlatList.end();++itr)
7121 _ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply);
7123 AuraList const& auraDamagePCTList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
7124 for(AuraList::const_iterator itr = auraDamagePCTList.begin(); itr!=auraDamagePCTList.end();++itr)
7125 _ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply);
7128 void Player::_ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply)
7130 // generic not weapon specific case processes in aura code
7131 if(aura->GetSpellProto()->EquippedItemClass == -1)
7132 return;
7134 BaseModGroup mod = BASEMOD_END;
7135 switch(attackType)
7137 case BASE_ATTACK: mod = CRIT_PERCENTAGE; break;
7138 case OFF_ATTACK: mod = OFFHAND_CRIT_PERCENTAGE;break;
7139 case RANGED_ATTACK: mod = RANGED_CRIT_PERCENTAGE; break;
7140 default: return;
7143 if (item->IsFitToSpellRequirements(aura->GetSpellProto()))
7145 HandleBaseModValue(mod, FLAT_MOD, float (aura->GetModifier()->m_amount), apply);
7149 void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply)
7151 // ignore spell mods for not wands
7152 Modifier const* modifier = aura->GetModifier();
7153 if((modifier->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)==0 && (getClassMask() & CLASSMASK_WAND_USERS)==0)
7154 return;
7156 // generic not weapon specific case processes in aura code
7157 if(aura->GetSpellProto()->EquippedItemClass == -1)
7158 return;
7160 UnitMods unitMod = UNIT_MOD_END;
7161 switch(attackType)
7163 case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break;
7164 case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break;
7165 case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break;
7166 default: return;
7169 UnitModifierType unitModType = TOTAL_VALUE;
7170 switch(modifier->m_auraname)
7172 case SPELL_AURA_MOD_DAMAGE_DONE: unitModType = TOTAL_VALUE; break;
7173 case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: unitModType = TOTAL_PCT; break;
7174 default: return;
7177 if (item->IsFitToSpellRequirements(aura->GetSpellProto()))
7179 HandleStatModifier(unitMod, unitModType, float(modifier->m_amount),apply);
7183 void Player::ApplyItemEquipSpell(Item *item, bool apply, bool form_change)
7185 if(!item)
7186 return;
7188 ItemPrototype const *proto = item->GetProto();
7189 if(!proto)
7190 return;
7192 for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
7194 _Spell const& spellData = proto->Spells[i];
7196 // no spell
7197 if(!spellData.SpellId )
7198 continue;
7200 // wrong triggering type
7201 if(apply && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP)
7202 continue;
7204 // check if it is valid spell
7205 SpellEntry const* spellproto = sSpellStore.LookupEntry(spellData.SpellId);
7206 if(!spellproto)
7207 continue;
7209 ApplyEquipSpell(spellproto,item,apply,form_change);
7213 void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply, bool form_change)
7215 if(apply)
7217 // Cannot be used in this stance/form
7218 if(GetErrorAtShapeshiftedCast(spellInfo, m_form) != SPELL_CAST_OK)
7219 return;
7221 if(form_change) // check aura active state from other form
7223 bool found = false;
7224 for (int k=0; k < MAX_EFFECT_INDEX; ++k)
7226 spellEffectPair spair = spellEffectPair(spellInfo->Id, SpellEffectIndex(k));
7227 for (AuraMap::const_iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair); ++iter)
7229 if(!item || iter->second->GetCastItemGUID() == item->GetGUID())
7231 found = true;
7232 break;
7235 if(found)
7236 break;
7239 if(found) // and skip re-cast already active aura at form change
7240 return;
7243 DEBUG_LOG("WORLD: cast %s Equip spellId - %i", (item ? "item" : "itemset"), spellInfo->Id);
7245 CastSpell(this,spellInfo,true,item);
7247 else
7249 if(form_change) // check aura compatibility
7251 // Cannot be used in this stance/form
7252 if(GetErrorAtShapeshiftedCast(spellInfo, m_form)==SPELL_CAST_OK)
7253 return; // and remove only not compatible at form change
7256 if(item)
7257 RemoveAurasDueToItemSpell(item,spellInfo->Id); // un-apply all spells , not only at-equipped
7258 else
7259 RemoveAurasDueToSpell(spellInfo->Id); // un-apply spell (item set case)
7263 void Player::UpdateEquipSpellsAtFormChange()
7265 for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
7267 if(m_items[i] && !m_items[i]->IsBroken())
7269 ApplyItemEquipSpell(m_items[i],false,true); // remove spells that not fit to form
7270 ApplyItemEquipSpell(m_items[i],true,true); // add spells that fit form but not active
7274 // item set bonuses not dependent from item broken state
7275 for(size_t setindex = 0; setindex < ItemSetEff.size(); ++setindex)
7277 ItemSetEffect* eff = ItemSetEff[setindex];
7278 if(!eff)
7279 continue;
7281 for(uint32 y=0;y<8; ++y)
7283 SpellEntry const* spellInfo = eff->spells[y];
7284 if(!spellInfo)
7285 continue;
7287 ApplyEquipSpell(spellInfo,NULL,false,true); // remove spells that not fit to form
7288 ApplyEquipSpell(spellInfo,NULL,true,true); // add spells that fit form but not active
7293 void Player::CastItemCombatSpell(Unit* Target, WeaponAttackType attType)
7295 Item *item = GetWeaponForAttack(attType, true, false);
7296 if(!item)
7297 return;
7299 ItemPrototype const *proto = item->GetProto();
7300 if(!proto)
7301 return;
7303 if (!Target || Target == this )
7304 return;
7306 for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
7308 _Spell const& spellData = proto->Spells[i];
7310 // no spell
7311 if(!spellData.SpellId )
7312 continue;
7314 // wrong triggering type
7315 if(spellData.SpellTrigger != ITEM_SPELLTRIGGER_CHANCE_ON_HIT)
7316 continue;
7318 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId);
7319 if(!spellInfo)
7321 sLog.outError("WORLD: unknown Item spellid %i", spellData.SpellId);
7322 continue;
7325 // not allow proc extra attack spell at extra attack
7326 if( m_extraAttacks && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_ADD_EXTRA_ATTACKS) )
7327 return;
7329 float chance = (float)spellInfo->procChance;
7331 if(spellData.SpellPPMRate)
7333 uint32 WeaponSpeed = proto->Delay;
7334 chance = GetPPMProcChance(WeaponSpeed, spellData.SpellPPMRate);
7336 else if(chance > 100.0f)
7338 chance = GetWeaponProcChance();
7341 if (roll_chance_f(chance))
7342 CastSpell(Target, spellInfo->Id, true, item);
7345 // item combat enchantments
7346 for(int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot)
7348 uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot));
7349 SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
7350 if(!pEnchant) continue;
7351 for (int s = 0; s < 3; ++s)
7353 if (pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
7354 continue;
7356 SpellEntry const *spellInfo = sSpellStore.LookupEntry(pEnchant->spellid[s]);
7357 if (!spellInfo)
7359 sLog.outError("Player::CastItemCombatSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]);
7360 continue;
7363 // Use first rank to access spell item enchant procs
7364 float ppmRate = sSpellMgr.GetItemEnchantProcChance(spellInfo->Id);
7366 float chance = ppmRate
7367 ? GetPPMProcChance(proto->Delay, ppmRate)
7368 : pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance();
7371 ApplySpellMod(spellInfo->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance);
7372 ApplySpellMod(spellInfo->Id,SPELLMOD_FREQUENCY_OF_SUCCESS,chance);
7374 if (roll_chance_f(chance))
7376 if(IsPositiveSpell(pEnchant->spellid[s]))
7377 CastSpell(this, pEnchant->spellid[s], true, item);
7378 else
7379 CastSpell(Target, pEnchant->spellid[s], true, item);
7385 void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 cast_count, uint32 glyphIndex)
7387 ItemPrototype const* proto = item->GetProto();
7388 // special learning case
7389 if(proto->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN || proto->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN_PET)
7391 uint32 learn_spell_id = proto->Spells[0].SpellId;
7392 uint32 learning_spell_id = proto->Spells[1].SpellId;
7394 SpellEntry const *spellInfo = sSpellStore.LookupEntry(learn_spell_id);
7395 if (!spellInfo)
7397 sLog.outError("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, learn_spell_id);
7398 SendEquipError(EQUIP_ERR_NONE, item);
7399 return;
7402 Spell *spell = new Spell(this, spellInfo, false);
7403 spell->m_CastItem = item;
7404 spell->m_cast_count = cast_count; //set count of casts
7405 spell->m_currentBasePoints[EFFECT_INDEX_0] = learning_spell_id;
7406 spell->prepare(&targets);
7407 return;
7410 // use triggered flag only for items with many spell casts and for not first cast
7411 int count = 0;
7413 // item spells casted at use
7414 for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
7416 _Spell const& spellData = proto->Spells[i];
7418 // no spell
7419 if(!spellData.SpellId)
7420 continue;
7422 // wrong triggering type
7423 if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE)
7424 continue;
7426 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId);
7427 if(!spellInfo)
7429 sLog.outError("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring",proto->ItemId, spellData.SpellId);
7430 continue;
7433 Spell *spell = new Spell(this, spellInfo, (count > 0));
7434 spell->m_CastItem = item;
7435 spell->m_cast_count = cast_count; // set count of casts
7436 spell->m_glyphIndex = glyphIndex; // glyph index
7437 spell->prepare(&targets);
7439 ++count;
7442 // Item enchantments spells casted at use
7443 for(int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot)
7445 uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot));
7446 SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
7447 if(!pEnchant) continue;
7448 for (int s = 0; s < 3; ++s)
7450 if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_USE_SPELL)
7451 continue;
7453 SpellEntry const *spellInfo = sSpellStore.LookupEntry(pEnchant->spellid[s]);
7454 if (!spellInfo)
7456 sLog.outError("Player::CastItemUseSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]);
7457 continue;
7460 Spell *spell = new Spell(this, spellInfo, (count > 0));
7461 spell->m_CastItem = item;
7462 spell->m_cast_count = cast_count; // set count of casts
7463 spell->m_glyphIndex = glyphIndex; // glyph index
7464 spell->prepare(&targets);
7466 ++count;
7471 void Player::_RemoveAllItemMods()
7473 DEBUG_LOG("_RemoveAllItemMods start.");
7475 for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
7477 if(m_items[i])
7479 ItemPrototype const *proto = m_items[i]->GetProto();
7480 if(!proto)
7481 continue;
7483 // item set bonuses not dependent from item broken state
7484 if(proto->ItemSet)
7485 RemoveItemsSetItem(this,proto);
7487 if(m_items[i]->IsBroken())
7488 continue;
7490 ApplyItemEquipSpell(m_items[i],false);
7491 ApplyEnchantment(m_items[i], false);
7495 for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
7497 if(m_items[i])
7499 if(m_items[i]->IsBroken())
7500 continue;
7501 ItemPrototype const *proto = m_items[i]->GetProto();
7502 if(!proto)
7503 continue;
7505 uint32 attacktype = Player::GetAttackBySlot(i);
7506 if(attacktype < MAX_ATTACK)
7507 _ApplyWeaponDependentAuraMods(m_items[i],WeaponAttackType(attacktype),false);
7509 _ApplyItemBonuses(proto,i, false);
7511 if( i == EQUIPMENT_SLOT_RANGED )
7512 _ApplyAmmoBonuses();
7516 DEBUG_LOG("_RemoveAllItemMods complete.");
7519 void Player::_ApplyAllItemMods()
7521 DEBUG_LOG("_ApplyAllItemMods start.");
7523 for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
7525 if(m_items[i])
7527 if(m_items[i]->IsBroken())
7528 continue;
7530 ItemPrototype const *proto = m_items[i]->GetProto();
7531 if(!proto)
7532 continue;
7534 uint32 attacktype = Player::GetAttackBySlot(i);
7535 if(attacktype < MAX_ATTACK)
7536 _ApplyWeaponDependentAuraMods(m_items[i],WeaponAttackType(attacktype),true);
7538 _ApplyItemBonuses(proto,i, true);
7540 if( i == EQUIPMENT_SLOT_RANGED )
7541 _ApplyAmmoBonuses();
7545 for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
7547 if(m_items[i])
7549 ItemPrototype const *proto = m_items[i]->GetProto();
7550 if(!proto)
7551 continue;
7553 // item set bonuses not dependent from item broken state
7554 if(proto->ItemSet)
7555 AddItemsSetItem(this,m_items[i]);
7557 if(m_items[i]->IsBroken())
7558 continue;
7560 ApplyItemEquipSpell(m_items[i],true);
7561 ApplyEnchantment(m_items[i], true);
7565 DEBUG_LOG("_ApplyAllItemMods complete.");
7568 void Player::_ApplyAllLevelScaleItemMods(bool apply)
7570 for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
7572 if(m_items[i])
7574 if(m_items[i]->IsBroken())
7575 continue;
7577 ItemPrototype const *proto = m_items[i]->GetProto();
7578 if(!proto)
7579 continue;
7581 _ApplyItemBonuses(proto,i, apply, true);
7586 void Player::_ApplyAmmoBonuses()
7588 // check ammo
7589 uint32 ammo_id = GetUInt32Value(PLAYER_AMMO_ID);
7590 if(!ammo_id)
7591 return;
7593 float currentAmmoDPS;
7595 ItemPrototype const *ammo_proto = ObjectMgr::GetItemPrototype( ammo_id );
7596 if( !ammo_proto || ammo_proto->Class!=ITEM_CLASS_PROJECTILE || !CheckAmmoCompatibility(ammo_proto))
7597 currentAmmoDPS = 0.0f;
7598 else
7599 currentAmmoDPS = ammo_proto->Damage[0].DamageMin;
7601 if(currentAmmoDPS == GetAmmoDPS())
7602 return;
7604 m_ammoDPS = currentAmmoDPS;
7606 if(CanModifyStats())
7607 UpdateDamagePhysical(RANGED_ATTACK);
7610 bool Player::CheckAmmoCompatibility(const ItemPrototype *ammo_proto) const
7612 if(!ammo_proto)
7613 return false;
7615 // check ranged weapon
7616 Item *weapon = GetWeaponForAttack( RANGED_ATTACK, true, false );
7617 if (!weapon)
7618 return false;
7620 ItemPrototype const* weapon_proto = weapon->GetProto();
7621 if(!weapon_proto || weapon_proto->Class!=ITEM_CLASS_WEAPON )
7622 return false;
7624 // check ammo ws. weapon compatibility
7625 switch(weapon_proto->SubClass)
7627 case ITEM_SUBCLASS_WEAPON_BOW:
7628 case ITEM_SUBCLASS_WEAPON_CROSSBOW:
7629 if(ammo_proto->SubClass!=ITEM_SUBCLASS_ARROW)
7630 return false;
7631 break;
7632 case ITEM_SUBCLASS_WEAPON_GUN:
7633 if(ammo_proto->SubClass!=ITEM_SUBCLASS_BULLET)
7634 return false;
7635 break;
7636 default:
7637 return false;
7640 return true;
7643 /* If in a battleground a player dies, and an enemy removes the insignia, the player's bones is lootable
7644 Called by remove insignia spell effect */
7645 void Player::RemovedInsignia(Player* looterPlr)
7647 if (!GetBattleGroundId())
7648 return;
7650 // If not released spirit, do it !
7651 if(m_deathTimer > 0)
7653 m_deathTimer = 0;
7654 BuildPlayerRepop();
7655 RepopAtGraveyard();
7658 Corpse *corpse = GetCorpse();
7659 if (!corpse)
7660 return;
7662 // We have to convert player corpse to bones, not to be able to resurrect there
7663 // SpawnCorpseBones isn't handy, 'cos it saves player while he in BG
7664 Corpse *bones = sObjectAccessor.ConvertCorpseForPlayer(GetGUID(),true);
7665 if (!bones)
7666 return;
7668 // Now we must make bones lootable, and send player loot
7669 bones->SetFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);
7671 // We store the level of our player in the gold field
7672 // We retrieve this information at Player::SendLoot()
7673 bones->loot.gold = getLevel();
7674 bones->lootRecipient = looterPlr;
7675 looterPlr->SendLoot(bones->GetGUID(), LOOT_INSIGNIA);
7678 void Player::SendLootRelease(ObjectGuid guid)
7680 WorldPacket data( SMSG_LOOT_RELEASE_RESPONSE, (8+1) );
7681 data << guid;
7682 data << uint8(1);
7683 SendDirectMessage( &data );
7686 void Player::SendLoot(ObjectGuid guid, LootType loot_type)
7688 if (uint64 lguid = GetLootGUID())
7689 m_session->DoLootRelease(lguid);
7691 Loot *loot = 0;
7692 PermissionTypes permission = ALL_PERMISSION;
7694 DEBUG_LOG("Player::SendLoot");
7695 switch(guid.GetHigh())
7697 case HIGHGUID_GAMEOBJECT:
7699 DEBUG_LOG(" IS_GAMEOBJECT_GUID(guid)");
7700 GameObject *go = GetMap()->GetGameObject(guid);
7702 // not check distance for GO in case owned GO (fishing bobber case, for example)
7703 // And permit out of range GO with no owner in case fishing hole
7704 if (!go || (loot_type != LOOT_FISHINGHOLE && (loot_type != LOOT_FISHING || go->GetOwnerGUID() != GetGUID()) && !go->IsWithinDistInMap(this,INTERACTION_DISTANCE)))
7706 SendLootRelease(guid);
7707 return;
7710 loot = &go->loot;
7712 if (go->getLootState() == GO_READY)
7714 uint32 lootid = go->GetGOInfo()->GetLootId();
7715 if ((go->GetEntry() == BG_AV_OBJECTID_MINE_N || go->GetEntry() == BG_AV_OBJECTID_MINE_S))
7716 if (BattleGround *bg = GetBattleGround())
7717 if (bg->GetTypeID() == BATTLEGROUND_AV)
7718 if (!(((BattleGroundAV*)bg)->PlayerCanDoMineQuest(go->GetEntry(), GetTeam())))
7720 SendLootRelease(guid);
7721 return;
7724 if (lootid)
7726 DEBUG_LOG(" if(lootid)");
7727 loot->clear();
7728 loot->FillLoot(lootid, LootTemplates_Gameobject, this, false);
7729 loot->generateMoneyLoot(go->GetGOInfo()->MinMoneyLoot, go->GetGOInfo()->MaxMoneyLoot);
7732 if (loot_type == LOOT_FISHING)
7733 go->getFishLoot(loot,this);
7735 go->SetLootState(GO_ACTIVATED);
7737 break;
7739 case HIGHGUID_ITEM:
7741 Item *item = GetItemByGuid( guid );
7743 if (!item)
7745 SendLootRelease(guid);
7746 return;
7749 loot = &item->loot;
7751 if (!item->m_lootGenerated)
7753 item->m_lootGenerated = true;
7754 loot->clear();
7756 switch(loot_type)
7758 case LOOT_DISENCHANTING:
7759 loot->FillLoot(item->GetProto()->DisenchantID, LootTemplates_Disenchant, this,true);
7760 break;
7761 case LOOT_PROSPECTING:
7762 loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this,true);
7763 break;
7764 case LOOT_MILLING:
7765 loot->FillLoot(item->GetEntry(), LootTemplates_Milling, this,true);
7766 break;
7767 default:
7768 loot->FillLoot(item->GetEntry(), LootTemplates_Item, this,true);
7769 loot->generateMoneyLoot(item->GetProto()->MinMoneyLoot,item->GetProto()->MaxMoneyLoot);
7770 break;
7773 break;
7775 case HIGHGUID_CORPSE: // remove insignia
7777 Corpse *bones = GetMap()->GetCorpse(guid);
7779 if (!bones || !((loot_type == LOOT_CORPSE) || (loot_type == LOOT_INSIGNIA)) || (bones->GetType() != CORPSE_BONES) )
7781 SendLootRelease(guid);
7782 return;
7785 loot = &bones->loot;
7787 if (!bones->lootForBody)
7789 bones->lootForBody = true;
7790 uint32 pLevel = bones->loot.gold;
7791 bones->loot.clear();
7792 if (GetBattleGround()->GetTypeID() == BATTLEGROUND_AV)
7793 loot->FillLoot(0, LootTemplates_Creature, this, false);
7794 // It may need a better formula
7795 // Now it works like this: lvl10: ~6copper, lvl70: ~9silver
7796 bones->loot.gold = (uint32)( urand(50, 150) * 0.016f * pow( ((float)pLevel)/5.76f, 2.5f) * sWorld.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY) );
7799 if (bones->lootRecipient != this)
7800 permission = NONE_PERMISSION;
7801 break;
7803 case HIGHGUID_UNIT:
7805 Creature *creature = GetMap()->GetCreature(guid);
7807 // must be in range and creature must be alive for pickpocket and must be dead for another loot
7808 if (!creature || creature->isAlive()!=(loot_type == LOOT_PICKPOCKETING) || !creature->IsWithinDistInMap(this,INTERACTION_DISTANCE))
7810 SendLootRelease(guid);
7811 return;
7814 if (loot_type == LOOT_PICKPOCKETING && IsFriendlyTo(creature))
7816 SendLootRelease(guid);
7817 return;
7820 loot = &creature->loot;
7822 if (loot_type == LOOT_PICKPOCKETING)
7824 if (!creature->lootForPickPocketed)
7826 creature->lootForPickPocketed = true;
7827 loot->clear();
7829 if (uint32 lootid = creature->GetCreatureInfo()->pickpocketLootId)
7830 loot->FillLoot(lootid, LootTemplates_Pickpocketing, this, false);
7832 // Generate extra money for pick pocket loot
7833 const uint32 a = urand(0, creature->getLevel()/2);
7834 const uint32 b = urand(0, getLevel()/2);
7835 loot->gold = uint32(10 * (a + b) * sWorld.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY));
7838 else
7840 // the player whose group may loot the corpse
7841 Player *recipient = creature->GetLootRecipient();
7842 if (!recipient)
7844 creature->SetLootRecipient(this);
7845 recipient = this;
7848 if (creature->lootForPickPocketed)
7850 creature->lootForPickPocketed = false;
7851 loot->clear();
7854 if (!creature->lootForBody)
7856 creature->lootForBody = true;
7857 loot->clear();
7859 if (uint32 lootid = creature->GetCreatureInfo()->lootid)
7860 loot->FillLoot(lootid, LootTemplates_Creature, recipient, false);
7862 loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold);
7864 if (Group* group = creature->GetGroupLootRecipient())
7866 group->UpdateLooterGuid(creature,true);
7868 switch (group->GetLootMethod())
7870 case GROUP_LOOT:
7871 // GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with quality<threshold, round robin
7872 group->GroupLoot(creature, loot);
7873 break;
7874 case NEED_BEFORE_GREED:
7875 group->NeedBeforeGreed(creature, loot);
7876 break;
7877 case MASTER_LOOT:
7878 group->MasterLoot(creature, loot);
7879 break;
7880 default:
7881 break;
7886 // possible only if creature->lootForBody && loot->empty() at spell cast check
7887 if (loot_type == LOOT_SKINNING)
7889 if (!creature->lootForSkin)
7891 creature->lootForSkin = true;
7892 loot->clear();
7893 loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this, false);
7895 // let reopen skinning loot if will closed.
7896 if (!loot->empty())
7897 creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
7900 // set group rights only for loot_type != LOOT_SKINNING
7901 else
7903 if(Group* group = creature->GetGroupLootRecipient())
7905 if (group == GetGroup())
7907 if (group->GetLootMethod() == FREE_FOR_ALL)
7908 permission = ALL_PERMISSION;
7909 else if (group->GetLooterGuid() == GetGUID())
7911 if (group->GetLootMethod() == MASTER_LOOT)
7912 permission = MASTER_PERMISSION;
7913 else
7914 permission = ALL_PERMISSION;
7916 else
7917 permission = GROUP_PERMISSION;
7919 else
7920 permission = NONE_PERMISSION;
7922 else if (recipient == this)
7923 permission = ALL_PERMISSION;
7924 else
7925 permission = NONE_PERMISSION;
7928 break;
7930 default:
7932 sLog.outError("%s is unsupported for looting.", guid.GetString().c_str());
7933 return;
7937 SetLootGUID(guid);
7939 // LOOT_INSIGNIA and LOOT_FISHINGHOLE unsupported by client
7940 switch(loot_type)
7942 case LOOT_INSIGNIA: loot_type = LOOT_SKINNING; break;
7943 case LOOT_FISHINGHOLE: loot_type = LOOT_FISHING; break;
7944 default: break;
7947 // need know merged fishing/corpse loot type for achievements
7948 loot->loot_type = loot_type;
7950 WorldPacket data(SMSG_LOOT_RESPONSE, (9+50)); // we guess size
7952 data << guid;
7953 data << uint8(loot_type);
7954 data << LootView(*loot, this, permission);
7956 SendDirectMessage(&data);
7958 // add 'this' player as one of the players that are looting 'loot'
7959 if (permission != NONE_PERMISSION)
7960 loot->AddLooter(GetGUID());
7962 if (loot_type == LOOT_CORPSE && !guid.IsItem())
7963 SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
7966 void Player::SendNotifyLootMoneyRemoved()
7968 WorldPacket data(SMSG_LOOT_CLEAR_MONEY, 0);
7969 GetSession()->SendPacket( &data );
7972 void Player::SendNotifyLootItemRemoved(uint8 lootSlot)
7974 WorldPacket data(SMSG_LOOT_REMOVED, 1);
7975 data << uint8(lootSlot);
7976 GetSession()->SendPacket( &data );
7979 void Player::SendUpdateWorldState(uint32 Field, uint32 Value)
7981 WorldPacket data(SMSG_UPDATE_WORLD_STATE, 8);
7982 data << Field;
7983 data << Value;
7984 GetSession()->SendPacket(&data);
7987 static WorldStatePair AV_world_states[] =
7989 { 0x7ae, 0x1 }, // 7 snowfall n
7990 { 0x532, 0x1 }, // 8 frostwolfhut hc
7991 { 0x531, 0x0 }, // 9 frostwolfhut ac
7992 { 0x52e, 0x0 }, // 10 stormpike firstaid a_a
7993 { 0x571, 0x0 }, // 11 east frostwolf tower horde assaulted -unused
7994 { 0x570, 0x0 }, // 12 west frostwolf tower horde assaulted - unused
7995 { 0x567, 0x1 }, // 13 frostwolfe c
7996 { 0x566, 0x1 }, // 14 frostwolfw c
7997 { 0x550, 0x1 }, // 15 irondeep (N) ally
7998 { 0x544, 0x0 }, // 16 ice grave a_a
7999 { 0x536, 0x0 }, // 17 stormpike grave h_c
8000 { 0x535, 0x1 }, // 18 stormpike grave a_c
8001 { 0x518, 0x0 }, // 19 stoneheart grave a_a
8002 { 0x517, 0x0 }, // 20 stoneheart grave h_a
8003 { 0x574, 0x0 }, // 21 1396 unk
8004 { 0x573, 0x0 }, // 22 iceblood tower horde assaulted -unused
8005 { 0x572, 0x0 }, // 23 towerpoint horde assaulted - unused
8006 { 0x56f, 0x0 }, // 24 1391 unk
8007 { 0x56e, 0x0 }, // 25 iceblood a
8008 { 0x56d, 0x0 }, // 26 towerp a
8009 { 0x56c, 0x0 }, // 27 frostwolfe a
8010 { 0x56b, 0x0 }, // 28 froswolfw a
8011 { 0x56a, 0x1 }, // 29 1386 unk
8012 { 0x569, 0x1 }, // 30 iceblood c
8013 { 0x568, 0x1 }, // 31 towerp c
8014 { 0x565, 0x0 }, // 32 stoneh tower a
8015 { 0x564, 0x0 }, // 33 icewing tower a
8016 { 0x563, 0x0 }, // 34 dunn a
8017 { 0x562, 0x0 }, // 35 duns a
8018 { 0x561, 0x0 }, // 36 stoneheart bunker alliance assaulted - unused
8019 { 0x560, 0x0 }, // 37 icewing bunker alliance assaulted - unused
8020 { 0x55f, 0x0 }, // 38 dunbaldar south alliance assaulted - unused
8021 { 0x55e, 0x0 }, // 39 dunbaldar north alliance assaulted - unused
8022 { 0x55d, 0x0 }, // 40 stone tower d
8023 { 0x3c6, 0x0 }, // 41 966 unk
8024 { 0x3c4, 0x0 }, // 42 964 unk
8025 { 0x3c2, 0x0 }, // 43 962 unk
8026 { 0x516, 0x1 }, // 44 stoneheart grave a_c
8027 { 0x515, 0x0 }, // 45 stonheart grave h_c
8028 { 0x3b6, 0x0 }, // 46 950 unk
8029 { 0x55c, 0x0 }, // 47 icewing tower d
8030 { 0x55b, 0x0 }, // 48 dunn d
8031 { 0x55a, 0x0 }, // 49 duns d
8032 { 0x559, 0x0 }, // 50 1369 unk
8033 { 0x558, 0x0 }, // 51 iceblood d
8034 { 0x557, 0x0 }, // 52 towerp d
8035 { 0x556, 0x0 }, // 53 frostwolfe d
8036 { 0x555, 0x0 }, // 54 frostwolfw d
8037 { 0x554, 0x1 }, // 55 stoneh tower c
8038 { 0x553, 0x1 }, // 56 icewing tower c
8039 { 0x552, 0x1 }, // 57 dunn c
8040 { 0x551, 0x1 }, // 58 duns c
8041 { 0x54f, 0x0 }, // 59 irondeep (N) horde
8042 { 0x54e, 0x0 }, // 60 irondeep (N) ally
8043 { 0x54d, 0x1 }, // 61 mine (S) neutral
8044 { 0x54c, 0x0 }, // 62 mine (S) horde
8045 { 0x54b, 0x0 }, // 63 mine (S) ally
8046 { 0x545, 0x0 }, // 64 iceblood h_a
8047 { 0x543, 0x1 }, // 65 iceblod h_c
8048 { 0x542, 0x0 }, // 66 iceblood a_c
8049 { 0x540, 0x0 }, // 67 snowfall h_a
8050 { 0x53f, 0x0 }, // 68 snowfall a_a
8051 { 0x53e, 0x0 }, // 69 snowfall h_c
8052 { 0x53d, 0x0 }, // 70 snowfall a_c
8053 { 0x53c, 0x0 }, // 71 frostwolf g h_a
8054 { 0x53b, 0x0 }, // 72 frostwolf g a_a
8055 { 0x53a, 0x1 }, // 73 frostwolf g h_c
8056 { 0x539, 0x0 }, // 74 frostwolf g a_c
8057 { 0x538, 0x0 }, // 75 stormpike grave h_a
8058 { 0x537, 0x0 }, // 76 stormpike grave a_a
8059 { 0x534, 0x0 }, // 77 frostwolf hut h_a
8060 { 0x533, 0x0 }, // 78 frostwolf hut a_a
8061 { 0x530, 0x0 }, // 79 stormpike first aid h_a
8062 { 0x52f, 0x0 }, // 80 stormpike first aid h_c
8063 { 0x52d, 0x1 }, // 81 stormpike first aid a_c
8064 { 0x0, 0x0 }
8067 static WorldStatePair WS_world_states[] =
8069 { 0x62d, 0x0 }, // 7 1581 alliance flag captures
8070 { 0x62e, 0x0 }, // 8 1582 horde flag captures
8071 { 0x609, 0x0 }, // 9 1545 unk, set to 1 on alliance flag pickup...
8072 { 0x60a, 0x0 }, // 10 1546 unk, set to 1 on horde flag pickup, after drop it's -1
8073 { 0x60b, 0x2 }, // 11 1547 unk
8074 { 0x641, 0x3 }, // 12 1601 unk (max flag captures?)
8075 { 0x922, 0x1 }, // 13 2338 horde (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
8076 { 0x923, 0x1 }, // 14 2339 alliance (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
8077 { 0x1097,0x1 }, // 15 4247 show time limit?
8078 { 0x1098,0x19 }, // 16 4248 time remaining in minutes
8079 { 0x0, 0x0 }
8082 static WorldStatePair AB_world_states[] =
8084 { 0x6e7, 0x0 }, // 7 1767 stables alliance
8085 { 0x6e8, 0x0 }, // 8 1768 stables horde
8086 { 0x6e9, 0x0 }, // 9 1769 unk, ST?
8087 { 0x6ea, 0x0 }, // 10 1770 stables (show/hide)
8088 { 0x6ec, 0x0 }, // 11 1772 farm (0 - horde controlled, 1 - alliance controlled)
8089 { 0x6ed, 0x0 }, // 12 1773 farm (show/hide)
8090 { 0x6ee, 0x0 }, // 13 1774 farm color
8091 { 0x6ef, 0x0 }, // 14 1775 gold mine color, may be FM?
8092 { 0x6f0, 0x0 }, // 15 1776 alliance resources
8093 { 0x6f1, 0x0 }, // 16 1777 horde resources
8094 { 0x6f2, 0x0 }, // 17 1778 horde bases
8095 { 0x6f3, 0x0 }, // 18 1779 alliance bases
8096 { 0x6f4, 0x7d0 }, // 19 1780 max resources (2000)
8097 { 0x6f6, 0x0 }, // 20 1782 blacksmith color
8098 { 0x6f7, 0x0 }, // 21 1783 blacksmith (show/hide)
8099 { 0x6f8, 0x0 }, // 22 1784 unk, bs?
8100 { 0x6f9, 0x0 }, // 23 1785 unk, bs?
8101 { 0x6fb, 0x0 }, // 24 1787 gold mine (0 - horde contr, 1 - alliance contr)
8102 { 0x6fc, 0x0 }, // 25 1788 gold mine (0 - conflict, 1 - horde)
8103 { 0x6fd, 0x0 }, // 26 1789 gold mine (1 - show/0 - hide)
8104 { 0x6fe, 0x0 }, // 27 1790 gold mine color
8105 { 0x700, 0x0 }, // 28 1792 gold mine color, wtf?, may be LM?
8106 { 0x701, 0x0 }, // 29 1793 lumber mill color (0 - conflict, 1 - horde contr)
8107 { 0x702, 0x0 }, // 30 1794 lumber mill (show/hide)
8108 { 0x703, 0x0 }, // 31 1795 lumber mill color color
8109 { 0x732, 0x1 }, // 32 1842 stables (1 - uncontrolled)
8110 { 0x733, 0x1 }, // 33 1843 gold mine (1 - uncontrolled)
8111 { 0x734, 0x1 }, // 34 1844 lumber mill (1 - uncontrolled)
8112 { 0x735, 0x1 }, // 35 1845 farm (1 - uncontrolled)
8113 { 0x736, 0x1 }, // 36 1846 blacksmith (1 - uncontrolled)
8114 { 0x745, 0x2 }, // 37 1861 unk
8115 { 0x7a3, 0x708 }, // 38 1955 warning limit (1800)
8116 { 0x0, 0x0 }
8119 static WorldStatePair EY_world_states[] =
8121 { 0xac1, 0x0 }, // 7 2753 Horde Bases
8122 { 0xac0, 0x0 }, // 8 2752 Alliance Bases
8123 { 0xab6, 0x0 }, // 9 2742 Mage Tower - Horde conflict
8124 { 0xab5, 0x0 }, // 10 2741 Mage Tower - Alliance conflict
8125 { 0xab4, 0x0 }, // 11 2740 Fel Reaver - Horde conflict
8126 { 0xab3, 0x0 }, // 12 2739 Fel Reaver - Alliance conflict
8127 { 0xab2, 0x0 }, // 13 2738 Draenei - Alliance conflict
8128 { 0xab1, 0x0 }, // 14 2737 Draenei - Horde conflict
8129 { 0xab0, 0x0 }, // 15 2736 unk // 0 at start
8130 { 0xaaf, 0x0 }, // 16 2735 unk // 0 at start
8131 { 0xaad, 0x0 }, // 17 2733 Draenei - Horde control
8132 { 0xaac, 0x0 }, // 18 2732 Draenei - Alliance control
8133 { 0xaab, 0x1 }, // 19 2731 Draenei uncontrolled (1 - yes, 0 - no)
8134 { 0xaaa, 0x0 }, // 20 2730 Mage Tower - Alliance control
8135 { 0xaa9, 0x0 }, // 21 2729 Mage Tower - Horde control
8136 { 0xaa8, 0x1 }, // 22 2728 Mage Tower uncontrolled (1 - yes, 0 - no)
8137 { 0xaa7, 0x0 }, // 23 2727 Fel Reaver - Horde control
8138 { 0xaa6, 0x0 }, // 24 2726 Fel Reaver - Alliance control
8139 { 0xaa5, 0x1 }, // 25 2725 Fel Reaver uncontrolled (1 - yes, 0 - no)
8140 { 0xaa4, 0x0 }, // 26 2724 Boold Elf - Horde control
8141 { 0xaa3, 0x0 }, // 27 2723 Boold Elf - Alliance control
8142 { 0xaa2, 0x1 }, // 28 2722 Boold Elf uncontrolled (1 - yes, 0 - no)
8143 { 0xac5, 0x1 }, // 29 2757 Flag (1 - show, 0 - hide) - doesn't work exactly this way!
8144 { 0xad2, 0x1 }, // 30 2770 Horde top-stats (1 - show, 0 - hide) // 02 -> horde picked up the flag
8145 { 0xad1, 0x1 }, // 31 2769 Alliance top-stats (1 - show, 0 - hide) // 02 -> alliance picked up the flag
8146 { 0xabe, 0x0 }, // 32 2750 Horde resources
8147 { 0xabd, 0x0 }, // 33 2749 Alliance resources
8148 { 0xa05, 0x8e }, // 34 2565 unk, constant?
8149 { 0xaa0, 0x0 }, // 35 2720 Capturing progress-bar (100 -> empty (only grey), 0 -> blue|red (no grey), default 0)
8150 { 0xa9f, 0x0 }, // 36 2719 Capturing progress-bar (0 - left, 100 - right)
8151 { 0xa9e, 0x0 }, // 37 2718 Capturing progress-bar (1 - show, 0 - hide)
8152 { 0xc0d, 0x17b }, // 38 3085 unk
8153 // and some more ... unknown
8154 { 0x0, 0x0 }
8157 static WorldStatePair HP_world_states[] = // Hellfire Peninsula
8159 { 0x9ba, 0x1 }, // 10
8160 { 0x9b9, 0x1 }, // 11
8161 { 0x9b5, 0x0 }, // 12
8162 { 0x9b4, 0x1 }, // 13
8163 { 0x9b3, 0x0 }, // 14
8164 { 0x9b2, 0x0 }, // 15
8165 { 0x9b1, 0x1 }, // 16
8166 { 0x9b0, 0x0 }, // 17
8167 { 0x9ae, 0x0 }, // 18 horde pvp objectives captured
8168 { 0x9ac, 0x0 }, // 19
8169 { 0x9a8, 0x0 }, // 20
8170 { 0x9a7, 0x0 }, // 21
8171 { 0x9a6, 0x1 }, // 22
8172 { 0x0, 0x0 }
8175 static WorldStatePair TF_world_states[] = // Terokkar Forest
8177 { 0xa41, 0x0 }, // 10
8178 { 0xa40, 0x14 }, // 11
8179 { 0xa3f, 0x0 }, // 12
8180 { 0xa3e, 0x0 }, // 13
8181 { 0xa3d, 0x5 }, // 14
8182 { 0xa3c, 0x0 }, // 15
8183 { 0xa87, 0x0 }, // 16
8184 { 0xa86, 0x0 }, // 17
8185 { 0xa85, 0x0 }, // 18
8186 { 0xa84, 0x0 }, // 19
8187 { 0xa83, 0x0 }, // 20
8188 { 0xa82, 0x0 }, // 21
8189 { 0xa81, 0x0 }, // 22
8190 { 0xa80, 0x0 }, // 23
8191 { 0xa7e, 0x0 }, // 24
8192 { 0xa7d, 0x0 }, // 25
8193 { 0xa7c, 0x0 }, // 26
8194 { 0xa7b, 0x0 }, // 27
8195 { 0xa7a, 0x0 }, // 28
8196 { 0xa79, 0x0 }, // 29
8197 { 0x9d0, 0x5 }, // 30
8198 { 0x9ce, 0x0 }, // 31
8199 { 0x9cd, 0x0 }, // 32
8200 { 0x9cc, 0x0 }, // 33
8201 { 0xa88, 0x0 }, // 34
8202 { 0xad0, 0x0 }, // 35
8203 { 0xacf, 0x1 }, // 36
8204 { 0x0, 0x0 }
8207 static WorldStatePair ZM_world_states[] = // Terokkar Forest
8209 { 0x9e1, 0x0 }, // 10
8210 { 0x9e0, 0x0 }, // 11
8211 { 0x9df, 0x0 }, // 12
8212 { 0xa5d, 0x1 }, // 13
8213 { 0xa5c, 0x0 }, // 14
8214 { 0xa5b, 0x1 }, // 15
8215 { 0xa5a, 0x0 }, // 16
8216 { 0xa59, 0x1 }, // 17
8217 { 0xa58, 0x0 }, // 18
8218 { 0xa57, 0x0 }, // 19
8219 { 0xa56, 0x0 }, // 20
8220 { 0xa55, 0x1 }, // 21
8221 { 0xa54, 0x0 }, // 22
8222 { 0x9e7, 0x0 }, // 23
8223 { 0x9e6, 0x0 }, // 24
8224 { 0x9e5, 0x0 }, // 25
8225 { 0xa00, 0x0 }, // 26
8226 { 0x9ff, 0x1 }, // 27
8227 { 0x9fe, 0x0 }, // 28
8228 { 0x9fd, 0x0 }, // 29
8229 { 0x9fc, 0x1 }, // 30
8230 { 0x9fb, 0x0 }, // 31
8231 { 0xa62, 0x0 }, // 32
8232 { 0xa61, 0x1 }, // 33
8233 { 0xa60, 0x1 }, // 34
8234 { 0xa5f, 0x0 }, // 35
8235 { 0x0, 0x0 }
8238 void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
8240 // data depends on zoneid/mapid...
8241 BattleGround* bg = GetBattleGround();
8242 uint32 mapid = GetMapId();
8244 DEBUG_LOG("Sending SMSG_INIT_WORLD_STATES to Map:%u, Zone: %u", mapid, zoneid);
8246 uint32 count = 0; // count of world states in packet
8248 WorldPacket data(SMSG_INIT_WORLD_STATES, (4+4+4+2+8*8));// guess
8249 data << uint32(mapid); // mapid
8250 data << uint32(zoneid); // zone id
8251 data << uint32(areaid); // area id, new 2.1.0
8252 size_t count_pos = data.wpos();
8253 data << uint16(0); // count of uint64 blocks, placeholder
8255 // common fields
8256 FillInitialWorldState(data, count, 0x8d8, 0x0); // 1
8257 FillInitialWorldState(data, count, 0x8d7, 0x0); // 2
8258 FillInitialWorldState(data, count, 0x8d6, 0x0); // 3
8259 FillInitialWorldState(data, count, 0x8d5, 0x0); // 4
8260 FillInitialWorldState(data, count, 0x8d4, 0x0); // 5
8261 FillInitialWorldState(data, count, 0x8d3, 0x0); // 6
8262 // 7 1 - Arena season in progress, 0 - end of season
8263 FillInitialWorldState(data, count, 0xC77, sWorld.getConfig(CONFIG_BOOL_ARENA_SEASON_IN_PROGRESS));
8264 // 8 Arena season id
8265 FillInitialWorldState(data, count, 0xF3D, sWorld.getConfig(CONFIG_UINT32_ARENA_SEASON_ID));
8267 if(mapid == 530) // Outland
8269 FillInitialWorldState(data, count, 0x9bf, 0x0);
8270 FillInitialWorldState(data, count, 0x9bd, 0xF);
8271 FillInitialWorldState(data, count, 0x9bb, 0xF);
8273 switch(zoneid)
8275 case 1:
8276 case 11:
8277 case 12:
8278 case 38:
8279 case 40:
8280 case 51:
8281 case 1519:
8282 case 1537:
8283 case 2257:
8284 break;
8285 case 2597: // AV
8286 if (bg && bg->GetTypeID() == BATTLEGROUND_AV)
8287 bg->FillInitialWorldStates(data, count);
8288 else
8289 FillInitialWorldState(data,count, AV_world_states);
8290 break;
8291 case 3277: // WS
8292 if (bg && bg->GetTypeID() == BATTLEGROUND_WS)
8293 bg->FillInitialWorldStates(data, count);
8294 else
8295 FillInitialWorldState(data,count, WS_world_states);
8296 break;
8297 case 3358: // AB
8298 if (bg && bg->GetTypeID() == BATTLEGROUND_AB)
8299 bg->FillInitialWorldStates(data, count);
8300 else
8301 FillInitialWorldState(data,count, AB_world_states);
8302 break;
8303 case 3820: // EY
8304 if (bg && bg->GetTypeID() == BATTLEGROUND_EY)
8305 bg->FillInitialWorldStates(data, count);
8306 else
8307 FillInitialWorldState(data,count, EY_world_states);
8308 break;
8309 case 3483: // Hellfire Peninsula
8310 FillInitialWorldState(data,count, HP_world_states);
8311 break;
8312 case 3519: // Terokkar Forest
8313 FillInitialWorldState(data,count, TF_world_states);
8314 break;
8315 case 3521: // Zangarmarsh
8316 FillInitialWorldState(data,count, ZM_world_states);
8317 break;
8318 case 3698: // Nagrand Arena
8319 if (bg && bg->GetTypeID() == BATTLEGROUND_NA)
8320 bg->FillInitialWorldStates(data, count);
8321 else
8323 FillInitialWorldState(data,count,0xa0f,0x0);// 7
8324 FillInitialWorldState(data,count,0xa10,0x0);// 8
8325 FillInitialWorldState(data,count,0xa11,0x0);// 9 show
8327 break;
8328 case 3702: // Blade's Edge Arena
8329 if (bg && bg->GetTypeID() == BATTLEGROUND_BE)
8330 bg->FillInitialWorldStates(data, count);
8331 else
8333 FillInitialWorldState(data,count,0x9f0,0x0);// 7 gold
8334 FillInitialWorldState(data,count,0x9f1,0x0);// 8 green
8335 FillInitialWorldState(data,count,0x9f3,0x0);// 9 show
8337 break;
8338 case 3968: // Ruins of Lordaeron
8339 if (bg && bg->GetTypeID() == BATTLEGROUND_RL)
8340 bg->FillInitialWorldStates(data, count);
8341 else
8343 FillInitialWorldState(data,count,0xbb8,0x0);// 7 gold
8344 FillInitialWorldState(data,count,0xbb9,0x0);// 8 green
8345 FillInitialWorldState(data,count,0xbba,0x0);// 9 show
8347 break;
8348 case 3703: // Shattrath City
8349 break;
8350 default:
8351 FillInitialWorldState(data,count, 0x914, 0x0); // 7
8352 FillInitialWorldState(data,count, 0x913, 0x0); // 8
8353 FillInitialWorldState(data,count, 0x912, 0x0); // 9
8354 FillInitialWorldState(data,count, 0x915, 0x0); // 10
8355 break;
8358 FillBGWeekendWorldStates(data,count);
8360 data.put<uint16>(count_pos,count); // set actual world state amount
8362 GetSession()->SendPacket(&data);
8366 void Player::FillBGWeekendWorldStates(WorldPacket& data, uint32& count)
8368 for(uint32 i = 1; i < sBattlemasterListStore.GetNumRows(); ++i)
8370 BattlemasterListEntry const * bl = sBattlemasterListStore.LookupEntry(i);
8371 if (bl && bl->HolidayWorldStateId)
8373 if (BattleGroundMgr::IsBGWeekend(BattleGroundTypeId(bl->id)))
8374 FillInitialWorldState(data, count, bl->HolidayWorldStateId, 1);
8375 else
8376 FillInitialWorldState(data, count, bl->HolidayWorldStateId, 0);
8381 uint32 Player::GetXPRestBonus(uint32 xp)
8383 uint32 rested_bonus = (uint32)GetRestBonus(); // xp for each rested bonus
8385 if(rested_bonus > xp) // max rested_bonus == xp or (r+x) = 200% xp
8386 rested_bonus = xp;
8388 SetRestBonus( GetRestBonus() - rested_bonus);
8390 DETAIL_LOG("Player gain %u xp (+ %u Rested Bonus). Rested points=%f",xp+rested_bonus,rested_bonus,GetRestBonus());
8391 return rested_bonus;
8394 void Player::SetBindPoint(uint64 guid)
8396 WorldPacket data(SMSG_BINDER_CONFIRM, 8);
8397 data << uint64(guid);
8398 GetSession()->SendPacket( &data );
8401 void Player::SendTalentWipeConfirm(uint64 guid)
8403 WorldPacket data(MSG_TALENT_WIPE_CONFIRM, (8+4));
8404 data << uint64(guid);
8405 data << uint32(resetTalentsCost());
8406 GetSession()->SendPacket( &data );
8409 void Player::SendPetSkillWipeConfirm()
8411 Pet* pet = GetPet();
8412 if(!pet)
8413 return;
8414 WorldPacket data(SMSG_PET_UNLEARN_CONFIRM, (8+4));
8415 data << pet->GetGUID();
8416 data << uint32(pet->resetTalentsCost());
8417 GetSession()->SendPacket( &data );
8420 /*********************************************************/
8421 /*** STORAGE SYSTEM ***/
8422 /*********************************************************/
8424 void Player::SetVirtualItemSlot( uint8 i, Item* item)
8426 ASSERT(i < 3);
8427 if(i < 2 && item)
8429 if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
8430 return;
8431 uint32 charges = item->GetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT);
8432 if(charges == 0)
8433 return;
8434 if(charges > 1)
8435 item->SetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT,charges-1);
8436 else if(charges <= 1)
8438 ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false);
8439 item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
8444 void Player::SetSheath( SheathState sheathed )
8446 switch (sheathed)
8448 case SHEATH_STATE_UNARMED: // no prepared weapon
8449 SetVirtualItemSlot(0,NULL);
8450 SetVirtualItemSlot(1,NULL);
8451 SetVirtualItemSlot(2,NULL);
8452 break;
8453 case SHEATH_STATE_MELEE: // prepared melee weapon
8455 SetVirtualItemSlot(0,GetWeaponForAttack(BASE_ATTACK,true,true));
8456 SetVirtualItemSlot(1,GetWeaponForAttack(OFF_ATTACK,true,true));
8457 SetVirtualItemSlot(2,NULL);
8458 }; break;
8459 case SHEATH_STATE_RANGED: // prepared ranged weapon
8460 SetVirtualItemSlot(0,NULL);
8461 SetVirtualItemSlot(1,NULL);
8462 SetVirtualItemSlot(2,GetWeaponForAttack(RANGED_ATTACK,true,true));
8463 break;
8464 default:
8465 SetVirtualItemSlot(0,NULL);
8466 SetVirtualItemSlot(1,NULL);
8467 SetVirtualItemSlot(2,NULL);
8468 break;
8470 Unit::SetSheath(sheathed); // this must visualize Sheath changing for other players...
8473 uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap ) const
8475 uint8 pClass = getClass();
8477 uint8 slots[4];
8478 slots[0] = NULL_SLOT;
8479 slots[1] = NULL_SLOT;
8480 slots[2] = NULL_SLOT;
8481 slots[3] = NULL_SLOT;
8482 switch( proto->InventoryType )
8484 case INVTYPE_HEAD:
8485 slots[0] = EQUIPMENT_SLOT_HEAD;
8486 break;
8487 case INVTYPE_NECK:
8488 slots[0] = EQUIPMENT_SLOT_NECK;
8489 break;
8490 case INVTYPE_SHOULDERS:
8491 slots[0] = EQUIPMENT_SLOT_SHOULDERS;
8492 break;
8493 case INVTYPE_BODY:
8494 slots[0] = EQUIPMENT_SLOT_BODY;
8495 break;
8496 case INVTYPE_CHEST:
8497 slots[0] = EQUIPMENT_SLOT_CHEST;
8498 break;
8499 case INVTYPE_ROBE:
8500 slots[0] = EQUIPMENT_SLOT_CHEST;
8501 break;
8502 case INVTYPE_WAIST:
8503 slots[0] = EQUIPMENT_SLOT_WAIST;
8504 break;
8505 case INVTYPE_LEGS:
8506 slots[0] = EQUIPMENT_SLOT_LEGS;
8507 break;
8508 case INVTYPE_FEET:
8509 slots[0] = EQUIPMENT_SLOT_FEET;
8510 break;
8511 case INVTYPE_WRISTS:
8512 slots[0] = EQUIPMENT_SLOT_WRISTS;
8513 break;
8514 case INVTYPE_HANDS:
8515 slots[0] = EQUIPMENT_SLOT_HANDS;
8516 break;
8517 case INVTYPE_FINGER:
8518 slots[0] = EQUIPMENT_SLOT_FINGER1;
8519 slots[1] = EQUIPMENT_SLOT_FINGER2;
8520 break;
8521 case INVTYPE_TRINKET:
8522 slots[0] = EQUIPMENT_SLOT_TRINKET1;
8523 slots[1] = EQUIPMENT_SLOT_TRINKET2;
8524 break;
8525 case INVTYPE_CLOAK:
8526 slots[0] = EQUIPMENT_SLOT_BACK;
8527 break;
8528 case INVTYPE_WEAPON:
8530 slots[0] = EQUIPMENT_SLOT_MAINHAND;
8532 // suggest offhand slot only if know dual wielding
8533 // (this will be replace mainhand weapon at auto equip instead unwonted "you don't known dual wielding" ...
8534 if(CanDualWield())
8535 slots[1] = EQUIPMENT_SLOT_OFFHAND;
8536 break;
8538 case INVTYPE_SHIELD:
8539 slots[0] = EQUIPMENT_SLOT_OFFHAND;
8540 break;
8541 case INVTYPE_RANGED:
8542 slots[0] = EQUIPMENT_SLOT_RANGED;
8543 break;
8544 case INVTYPE_2HWEAPON:
8545 slots[0] = EQUIPMENT_SLOT_MAINHAND;
8546 if (CanDualWield() && CanTitanGrip())
8547 slots[1] = EQUIPMENT_SLOT_OFFHAND;
8548 break;
8549 case INVTYPE_TABARD:
8550 slots[0] = EQUIPMENT_SLOT_TABARD;
8551 break;
8552 case INVTYPE_WEAPONMAINHAND:
8553 slots[0] = EQUIPMENT_SLOT_MAINHAND;
8554 break;
8555 case INVTYPE_WEAPONOFFHAND:
8556 slots[0] = EQUIPMENT_SLOT_OFFHAND;
8557 break;
8558 case INVTYPE_HOLDABLE:
8559 slots[0] = EQUIPMENT_SLOT_OFFHAND;
8560 break;
8561 case INVTYPE_THROWN:
8562 slots[0] = EQUIPMENT_SLOT_RANGED;
8563 break;
8564 case INVTYPE_RANGEDRIGHT:
8565 slots[0] = EQUIPMENT_SLOT_RANGED;
8566 break;
8567 case INVTYPE_BAG:
8568 slots[0] = INVENTORY_SLOT_BAG_START + 0;
8569 slots[1] = INVENTORY_SLOT_BAG_START + 1;
8570 slots[2] = INVENTORY_SLOT_BAG_START + 2;
8571 slots[3] = INVENTORY_SLOT_BAG_START + 3;
8572 break;
8573 case INVTYPE_RELIC:
8575 switch(proto->SubClass)
8577 case ITEM_SUBCLASS_ARMOR_LIBRAM:
8578 if (pClass == CLASS_PALADIN)
8579 slots[0] = EQUIPMENT_SLOT_RANGED;
8580 break;
8581 case ITEM_SUBCLASS_ARMOR_IDOL:
8582 if (pClass == CLASS_DRUID)
8583 slots[0] = EQUIPMENT_SLOT_RANGED;
8584 break;
8585 case ITEM_SUBCLASS_ARMOR_TOTEM:
8586 if (pClass == CLASS_SHAMAN)
8587 slots[0] = EQUIPMENT_SLOT_RANGED;
8588 break;
8589 case ITEM_SUBCLASS_ARMOR_MISC:
8590 if (pClass == CLASS_WARLOCK)
8591 slots[0] = EQUIPMENT_SLOT_RANGED;
8592 break;
8593 case ITEM_SUBCLASS_ARMOR_SIGIL:
8594 if (pClass == CLASS_DEATH_KNIGHT)
8595 slots[0] = EQUIPMENT_SLOT_RANGED;
8596 break;
8598 break;
8600 default :
8601 return NULL_SLOT;
8604 if( slot != NULL_SLOT )
8606 if( swap || !GetItemByPos( INVENTORY_SLOT_BAG_0, slot ) )
8608 for (int i = 0; i < 4; ++i)
8610 if ( slots[i] == slot )
8611 return slot;
8615 else
8617 // search free slot at first
8618 for (int i = 0; i < 4; ++i)
8620 if ( slots[i] != NULL_SLOT && !GetItemByPos( INVENTORY_SLOT_BAG_0, slots[i] ) )
8622 // in case 2hand equipped weapon (without titan grip) offhand slot empty but not free
8623 if(slots[i]!=EQUIPMENT_SLOT_OFFHAND || !IsTwoHandUsed())
8624 return slots[i];
8628 // if not found free and can swap return first appropriate from used
8629 for (int i = 0; i < 4; ++i)
8631 if ( slots[i] != NULL_SLOT && swap )
8632 return slots[i];
8636 // no free position
8637 return NULL_SLOT;
8640 uint8 Player::CanUnequipItems( uint32 item, uint32 count ) const
8642 Item *pItem;
8643 uint32 tempcount = 0;
8645 uint8 res = EQUIP_ERR_OK;
8647 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
8649 pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8650 if( pItem && pItem->GetEntry() == item )
8652 uint8 ires = CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false);
8653 if(ires==EQUIP_ERR_OK)
8655 tempcount += pItem->GetCount();
8656 if( tempcount >= count )
8657 return EQUIP_ERR_OK;
8659 else
8660 res = ires;
8663 for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
8665 pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8666 if( pItem && pItem->GetEntry() == item )
8668 tempcount += pItem->GetCount();
8669 if( tempcount >= count )
8670 return EQUIP_ERR_OK;
8673 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
8675 pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8676 if( pItem && pItem->GetEntry() == item )
8678 tempcount += pItem->GetCount();
8679 if( tempcount >= count )
8680 return EQUIP_ERR_OK;
8683 Bag *pBag;
8684 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
8686 pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8687 if( pBag )
8689 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
8691 pItem = GetItemByPos( i, j );
8692 if( pItem && pItem->GetEntry() == item )
8694 tempcount += pItem->GetCount();
8695 if( tempcount >= count )
8696 return EQUIP_ERR_OK;
8702 // not found req. item count and have unequippable items
8703 return res;
8706 uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) const
8708 uint32 count = 0;
8709 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
8711 Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8712 if( pItem && pItem != skipItem && pItem->GetEntry() == item )
8713 count += pItem->GetCount();
8715 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
8717 Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8718 if( pItem && pItem != skipItem && pItem->GetEntry() == item )
8719 count += pItem->GetCount();
8721 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
8723 Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8724 if( pBag )
8725 count += pBag->GetItemCount(item,skipItem);
8728 if(skipItem && skipItem->GetProto()->GemProperties)
8730 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
8732 Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8733 if( pItem && pItem != skipItem && pItem->GetProto()->Socket[0].Color )
8734 count += pItem->GetGemCountWithID(item);
8738 if(inBankAlso)
8740 for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
8742 Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8743 if( pItem && pItem != skipItem && pItem->GetEntry() == item )
8744 count += pItem->GetCount();
8746 for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
8748 Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8749 if( pBag )
8750 count += pBag->GetItemCount(item,skipItem);
8753 if(skipItem && skipItem->GetProto()->GemProperties)
8755 for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
8757 Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
8758 if( pItem && pItem != skipItem && pItem->GetProto()->Socket[0].Color )
8759 count += pItem->GetGemCountWithID(item);
8764 return count;
8767 uint32 Player::GetItemCountWithLimitCategory( uint32 limitCategory ) const
8769 uint32 count = 0;
8770 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
8771 if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8772 if (pItem->GetProto()->ItemLimitCategory == limitCategory)
8773 count += pItem->GetCount();
8775 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
8776 if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8777 if (pItem->GetProto()->ItemLimitCategory == limitCategory)
8778 count += pItem->GetCount();
8780 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
8781 if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8782 count += pBag->GetItemCountWithLimitCategory(limitCategory);
8784 for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
8785 if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8786 if (pItem->GetProto()->ItemLimitCategory == limitCategory)
8787 count += pItem->GetCount();
8789 for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
8790 if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8791 count += pBag->GetItemCountWithLimitCategory(limitCategory);
8793 return count;
8796 Item* Player::GetItemByEntry( uint32 item ) const
8798 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
8799 if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8800 if (pItem->GetEntry() == item)
8801 return pItem;
8803 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
8804 if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8805 if (pItem->GetEntry() == item)
8806 return pItem;
8808 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
8809 if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i))
8810 if (Item* itemPtr = pBag->GetItemByEntry(item))
8811 return itemPtr;
8813 return NULL;
8816 Item* Player::GetItemByLimitedCategory(uint32 limitedCategory) const
8818 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
8819 if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8820 if (pItem->GetProto()->ItemLimitCategory == limitedCategory)
8821 return pItem;
8823 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
8824 if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8825 if (pItem->GetProto()->ItemLimitCategory == limitedCategory)
8826 return pItem;
8828 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
8829 if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i))
8830 if (Item* itemPtr = pBag->GetItemByLimitedCategory(limitedCategory))
8831 return itemPtr;
8833 return NULL;
8836 Item* Player::GetItemByGuid(ObjectGuid guid) const
8838 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
8839 if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8840 if (pItem->GetObjectGuid() == guid)
8841 return pItem;
8843 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
8844 if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8845 if (pItem->GetObjectGuid() == guid)
8846 return pItem;
8848 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
8849 if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8850 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
8851 if (Item* pItem = pBag->GetItemByPos(j))
8852 if (pItem->GetObjectGuid() == guid)
8853 return pItem;
8855 for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
8856 if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
8857 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
8858 if (Item* pItem = pBag->GetItemByPos(j))
8859 if (pItem->GetObjectGuid() == guid)
8860 return pItem;
8862 return NULL;
8865 Item* Player::GetItemByPos( uint16 pos ) const
8867 uint8 bag = pos >> 8;
8868 uint8 slot = pos & 255;
8869 return GetItemByPos( bag, slot );
8872 Item* Player::GetItemByPos( uint8 bag, uint8 slot ) const
8874 if( bag == INVENTORY_SLOT_BAG_0 && ( slot < BANK_SLOT_BAG_END || (slot >= KEYRING_SLOT_START && slot < CURRENCYTOKEN_SLOT_END )) )
8875 return m_items[slot];
8876 else if ((bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)
8877 || (bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END) )
8879 Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
8880 if ( pBag )
8881 return pBag->GetItemByPos(slot);
8883 return NULL;
8886 Item* Player::GetWeaponForAttack(WeaponAttackType attackType, bool nonbroken, bool useable) const
8888 uint8 slot;
8889 switch (attackType)
8891 case BASE_ATTACK: slot = EQUIPMENT_SLOT_MAINHAND; break;
8892 case OFF_ATTACK: slot = EQUIPMENT_SLOT_OFFHAND; break;
8893 case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break;
8894 default: return NULL;
8897 Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
8898 if (!item || item->GetProto()->Class != ITEM_CLASS_WEAPON)
8899 return NULL;
8901 if (useable && !IsUseEquipedWeapon(attackType==BASE_ATTACK))
8902 return NULL;
8904 if (nonbroken && item->IsBroken())
8905 return NULL;
8907 return item;
8910 Item* Player::GetShield(bool useable) const
8912 Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
8913 if (!item || item->GetProto()->Class != ITEM_CLASS_ARMOR)
8914 return NULL;
8916 if(!useable)
8917 return item;
8919 if( item->IsBroken())
8920 return NULL;
8922 return item;
8925 uint32 Player::GetAttackBySlot( uint8 slot )
8927 switch(slot)
8929 case EQUIPMENT_SLOT_MAINHAND: return BASE_ATTACK;
8930 case EQUIPMENT_SLOT_OFFHAND: return OFF_ATTACK;
8931 case EQUIPMENT_SLOT_RANGED: return RANGED_ATTACK;
8932 default: return MAX_ATTACK;
8936 bool Player::IsInventoryPos( uint8 bag, uint8 slot )
8938 if( bag == INVENTORY_SLOT_BAG_0 && slot == NULL_SLOT )
8939 return true;
8940 if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END ) )
8941 return true;
8942 if( bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END )
8943 return true;
8944 if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= KEYRING_SLOT_START && slot < CURRENCYTOKEN_SLOT_END ) )
8945 return true;
8946 return false;
8949 bool Player::IsEquipmentPos( uint8 bag, uint8 slot )
8951 if( bag == INVENTORY_SLOT_BAG_0 && ( slot < EQUIPMENT_SLOT_END ) )
8952 return true;
8953 if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END ) )
8954 return true;
8955 return false;
8958 bool Player::IsBankPos( uint8 bag, uint8 slot )
8960 if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END ) )
8961 return true;
8962 if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) )
8963 return true;
8964 if( bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END )
8965 return true;
8966 return false;
8969 bool Player::IsBagPos( uint16 pos )
8971 uint8 bag = pos >> 8;
8972 uint8 slot = pos & 255;
8973 if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END ) )
8974 return true;
8975 if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) )
8976 return true;
8977 return false;
8980 bool Player::IsValidPos( uint8 bag, uint8 slot, bool explicit_pos ) const
8982 // post selected
8983 if(bag == NULL_BAG && !explicit_pos)
8984 return true;
8986 if (bag == INVENTORY_SLOT_BAG_0)
8988 // any post selected
8989 if (slot == NULL_SLOT && !explicit_pos)
8990 return true;
8992 // equipment
8993 if (slot < EQUIPMENT_SLOT_END)
8994 return true;
8996 // bag equip slots
8997 if (slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END)
8998 return true;
9000 // backpack slots
9001 if (slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END)
9002 return true;
9004 // keyring slots
9005 if (slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END)
9006 return true;
9008 // bank main slots
9009 if (slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END)
9010 return true;
9012 // bank bag slots
9013 if (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END)
9014 return true;
9016 return false;
9019 // bag content slots
9020 if (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)
9022 Bag* pBag = (Bag*)GetItemByPos (INVENTORY_SLOT_BAG_0, bag);
9023 if(!pBag)
9024 return false;
9026 // any post selected
9027 if (slot == NULL_SLOT && !explicit_pos)
9028 return true;
9030 return slot < pBag->GetBagSize();
9033 // bank bag content slots
9034 if( bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END )
9036 Bag* pBag = (Bag*)GetItemByPos (INVENTORY_SLOT_BAG_0, bag);
9037 if(!pBag)
9038 return false;
9040 // any post selected
9041 if (slot == NULL_SLOT && !explicit_pos)
9042 return true;
9044 return slot < pBag->GetBagSize();
9047 // where this?
9048 return false;
9052 bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const
9054 uint32 tempcount = 0;
9055 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
9057 Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9058 if( pItem && pItem->GetEntry() == item )
9060 tempcount += pItem->GetCount();
9061 if( tempcount >= count )
9062 return true;
9065 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
9067 Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9068 if( pItem && pItem->GetEntry() == item )
9070 tempcount += pItem->GetCount();
9071 if( tempcount >= count )
9072 return true;
9075 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
9077 if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
9079 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
9081 Item* pItem = GetItemByPos( i, j );
9082 if( pItem && pItem->GetEntry() == item )
9084 tempcount += pItem->GetCount();
9085 if( tempcount >= count )
9086 return true;
9092 if(inBankAlso)
9094 for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
9096 Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9097 if( pItem && pItem->GetEntry() == item )
9099 tempcount += pItem->GetCount();
9100 if( tempcount >= count )
9101 return true;
9104 for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
9106 if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
9108 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
9110 Item* pItem = GetItemByPos( i, j );
9111 if( pItem && pItem->GetEntry() == item )
9113 tempcount += pItem->GetCount();
9114 if( tempcount >= count )
9115 return true;
9122 return false;
9125 bool Player::HasItemOrGemWithIdEquipped( uint32 item, uint32 count, uint8 except_slot ) const
9127 uint32 tempcount = 0;
9128 for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
9130 if(i==int(except_slot))
9131 continue;
9133 Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9134 if( pItem && pItem->GetEntry() == item)
9136 tempcount += pItem->GetCount();
9137 if( tempcount >= count )
9138 return true;
9142 ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(item);
9143 if (pProto && pProto->GemProperties)
9145 for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
9147 if(i==int(except_slot))
9148 continue;
9150 Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9151 if( pItem && pItem->GetProto()->Socket[0].Color)
9153 tempcount += pItem->GetGemCountWithID(item);
9154 if( tempcount >= count )
9155 return true;
9160 return false;
9163 bool Player::HasItemOrGemWithLimitCategoryEquipped( uint32 limitCategory, uint32 count, uint8 except_slot ) const
9165 uint32 tempcount = 0;
9166 for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
9168 if(i==int(except_slot))
9169 continue;
9171 Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9172 if (!pItem)
9173 continue;
9175 ItemPrototype const *pProto = pItem->GetProto();
9176 if (!pProto)
9177 continue;
9179 if (pProto->ItemLimitCategory == limitCategory)
9181 tempcount += pItem->GetCount();
9182 if( tempcount >= count )
9183 return true;
9186 if( pProto->Socket[0].Color)
9188 tempcount += pItem->GetGemCountWithLimitCategory(limitCategory);
9189 if( tempcount >= count )
9190 return true;
9194 return false;
9197 uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count ) const
9199 ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(entry);
9200 if( !pProto )
9202 if(no_space_count)
9203 *no_space_count = count;
9204 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9207 // no maximum
9208 if(pProto->MaxCount > 0)
9210 uint32 curcount = GetItemCount(pProto->ItemId,true,pItem);
9212 if (curcount + count > uint32(pProto->MaxCount))
9214 if(no_space_count)
9215 *no_space_count = count +curcount - pProto->MaxCount;
9216 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9220 // check unique-equipped limit
9221 if (pProto->ItemLimitCategory)
9223 ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(pProto->ItemLimitCategory);
9224 if (!limitEntry)
9226 if(no_space_count)
9227 *no_space_count = count;
9228 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
9231 if (limitEntry->mode == ITEM_LIMIT_CATEGORY_MODE_HAVE)
9233 uint32 curcount = GetItemCountWithLimitCategory(pProto->ItemLimitCategory);
9235 if (curcount + count > uint32(limitEntry->maxCount))
9237 if(no_space_count)
9238 *no_space_count = count + curcount - limitEntry->maxCount;
9239 return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS;
9244 return EQUIP_ERR_OK;
9247 bool Player::HasItemTotemCategory( uint32 TotemCategory ) const
9249 Item *pItem;
9250 for(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
9252 pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9253 if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
9254 return true;
9256 for(uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
9258 pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9259 if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
9260 return true;
9262 for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
9264 if(Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
9266 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
9268 pItem = GetItemByPos( i, j );
9269 if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
9270 return true;
9274 return false;
9277 uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool swap, Item* pSrcItem ) const
9279 Item* pItem2 = GetItemByPos( bag, slot );
9281 // ignore move item (this slot will be empty at move)
9282 if (pItem2==pSrcItem)
9283 pItem2 = NULL;
9285 uint32 need_space;
9287 // empty specific slot - check item fit to slot
9288 if (!pItem2 || swap)
9290 if (bag == INVENTORY_SLOT_BAG_0)
9292 // keyring case
9293 if (slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_START+GetMaxKeyringSize() && !(pProto->BagFamily & BAG_FAMILY_MASK_KEYS))
9294 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9296 // currencytoken case
9297 if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END && !(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS))
9298 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9300 // prevent cheating
9301 if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END || slot >= PLAYER_SLOT_END)
9302 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9304 else
9306 Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
9307 if (!pBag)
9308 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9310 ItemPrototype const* pBagProto = pBag->GetProto();
9311 if (!pBagProto)
9312 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9314 if (slot >= pBagProto->ContainerSlots)
9315 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9317 if (!ItemCanGoIntoBag(pProto,pBagProto))
9318 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9321 // non empty stack with space
9322 need_space = pProto->GetMaxStackSize();
9324 // non empty slot, check item type
9325 else
9327 // can be merged at least partly
9328 uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
9329 if (res != EQUIP_ERR_OK)
9330 return res;
9332 // free stack space or infinity
9333 need_space = pProto->GetMaxStackSize() - pItem2->GetCount();
9336 if (need_space > count)
9337 need_space = count;
9339 ItemPosCount newPosition = ItemPosCount((bag << 8) | slot, need_space);
9340 if (!newPosition.isContainedIn(dest))
9342 dest.push_back(newPosition);
9343 count -= need_space;
9345 return EQUIP_ERR_OK;
9348 uint8 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
9350 // skip specific bag already processed in first called _CanStoreItem_InBag
9351 if (bag==skip_bag)
9352 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9354 // skip not existed bag or self targeted bag
9355 Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
9356 if (!pBag || pBag==pSrcItem)
9357 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9359 ItemPrototype const* pBagProto = pBag->GetProto();
9360 if (!pBagProto)
9361 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9363 // specialized bag mode or non-specilized
9364 if (non_specialized != (pBagProto->Class == ITEM_CLASS_CONTAINER && pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER))
9365 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9367 if (!ItemCanGoIntoBag(pProto,pBagProto))
9368 return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
9370 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
9372 // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
9373 if (j==skip_slot)
9374 continue;
9376 Item* pItem2 = GetItemByPos( bag, j );
9378 // ignore move item (this slot will be empty at move)
9379 if (pItem2 == pSrcItem)
9380 pItem2 = NULL;
9382 // if merge skip empty, if !merge skip non-empty
9383 if ((pItem2 != NULL) != merge)
9384 continue;
9386 uint32 need_space = pProto->GetMaxStackSize();
9388 if (pItem2)
9390 // can be merged at least partly
9391 uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
9392 if (res != EQUIP_ERR_OK)
9393 continue;
9395 // descrease at current stacksize
9396 need_space -= pItem2->GetCount();
9399 if (need_space > count)
9400 need_space = count;
9402 ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space);
9403 if (!newPosition.isContainedIn(dest))
9405 dest.push_back(newPosition);
9406 count -= need_space;
9408 if (count==0)
9409 return EQUIP_ERR_OK;
9412 return EQUIP_ERR_OK;
9415 uint8 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
9417 for(uint32 j = slot_begin; j < slot_end; ++j)
9419 // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
9420 if (INVENTORY_SLOT_BAG_0==skip_bag && j==skip_slot)
9421 continue;
9423 Item* pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, j );
9425 // ignore move item (this slot will be empty at move)
9426 if (pItem2==pSrcItem)
9427 pItem2 = NULL;
9429 // if merge skip empty, if !merge skip non-empty
9430 if ((pItem2 != NULL) != merge)
9431 continue;
9433 uint32 need_space = pProto->GetMaxStackSize();
9435 if (pItem2)
9437 // can be merged at least partly
9438 uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
9439 if (res != EQUIP_ERR_OK)
9440 continue;
9442 // descrease at current stacksize
9443 need_space -= pItem2->GetCount();
9446 if (need_space > count)
9447 need_space = count;
9449 ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space);
9450 if (!newPosition.isContainedIn(dest))
9452 dest.push_back(newPosition);
9453 count -= need_space;
9455 if (count==0)
9456 return EQUIP_ERR_OK;
9459 return EQUIP_ERR_OK;
9462 uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 entry, uint32 count, Item *pItem, bool swap, uint32* no_space_count ) const
9464 DEBUG_LOG( "STORAGE: CanStoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, entry, count);
9466 ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(entry);
9467 if (!pProto)
9469 if (no_space_count)
9470 *no_space_count = count;
9471 return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND;
9474 if (pItem)
9476 // item used
9477 if(pItem->m_lootGenerated)
9479 if (no_space_count)
9480 *no_space_count = count;
9481 return EQUIP_ERR_ALREADY_LOOTED;
9484 if (pItem->IsBindedNotWith(this))
9486 if (no_space_count)
9487 *no_space_count = count;
9488 return EQUIP_ERR_DONT_OWN_THAT_ITEM;
9492 // check count of items (skip for auto move for same player from bank)
9493 uint32 no_similar_count = 0; // can't store this amount similar items
9494 uint8 res = _CanTakeMoreSimilarItems(entry,count,pItem,&no_similar_count);
9495 if (res!=EQUIP_ERR_OK)
9497 if (count==no_similar_count)
9499 if (no_space_count)
9500 *no_space_count = no_similar_count;
9501 return res;
9503 count -= no_similar_count;
9506 // in specific slot
9507 if (bag != NULL_BAG && slot != NULL_SLOT)
9509 res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem);
9510 if (res!=EQUIP_ERR_OK)
9512 if (no_space_count)
9513 *no_space_count = count + no_similar_count;
9514 return res;
9517 if (count==0)
9519 if (no_similar_count==0)
9520 return EQUIP_ERR_OK;
9522 if (no_space_count)
9523 *no_space_count = count + no_similar_count;
9524 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9528 // not specific slot or have space for partly store only in specific slot
9530 // in specific bag
9531 if (bag != NULL_BAG)
9533 // search stack in bag for merge to
9534 if (pProto->Stackable != 1)
9536 if (bag == INVENTORY_SLOT_BAG_0) // inventory
9538 res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,true,pItem,bag,slot);
9539 if (res!=EQUIP_ERR_OK)
9541 if (no_space_count)
9542 *no_space_count = count + no_similar_count;
9543 return res;
9546 if (count==0)
9548 if (no_similar_count==0)
9549 return EQUIP_ERR_OK;
9551 if (no_space_count)
9552 *no_space_count = count + no_similar_count;
9553 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9556 res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
9557 if (res!=EQUIP_ERR_OK)
9559 if (no_space_count)
9560 *no_space_count = count + no_similar_count;
9561 return res;
9564 if (count==0)
9566 if (no_similar_count==0)
9567 return EQUIP_ERR_OK;
9569 if (no_space_count)
9570 *no_space_count = count + no_similar_count;
9571 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9574 else // equipped bag
9576 // we need check 2 time (specialized/non_specialized), use NULL_BAG to prevent skipping bag
9577 res = _CanStoreItem_InBag(bag,dest,pProto,count,true,false,pItem,NULL_BAG,slot);
9578 if (res!=EQUIP_ERR_OK)
9579 res = _CanStoreItem_InBag(bag,dest,pProto,count,true,true,pItem,NULL_BAG,slot);
9581 if (res!=EQUIP_ERR_OK)
9583 if (no_space_count)
9584 *no_space_count = count + no_similar_count;
9585 return res;
9588 if (count==0)
9590 if (no_similar_count==0)
9591 return EQUIP_ERR_OK;
9593 if (no_space_count)
9594 *no_space_count = count + no_similar_count;
9595 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9600 // search free slot in bag for place to
9601 if(bag == INVENTORY_SLOT_BAG_0) // inventory
9603 // search free slot - keyring case
9604 if (pProto->BagFamily & BAG_FAMILY_MASK_KEYS)
9606 uint32 keyringSize = GetMaxKeyringSize();
9607 res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_START+keyringSize,dest,pProto,count,false,pItem,bag,slot);
9608 if (res!=EQUIP_ERR_OK)
9610 if (no_space_count)
9611 *no_space_count = count + no_similar_count;
9612 return res;
9615 if (count==0)
9617 if (no_similar_count==0)
9618 return EQUIP_ERR_OK;
9620 if (no_space_count)
9621 *no_space_count = count + no_similar_count;
9622 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9625 res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,false,pItem,bag,slot);
9626 if (res!=EQUIP_ERR_OK)
9628 if (no_space_count)
9629 *no_space_count = count + no_similar_count;
9630 return res;
9633 if (count==0)
9635 if (no_similar_count==0)
9636 return EQUIP_ERR_OK;
9638 if (no_space_count)
9639 *no_space_count = count + no_similar_count;
9640 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9643 else if (pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS)
9645 res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,false,pItem,bag,slot);
9646 if (res!=EQUIP_ERR_OK)
9648 if (no_space_count)
9649 *no_space_count = count + no_similar_count;
9650 return res;
9653 if (count==0)
9655 if (no_similar_count==0)
9656 return EQUIP_ERR_OK;
9658 if (no_space_count)
9659 *no_space_count = count + no_similar_count;
9660 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9664 res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
9665 if (res!=EQUIP_ERR_OK)
9667 if (no_space_count)
9668 *no_space_count = count + no_similar_count;
9669 return res;
9672 if (count==0)
9674 if (no_similar_count==0)
9675 return EQUIP_ERR_OK;
9677 if (no_space_count)
9678 *no_space_count = count + no_similar_count;
9679 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9682 else // equipped bag
9684 res = _CanStoreItem_InBag(bag,dest,pProto,count,false,false,pItem,NULL_BAG,slot);
9685 if (res!=EQUIP_ERR_OK)
9686 res = _CanStoreItem_InBag(bag,dest,pProto,count,false,true,pItem,NULL_BAG,slot);
9688 if (res!=EQUIP_ERR_OK)
9690 if (no_space_count)
9691 *no_space_count = count + no_similar_count;
9692 return res;
9695 if (count==0)
9697 if (no_similar_count==0)
9698 return EQUIP_ERR_OK;
9700 if (no_space_count)
9701 *no_space_count = count + no_similar_count;
9702 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9707 // not specific bag or have space for partly store only in specific bag
9709 // search stack for merge to
9710 if (pProto->Stackable != 1)
9712 res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,true,pItem,bag,slot);
9713 if (res!=EQUIP_ERR_OK)
9715 if (no_space_count)
9716 *no_space_count = count + no_similar_count;
9717 return res;
9720 if (count==0)
9722 if (no_similar_count==0)
9723 return EQUIP_ERR_OK;
9725 if (no_space_count)
9726 *no_space_count = count + no_similar_count;
9727 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9730 res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
9731 if (res!=EQUIP_ERR_OK)
9733 if (no_space_count)
9734 *no_space_count = count + no_similar_count;
9735 return res;
9738 if (count==0)
9740 if (no_similar_count==0)
9741 return EQUIP_ERR_OK;
9743 if (no_space_count)
9744 *no_space_count = count + no_similar_count;
9745 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9748 if (pProto->BagFamily)
9750 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
9752 res = _CanStoreItem_InBag(i,dest,pProto,count,true,false,pItem,bag,slot);
9753 if (res!=EQUIP_ERR_OK)
9754 continue;
9756 if (count==0)
9758 if (no_similar_count==0)
9759 return EQUIP_ERR_OK;
9761 if (no_space_count)
9762 *no_space_count = count + no_similar_count;
9763 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9768 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
9770 res = _CanStoreItem_InBag(i,dest,pProto,count,true,true,pItem,bag,slot);
9771 if (res!=EQUIP_ERR_OK)
9772 continue;
9774 if (count==0)
9776 if (no_similar_count==0)
9777 return EQUIP_ERR_OK;
9779 if (no_space_count)
9780 *no_space_count = count + no_similar_count;
9781 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9786 // search free slot - special bag case
9787 if (pProto->BagFamily)
9789 if (pProto->BagFamily & BAG_FAMILY_MASK_KEYS)
9791 uint32 keyringSize = GetMaxKeyringSize();
9792 res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_START+keyringSize,dest,pProto,count,false,pItem,bag,slot);
9793 if (res!=EQUIP_ERR_OK)
9795 if (no_space_count)
9796 *no_space_count = count + no_similar_count;
9797 return res;
9800 if (count==0)
9802 if (no_similar_count==0)
9803 return EQUIP_ERR_OK;
9805 if (no_space_count)
9806 *no_space_count = count + no_similar_count;
9807 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9810 else if (pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS)
9812 res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,false,pItem,bag,slot);
9813 if (res!=EQUIP_ERR_OK)
9815 if (no_space_count)
9816 *no_space_count = count + no_similar_count;
9817 return res;
9820 if (count==0)
9822 if (no_similar_count==0)
9823 return EQUIP_ERR_OK;
9825 if (no_space_count)
9826 *no_space_count = count + no_similar_count;
9827 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9831 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
9833 res = _CanStoreItem_InBag(i,dest,pProto,count,false,false,pItem,bag,slot);
9834 if (res!=EQUIP_ERR_OK)
9835 continue;
9837 if (count==0)
9839 if (no_similar_count==0)
9840 return EQUIP_ERR_OK;
9842 if (no_space_count)
9843 *no_space_count = count + no_similar_count;
9844 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9849 // search free slot
9850 res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
9851 if (res!=EQUIP_ERR_OK)
9853 if (no_space_count)
9854 *no_space_count = count + no_similar_count;
9855 return res;
9858 if (count==0)
9860 if (no_similar_count==0)
9861 return EQUIP_ERR_OK;
9863 if (no_space_count)
9864 *no_space_count = count + no_similar_count;
9865 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9868 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
9870 res = _CanStoreItem_InBag(i,dest,pProto,count,false,true,pItem,bag,slot);
9871 if (res!=EQUIP_ERR_OK)
9872 continue;
9874 if (count==0)
9876 if (no_similar_count==0)
9877 return EQUIP_ERR_OK;
9879 if (no_space_count)
9880 *no_space_count = count + no_similar_count;
9881 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
9885 if (no_space_count)
9886 *no_space_count = count + no_similar_count;
9888 return EQUIP_ERR_INVENTORY_FULL;
9891 //////////////////////////////////////////////////////////////////////////
9892 uint8 Player::CanStoreItems( Item **pItems,int count) const
9894 Item *pItem2;
9896 // fill space table
9897 int inv_slot_items[INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START];
9898 int inv_bags[INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE];
9899 int inv_keys[KEYRING_SLOT_END-KEYRING_SLOT_START];
9900 int inv_tokens[CURRENCYTOKEN_SLOT_END-CURRENCYTOKEN_SLOT_START];
9902 memset(inv_slot_items,0,sizeof(int)*(INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START));
9903 memset(inv_bags,0,sizeof(int)*(INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START)*MAX_BAG_SIZE);
9904 memset(inv_keys,0,sizeof(int)*(KEYRING_SLOT_END-KEYRING_SLOT_START));
9905 memset(inv_tokens,0,sizeof(int)*(CURRENCYTOKEN_SLOT_END-CURRENCYTOKEN_SLOT_START));
9907 for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
9909 pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9911 if (pItem2 && !pItem2->IsInTrade())
9913 inv_slot_items[i-INVENTORY_SLOT_ITEM_START] = pItem2->GetCount();
9917 for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
9919 pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9921 if (pItem2 && !pItem2->IsInTrade())
9923 inv_keys[i-KEYRING_SLOT_START] = pItem2->GetCount();
9927 for(int i = CURRENCYTOKEN_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
9929 pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
9931 if (pItem2 && !pItem2->IsInTrade())
9933 inv_tokens[i-CURRENCYTOKEN_SLOT_START] = pItem2->GetCount();
9937 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
9939 if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
9941 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
9943 pItem2 = GetItemByPos( i, j );
9944 if (pItem2 && !pItem2->IsInTrade())
9946 inv_bags[i-INVENTORY_SLOT_BAG_START][j] = pItem2->GetCount();
9952 // check free space for all items
9953 for (int k = 0; k < count; ++k)
9955 Item *pItem = pItems[k];
9957 // no item
9958 if (!pItem) continue;
9960 DEBUG_LOG( "STORAGE: CanStoreItems %i. item = %u, count = %u", k+1, pItem->GetEntry(), pItem->GetCount());
9961 ItemPrototype const *pProto = pItem->GetProto();
9963 // strange item
9964 if( !pProto )
9965 return EQUIP_ERR_ITEM_NOT_FOUND;
9967 // item used
9968 if(pItem->m_lootGenerated)
9969 return EQUIP_ERR_ALREADY_LOOTED;
9971 // item it 'bind'
9972 if(pItem->IsBindedNotWith(this))
9973 return EQUIP_ERR_DONT_OWN_THAT_ITEM;
9975 Bag *pBag;
9976 ItemPrototype const *pBagProto;
9978 // item is 'one item only'
9979 uint8 res = CanTakeMoreSimilarItems(pItem);
9980 if(res != EQUIP_ERR_OK)
9981 return res;
9983 // search stack for merge to
9984 if( pProto->Stackable != 1 )
9986 bool b_found = false;
9988 for(int t = KEYRING_SLOT_START; t < KEYRING_SLOT_END; ++t)
9990 pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
9991 if( pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_keys[t-KEYRING_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize())
9993 inv_keys[t-KEYRING_SLOT_START] += pItem->GetCount();
9994 b_found = true;
9995 break;
9998 if (b_found) continue;
10000 for(int t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; ++t)
10002 pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
10003 if( pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_tokens[t-CURRENCYTOKEN_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize())
10005 inv_tokens[t-CURRENCYTOKEN_SLOT_START] += pItem->GetCount();
10006 b_found = true;
10007 break;
10010 if (b_found) continue;
10012 for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t)
10014 pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
10015 if( pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_slot_items[t-INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->GetMaxStackSize())
10017 inv_slot_items[t-INVENTORY_SLOT_ITEM_START] += pItem->GetCount();
10018 b_found = true;
10019 break;
10022 if (b_found) continue;
10024 for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
10026 pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
10027 if( pBag )
10029 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
10031 pItem2 = GetItemByPos( t, j );
10032 if( pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_bags[t-INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->GetMaxStackSize())
10034 inv_bags[t-INVENTORY_SLOT_BAG_START][j] += pItem->GetCount();
10035 b_found = true;
10036 break;
10041 if (b_found) continue;
10044 // special bag case
10045 if( pProto->BagFamily )
10047 bool b_found = false;
10048 if(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)
10050 uint32 keyringSize = GetMaxKeyringSize();
10051 for(uint32 t = KEYRING_SLOT_START; t < KEYRING_SLOT_START+keyringSize; ++t)
10053 if( inv_keys[t-KEYRING_SLOT_START] == 0 )
10055 inv_keys[t-KEYRING_SLOT_START] = 1;
10056 b_found = true;
10057 break;
10062 if (b_found) continue;
10064 if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS)
10066 for(uint32 t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; ++t)
10068 if( inv_tokens[t-CURRENCYTOKEN_SLOT_START] == 0 )
10070 inv_tokens[t-CURRENCYTOKEN_SLOT_START] = 1;
10071 b_found = true;
10072 break;
10077 if (b_found) continue;
10079 for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
10081 pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
10082 if( pBag )
10084 pBagProto = pBag->GetProto();
10086 // not plain container check
10087 if( pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER) &&
10088 ItemCanGoIntoBag(pProto,pBagProto) )
10090 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
10092 if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 )
10094 inv_bags[t-INVENTORY_SLOT_BAG_START][j] = 1;
10095 b_found = true;
10096 break;
10102 if (b_found) continue;
10105 // search free slot
10106 bool b_found = false;
10107 for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t)
10109 if( inv_slot_items[t-INVENTORY_SLOT_ITEM_START] == 0 )
10111 inv_slot_items[t-INVENTORY_SLOT_ITEM_START] = 1;
10112 b_found = true;
10113 break;
10116 if (b_found) continue;
10118 // search free slot in bags
10119 for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
10121 pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
10122 if( pBag )
10124 pBagProto = pBag->GetProto();
10126 // special bag already checked
10127 if( pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER))
10128 continue;
10130 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
10132 if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 )
10134 inv_bags[t-INVENTORY_SLOT_BAG_START][j] = 1;
10135 b_found = true;
10136 break;
10142 // no free slot found?
10143 if (!b_found)
10144 return EQUIP_ERR_INVENTORY_FULL;
10147 return EQUIP_ERR_OK;
10150 //////////////////////////////////////////////////////////////////////////
10151 uint8 Player::CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, bool swap ) const
10153 dest = 0;
10154 Item *pItem = Item::CreateItem( item, 1, this );
10155 if( pItem )
10157 uint8 result = CanEquipItem(slot, dest, pItem, swap );
10158 delete pItem;
10159 return result;
10162 return EQUIP_ERR_ITEM_NOT_FOUND;
10165 uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading ) const
10167 dest = 0;
10168 if( pItem )
10170 DEBUG_LOG( "STORAGE: CanEquipItem slot = %u, item = %u, count = %u", slot, pItem->GetEntry(), pItem->GetCount());
10171 ItemPrototype const *pProto = pItem->GetProto();
10172 if( pProto )
10174 // item used
10175 if(pItem->m_lootGenerated)
10176 return EQUIP_ERR_ALREADY_LOOTED;
10178 if(pItem->IsBindedNotWith(this))
10179 return EQUIP_ERR_DONT_OWN_THAT_ITEM;
10181 // check count of items (skip for auto move for same player from bank)
10182 uint8 res = CanTakeMoreSimilarItems(pItem);
10183 if(res != EQUIP_ERR_OK)
10184 return res;
10186 // check this only in game
10187 if(not_loading)
10189 // May be here should be more stronger checks; STUNNED checked
10190 // ROOT, CONFUSED, DISTRACTED, FLEEING this needs to be checked.
10191 if (hasUnitState(UNIT_STAT_STUNNED))
10192 return EQUIP_ERR_YOU_ARE_STUNNED;
10194 // do not allow equipping gear except weapons, offhands, projectiles, relics in
10195 // - combat
10196 // - in-progress arenas
10197 if( !pProto->CanChangeEquipStateInCombat() )
10199 if( isInCombat() )
10200 return EQUIP_ERR_NOT_IN_COMBAT;
10202 if(BattleGround* bg = GetBattleGround())
10203 if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS )
10204 return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
10207 if(isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer != 0)
10208 return EQUIP_ERR_CANT_DO_RIGHT_NOW; // maybe exist better err
10210 if(IsNonMeleeSpellCasted(false))
10211 return EQUIP_ERR_CANT_DO_RIGHT_NOW;
10214 ScalingStatDistributionEntry const *ssd = pProto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(pProto->ScalingStatDistribution) : 0;
10215 // 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)
10216 if (ssd && ssd->MaxLevel < DEFAULT_MAX_LEVEL && ssd->MaxLevel < getLevel())
10217 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
10219 uint8 eslot = FindEquipSlot( pProto, slot, swap );
10220 if (eslot == NULL_SLOT)
10221 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
10223 uint8 msg = CanUseItem(pItem , not_loading);
10224 if (msg != EQUIP_ERR_OK)
10225 return msg;
10226 if (!swap && GetItemByPos(INVENTORY_SLOT_BAG_0, eslot))
10227 return EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE;
10229 // if swap ignore item (equipped also)
10230 if (uint8 res2 = CanEquipUniqueItem(pItem, swap ? eslot : NULL_SLOT))
10231 return res2;
10233 // check unique-equipped special item classes
10234 if (pProto->Class == ITEM_CLASS_QUIVER)
10236 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
10238 if (Item* pBag = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
10240 if (pBag != pItem)
10242 if (ItemPrototype const* pBagProto = pBag->GetProto())
10244 if (pBagProto->Class==pProto->Class && (!swap || pBag->GetSlot() != eslot))
10245 return (pBagProto->SubClass == ITEM_SUBCLASS_AMMO_POUCH)
10246 ? EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH
10247 : EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER;
10254 uint32 type = pProto->InventoryType;
10256 if (eslot == EQUIPMENT_SLOT_OFFHAND)
10258 if (type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND)
10260 if (!CanDualWield())
10261 return EQUIP_ERR_CANT_DUAL_WIELD;
10263 else if (type == INVTYPE_2HWEAPON)
10265 if (!CanDualWield() || !CanTitanGrip())
10266 return EQUIP_ERR_CANT_DUAL_WIELD;
10269 if (IsTwoHandUsed())
10270 return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED;
10273 // equip two-hand weapon case (with possible unequip 2 items)
10274 if (type == INVTYPE_2HWEAPON)
10276 if (eslot == EQUIPMENT_SLOT_OFFHAND)
10278 if (!CanTitanGrip())
10279 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
10281 else if (eslot != EQUIPMENT_SLOT_MAINHAND)
10282 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
10284 if (!CanTitanGrip())
10286 // offhand item must can be stored in inventory for offhand item and it also must be unequipped
10287 Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND );
10288 ItemPosCountVec off_dest;
10289 if (offItem && (!not_loading ||
10290 CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0) << 8 | EQUIPMENT_SLOT_OFFHAND,false) != EQUIP_ERR_OK ||
10291 CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false ) != EQUIP_ERR_OK ))
10292 return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_INVENTORY_FULL;
10295 dest = ((INVENTORY_SLOT_BAG_0 << 8) | eslot);
10296 return EQUIP_ERR_OK;
10300 return !swap ? EQUIP_ERR_ITEM_NOT_FOUND : EQUIP_ERR_ITEMS_CANT_BE_SWAPPED;
10303 uint8 Player::CanUnequipItem( uint16 pos, bool swap ) const
10305 // Applied only to equipped items and bank bags
10306 if(!IsEquipmentPos(pos) && !IsBagPos(pos))
10307 return EQUIP_ERR_OK;
10309 Item* pItem = GetItemByPos(pos);
10311 // Applied only to existed equipped item
10312 if( !pItem )
10313 return EQUIP_ERR_OK;
10315 DEBUG_LOG( "STORAGE: CanUnequipItem slot = %u, item = %u, count = %u", pos, pItem->GetEntry(), pItem->GetCount());
10317 ItemPrototype const *pProto = pItem->GetProto();
10318 if( !pProto )
10319 return EQUIP_ERR_ITEM_NOT_FOUND;
10321 // item used
10322 if(pItem->m_lootGenerated)
10323 return EQUIP_ERR_ALREADY_LOOTED;
10325 // do not allow unequipping gear except weapons, offhands, projectiles, relics in
10326 // - combat
10327 // - in-progress arenas
10328 if( !pProto->CanChangeEquipStateInCombat() )
10330 if( isInCombat() )
10331 return EQUIP_ERR_NOT_IN_COMBAT;
10333 if(BattleGround* bg = GetBattleGround())
10334 if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS )
10335 return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
10338 if(!swap && pItem->IsBag() && !((Bag*)pItem)->IsEmpty())
10339 return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS;
10341 return EQUIP_ERR_OK;
10344 uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pItem, bool swap, bool not_loading ) const
10346 if (!pItem)
10347 return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
10349 uint32 count = pItem->GetCount();
10351 DEBUG_LOG( "STORAGE: CanBankItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), pItem->GetCount());
10352 ItemPrototype const *pProto = pItem->GetProto();
10353 if (!pProto)
10354 return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
10356 // item used
10357 if(pItem->m_lootGenerated)
10358 return EQUIP_ERR_ALREADY_LOOTED;
10360 if (pItem->IsBindedNotWith(this))
10361 return EQUIP_ERR_DONT_OWN_THAT_ITEM;
10363 // check count of items (skip for auto move for same player from bank)
10364 uint8 res = CanTakeMoreSimilarItems(pItem);
10365 if (res != EQUIP_ERR_OK)
10366 return res;
10368 // in specific slot
10369 if (bag != NULL_BAG && slot != NULL_SLOT)
10371 if (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END)
10373 if (!pItem->IsBag())
10374 return EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT;
10376 if (slot - BANK_SLOT_BAG_START >= GetBankBagSlotCount())
10377 return EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT;
10379 if (uint8 cantuse = CanUseItem( pItem, not_loading ) != EQUIP_ERR_OK)
10380 return cantuse;
10383 res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem);
10384 if (res!=EQUIP_ERR_OK)
10385 return res;
10387 if (count==0)
10388 return EQUIP_ERR_OK;
10391 // not specific slot or have space for partly store only in specific slot
10393 // in specific bag
10394 if( bag != NULL_BAG )
10396 if( pProto->InventoryType == INVTYPE_BAG )
10398 Bag *pBag = (Bag*)pItem;
10399 if( pBag && !pBag->IsEmpty() )
10400 return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG;
10403 // search stack in bag for merge to
10404 if( pProto->Stackable != 1 )
10406 if( bag == INVENTORY_SLOT_BAG_0 )
10408 res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
10409 if(res!=EQUIP_ERR_OK)
10410 return res;
10412 if(count==0)
10413 return EQUIP_ERR_OK;
10415 else
10417 res = _CanStoreItem_InBag(bag,dest,pProto,count,true,false,pItem,NULL_BAG,slot);
10418 if(res!=EQUIP_ERR_OK)
10419 res = _CanStoreItem_InBag(bag,dest,pProto,count,true,true,pItem,NULL_BAG,slot);
10421 if(res!=EQUIP_ERR_OK)
10422 return res;
10424 if(count==0)
10425 return EQUIP_ERR_OK;
10429 // search free slot in bag
10430 if( bag == INVENTORY_SLOT_BAG_0 )
10432 res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
10433 if(res!=EQUIP_ERR_OK)
10434 return res;
10436 if(count==0)
10437 return EQUIP_ERR_OK;
10439 else
10441 res = _CanStoreItem_InBag(bag, dest, pProto, count, false, false, pItem, NULL_BAG, slot);
10442 if(res != EQUIP_ERR_OK)
10443 res = _CanStoreItem_InBag(bag, dest, pProto, count, false, true, pItem, NULL_BAG, slot);
10445 if(res != EQUIP_ERR_OK)
10446 return res;
10448 if(count == 0)
10449 return EQUIP_ERR_OK;
10453 // not specific bag or have space for partly store only in specific bag
10455 // search stack for merge to
10456 if( pProto->Stackable != 1 )
10458 // in slots
10459 res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
10460 if(res != EQUIP_ERR_OK)
10461 return res;
10463 if(count == 0)
10464 return EQUIP_ERR_OK;
10466 // in special bags
10467 if( pProto->BagFamily )
10469 for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
10471 res = _CanStoreItem_InBag(i,dest,pProto,count,true,false,pItem,bag,slot);
10472 if(res!=EQUIP_ERR_OK)
10473 continue;
10475 if(count==0)
10476 return EQUIP_ERR_OK;
10480 for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
10482 res = _CanStoreItem_InBag(i,dest,pProto,count,true,true,pItem,bag,slot);
10483 if(res!=EQUIP_ERR_OK)
10484 continue;
10486 if(count==0)
10487 return EQUIP_ERR_OK;
10491 // search free place in special bag
10492 if( pProto->BagFamily )
10494 for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
10496 res = _CanStoreItem_InBag(i,dest,pProto,count,false,false,pItem,bag,slot);
10497 if(res!=EQUIP_ERR_OK)
10498 continue;
10500 if(count==0)
10501 return EQUIP_ERR_OK;
10505 // search free space
10506 res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
10507 if(res!=EQUIP_ERR_OK)
10508 return res;
10510 if(count==0)
10511 return EQUIP_ERR_OK;
10513 for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
10515 res = _CanStoreItem_InBag(i,dest,pProto,count,false,true,pItem,bag,slot);
10516 if(res!=EQUIP_ERR_OK)
10517 continue;
10519 if(count==0)
10520 return EQUIP_ERR_OK;
10522 return EQUIP_ERR_BANK_FULL;
10525 uint8 Player::CanUseItem( Item *pItem, bool not_loading ) const
10527 if (pItem)
10529 DEBUG_LOG( "STORAGE: CanUseItem item = %u", pItem->GetEntry());
10531 if (!isAlive() && not_loading)
10532 return EQUIP_ERR_YOU_ARE_DEAD;
10534 //if (isStunned())
10535 // return EQUIP_ERR_YOU_ARE_STUNNED;
10537 ItemPrototype const *pProto = pItem->GetProto();
10538 if (pProto)
10540 if (pItem->IsBindedNotWith(this))
10541 return EQUIP_ERR_DONT_OWN_THAT_ITEM;
10543 if ((pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0)
10544 return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
10546 if (uint32 item_use_skill = pItem->GetSkill())
10548 if (GetSkillValue(item_use_skill) == 0)
10550 // armor items with scaling stats can downgrade armor skill reqs if related class can learn armor use at some level
10551 if (pProto->Class != ITEM_CLASS_ARMOR)
10552 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
10554 ScalingStatDistributionEntry const *ssd = pProto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(pProto->ScalingStatDistribution) : NULL;
10555 if (!ssd)
10556 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
10558 bool allowScaleSkill = false;
10559 for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i)
10561 SkillLineAbilityEntry const *skillInfo = sSkillLineAbilityStore.LookupEntry(i);
10562 if (!skillInfo)
10563 continue;
10565 if (skillInfo->skillId != item_use_skill)
10566 continue;
10568 // can't learn
10569 if (skillInfo->classmask && (skillInfo->classmask & getClassMask()) == 0)
10570 continue;
10572 if (skillInfo->racemask && (skillInfo->racemask & getRaceMask()) == 0)
10573 continue;
10575 allowScaleSkill = true;
10576 break;
10579 if (!allowScaleSkill)
10580 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
10584 if (pProto->RequiredSkill != 0)
10586 if (GetSkillValue( pProto->RequiredSkill ) == 0)
10587 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
10589 if (GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank)
10590 return EQUIP_ERR_CANT_EQUIP_SKILL;
10593 if (pProto->RequiredSpell != 0 && !HasSpell(pProto->RequiredSpell))
10594 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
10596 if (pProto->RequiredReputationFaction && uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank)
10597 return EQUIP_ERR_CANT_EQUIP_REPUTATION;
10599 if (getLevel() < pProto->RequiredLevel)
10600 return EQUIP_ERR_CANT_EQUIP_LEVEL_I;
10602 return EQUIP_ERR_OK;
10605 return EQUIP_ERR_ITEM_NOT_FOUND;
10608 bool Player::CanUseItem( ItemPrototype const *pProto )
10610 // Used by group, function NeedBeforeGreed, to know if a prototype can be used by a player
10612 if( pProto )
10614 if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
10615 return false;
10616 if( pProto->RequiredSkill != 0 )
10618 if( GetSkillValue( pProto->RequiredSkill ) == 0 )
10619 return false;
10620 else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
10621 return false;
10623 if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
10624 return false;
10625 if( getLevel() < pProto->RequiredLevel )
10626 return false;
10627 return true;
10629 return false;
10632 uint8 Player::CanUseAmmo( uint32 item ) const
10634 DEBUG_LOG( "STORAGE: CanUseAmmo item = %u", item);
10635 if( !isAlive() )
10636 return EQUIP_ERR_YOU_ARE_DEAD;
10637 //if( isStunned() )
10638 // return EQUIP_ERR_YOU_ARE_STUNNED;
10639 ItemPrototype const *pProto = ObjectMgr::GetItemPrototype( item );
10640 if( pProto )
10642 if( pProto->InventoryType!= INVTYPE_AMMO )
10643 return EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE;
10644 if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
10645 return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
10646 if( pProto->RequiredSkill != 0 )
10648 if( GetSkillValue( pProto->RequiredSkill ) == 0 )
10649 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
10650 else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
10651 return EQUIP_ERR_CANT_EQUIP_SKILL;
10653 if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
10654 return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
10655 /*if( GetReputationMgr().GetReputation() < pProto->RequiredReputation )
10656 return EQUIP_ERR_CANT_EQUIP_REPUTATION;
10658 if( getLevel() < pProto->RequiredLevel )
10659 return EQUIP_ERR_CANT_EQUIP_LEVEL_I;
10661 // Requires No Ammo
10662 if(GetDummyAura(46699))
10663 return EQUIP_ERR_BAG_FULL6;
10665 return EQUIP_ERR_OK;
10667 return EQUIP_ERR_ITEM_NOT_FOUND;
10670 void Player::SetAmmo( uint32 item )
10672 if(!item)
10673 return;
10675 // already set
10676 if( GetUInt32Value(PLAYER_AMMO_ID) == item )
10677 return;
10679 // check ammo
10680 if (item)
10682 uint8 msg = CanUseAmmo( item );
10683 if (msg != EQUIP_ERR_OK)
10685 SendEquipError(msg, NULL, NULL, item);
10686 return;
10690 SetUInt32Value(PLAYER_AMMO_ID, item);
10692 _ApplyAmmoBonuses();
10695 void Player::RemoveAmmo()
10697 SetUInt32Value(PLAYER_AMMO_ID, 0);
10699 m_ammoDPS = 0.0f;
10701 if (CanModifyStats())
10702 UpdateDamagePhysical(RANGED_ATTACK);
10705 // Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
10706 Item* Player::StoreNewItem( ItemPosCountVec const& dest, uint32 item, bool update,int32 randomPropertyId )
10708 uint32 count = 0;
10709 for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ++itr)
10710 count += itr->count;
10712 Item *pItem = Item::CreateItem( item, count, this );
10713 if( pItem )
10715 ItemAddedQuestCheck( item, count );
10716 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, item, count);
10717 if(randomPropertyId)
10718 pItem->SetItemRandomProperties(randomPropertyId);
10719 pItem = StoreItem( dest, pItem, update );
10721 return pItem;
10724 Item* Player::StoreItem( ItemPosCountVec const& dest, Item* pItem, bool update )
10726 if( !pItem )
10727 return NULL;
10729 Item* lastItem = pItem;
10730 uint32 entry = pItem->GetEntry();
10731 for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); )
10733 uint16 pos = itr->pos;
10734 uint32 count = itr->count;
10736 ++itr;
10738 if(itr == dest.end())
10740 lastItem = _StoreItem(pos,pItem,count,false,update);
10741 break;
10744 lastItem = _StoreItem(pos,pItem,count,true,update);
10746 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM, entry);
10747 return lastItem;
10750 // Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
10751 Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, bool update )
10753 if( !pItem )
10754 return NULL;
10756 uint8 bag = pos >> 8;
10757 uint8 slot = pos & 255;
10759 DEBUG_LOG( "STORAGE: StoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), count);
10761 Item *pItem2 = GetItemByPos( bag, slot );
10763 if (!pItem2)
10765 if (clone)
10766 pItem = pItem->CloneItem(count, this);
10767 else
10768 pItem->SetCount(count);
10770 if (!pItem)
10771 return NULL;
10773 if (pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP ||
10774 pItem->GetProto()->Bonding == BIND_QUEST_ITEM ||
10775 (pItem->GetProto()->Bonding == BIND_WHEN_EQUIPED && IsBagPos(pos)))
10776 pItem->SetBinding( true );
10778 if (bag == INVENTORY_SLOT_BAG_0)
10780 m_items[slot] = pItem;
10781 SetUInt64Value( PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), pItem->GetGUID() );
10782 pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, GetGUID() );
10783 pItem->SetUInt64Value( ITEM_FIELD_OWNER, GetGUID() );
10785 pItem->SetSlot( slot );
10786 pItem->SetContainer( NULL );
10788 // need update known currency
10789 if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END)
10790 UpdateKnownCurrencies(pItem->GetEntry(), true);
10792 if (IsInWorld() && update)
10794 pItem->AddToWorld();
10795 pItem->SendCreateUpdateToPlayer( this );
10798 pItem->SetState(ITEM_CHANGED, this);
10800 else if (Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag ))
10802 pBag->StoreItem( slot, pItem, update );
10803 if( IsInWorld() && update )
10805 pItem->AddToWorld();
10806 pItem->SendCreateUpdateToPlayer( this );
10808 pItem->SetState(ITEM_CHANGED, this);
10809 pBag->SetState(ITEM_CHANGED, this);
10812 AddEnchantmentDurations(pItem);
10813 AddItemDurations(pItem);
10815 return pItem;
10817 else
10819 if (pItem2->GetProto()->Bonding == BIND_WHEN_PICKED_UP ||
10820 pItem2->GetProto()->Bonding == BIND_QUEST_ITEM ||
10821 (pItem2->GetProto()->Bonding == BIND_WHEN_EQUIPED && IsBagPos(pos)))
10822 pItem2->SetBinding( true );
10824 pItem2->SetCount( pItem2->GetCount() + count );
10825 if (IsInWorld() && update)
10826 pItem2->SendCreateUpdateToPlayer( this );
10828 if (!clone)
10830 // delete item (it not in any slot currently)
10831 if (IsInWorld() && update)
10833 pItem->RemoveFromWorld();
10834 pItem->DestroyForPlayer( this );
10837 RemoveEnchantmentDurations(pItem);
10838 RemoveItemDurations(pItem);
10840 pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
10841 pItem->SetState(ITEM_REMOVED, this);
10844 // AddItemDurations(pItem2); - pItem2 already have duration listed for player
10845 AddEnchantmentDurations(pItem2);
10847 pItem2->SetState(ITEM_CHANGED, this);
10849 return pItem2;
10853 Item* Player::EquipNewItem( uint16 pos, uint32 item, bool update )
10855 if (Item *pItem = Item::CreateItem( item, 1, this ))
10857 ItemAddedQuestCheck( item, 1 );
10858 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, item, 1);
10859 return EquipItem( pos, pItem, update );
10862 return NULL;
10865 Item* Player::EquipItem( uint16 pos, Item *pItem, bool update )
10867 AddEnchantmentDurations(pItem);
10868 AddItemDurations(pItem);
10870 uint8 bag = pos >> 8;
10871 uint8 slot = pos & 255;
10873 Item *pItem2 = GetItemByPos( bag, slot );
10875 if( !pItem2 )
10877 VisualizeItem( slot, pItem);
10879 if(isAlive())
10881 ItemPrototype const *pProto = pItem->GetProto();
10883 // item set bonuses applied only at equip and removed at unequip, and still active for broken items
10884 if(pProto && pProto->ItemSet)
10885 AddItemsSetItem(this, pItem);
10887 _ApplyItemMods(pItem, slot, true);
10889 if(pProto && isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer == 0)
10891 uint32 cooldownSpell = SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_5s;
10893 if (getClass() == CLASS_ROGUE)
10894 cooldownSpell = SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_0s;
10896 SpellEntry const* spellProto = sSpellStore.LookupEntry(cooldownSpell);
10898 if (!spellProto)
10899 sLog.outError("Weapon switch cooldown spell %u couldn't be found in Spell.dbc", cooldownSpell);
10900 else
10902 m_weaponChangeTimer = spellProto->StartRecoveryTime;
10904 WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+4);
10905 data << uint64(GetGUID());
10906 data << uint8(1);
10907 data << uint32(cooldownSpell);
10908 data << uint32(0);
10909 GetSession()->SendPacket(&data);
10914 if( IsInWorld() && update )
10916 pItem->AddToWorld();
10917 pItem->SendCreateUpdateToPlayer( this );
10920 ApplyEquipCooldown(pItem);
10922 if( slot == EQUIPMENT_SLOT_MAINHAND )
10924 UpdateExpertise(BASE_ATTACK);
10925 UpdateArmorPenetration();
10927 else if( slot == EQUIPMENT_SLOT_OFFHAND )
10929 UpdateExpertise(OFF_ATTACK);
10930 UpdateArmorPenetration();
10933 else
10935 pItem2->SetCount( pItem2->GetCount() + pItem->GetCount() );
10936 if( IsInWorld() && update )
10937 pItem2->SendCreateUpdateToPlayer( this );
10939 // delete item (it not in any slot currently)
10940 //pItem->DeleteFromDB();
10941 if( IsInWorld() && update )
10943 pItem->RemoveFromWorld();
10944 pItem->DestroyForPlayer( this );
10947 RemoveEnchantmentDurations(pItem);
10948 RemoveItemDurations(pItem);
10950 pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
10951 pItem->SetState(ITEM_REMOVED, this);
10952 pItem2->SetState(ITEM_CHANGED, this);
10954 ApplyEquipCooldown(pItem2);
10956 return pItem2;
10959 // only for full equip instead adding to stack
10960 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry());
10961 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM, slot+1);
10963 return pItem;
10966 void Player::QuickEquipItem( uint16 pos, Item *pItem)
10968 if( pItem )
10970 AddEnchantmentDurations(pItem);
10971 AddItemDurations(pItem);
10973 uint8 slot = pos & 255;
10974 VisualizeItem( slot, pItem);
10976 if( IsInWorld() )
10978 pItem->AddToWorld();
10979 pItem->SendCreateUpdateToPlayer( this );
10982 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry());
10983 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM, slot+1);
10987 void Player::SetVisibleItemSlot(uint8 slot, Item *pItem)
10989 if(pItem)
10991 SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), pItem->GetEntry());
10992 SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0, pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT));
10993 SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 1, pItem->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT));
10995 else
10997 SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), 0);
10998 SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0);
11002 void Player::VisualizeItem( uint8 slot, Item *pItem)
11004 if(!pItem)
11005 return;
11007 // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
11008 if( pItem->GetProto()->Bonding == BIND_WHEN_EQUIPED || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
11009 pItem->SetBinding( true );
11011 DEBUG_LOG( "STORAGE: EquipItem slot = %u, item = %u", slot, pItem->GetEntry());
11013 m_items[slot] = pItem;
11014 SetUInt64Value( PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), pItem->GetGUID() );
11015 pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, GetGUID() );
11016 pItem->SetUInt64Value( ITEM_FIELD_OWNER, GetGUID() );
11017 pItem->SetSlot( slot );
11018 pItem->SetContainer( NULL );
11020 if( slot < EQUIPMENT_SLOT_END )
11021 SetVisibleItemSlot(slot, pItem);
11023 pItem->SetState(ITEM_CHANGED, this);
11026 void Player::RemoveItem( uint8 bag, uint8 slot, bool update )
11028 // note: removeitem does not actually change the item
11029 // it only takes the item out of storage temporarily
11030 // note2: if removeitem is to be used for delinking
11031 // the item must be removed from the player's updatequeue
11033 Item *pItem = GetItemByPos( bag, slot );
11034 if( pItem )
11036 DEBUG_LOG( "STORAGE: RemoveItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
11038 RemoveEnchantmentDurations(pItem);
11039 RemoveItemDurations(pItem);
11041 if( bag == INVENTORY_SLOT_BAG_0 )
11043 if ( slot < INVENTORY_SLOT_BAG_END )
11045 ItemPrototype const *pProto = pItem->GetProto();
11046 // item set bonuses applied only at equip and removed at unequip, and still active for broken items
11048 if(pProto && pProto->ItemSet)
11049 RemoveItemsSetItem(this, pProto);
11051 _ApplyItemMods(pItem, slot, false);
11053 // remove item dependent auras and casts (only weapon and armor slots)
11054 if(slot < EQUIPMENT_SLOT_END)
11056 RemoveItemDependentAurasAndCasts(pItem);
11058 // remove held enchantments, update expertise
11059 if ( slot == EQUIPMENT_SLOT_MAINHAND )
11061 if (pItem->GetItemSuffixFactor())
11063 pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_3);
11064 pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_4);
11066 else
11068 pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0);
11069 pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1);
11072 UpdateExpertise(BASE_ATTACK);
11073 UpdateArmorPenetration();
11075 else if( slot == EQUIPMENT_SLOT_OFFHAND )
11077 UpdateExpertise(OFF_ATTACK);
11078 UpdateArmorPenetration();
11082 // need update known currency
11083 else if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END)
11084 UpdateKnownCurrencies(pItem->GetEntry(), false);
11086 m_items[slot] = NULL;
11087 SetUInt64Value(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), 0);
11089 if ( slot < EQUIPMENT_SLOT_END )
11090 SetVisibleItemSlot(slot, NULL);
11092 else
11094 Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
11095 if( pBag )
11096 pBag->RemoveItem(slot, update);
11098 pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, 0 );
11099 // pItem->SetUInt64Value( ITEM_FIELD_OWNER, 0 ); not clear owner at remove (it will be set at store). This used in mail and auction code
11100 pItem->SetSlot( NULL_SLOT );
11101 if( IsInWorld() && update )
11102 pItem->SendCreateUpdateToPlayer( this );
11106 // Common operation need to remove item from inventory without delete in trade, auction, guild bank, mail....
11107 void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
11109 if(Item* it = GetItemByPos(bag,slot))
11111 ItemRemovedQuestCheck(it->GetEntry(), it->GetCount());
11112 RemoveItem(bag, slot, update);
11113 it->RemoveFromUpdateQueueOf(this);
11114 if(it->IsInWorld())
11116 it->RemoveFromWorld();
11117 it->DestroyForPlayer( this );
11122 // Common operation need to add item from inventory without delete in trade, guild bank, mail....
11123 void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB)
11125 // update quest counters
11126 ItemAddedQuestCheck(pItem->GetEntry(), pItem->GetCount());
11127 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, pItem->GetEntry(), pItem->GetCount());
11129 // store item
11130 Item* pLastItem = StoreItem(dest, pItem, update);
11132 // only set if not merged to existed stack (pItem can be deleted already but we can compare pointers any way)
11133 if(pLastItem == pItem)
11135 // update owner for last item (this can be original item with wrong owner
11136 if(pLastItem->GetOwnerGUID() != GetGUID())
11137 pLastItem->SetOwnerGUID(GetGUID());
11139 // if this original item then it need create record in inventory
11140 // in case trade we already have item in other player inventory
11141 pLastItem->SetState(in_characterInventoryDB ? ITEM_CHANGED : ITEM_NEW, this);
11145 void Player::DestroyItem( uint8 bag, uint8 slot, bool update )
11147 Item *pItem = GetItemByPos( bag, slot );
11148 if( pItem )
11150 DEBUG_LOG( "STORAGE: DestroyItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
11152 // start from destroy contained items (only equipped bag can have its)
11153 if (pItem->IsBag() && pItem->IsEquipped()) // this also prevent infinity loop if empty bag stored in bag==slot
11155 for (int i = 0; i < MAX_BAG_SIZE; ++i)
11156 DestroyItem(slot, i, update);
11159 if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
11160 CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
11162 RemoveEnchantmentDurations(pItem);
11163 RemoveItemDurations(pItem);
11165 ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount() );
11167 if( bag == INVENTORY_SLOT_BAG_0 )
11169 SetUInt64Value(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), 0);
11171 // equipment and equipped bags can have applied bonuses
11172 if ( slot < INVENTORY_SLOT_BAG_END )
11174 ItemPrototype const *pProto = pItem->GetProto();
11176 // item set bonuses applied only at equip and removed at unequip, and still active for broken items
11177 if(pProto && pProto->ItemSet)
11178 RemoveItemsSetItem(this, pProto);
11180 _ApplyItemMods(pItem, slot, false);
11183 if ( slot < EQUIPMENT_SLOT_END )
11185 // remove item dependent auras and casts (only weapon and armor slots)
11186 RemoveItemDependentAurasAndCasts(pItem);
11188 // update expertise
11189 if( slot == EQUIPMENT_SLOT_MAINHAND )
11191 UpdateExpertise(BASE_ATTACK);
11192 UpdateArmorPenetration();
11194 else if( slot == EQUIPMENT_SLOT_OFFHAND )
11196 UpdateExpertise(OFF_ATTACK);
11197 UpdateArmorPenetration();
11200 // equipment visual show
11201 SetVisibleItemSlot(slot, NULL);
11203 // need update known currency
11204 else if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END)
11205 UpdateKnownCurrencies(pItem->GetEntry(), false);
11207 m_items[slot] = NULL;
11209 else if(Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag ))
11210 pBag->RemoveItem(slot, update);
11212 if( IsInWorld() && update )
11214 pItem->RemoveFromWorld();
11215 pItem->DestroyForPlayer(this);
11218 //pItem->SetOwnerGUID(0);
11219 pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, 0 );
11220 pItem->SetSlot( NULL_SLOT );
11221 pItem->SetState(ITEM_REMOVED, this);
11225 void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool unequip_check)
11227 DEBUG_LOG( "STORAGE: DestroyItemCount item = %u, count = %u", item, count);
11228 uint32 remcount = 0;
11230 // in inventory
11231 for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
11233 if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11235 if (pItem->GetEntry() == item)
11237 if (pItem->GetCount() + remcount <= count)
11239 // all items in inventory can unequipped
11240 remcount += pItem->GetCount();
11241 DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
11243 if (remcount >= count)
11244 return;
11246 else
11248 ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
11249 pItem->SetCount( pItem->GetCount() - count + remcount );
11250 if (IsInWorld() & update)
11251 pItem->SendCreateUpdateToPlayer( this );
11252 pItem->SetState(ITEM_CHANGED, this);
11253 return;
11259 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
11261 if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11263 if (pItem->GetEntry() == item)
11265 if (pItem->GetCount() + remcount <= count)
11267 // all keys can be unequipped
11268 remcount += pItem->GetCount();
11269 DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
11271 if (remcount >= count)
11272 return;
11274 else
11276 ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
11277 pItem->SetCount( pItem->GetCount() - count + remcount );
11278 if (IsInWorld() & update)
11279 pItem->SendCreateUpdateToPlayer( this );
11280 pItem->SetState(ITEM_CHANGED, this);
11281 return;
11287 // in inventory bags
11288 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
11290 if(Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11292 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
11294 if(Item* pItem = pBag->GetItemByPos(j))
11296 if (pItem->GetEntry() == item)
11298 // all items in bags can be unequipped
11299 if (pItem->GetCount() + remcount <= count)
11301 remcount += pItem->GetCount();
11302 DestroyItem( i, j, update );
11304 if (remcount >= count)
11305 return;
11307 else
11309 ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
11310 pItem->SetCount( pItem->GetCount() - count + remcount );
11311 if (IsInWorld() && update)
11312 pItem->SendCreateUpdateToPlayer( this );
11313 pItem->SetState(ITEM_CHANGED, this);
11314 return;
11322 // in equipment and bag list
11323 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
11325 if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11327 if (pItem && pItem->GetEntry() == item)
11329 if (pItem->GetCount() + remcount <= count)
11331 if (!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false) == EQUIP_ERR_OK )
11333 remcount += pItem->GetCount();
11334 DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
11336 if (remcount >= count)
11337 return;
11340 else
11342 ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
11343 pItem->SetCount( pItem->GetCount() - count + remcount );
11344 if (IsInWorld() & update)
11345 pItem->SendCreateUpdateToPlayer( this );
11346 pItem->SetState(ITEM_CHANGED, this);
11347 return;
11354 void Player::DestroyZoneLimitedItem( bool update, uint32 new_zone )
11356 DEBUG_LOG( "STORAGE: DestroyZoneLimitedItem in map %u and area %u", GetMapId(), new_zone );
11358 // in inventory
11359 for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
11360 if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11361 if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
11362 DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
11364 for(int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
11365 if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11366 if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
11367 DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
11369 // in inventory bags
11370 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
11371 if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11372 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
11373 if (Item* pItem = pBag->GetItemByPos(j))
11374 if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
11375 DestroyItem(i, j, update);
11377 // in equipment and bag list
11378 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
11379 if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11380 if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
11381 DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
11384 void Player::DestroyConjuredItems( bool update )
11386 // used when entering arena
11387 // destroys all conjured items
11388 DEBUG_LOG( "STORAGE: DestroyConjuredItems" );
11390 // in inventory
11391 for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
11392 if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11393 if (pItem->IsConjuredConsumable())
11394 DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
11396 // in inventory bags
11397 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
11398 if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11399 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
11400 if (Item* pItem = pBag->GetItemByPos(j))
11401 if (pItem->IsConjuredConsumable())
11402 DestroyItem( i, j, update);
11404 // in equipment and bag list
11405 for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
11406 if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
11407 if (pItem->IsConjuredConsumable())
11408 DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
11411 void Player::DestroyItemCount( Item* pItem, uint32 &count, bool update )
11413 if(!pItem)
11414 return;
11416 DEBUG_LOG( "STORAGE: DestroyItemCount item (GUID: %u, Entry: %u) count = %u", pItem->GetGUIDLow(),pItem->GetEntry(), count);
11418 if( pItem->GetCount() <= count )
11420 count -= pItem->GetCount();
11422 DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), update);
11424 else
11426 ItemRemovedQuestCheck( pItem->GetEntry(), count);
11427 pItem->SetCount( pItem->GetCount() - count );
11428 count = 0;
11429 if( IsInWorld() & update )
11430 pItem->SendCreateUpdateToPlayer( this );
11431 pItem->SetState(ITEM_CHANGED, this);
11435 void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
11437 uint8 srcbag = src >> 8;
11438 uint8 srcslot = src & 255;
11440 uint8 dstbag = dst >> 8;
11441 uint8 dstslot = dst & 255;
11443 Item *pSrcItem = GetItemByPos( srcbag, srcslot );
11444 if( !pSrcItem )
11446 SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL );
11447 return;
11450 if(pSrcItem->m_lootGenerated) // prevent split looting item (item
11452 //best error message found for attempting to split while looting
11453 SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL );
11454 return;
11457 // not let split all items (can be only at cheating)
11458 if(pSrcItem->GetCount() == count)
11460 SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL );
11461 return;
11464 // not let split more existed items (can be only at cheating)
11465 if(pSrcItem->GetCount() < count)
11467 SendEquipError( EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT, pSrcItem, NULL );
11468 return;
11471 DEBUG_LOG( "STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count);
11472 Item *pNewItem = pSrcItem->CloneItem( count, this );
11473 if( !pNewItem )
11475 SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL );
11476 return;
11479 if( IsInventoryPos( dst ) )
11481 // change item amount before check (for unique max count check)
11482 pSrcItem->SetCount( pSrcItem->GetCount() - count );
11484 ItemPosCountVec dest;
11485 uint8 msg = CanStoreItem( dstbag, dstslot, dest, pNewItem, false );
11486 if( msg != EQUIP_ERR_OK )
11488 delete pNewItem;
11489 pSrcItem->SetCount( pSrcItem->GetCount() + count );
11490 SendEquipError( msg, pSrcItem, NULL );
11491 return;
11494 if( IsInWorld() )
11495 pSrcItem->SendCreateUpdateToPlayer( this );
11496 pSrcItem->SetState(ITEM_CHANGED, this);
11497 StoreItem( dest, pNewItem, true);
11499 else if( IsBankPos ( dst ) )
11501 // change item amount before check (for unique max count check)
11502 pSrcItem->SetCount( pSrcItem->GetCount() - count );
11504 ItemPosCountVec dest;
11505 uint8 msg = CanBankItem( dstbag, dstslot, dest, pNewItem, false );
11506 if( msg != EQUIP_ERR_OK )
11508 delete pNewItem;
11509 pSrcItem->SetCount( pSrcItem->GetCount() + count );
11510 SendEquipError( msg, pSrcItem, NULL );
11511 return;
11514 if( IsInWorld() )
11515 pSrcItem->SendCreateUpdateToPlayer( this );
11516 pSrcItem->SetState(ITEM_CHANGED, this);
11517 BankItem( dest, pNewItem, true);
11519 else if( IsEquipmentPos ( dst ) )
11521 // change item amount before check (for unique max count check), provide space for splitted items
11522 pSrcItem->SetCount( pSrcItem->GetCount() - count );
11524 uint16 dest;
11525 uint8 msg = CanEquipItem( dstslot, dest, pNewItem, false );
11526 if( msg != EQUIP_ERR_OK )
11528 delete pNewItem;
11529 pSrcItem->SetCount( pSrcItem->GetCount() + count );
11530 SendEquipError( msg, pSrcItem, NULL );
11531 return;
11534 if( IsInWorld() )
11535 pSrcItem->SendCreateUpdateToPlayer( this );
11536 pSrcItem->SetState(ITEM_CHANGED, this);
11537 EquipItem( dest, pNewItem, true);
11538 AutoUnequipOffhandIfNeed();
11542 void Player::SwapItem( uint16 src, uint16 dst )
11544 uint8 srcbag = src >> 8;
11545 uint8 srcslot = src & 255;
11547 uint8 dstbag = dst >> 8;
11548 uint8 dstslot = dst & 255;
11550 Item *pSrcItem = GetItemByPos( srcbag, srcslot );
11551 Item *pDstItem = GetItemByPos( dstbag, dstslot );
11553 if (!pSrcItem)
11554 return;
11556 DEBUG_LOG( "STORAGE: SwapItem bag = %u, slot = %u, item = %u", dstbag, dstslot, pSrcItem->GetEntry());
11558 if (!isAlive())
11560 SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, pSrcItem, pDstItem );
11561 return;
11564 // SRC checks
11566 // check unequip potability for equipped items and bank bags
11567 if (IsEquipmentPos(src) || IsBagPos(src))
11569 // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later)
11570 uint8 msg = CanUnequipItem( src, !IsBagPos ( src ) || IsBagPos ( dst ) || (pDstItem && pDstItem->IsBag() && ((Bag*)pDstItem)->IsEmpty()));
11571 if (msg != EQUIP_ERR_OK)
11573 SendEquipError( msg, pSrcItem, pDstItem );
11574 return;
11578 // prevent put equipped/bank bag in self
11579 if (IsBagPos(src) && srcslot == dstbag)
11581 SendEquipError( EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem );
11582 return;
11585 // prevent put equipped/bank bag in self
11586 if (IsBagPos(dst) && dstslot == srcbag)
11588 SendEquipError( EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pDstItem, pSrcItem );
11589 return;
11592 // DST checks
11594 if (pDstItem)
11596 // check unequip potability for equipped items and bank bags
11597 if(IsEquipmentPos ( dst ) || IsBagPos ( dst ))
11599 // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later)
11600 uint8 msg = CanUnequipItem( dst, !IsBagPos ( dst ) || IsBagPos ( src ) || (pSrcItem->IsBag() && ((Bag*)pSrcItem)->IsEmpty()));
11601 if(msg != EQUIP_ERR_OK)
11603 SendEquipError( msg, pSrcItem, pDstItem );
11604 return;
11609 // NOW this is or item move (swap with empty), or swap with another item (including bags in bag possitions)
11610 // or swap empty bag with another empty or not empty bag (with items exchange)
11612 // Move case
11613 if( !pDstItem )
11615 if( IsInventoryPos( dst ) )
11617 ItemPosCountVec dest;
11618 uint8 msg = CanStoreItem( dstbag, dstslot, dest, pSrcItem, false );
11619 if( msg != EQUIP_ERR_OK )
11621 SendEquipError( msg, pSrcItem, NULL );
11622 return;
11625 RemoveItem(srcbag, srcslot, true);
11626 StoreItem( dest, pSrcItem, true);
11628 else if( IsBankPos ( dst ) )
11630 ItemPosCountVec dest;
11631 uint8 msg = CanBankItem( dstbag, dstslot, dest, pSrcItem, false);
11632 if( msg != EQUIP_ERR_OK )
11634 SendEquipError( msg, pSrcItem, NULL );
11635 return;
11638 RemoveItem(srcbag, srcslot, true);
11639 BankItem( dest, pSrcItem, true);
11641 else if( IsEquipmentPos ( dst ) )
11643 uint16 dest;
11644 uint8 msg = CanEquipItem( dstslot, dest, pSrcItem, false );
11645 if( msg != EQUIP_ERR_OK )
11647 SendEquipError( msg, pSrcItem, NULL );
11648 return;
11651 RemoveItem(srcbag, srcslot, true);
11652 EquipItem(dest, pSrcItem, true);
11653 AutoUnequipOffhandIfNeed();
11656 return;
11659 // attempt merge to / fill target item
11660 if(!pSrcItem->IsBag() && !pDstItem->IsBag())
11662 uint8 msg;
11663 ItemPosCountVec sDest;
11664 uint16 eDest;
11665 if( IsInventoryPos( dst ) )
11666 msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, false );
11667 else if( IsBankPos ( dst ) )
11668 msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, false );
11669 else if( IsEquipmentPos ( dst ) )
11670 msg = CanEquipItem( dstslot, eDest, pSrcItem, false );
11671 else
11672 return;
11674 // can be merge/fill
11675 if(msg == EQUIP_ERR_OK)
11677 if( pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetProto()->GetMaxStackSize())
11679 RemoveItem(srcbag, srcslot, true);
11681 if( IsInventoryPos( dst ) )
11682 StoreItem( sDest, pSrcItem, true);
11683 else if( IsBankPos ( dst ) )
11684 BankItem( sDest, pSrcItem, true);
11685 else if( IsEquipmentPos ( dst ) )
11687 EquipItem( eDest, pSrcItem, true);
11688 AutoUnequipOffhandIfNeed();
11691 else
11693 pSrcItem->SetCount( pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetProto()->GetMaxStackSize());
11694 pDstItem->SetCount( pSrcItem->GetProto()->GetMaxStackSize());
11695 pSrcItem->SetState(ITEM_CHANGED, this);
11696 pDstItem->SetState(ITEM_CHANGED, this);
11697 if( IsInWorld() )
11699 pSrcItem->SendCreateUpdateToPlayer( this );
11700 pDstItem->SendCreateUpdateToPlayer( this );
11703 return;
11707 // impossible merge/fill, do real swap
11708 uint8 msg;
11710 // check src->dest move possibility
11711 ItemPosCountVec sDest;
11712 uint16 eDest = 0;
11713 if( IsInventoryPos( dst ) )
11714 msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, true );
11715 else if( IsBankPos( dst ) )
11716 msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, true );
11717 else if( IsEquipmentPos( dst ) )
11719 msg = CanEquipItem( dstslot, eDest, pSrcItem, true );
11720 if( msg == EQUIP_ERR_OK )
11721 msg = CanUnequipItem( eDest, true );
11724 if( msg != EQUIP_ERR_OK )
11726 SendEquipError( msg, pSrcItem, pDstItem );
11727 return;
11730 // check dest->src move possibility
11731 ItemPosCountVec sDest2;
11732 uint16 eDest2 = 0;
11733 if( IsInventoryPos( src ) )
11734 msg = CanStoreItem( srcbag, srcslot, sDest2, pDstItem, true );
11735 else if( IsBankPos( src ) )
11736 msg = CanBankItem( srcbag, srcslot, sDest2, pDstItem, true );
11737 else if( IsEquipmentPos( src ) )
11739 msg = CanEquipItem( srcslot, eDest2, pDstItem, true);
11740 if( msg == EQUIP_ERR_OK )
11741 msg = CanUnequipItem( eDest2, true);
11744 if( msg != EQUIP_ERR_OK )
11746 SendEquipError( msg, pDstItem, pSrcItem );
11747 return;
11750 // Check bag swap with item exchange (one from empty in not bag possition (equipped (not possible in fact) or store)
11751 if(pSrcItem->IsBag() && pDstItem->IsBag())
11753 Bag* emptyBag = NULL;
11754 Bag* fullBag = NULL;
11755 if(((Bag*)pSrcItem)->IsEmpty() && !IsBagPos(src))
11757 emptyBag = (Bag*)pSrcItem;
11758 fullBag = (Bag*)pDstItem;
11760 else if(((Bag*)pDstItem)->IsEmpty() && !IsBagPos(dst))
11762 emptyBag = (Bag*)pDstItem;
11763 fullBag = (Bag*)pSrcItem;
11766 // bag swap (with items exchange) case
11767 if(emptyBag && fullBag)
11769 ItemPrototype const* emotyProto = emptyBag->GetProto();
11771 uint32 count = 0;
11773 for(uint32 i=0; i < fullBag->GetBagSize(); ++i)
11775 Item *bagItem = fullBag->GetItemByPos(i);
11776 if (!bagItem)
11777 continue;
11779 ItemPrototype const* bagItemProto = bagItem->GetProto();
11780 if (!bagItemProto || !ItemCanGoIntoBag(bagItemProto, emotyProto))
11782 // one from items not go to empty target bag
11783 SendEquipError( EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem );
11784 return;
11787 ++count;
11791 if (count > emptyBag->GetBagSize())
11793 // too small targeted bag
11794 SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pSrcItem, pDstItem );
11795 return;
11798 // Items swap
11799 count = 0; // will pos in new bag
11800 for(uint32 i = 0; i< fullBag->GetBagSize(); ++i)
11802 Item *bagItem = fullBag->GetItemByPos(i);
11803 if (!bagItem)
11804 continue;
11806 fullBag->RemoveItem(i, true);
11807 emptyBag->StoreItem(count, bagItem, true);
11808 bagItem->SetState(ITEM_CHANGED, this);
11810 ++count;
11815 // now do moves, remove...
11816 RemoveItem(dstbag, dstslot, false);
11817 RemoveItem(srcbag, srcslot, false);
11819 // add to dest
11820 if (IsInventoryPos(dst))
11821 StoreItem(sDest, pSrcItem, true);
11822 else if (IsBankPos(dst))
11823 BankItem(sDest, pSrcItem, true);
11824 else if (IsEquipmentPos(dst))
11825 EquipItem(eDest, pSrcItem, true);
11827 // add to src
11828 if (IsInventoryPos(src))
11829 StoreItem(sDest2, pDstItem, true);
11830 else if (IsBankPos(src))
11831 BankItem(sDest2, pDstItem, true);
11832 else if (IsEquipmentPos(src))
11833 EquipItem(eDest2, pDstItem, true);
11835 AutoUnequipOffhandIfNeed();
11838 void Player::AddItemToBuyBackSlot( Item *pItem )
11840 if (pItem)
11842 uint32 slot = m_currentBuybackSlot;
11843 // if current back slot non-empty search oldest or free
11844 if (m_items[slot])
11846 uint32 oldest_time = GetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 );
11847 uint32 oldest_slot = BUYBACK_SLOT_START;
11849 for(uint32 i = BUYBACK_SLOT_START+1; i < BUYBACK_SLOT_END; ++i )
11851 // found empty
11852 if (!m_items[i])
11854 slot = i;
11855 break;
11858 uint32 i_time = GetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + i - BUYBACK_SLOT_START);
11860 if (oldest_time > i_time)
11862 oldest_time = i_time;
11863 oldest_slot = i;
11867 // find oldest
11868 slot = oldest_slot;
11871 RemoveItemFromBuyBackSlot( slot, true );
11872 DEBUG_LOG( "STORAGE: AddItemToBuyBackSlot item = %u, slot = %u", pItem->GetEntry(), slot);
11874 m_items[slot] = pItem;
11875 time_t base = time(NULL);
11876 uint32 etime = uint32(base - m_logintime + (30 * 3600));
11877 uint32 eslot = slot - BUYBACK_SLOT_START;
11879 SetUInt64Value( PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), pItem->GetGUID() );
11880 if (ItemPrototype const *pProto = pItem->GetProto())
11881 SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, pProto->SellPrice * pItem->GetCount() );
11882 else
11883 SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0 );
11884 SetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, (uint32)etime );
11886 // move to next (for non filled list is move most optimized choice)
11887 if (m_currentBuybackSlot < BUYBACK_SLOT_END - 1)
11888 ++m_currentBuybackSlot;
11892 Item* Player::GetItemFromBuyBackSlot( uint32 slot )
11894 DEBUG_LOG( "STORAGE: GetItemFromBuyBackSlot slot = %u", slot);
11895 if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END)
11896 return m_items[slot];
11897 return NULL;
11900 void Player::RemoveItemFromBuyBackSlot( uint32 slot, bool del )
11902 DEBUG_LOG( "STORAGE: RemoveItemFromBuyBackSlot slot = %u", slot);
11903 if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END)
11905 Item *pItem = m_items[slot];
11906 if (pItem)
11908 pItem->RemoveFromWorld();
11909 if(del) pItem->SetState(ITEM_REMOVED, this);
11912 m_items[slot] = NULL;
11914 uint32 eslot = slot - BUYBACK_SLOT_START;
11915 SetUInt64Value( PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), 0 );
11916 SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0 );
11917 SetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0 );
11919 // if current backslot is filled set to now free slot
11920 if (m_items[m_currentBuybackSlot])
11921 m_currentBuybackSlot = slot;
11925 void Player::SendEquipError( uint8 msg, Item* pItem, Item *pItem2, uint32 itemid /*= 0*/ )
11927 DEBUG_LOG( "WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)", msg);
11928 WorldPacket data(SMSG_INVENTORY_CHANGE_FAILURE, 1+8+8+1);
11929 data << uint8(msg);
11931 if (msg != EQUIP_ERR_OK)
11933 data << uint64(pItem ? pItem->GetGUID() : 0);
11934 data << uint64(pItem2 ? pItem2->GetGUID() : 0);
11935 data << uint8(0); // bag type subclass, used with EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM and EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG2
11937 switch(msg)
11939 case EQUIP_ERR_CANT_EQUIP_LEVEL_I:
11940 case EQUIP_ERR_PURCHASE_LEVEL_TOO_LOW:
11942 ItemPrototype const* proto = pItem ? pItem->GetProto() : sObjectMgr.GetItemPrototype(itemid);
11943 data << uint32(proto ? proto->RequiredLevel : 0);
11944 break;
11946 case EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM: // no idea about this one...
11948 data << uint64(0);
11949 data << uint32(0);
11950 data << uint64(0);
11951 break;
11953 case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS:
11954 case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED_IS:
11955 case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS:
11957 ItemPrototype const* proto = pItem ? pItem->GetProto() : sObjectMgr.GetItemPrototype(itemid);
11958 data << uint32(proto ? proto->ItemLimitCategory : 0);
11959 break;
11961 default:
11962 break;
11965 GetSession()->SendPacket(&data);
11968 void Player::SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 param )
11970 DEBUG_LOG( "WORLD: Sent SMSG_BUY_FAILED" );
11971 WorldPacket data( SMSG_BUY_FAILED, (8+4+4+1) );
11972 data << uint64(pCreature ? pCreature->GetGUID() : 0);
11973 data << uint32(item);
11974 if (param > 0)
11975 data << uint32(param);
11976 data << uint8(msg);
11977 GetSession()->SendPacket(&data);
11980 void Player::SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param )
11982 DEBUG_LOG( "WORLD: Sent SMSG_SELL_ITEM" );
11983 WorldPacket data( SMSG_SELL_ITEM,(8+8+(param?4:0)+1)); // last check 2.0.10
11984 data << uint64(pCreature ? pCreature->GetGUID() : 0);
11985 data << uint64(guid);
11986 if (param > 0)
11987 data << uint32(param);
11988 data << uint8(msg);
11989 GetSession()->SendPacket(&data);
11992 void Player::ClearTrade()
11994 tradeGold = 0;
11995 acceptTrade = false;
11996 for(int i = 0; i < TRADE_SLOT_COUNT; ++i)
11997 tradeItems[i] = NULL_SLOT;
12000 void Player::TradeCancel(bool sendback)
12002 if (pTrader)
12004 // send yellow "Trade canceled" message to both traders
12005 WorldSession* ws;
12006 ws = GetSession();
12007 if (sendback)
12008 ws->SendCancelTrade();
12009 ws = pTrader->GetSession();
12010 if (!ws->PlayerLogout())
12011 ws->SendCancelTrade();
12013 // cleanup
12014 ClearTrade();
12015 pTrader->ClearTrade();
12016 // prevent loss of reference
12017 pTrader->pTrader = NULL;
12018 pTrader = NULL;
12022 void Player::UpdateItemDuration(uint32 time, bool realtimeonly)
12024 if (m_itemDuration.empty())
12025 return;
12027 DEBUG_LOG("Player::UpdateItemDuration(%u,%u)", time, realtimeonly);
12029 for(ItemDurationList::const_iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end(); )
12031 Item* item = *itr;
12032 ++itr; // current element can be erased in UpdateDuration
12034 if ((realtimeonly && (item->GetProto()->ExtraFlags & ITEM_EXTRA_REAL_TIME_DURATION)) || !realtimeonly)
12035 item->UpdateDuration(this,time);
12039 void Player::UpdateEnchantTime(uint32 time)
12041 for(EnchantDurationList::iterator itr = m_enchantDuration.begin(),next;itr != m_enchantDuration.end();itr=next)
12043 ASSERT(itr->item);
12044 next = itr;
12045 if (!itr->item->GetEnchantmentId(itr->slot))
12047 next = m_enchantDuration.erase(itr);
12049 else if (itr->leftduration <= time)
12051 ApplyEnchantment(itr->item, itr->slot, false, false);
12052 itr->item->ClearEnchantment(itr->slot);
12053 next = m_enchantDuration.erase(itr);
12055 else if (itr->leftduration > time)
12057 itr->leftduration -= time;
12058 ++next;
12063 void Player::AddEnchantmentDurations(Item *item)
12065 for(int x = 0; x < MAX_ENCHANTMENT_SLOT; ++x)
12067 if (!item->GetEnchantmentId(EnchantmentSlot(x)))
12068 continue;
12070 uint32 duration = item->GetEnchantmentDuration(EnchantmentSlot(x));
12071 if (duration > 0)
12072 AddEnchantmentDuration(item, EnchantmentSlot(x), duration);
12076 void Player::RemoveEnchantmentDurations(Item *item)
12078 for(EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end();)
12080 if (itr->item == item)
12082 // save duration in item
12083 item->SetEnchantmentDuration(EnchantmentSlot(itr->slot), itr->leftduration);
12084 itr = m_enchantDuration.erase(itr);
12086 else
12087 ++itr;
12091 void Player::RemoveAllEnchantments(EnchantmentSlot slot)
12093 // remove enchantments from equipped items first to clean up the m_enchantDuration list
12094 for(EnchantDurationList::iterator itr = m_enchantDuration.begin(), next; itr != m_enchantDuration.end(); itr = next)
12096 next = itr;
12097 if (itr->slot == slot)
12099 if (itr->item && itr->item->GetEnchantmentId(slot))
12101 // remove from stats
12102 ApplyEnchantment(itr->item, slot, false, false);
12103 // remove visual
12104 itr->item->ClearEnchantment(slot);
12106 // remove from update list
12107 next = m_enchantDuration.erase(itr);
12109 else
12110 ++next;
12113 // remove enchants from inventory items
12114 // NOTE: no need to remove these from stats, since these aren't equipped
12115 // in inventory
12116 for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
12117 if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
12118 if (pItem->GetEnchantmentId(slot))
12119 pItem->ClearEnchantment(slot);
12121 // in inventory bags
12122 for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
12123 if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
12124 for(uint32 j = 0; j < pBag->GetBagSize(); ++j)
12125 if (Item* pItem = pBag->GetItemByPos(j))
12126 if (pItem->GetEnchantmentId(slot))
12127 pItem->ClearEnchantment(slot);
12130 // duration == 0 will remove item enchant
12131 void Player::AddEnchantmentDuration(Item *item,EnchantmentSlot slot,uint32 duration)
12133 if (!item)
12134 return;
12136 if (slot >= MAX_ENCHANTMENT_SLOT)
12137 return;
12139 for(EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr)
12141 if (itr->item == item && itr->slot == slot)
12143 itr->item->SetEnchantmentDuration(itr->slot, itr->leftduration);
12144 m_enchantDuration.erase(itr);
12145 break;
12148 if (item && duration > 0 )
12150 GetSession()->SendItemEnchantTimeUpdate(GetGUID(), item->GetGUID(), slot, uint32(duration/1000));
12151 m_enchantDuration.push_back(EnchantDuration(item, slot, duration));
12155 void Player::ApplyEnchantment(Item *item,bool apply)
12157 for(uint32 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot)
12158 ApplyEnchantment(item, EnchantmentSlot(slot), apply);
12161 void Player::ApplyEnchantment(Item *item, EnchantmentSlot slot, bool apply, bool apply_dur, bool ignore_condition)
12163 if (!item)
12164 return;
12166 if (!item->IsEquipped())
12167 return;
12169 if (slot >= MAX_ENCHANTMENT_SLOT)
12170 return;
12172 uint32 enchant_id = item->GetEnchantmentId(slot);
12173 if (!enchant_id)
12174 return;
12176 SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
12177 if (!pEnchant)
12178 return;
12180 if (!ignore_condition && pEnchant->EnchantmentCondition && !((Player*)this)->EnchantmentFitsRequirements(pEnchant->EnchantmentCondition, -1))
12181 return;
12183 if (!item->IsBroken())
12185 for (int s = 0; s < 3; ++s)
12187 uint32 enchant_display_type = pEnchant->type[s];
12188 uint32 enchant_amount = pEnchant->amount[s];
12189 uint32 enchant_spell_id = pEnchant->spellid[s];
12191 switch(enchant_display_type)
12193 case ITEM_ENCHANTMENT_TYPE_NONE:
12194 break;
12195 case ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL:
12196 // processed in Player::CastItemCombatSpell
12197 break;
12198 case ITEM_ENCHANTMENT_TYPE_DAMAGE:
12199 if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
12200 HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(enchant_amount), apply);
12201 else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND)
12202 HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(enchant_amount), apply);
12203 else if (item->GetSlot() == EQUIPMENT_SLOT_RANGED)
12204 HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
12205 break;
12206 case ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL:
12207 if (enchant_spell_id)
12209 if (apply)
12211 int32 basepoints = 0;
12212 // Random Property Exist - try found basepoints for spell (basepoints depends from item suffix factor)
12213 if (item->GetItemRandomPropertyId())
12215 ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
12216 if (item_rand)
12218 // Search enchant_amount
12219 for (int k = 0; k < 3; ++k)
12221 if(item_rand->enchant_id[k] == enchant_id)
12223 basepoints = int32((item_rand->prefix[k] * item->GetItemSuffixFactor()) / 10000 );
12224 break;
12229 // Cast custom spell vs all equal basepoints getted from enchant_amount
12230 if (basepoints)
12231 CastCustomSpell(this, enchant_spell_id, &basepoints, &basepoints, &basepoints, true, item);
12232 else
12233 CastSpell(this, enchant_spell_id, true, item);
12235 else
12236 RemoveAurasDueToItemSpell(item, enchant_spell_id);
12238 break;
12239 case ITEM_ENCHANTMENT_TYPE_RESISTANCE:
12240 if (!enchant_amount)
12242 ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
12243 if(item_rand)
12245 for (int k = 0; k < 3; ++k)
12247 if(item_rand->enchant_id[k] == enchant_id)
12249 enchant_amount = uint32((item_rand->prefix[k] * item->GetItemSuffixFactor()) / 10000 );
12250 break;
12256 HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply);
12257 break;
12258 case ITEM_ENCHANTMENT_TYPE_STAT:
12260 if (!enchant_amount)
12262 ItemRandomSuffixEntry const *item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
12263 if(item_rand_suffix)
12265 for (int k = 0; k < 3; ++k)
12267 if(item_rand_suffix->enchant_id[k] == enchant_id)
12269 enchant_amount = uint32((item_rand_suffix->prefix[k] * item->GetItemSuffixFactor()) / 10000 );
12270 break;
12276 DEBUG_LOG("Adding %u to stat nb %u",enchant_amount,enchant_spell_id);
12277 switch (enchant_spell_id)
12279 case ITEM_MOD_MANA:
12280 DEBUG_LOG("+ %u MANA",enchant_amount);
12281 HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(enchant_amount), apply);
12282 break;
12283 case ITEM_MOD_HEALTH:
12284 DEBUG_LOG("+ %u HEALTH",enchant_amount);
12285 HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(enchant_amount), apply);
12286 break;
12287 case ITEM_MOD_AGILITY:
12288 DEBUG_LOG("+ %u AGILITY",enchant_amount);
12289 HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply);
12290 ApplyStatBuffMod(STAT_AGILITY, float(enchant_amount), apply);
12291 break;
12292 case ITEM_MOD_STRENGTH:
12293 DEBUG_LOG("+ %u STRENGTH",enchant_amount);
12294 HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply);
12295 ApplyStatBuffMod(STAT_STRENGTH, float(enchant_amount), apply);
12296 break;
12297 case ITEM_MOD_INTELLECT:
12298 DEBUG_LOG("+ %u INTELLECT",enchant_amount);
12299 HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply);
12300 ApplyStatBuffMod(STAT_INTELLECT, float(enchant_amount), apply);
12301 break;
12302 case ITEM_MOD_SPIRIT:
12303 DEBUG_LOG("+ %u SPIRIT",enchant_amount);
12304 HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply);
12305 ApplyStatBuffMod(STAT_SPIRIT, float(enchant_amount), apply);
12306 break;
12307 case ITEM_MOD_STAMINA:
12308 DEBUG_LOG("+ %u STAMINA",enchant_amount);
12309 HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply);
12310 ApplyStatBuffMod(STAT_STAMINA, float(enchant_amount), apply);
12311 break;
12312 case ITEM_MOD_DEFENSE_SKILL_RATING:
12313 ((Player*)this)->ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply);
12314 DEBUG_LOG("+ %u DEFENCE", enchant_amount);
12315 break;
12316 case ITEM_MOD_DODGE_RATING:
12317 ((Player*)this)->ApplyRatingMod(CR_DODGE, enchant_amount, apply);
12318 DEBUG_LOG("+ %u DODGE", enchant_amount);
12319 break;
12320 case ITEM_MOD_PARRY_RATING:
12321 ((Player*)this)->ApplyRatingMod(CR_PARRY, enchant_amount, apply);
12322 DEBUG_LOG("+ %u PARRY", enchant_amount);
12323 break;
12324 case ITEM_MOD_BLOCK_RATING:
12325 ((Player*)this)->ApplyRatingMod(CR_BLOCK, enchant_amount, apply);
12326 DEBUG_LOG("+ %u SHIELD_BLOCK", enchant_amount);
12327 break;
12328 case ITEM_MOD_HIT_MELEE_RATING:
12329 ((Player*)this)->ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
12330 DEBUG_LOG("+ %u MELEE_HIT", enchant_amount);
12331 break;
12332 case ITEM_MOD_HIT_RANGED_RATING:
12333 ((Player*)this)->ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
12334 DEBUG_LOG("+ %u RANGED_HIT", enchant_amount);
12335 break;
12336 case ITEM_MOD_HIT_SPELL_RATING:
12337 ((Player*)this)->ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
12338 DEBUG_LOG("+ %u SPELL_HIT", enchant_amount);
12339 break;
12340 case ITEM_MOD_CRIT_MELEE_RATING:
12341 ((Player*)this)->ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
12342 DEBUG_LOG("+ %u MELEE_CRIT", enchant_amount);
12343 break;
12344 case ITEM_MOD_CRIT_RANGED_RATING:
12345 ((Player*)this)->ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
12346 DEBUG_LOG("+ %u RANGED_CRIT", enchant_amount);
12347 break;
12348 case ITEM_MOD_CRIT_SPELL_RATING:
12349 ((Player*)this)->ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
12350 DEBUG_LOG("+ %u SPELL_CRIT", enchant_amount);
12351 break;
12352 // Values from ITEM_STAT_MELEE_HA_RATING to ITEM_MOD_HASTE_RANGED_RATING are never used
12353 // in Enchantments
12354 // case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
12355 // ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
12356 // break;
12357 // case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
12358 // ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
12359 // break;
12360 // case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
12361 // ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
12362 // break;
12363 // case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
12364 // ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
12365 // break;
12366 // case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
12367 // ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
12368 // break;
12369 // case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
12370 // ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
12371 // break;
12372 // case ITEM_MOD_HASTE_MELEE_RATING:
12373 // ((Player*)this)->ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
12374 // break;
12375 // case ITEM_MOD_HASTE_RANGED_RATING:
12376 // ((Player*)this)->ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
12377 // break;
12378 case ITEM_MOD_HASTE_SPELL_RATING:
12379 ((Player*)this)->ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
12380 break;
12381 case ITEM_MOD_HIT_RATING:
12382 ((Player*)this)->ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
12383 ((Player*)this)->ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
12384 ((Player*)this)->ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
12385 DEBUG_LOG("+ %u HIT", enchant_amount);
12386 break;
12387 case ITEM_MOD_CRIT_RATING:
12388 ((Player*)this)->ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
12389 ((Player*)this)->ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
12390 ((Player*)this)->ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
12391 DEBUG_LOG("+ %u CRITICAL", enchant_amount);
12392 break;
12393 // Values ITEM_MOD_HIT_TAKEN_RATING and ITEM_MOD_CRIT_TAKEN_RATING are never used in Enchantment
12394 // case ITEM_MOD_HIT_TAKEN_RATING:
12395 // ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
12396 // ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
12397 // ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
12398 // break;
12399 // case ITEM_MOD_CRIT_TAKEN_RATING:
12400 // ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
12401 // ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
12402 // ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
12403 // break;
12404 case ITEM_MOD_RESILIENCE_RATING:
12405 ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
12406 ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
12407 ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
12408 DEBUG_LOG("+ %u RESILIENCE", enchant_amount);
12409 break;
12410 case ITEM_MOD_HASTE_RATING:
12411 ((Player*)this)->ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
12412 ((Player*)this)->ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
12413 ((Player*)this)->ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
12414 DEBUG_LOG("+ %u HASTE", enchant_amount);
12415 break;
12416 case ITEM_MOD_EXPERTISE_RATING:
12417 ((Player*)this)->ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply);
12418 DEBUG_LOG("+ %u EXPERTISE", enchant_amount);
12419 break;
12420 case ITEM_MOD_ATTACK_POWER:
12421 HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(enchant_amount), apply);
12422 HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
12423 DEBUG_LOG("+ %u ATTACK_POWER", enchant_amount);
12424 break;
12425 case ITEM_MOD_RANGED_ATTACK_POWER:
12426 HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
12427 DEBUG_LOG("+ %u RANGED_ATTACK_POWER", enchant_amount);
12428 break;
12429 case ITEM_MOD_MANA_REGENERATION:
12430 ((Player*)this)->ApplyManaRegenBonus(enchant_amount, apply);
12431 DEBUG_LOG("+ %u MANA_REGENERATION", enchant_amount);
12432 break;
12433 case ITEM_MOD_ARMOR_PENETRATION_RATING:
12434 ((Player*)this)->ApplyRatingMod(CR_ARMOR_PENETRATION, enchant_amount, apply);
12435 DEBUG_LOG("+ %u ARMOR PENETRATION", enchant_amount);
12436 break;
12437 case ITEM_MOD_SPELL_POWER:
12438 ((Player*)this)->ApplySpellPowerBonus(enchant_amount, apply);
12439 DEBUG_LOG("+ %u SPELL_POWER", enchant_amount);
12440 break;
12441 case ITEM_MOD_BLOCK_VALUE:
12442 HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(enchant_amount), apply);
12443 break;
12444 case ITEM_MOD_FERAL_ATTACK_POWER:
12445 case ITEM_MOD_SPELL_HEALING_DONE: // deprecated
12446 case ITEM_MOD_SPELL_DAMAGE_DONE: // deprecated
12447 default:
12448 break;
12450 break;
12452 case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon
12454 if(getClass() == CLASS_SHAMAN)
12456 float addValue = 0.0f;
12457 if(item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
12459 addValue = float(enchant_amount * item->GetProto()->Delay / 1000.0f);
12460 HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, addValue, apply);
12462 else if(item->GetSlot() == EQUIPMENT_SLOT_OFFHAND )
12464 addValue = float(enchant_amount * item->GetProto()->Delay / 1000.0f);
12465 HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, addValue, apply);
12468 break;
12470 case ITEM_ENCHANTMENT_TYPE_USE_SPELL:
12471 // processed in Player::CastItemUseSpell
12472 break;
12473 case ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET:
12474 // nothing do..
12475 break;
12476 default:
12477 sLog.outError("Unknown item enchantment (id = %d) display type: %d", enchant_id, enchant_display_type);
12478 break;
12479 } /*switch(enchant_display_type)*/
12480 } /*for*/
12483 // visualize enchantment at player and equipped items
12484 if(slot == PERM_ENCHANTMENT_SLOT)
12485 SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 0, apply ? item->GetEnchantmentId(slot) : 0);
12487 if(slot == TEMP_ENCHANTMENT_SLOT)
12488 SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 1, apply ? item->GetEnchantmentId(slot) : 0);
12491 if(apply_dur)
12493 if(apply)
12495 // set duration
12496 uint32 duration = item->GetEnchantmentDuration(slot);
12497 if(duration > 0)
12498 AddEnchantmentDuration(item, slot, duration);
12500 else
12502 // duration == 0 will remove EnchantDuration
12503 AddEnchantmentDuration(item, slot, 0);
12508 void Player::SendEnchantmentDurations()
12510 for(EnchantDurationList::const_iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr)
12512 GetSession()->SendItemEnchantTimeUpdate(GetGUID(), itr->item->GetGUID(), itr->slot, uint32(itr->leftduration) / 1000);
12516 void Player::SendItemDurations()
12518 for(ItemDurationList::const_iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end(); ++itr)
12520 (*itr)->SendTimeUpdate(this);
12524 void Player::SendNewItem(Item *item, uint32 count, bool received, bool created, bool broadcast)
12526 if(!item) // prevent crash
12527 return;
12529 // last check 2.0.10
12530 WorldPacket data( SMSG_ITEM_PUSH_RESULT, (8+4+4+4+1+4+4+4+4+4) );
12531 data << uint64(GetGUID()); // player GUID
12532 data << uint32(received); // 0=looted, 1=from npc
12533 data << uint32(created); // 0=received, 1=created
12534 data << uint32(1); // IsShowChatMessage
12535 data << uint8(item->GetBagSlot()); // bagslot
12536 // item slot, but when added to stack: 0xFFFFFFFF
12537 data << uint32((item->GetCount() == count) ? item->GetSlot() : -1);
12538 data << uint32(item->GetEntry()); // item id
12539 data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
12540 data << uint32(item->GetItemRandomPropertyId()); // random item property id
12541 data << uint32(count); // count of items
12542 data << uint32(GetItemCount(item->GetEntry())); // count of items in inventory
12544 if (broadcast && GetGroup())
12545 GetGroup()->BroadcastPacket(&data, true);
12546 else
12547 GetSession()->SendPacket(&data);
12550 /*********************************************************/
12551 /*** GOSSIP SYSTEM ***/
12552 /*********************************************************/
12554 void Player::PrepareGossipMenu(WorldObject *pSource, uint32 menuId)
12556 PlayerMenu* pMenu = PlayerTalkClass;
12557 pMenu->ClearMenus();
12559 pMenu->GetGossipMenu().SetMenuId(menuId);
12561 GossipMenuItemsMapBounds pMenuItemBounds = sObjectMgr.GetGossipMenuItemsMapBounds(menuId);
12563 // if default menuId and no menu options exist for this, use options from default options
12564 if (pMenuItemBounds.first == pMenuItemBounds.second && menuId == GetDefaultGossipMenuForSource(pSource))
12565 pMenuItemBounds = sObjectMgr.GetGossipMenuItemsMapBounds(0);
12567 bool canTalkToCredit = pSource->GetTypeId() == TYPEID_UNIT;
12569 for(GossipMenuItemsMap::const_iterator itr = pMenuItemBounds.first; itr != pMenuItemBounds.second; ++itr)
12571 bool hasMenuItem = true;
12573 if (itr->second.cond_1 && !sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_1))
12574 continue;
12576 if (itr->second.cond_2 && !sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_2))
12577 continue;
12579 if (itr->second.cond_3 && !sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_3))
12580 continue;
12582 if (pSource->GetTypeId() == TYPEID_UNIT)
12584 Creature *pCreature = (Creature*)pSource;
12586 uint32 npcflags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS);
12588 if (!(itr->second.npc_option_npcflag & npcflags))
12589 continue;
12591 switch(itr->second.option_id)
12593 case GOSSIP_OPTION_GOSSIP:
12594 if (itr->second.action_menu_id) // has sub menu, so do not "talk" with this NPC yet
12595 canTalkToCredit = false;
12596 break;
12597 case GOSSIP_OPTION_QUESTGIVER:
12598 PrepareQuestMenu(pSource->GetGUID());
12599 hasMenuItem = false;
12600 break;
12601 case GOSSIP_OPTION_ARMORER:
12602 hasMenuItem = false; // added in special mode
12603 break;
12604 case GOSSIP_OPTION_SPIRITHEALER:
12605 if (!isDead())
12606 hasMenuItem = false;
12607 break;
12608 case GOSSIP_OPTION_VENDOR:
12610 VendorItemData const* vItems = pCreature->GetVendorItems();
12611 if (!vItems || vItems->Empty())
12613 sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.", pCreature->GetGUIDLow(), pCreature->GetEntry());
12614 hasMenuItem = false;
12616 break;
12618 case GOSSIP_OPTION_TRAINER:
12619 if (!pCreature->isCanTrainingOf(this, false))
12620 hasMenuItem = false;
12621 break;
12622 case GOSSIP_OPTION_UNLEARNTALENTS:
12623 if (!pCreature->isCanTrainingAndResetTalentsOf(this))
12624 hasMenuItem = false;
12625 break;
12626 case GOSSIP_OPTION_UNLEARNPETSKILLS:
12627 if (!GetPet() || GetPet()->getPetType() != HUNTER_PET || GetPet()->m_spells.size() <= 1 || pCreature->GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || pCreature->GetCreatureInfo()->trainer_class != CLASS_HUNTER)
12628 hasMenuItem = false;
12629 break;
12630 case GOSSIP_OPTION_TAXIVENDOR:
12631 if (GetSession()->SendLearnNewTaxiNode(pCreature))
12632 return;
12633 break;
12634 case GOSSIP_OPTION_BATTLEFIELD:
12635 if (!pCreature->isCanInteractWithBattleMaster(this, false))
12636 hasMenuItem = false;
12637 break;
12638 case GOSSIP_OPTION_STABLEPET:
12639 if (getClass() != CLASS_HUNTER)
12640 hasMenuItem = false;
12641 break;
12642 case GOSSIP_OPTION_SPIRITGUIDE:
12643 case GOSSIP_OPTION_INNKEEPER:
12644 case GOSSIP_OPTION_BANKER:
12645 case GOSSIP_OPTION_PETITIONER:
12646 case GOSSIP_OPTION_TABARDDESIGNER:
12647 case GOSSIP_OPTION_AUCTIONEER:
12648 break; // no checks
12649 default:
12650 sLog.outErrorDb("Creature entry %u have unknown gossip option %u for menu %u", pCreature->GetEntry(), itr->second.option_id, itr->second.menu_id);
12651 hasMenuItem = false;
12652 break;
12655 else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
12657 GameObject *pGo = (GameObject*)pSource;
12659 switch(itr->second.option_id)
12661 case GOSSIP_OPTION_QUESTGIVER:
12662 if (pGo->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
12663 PrepareQuestMenu(pSource->GetGUID());
12664 hasMenuItem = false;
12665 break;
12666 case GOSSIP_OPTION_GOSSIP:
12667 if (pGo->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER && pGo->GetGoType() != GAMEOBJECT_TYPE_GOOBER)
12668 hasMenuItem = false;
12669 break;
12670 default:
12671 hasMenuItem = false;
12672 break;
12676 if (hasMenuItem)
12678 std::string strOptionText = itr->second.option_text;
12679 std::string strBoxText = itr->second.box_text;
12681 int loc_idx = GetSession()->GetSessionDbLocaleIndex();
12683 if (loc_idx >= 0)
12685 uint32 idxEntry = MAKE_PAIR32(menuId, itr->second.id);
12687 if (GossipMenuItemsLocale const *no = sObjectMgr.GetGossipMenuItemsLocale(idxEntry))
12689 if (no->OptionText.size() > (size_t)loc_idx && !no->OptionText[loc_idx].empty())
12690 strOptionText = no->OptionText[loc_idx];
12692 if (no->BoxText.size() > (size_t)loc_idx && !no->BoxText[loc_idx].empty())
12693 strBoxText = no->BoxText[loc_idx];
12697 pMenu->GetGossipMenu().AddMenuItem(itr->second.option_icon, strOptionText, 0, itr->second.option_id, strBoxText, itr->second.box_money, itr->second.box_coded);
12698 pMenu->GetGossipMenu().AddGossipMenuItemData(itr->second.action_menu_id, itr->second.action_poi_id, itr->second.action_script_id);
12702 if (canTalkToCredit)
12704 if (pSource->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
12705 TalkedToCreature(pSource->GetEntry(), pSource->GetGUID());
12708 // some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
12709 /*if (pMenu->Empty())
12711 if (pCreature->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER))
12713 // output error message if need
12714 pCreature->isCanTrainingOf(this, true);
12717 if (pCreature->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_BATTLEMASTER))
12719 // output error message if need
12720 pCreature->isCanInteractWithBattleMaster(this, true);
12725 void Player::SendPreparedGossip(WorldObject *pSource)
12727 if (!pSource)
12728 return;
12730 if (pSource->GetTypeId() == TYPEID_UNIT)
12732 // in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
12733 if (!((Creature*)pSource)->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_GOSSIP) && !PlayerTalkClass->GetQuestMenu().Empty())
12735 SendPreparedQuest(pSource->GetGUID());
12736 return;
12739 else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
12741 // probably need to find a better way here
12742 if (!PlayerTalkClass->GetGossipMenu().GetMenuId() && !PlayerTalkClass->GetQuestMenu().Empty())
12744 SendPreparedQuest(pSource->GetGUID());
12745 return;
12749 // in case non empty gossip menu (that not included quests list size) show it
12750 // (quest entries from quest menu will be included in list)
12752 uint32 textId = GetGossipTextId(pSource);
12754 if (uint32 menuId = PlayerTalkClass->GetGossipMenu().GetMenuId())
12755 textId = GetGossipTextId(menuId);
12757 PlayerTalkClass->SendGossipMenu(textId, pSource->GetGUID());
12760 void Player::OnGossipSelect(WorldObject* pSource, uint32 gossipListId, uint32 menuId)
12762 GossipMenu& gossipmenu = PlayerTalkClass->GetGossipMenu();
12764 if (gossipListId >= gossipmenu.MenuItemCount())
12765 return;
12767 // if not same, then something funky is going on
12768 if (menuId != gossipmenu.GetMenuId())
12769 return;
12771 GossipMenuItem const& menu_item = gossipmenu.GetItem(gossipListId);
12773 uint32 gossipOptionId = menu_item.m_gOptionId;
12774 uint64 guid = pSource->GetGUID();
12775 uint32 moneyTake = menu_item.m_gBoxMoney;
12777 // if this function called and player have money for pay MoneyTake or cheating, proccess both cases
12778 if (moneyTake > 0)
12780 if (GetMoney() >= moneyTake)
12781 ModifyMoney(-int32(moneyTake));
12782 else
12783 return; // cheating
12786 if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
12788 if (gossipOptionId > GOSSIP_OPTION_QUESTGIVER)
12790 sLog.outError("Player guid %u request invalid gossip option for GameObject entry %u", GetGUIDLow(), pSource->GetEntry());
12791 return;
12795 GossipMenuItemData pMenuData = gossipmenu.GetItemData(gossipListId);
12797 switch(gossipOptionId)
12799 case GOSSIP_OPTION_GOSSIP:
12801 if (pMenuData.m_gAction_poi)
12802 PlayerTalkClass->SendPointOfInterest(pMenuData.m_gAction_poi);
12804 if (pMenuData.m_gAction_menu)
12806 PrepareGossipMenu(pSource, pMenuData.m_gAction_menu);
12807 SendPreparedGossip(pSource);
12810 if (pMenuData.m_gAction_script)
12812 if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
12813 GetMap()->ScriptsStart(sGossipScripts, pMenuData.m_gAction_script, this, pSource);
12814 else if (pSource->GetTypeId() == TYPEID_UNIT)
12815 GetMap()->ScriptsStart(sGossipScripts, pMenuData.m_gAction_script, pSource, this);
12818 break;
12820 case GOSSIP_OPTION_SPIRITHEALER:
12821 if (isDead())
12822 ((Creature*)pSource)->CastSpell(((Creature*)pSource),17251,true,NULL,NULL,GetGUID());
12823 break;
12824 case GOSSIP_OPTION_QUESTGIVER:
12825 PrepareQuestMenu(guid);
12826 SendPreparedQuest(guid);
12827 break;
12828 case GOSSIP_OPTION_VENDOR:
12829 case GOSSIP_OPTION_ARMORER:
12830 GetSession()->SendListInventory(guid);
12831 break;
12832 case GOSSIP_OPTION_STABLEPET:
12833 GetSession()->SendStablePet(guid);
12834 break;
12835 case GOSSIP_OPTION_TRAINER:
12836 GetSession()->SendTrainerList(guid);
12837 break;
12838 case GOSSIP_OPTION_UNLEARNTALENTS:
12839 PlayerTalkClass->CloseGossip();
12840 SendTalentWipeConfirm(guid);
12841 break;
12842 case GOSSIP_OPTION_UNLEARNPETSKILLS:
12843 PlayerTalkClass->CloseGossip();
12844 SendPetSkillWipeConfirm();
12845 break;
12846 case GOSSIP_OPTION_TAXIVENDOR:
12847 GetSession()->SendTaxiMenu(((Creature*)pSource));
12848 break;
12849 case GOSSIP_OPTION_INNKEEPER:
12850 PlayerTalkClass->CloseGossip();
12851 SetBindPoint(guid);
12852 break;
12853 case GOSSIP_OPTION_BANKER:
12854 GetSession()->SendShowBank(guid);
12855 break;
12856 case GOSSIP_OPTION_PETITIONER:
12857 PlayerTalkClass->CloseGossip();
12858 GetSession()->SendPetitionShowList(guid);
12859 break;
12860 case GOSSIP_OPTION_TABARDDESIGNER:
12861 PlayerTalkClass->CloseGossip();
12862 GetSession()->SendTabardVendorActivate(guid);
12863 break;
12864 case GOSSIP_OPTION_AUCTIONEER:
12865 GetSession()->SendAuctionHello(guid, ((Creature*)pSource));
12866 break;
12867 case GOSSIP_OPTION_SPIRITGUIDE:
12868 PrepareGossipMenu(pSource);
12869 SendPreparedGossip(pSource);
12870 break;
12871 case GOSSIP_OPTION_BATTLEFIELD:
12873 BattleGroundTypeId bgTypeId = sBattleGroundMgr.GetBattleMasterBG(pSource->GetEntry());
12875 if (bgTypeId == BATTLEGROUND_TYPE_NONE)
12877 sLog.outError("a user (guid %u) requested battlegroundlist from a npc who is no battlemaster", GetGUIDLow());
12878 return;
12881 GetSession()->SendBattlegGroundList(guid, bgTypeId);
12882 break;
12887 uint32 Player::GetGossipTextId(WorldObject *pSource)
12889 if (!pSource || pSource->GetTypeId() != TYPEID_UNIT || !((Creature*)pSource)->GetDBTableGUIDLow())
12890 return DEFAULT_GOSSIP_MESSAGE;
12892 if (uint32 pos = sObjectMgr.GetNpcGossip(((Creature*)pSource)->GetDBTableGUIDLow()))
12893 return pos;
12895 return DEFAULT_GOSSIP_MESSAGE;
12898 uint32 Player::GetGossipTextId(uint32 menuId)
12900 uint32 textId = DEFAULT_GOSSIP_MESSAGE;
12902 if (!menuId)
12903 return textId;
12905 GossipMenusMapBounds pMenuBounds = sObjectMgr.GetGossipMenusMapBounds(menuId);
12907 for(GossipMenusMap::const_iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr)
12909 if (sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_1) && sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_2))
12910 textId = itr->second.text_id;
12913 return textId;
12916 uint32 Player::GetDefaultGossipMenuForSource(WorldObject *pSource)
12918 if (pSource->GetTypeId() == TYPEID_UNIT)
12919 return ((Creature*)pSource)->GetCreatureInfo()->GossipMenuId;
12920 else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
12921 return((GameObject*)pSource)->GetGOInfo()->GetGossipMenuId();
12923 return 0;
12926 /*********************************************************/
12927 /*** QUEST SYSTEM ***/
12928 /*********************************************************/
12930 void Player::PrepareQuestMenu( uint64 guid )
12932 Object *pObject;
12933 QuestRelations* pObjectQR;
12934 QuestRelations* pObjectQIR;
12936 // pets also can have quests
12937 if (Creature *pCreature = GetMap()->GetCreatureOrPetOrVehicle(guid))
12939 pObject = (Object*)pCreature;
12940 pObjectQR = &sObjectMgr.mCreatureQuestRelations;
12941 pObjectQIR = &sObjectMgr.mCreatureQuestInvolvedRelations;
12943 else
12945 //we should obtain map pointer from GetMap() in 99% of cases. Special case
12946 //only for quests which cast teleport spells on player
12947 Map * _map = IsInWorld() ? GetMap() : sMapMgr.FindMap(GetMapId(), GetInstanceId());
12948 ASSERT(_map);
12949 GameObject *pGameObject = _map->GetGameObject(guid);
12950 if( pGameObject )
12952 pObject = (Object*)pGameObject;
12953 pObjectQR = &sObjectMgr.mGOQuestRelations;
12954 pObjectQIR = &sObjectMgr.mGOQuestInvolvedRelations;
12956 else
12957 return;
12960 QuestMenu &qm = PlayerTalkClass->GetQuestMenu();
12961 qm.ClearMenu();
12963 for(QuestRelations::const_iterator i = pObjectQIR->lower_bound(pObject->GetEntry()); i != pObjectQIR->upper_bound(pObject->GetEntry()); ++i)
12965 uint32 quest_id = i->second;
12966 QuestStatus status = GetQuestStatus( quest_id );
12967 if ( status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus( quest_id ) )
12968 qm.AddMenuItem(quest_id, 4);
12969 else if ( status == QUEST_STATUS_INCOMPLETE )
12970 qm.AddMenuItem(quest_id, 4);
12971 else if (status == QUEST_STATUS_AVAILABLE )
12972 qm.AddMenuItem(quest_id, 2);
12975 for(QuestRelations::const_iterator i = pObjectQR->lower_bound(pObject->GetEntry()); i != pObjectQR->upper_bound(pObject->GetEntry()); ++i)
12977 uint32 quest_id = i->second;
12978 Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id);
12979 if(!pQuest) continue;
12981 QuestStatus status = GetQuestStatus( quest_id );
12983 if (pQuest->IsAutoComplete() && CanTakeQuest(pQuest, false))
12984 qm.AddMenuItem(quest_id, 4);
12985 else if ( status == QUEST_STATUS_NONE && CanTakeQuest( pQuest, false ) )
12986 qm.AddMenuItem(quest_id, 2);
12990 void Player::SendPreparedQuest(uint64 guid)
12992 QuestMenu& questMenu = PlayerTalkClass->GetQuestMenu();
12994 if (questMenu.Empty())
12995 return;
12997 QuestMenuItem const& qmi0 = questMenu.GetItem(0);
12999 uint32 icon = qmi0.m_qIcon;
13001 // single element case
13002 if (questMenu.MenuItemCount() == 1)
13004 // Auto open -- maybe also should verify there is no greeting
13005 uint32 quest_id = qmi0.m_qId;
13006 Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id);
13008 if (pQuest)
13010 if (icon == 4 && !GetQuestRewardStatus(quest_id))
13011 PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, CanRewardQuest(pQuest, false), true);
13012 else if (icon == 4)
13013 PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, CanRewardQuest(pQuest, false), true);
13014 // Send completable on repeatable and autoCompletable quest if player don't have quest
13015 // TODO: verify if check for !pQuest->IsDaily() is really correct (possibly not)
13016 else if (pQuest->IsAutoComplete() && pQuest->IsRepeatable() && !pQuest->IsDailyOrWeekly())
13017 PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, CanCompleteRepeatableQuest(pQuest), true);
13018 else
13019 PlayerTalkClass->SendQuestGiverQuestDetails(pQuest, guid, true);
13022 // multiply entries
13023 else
13025 QEmote qe;
13026 qe._Delay = 0;
13027 qe._Emote = 0;
13028 std::string title = "";
13030 // need pet case for some quests
13031 if (Creature *pCreature = GetMap()->GetCreatureOrPetOrVehicle(guid))
13033 uint32 textid = GetGossipTextId(pCreature);
13035 GossipText const* gossiptext = sObjectMgr.GetGossipText(textid);
13036 if (!gossiptext)
13038 qe._Delay = 0; //TEXTEMOTE_MESSAGE; //zyg: player emote
13039 qe._Emote = 0; //TEXTEMOTE_HELLO; //zyg: NPC emote
13040 title = "";
13042 else
13044 qe = gossiptext->Options[0].Emotes[0];
13046 if(!gossiptext->Options[0].Text_0.empty())
13048 title = gossiptext->Options[0].Text_0;
13050 int loc_idx = GetSession()->GetSessionDbLocaleIndex();
13051 if (loc_idx >= 0)
13053 NpcTextLocale const *nl = sObjectMgr.GetNpcTextLocale(textid);
13054 if (nl)
13056 if ((int32)nl->Text_0[0].size() > loc_idx && !nl->Text_0[0][loc_idx].empty())
13057 title = nl->Text_0[0][loc_idx];
13061 else
13063 title = gossiptext->Options[0].Text_1;
13065 int loc_idx = GetSession()->GetSessionDbLocaleIndex();
13066 if (loc_idx >= 0)
13068 NpcTextLocale const *nl = sObjectMgr.GetNpcTextLocale(textid);
13069 if (nl)
13071 if ((int32)nl->Text_1[0].size() > loc_idx && !nl->Text_1[0][loc_idx].empty())
13072 title = nl->Text_1[0][loc_idx];
13078 PlayerTalkClass->SendQuestGiverQuestList(qe, title, guid);
13082 bool Player::IsActiveQuest( uint32 quest_id ) const
13084 QuestStatusMap::const_iterator itr = mQuestStatus.find(quest_id);
13086 return itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE;
13089 Quest const * Player::GetNextQuest( uint64 guid, Quest const *pQuest )
13091 Object *pObject;
13092 QuestRelations* pObjectQR;
13093 QuestRelations* pObjectQIR;
13095 if (Creature *pCreature = GetMap()->GetCreatureOrPetOrVehicle(guid))
13097 pObject = (Object*)pCreature;
13098 pObjectQR = &sObjectMgr.mCreatureQuestRelations;
13099 pObjectQIR = &sObjectMgr.mCreatureQuestInvolvedRelations;
13101 else
13103 //we should obtain map pointer from GetMap() in 99% of cases. Special case
13104 //only for quests which cast teleport spells on player
13105 Map * _map = IsInWorld() ? GetMap() : sMapMgr.FindMap(GetMapId(), GetInstanceId());
13106 ASSERT(_map);
13107 GameObject *pGameObject = _map->GetGameObject(guid);
13108 if( pGameObject )
13110 pObject = (Object*)pGameObject;
13111 pObjectQR = &sObjectMgr.mGOQuestRelations;
13112 pObjectQIR = &sObjectMgr.mGOQuestInvolvedRelations;
13114 else
13115 return NULL;
13118 uint32 nextQuestID = pQuest->GetNextQuestInChain();
13119 for(QuestRelations::const_iterator itr = pObjectQR->lower_bound(pObject->GetEntry()); itr != pObjectQR->upper_bound(pObject->GetEntry()); ++itr)
13121 if (itr->second == nextQuestID)
13122 return sObjectMgr.GetQuestTemplate(nextQuestID);
13125 return NULL;
13128 bool Player::CanSeeStartQuest( Quest const *pQuest )
13130 if( SatisfyQuestRace( pQuest, false ) && SatisfyQuestSkillOrClass( pQuest, false ) &&
13131 SatisfyQuestExclusiveGroup( pQuest, false ) && SatisfyQuestReputation( pQuest, false ) &&
13132 SatisfyQuestPreviousQuest( pQuest, false ) && SatisfyQuestNextChain( pQuest, false ) &&
13133 SatisfyQuestPrevChain( pQuest, false ) && SatisfyQuestDay( pQuest, false ) && SatisfyQuestWeek( pQuest, false ) )
13135 return getLevel() + sWorld.getConfig(CONFIG_UINT32_QUEST_HIGH_LEVEL_HIDE_DIFF) >= pQuest->GetMinLevel();
13138 return false;
13141 bool Player::CanTakeQuest( Quest const *pQuest, bool msg )
13143 return SatisfyQuestStatus( pQuest, msg ) && SatisfyQuestExclusiveGroup( pQuest, msg )
13144 && SatisfyQuestRace( pQuest, msg ) && SatisfyQuestLevel( pQuest, msg )
13145 && SatisfyQuestSkillOrClass( pQuest, msg ) && SatisfyQuestReputation( pQuest, msg )
13146 && SatisfyQuestPreviousQuest( pQuest, msg ) && SatisfyQuestTimed( pQuest, msg )
13147 && SatisfyQuestNextChain( pQuest, msg ) && SatisfyQuestPrevChain( pQuest, msg )
13148 && SatisfyQuestDay( pQuest, msg ) && SatisfyQuestWeek( pQuest, msg );
13151 bool Player::CanAddQuest( Quest const *pQuest, bool msg )
13153 if( !SatisfyQuestLog( msg ) )
13154 return false;
13156 uint32 srcitem = pQuest->GetSrcItemId();
13157 if( srcitem > 0 )
13159 uint32 count = pQuest->GetSrcItemCount();
13160 ItemPosCountVec dest;
13161 uint8 msg2 = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, srcitem, count );
13163 // player already have max number (in most case 1) source item, no additional item needed and quest can be added.
13164 if( msg2 == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
13165 return true;
13166 else if( msg2 != EQUIP_ERR_OK )
13168 SendEquipError(msg2, NULL, NULL, srcitem);
13169 return false;
13172 return true;
13175 bool Player::CanCompleteQuest( uint32 quest_id )
13177 if( quest_id )
13179 QuestStatusData& q_status = mQuestStatus[quest_id];
13180 if( q_status.m_status == QUEST_STATUS_COMPLETE )
13181 return false; // not allow re-complete quest
13183 Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id);
13185 if(!qInfo)
13186 return false;
13188 // auto complete quest
13189 if (qInfo->IsAutoComplete() && CanTakeQuest(qInfo, false))
13190 return true;
13192 if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
13195 if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
13197 for(int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
13199 if( qInfo->ReqItemCount[i] != 0 && q_status.m_itemcount[i] < qInfo->ReqItemCount[i] )
13200 return false;
13204 if ( qInfo->HasFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO) )
13206 for(int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
13208 if( qInfo->ReqCreatureOrGOId[i] == 0 )
13209 continue;
13211 if( qInfo->ReqCreatureOrGOCount[i] != 0 && q_status.m_creatureOrGOcount[i] < qInfo->ReqCreatureOrGOCount[i] )
13212 return false;
13216 if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT ) && !q_status.m_explored )
13217 return false;
13219 if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) && q_status.m_timer == 0 )
13220 return false;
13222 if ( qInfo->GetRewOrReqMoney() < 0 )
13224 if ( GetMoney() < uint32(-qInfo->GetRewOrReqMoney()) )
13225 return false;
13228 uint32 repFacId = qInfo->GetRepObjectiveFaction();
13229 if ( repFacId && GetReputationMgr().GetReputation(repFacId) < qInfo->GetRepObjectiveValue() )
13230 return false;
13232 return true;
13235 return false;
13238 bool Player::CanCompleteRepeatableQuest( Quest const *pQuest )
13240 // Solve problem that player don't have the quest and try complete it.
13241 // if repeatable she must be able to complete event if player don't have it.
13242 // Seem that all repeatable quest are DELIVER Flag so, no need to add more.
13243 if( !CanTakeQuest(pQuest, false) )
13244 return false;
13246 if (pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER) )
13247 for(int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
13248 if( pQuest->ReqItemId[i] && pQuest->ReqItemCount[i] && !HasItemCount(pQuest->ReqItemId[i], pQuest->ReqItemCount[i]) )
13249 return false;
13251 if( !CanRewardQuest(pQuest, false) )
13252 return false;
13254 return true;
13257 bool Player::CanRewardQuest( Quest const *pQuest, bool msg )
13259 // not auto complete quest and not completed quest (only cheating case, then ignore without message)
13260 if (!pQuest->IsAutoComplete() && GetQuestStatus(pQuest->GetQuestId()) != QUEST_STATUS_COMPLETE)
13261 return false;
13263 // daily quest can't be rewarded (25 daily quest already completed)
13264 if (!SatisfyQuestDay(pQuest,true) || !SatisfyQuestWeek(pQuest,true))
13265 return false;
13267 // rewarded and not repeatable quest (only cheating case, then ignore without message)
13268 if (GetQuestRewardStatus(pQuest->GetQuestId()))
13269 return false;
13271 // prevent receive reward with quest items in bank
13272 if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
13274 for(int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
13276 if( pQuest->ReqItemCount[i] != 0 &&
13277 GetItemCount(pQuest->ReqItemId[i]) < pQuest->ReqItemCount[i] )
13279 if(msg)
13280 SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL, pQuest->ReqItemId[i]);
13281 return false;
13286 // prevent receive reward with low money and GetRewOrReqMoney() < 0
13287 if (pQuest->GetRewOrReqMoney() < 0 && GetMoney() < uint32(-pQuest->GetRewOrReqMoney()) )
13288 return false;
13290 return true;
13293 bool Player::CanRewardQuest( Quest const *pQuest, uint32 reward, bool msg )
13295 // prevent receive reward with quest items in bank or for not completed quest
13296 if(!CanRewardQuest(pQuest,msg))
13297 return false;
13299 if ( pQuest->GetRewChoiceItemsCount() > 0 )
13301 if( pQuest->RewChoiceItemId[reward] )
13303 ItemPosCountVec dest;
13304 uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] );
13305 if( res != EQUIP_ERR_OK )
13307 SendEquipError( res, NULL, NULL, pQuest->RewChoiceItemId[reward] );
13308 return false;
13313 if ( pQuest->GetRewItemsCount() > 0 )
13315 for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i)
13317 if( pQuest->RewItemId[i] )
13319 ItemPosCountVec dest;
13320 uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] );
13321 if( res != EQUIP_ERR_OK )
13323 SendEquipError( res, NULL, NULL );
13324 return false;
13330 return true;
13333 void Player::SendPetTameFailure(PetTameFailureReason reason)
13335 WorldPacket data(SMSG_PET_TAME_FAILURE, 1);
13336 data << uint8(reason);
13337 GetSession()->SendPacket(&data);
13340 void Player::AddQuest( Quest const *pQuest, Object *questGiver )
13342 uint16 log_slot = FindQuestSlot( 0 );
13343 ASSERT(log_slot < MAX_QUEST_LOG_SIZE);
13345 uint32 quest_id = pQuest->GetQuestId();
13347 // if not exist then created with set uState==NEW and rewarded=false
13348 QuestStatusData& questStatusData = mQuestStatus[quest_id];
13350 // check for repeatable quests status reset
13351 questStatusData.m_status = QUEST_STATUS_INCOMPLETE;
13352 questStatusData.m_explored = false;
13354 if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
13356 for(int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
13357 questStatusData.m_itemcount[i] = 0;
13360 if ( pQuest->HasFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO) )
13362 for(int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
13363 questStatusData.m_creatureOrGOcount[i] = 0;
13366 GiveQuestSourceItem( pQuest );
13367 AdjustQuestReqItemCount( pQuest, questStatusData );
13369 if( pQuest->GetRepObjectiveFaction() )
13370 if(FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->GetRepObjectiveFaction()))
13371 GetReputationMgr().SetVisible(factionEntry);
13373 uint32 qtime = 0;
13374 if( pQuest->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) )
13376 uint32 limittime = pQuest->GetLimitTime();
13378 // shared timed quest
13379 if(questGiver && questGiver->GetTypeId()==TYPEID_PLAYER)
13380 limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / IN_MILLISECONDS;
13382 AddTimedQuest( quest_id );
13383 questStatusData.m_timer = limittime * IN_MILLISECONDS;
13384 qtime = static_cast<uint32>(time(NULL)) + limittime;
13386 else
13387 questStatusData.m_timer = 0;
13389 SetQuestSlot(log_slot, quest_id, qtime);
13391 if (questStatusData.uState != QUEST_NEW)
13392 questStatusData.uState = QUEST_CHANGED;
13394 //starting initial quest script
13395 if(questGiver && pQuest->GetQuestStartScript()!=0)
13396 GetMap()->ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this);
13398 // Some spells applied at quest activation
13399 SpellAreaForQuestMapBounds saBounds = sSpellMgr.GetSpellAreaForQuestMapBounds(quest_id,true);
13400 if(saBounds.first != saBounds.second)
13402 uint32 zone, area;
13403 GetZoneAndAreaId(zone,area);
13405 for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
13406 if(itr->second->autocast && itr->second->IsFitToRequirements(this,zone,area))
13407 if (!HasAura(itr->second->spellId, EFFECT_INDEX_0) )
13408 CastSpell(this,itr->second->spellId,true);
13411 UpdateForQuestWorldObjects();
13414 void Player::CompleteQuest( uint32 quest_id )
13416 if( quest_id )
13418 SetQuestStatus( quest_id, QUEST_STATUS_COMPLETE );
13420 uint16 log_slot = FindQuestSlot( quest_id );
13421 if( log_slot < MAX_QUEST_LOG_SIZE)
13422 SetQuestSlotState(log_slot,QUEST_STATE_COMPLETE);
13424 if(Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id))
13426 if( qInfo->HasFlag(QUEST_FLAGS_AUTO_REWARDED) )
13427 RewardQuest(qInfo,0,this,false);
13428 else
13429 SendQuestComplete( quest_id );
13434 void Player::IncompleteQuest( uint32 quest_id )
13436 if( quest_id )
13438 SetQuestStatus( quest_id, QUEST_STATUS_INCOMPLETE );
13440 uint16 log_slot = FindQuestSlot( quest_id );
13441 if( log_slot < MAX_QUEST_LOG_SIZE)
13442 RemoveQuestSlotState(log_slot,QUEST_STATE_COMPLETE);
13446 void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce )
13448 //this THING should be here to protect code from quest, which cast on player far teleport as a reward
13449 //should work fine, cause far teleport will be executed in Player::Update()
13450 SetCanDelayTeleport(true);
13452 uint32 quest_id = pQuest->GetQuestId();
13454 for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i )
13456 if (pQuest->ReqItemId[i])
13457 DestroyItemCount( pQuest->ReqItemId[i], pQuest->ReqItemCount[i], true);
13460 RemoveTimedQuest(quest_id);
13462 if (BattleGround* bg = GetBattleGround())
13463 if (bg->GetTypeID() == BATTLEGROUND_AV)
13464 ((BattleGroundAV*)bg)->HandleQuestComplete(pQuest->GetQuestId(), this);
13466 if (pQuest->GetRewChoiceItemsCount() > 0)
13468 if (uint32 itemId = pQuest->RewChoiceItemId[reward])
13470 ItemPosCountVec dest;
13471 if (CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, pQuest->RewChoiceItemCount[reward] ) == EQUIP_ERR_OK)
13473 Item* item = StoreNewItem( dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
13474 SendNewItem(item, pQuest->RewChoiceItemCount[reward], true, false);
13479 if (pQuest->GetRewItemsCount() > 0)
13481 for (uint32 i=0; i < pQuest->GetRewItemsCount(); ++i)
13483 if (uint32 itemId = pQuest->RewItemId[i])
13485 ItemPosCountVec dest;
13486 if (CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, pQuest->RewItemCount[i] ) == EQUIP_ERR_OK)
13488 Item* item = StoreNewItem( dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
13489 SendNewItem(item, pQuest->RewItemCount[i], true, false);
13495 RewardReputation( pQuest );
13497 uint16 log_slot = FindQuestSlot( quest_id );
13498 if (log_slot < MAX_QUEST_LOG_SIZE)
13499 SetQuestSlot(log_slot,0);
13501 QuestStatusData& q_status = mQuestStatus[quest_id];
13503 // Not give XP in case already completed once repeatable quest
13504 uint32 XP = q_status.m_rewarded ? 0 : uint32(pQuest->XPValue( this )*sWorld.getConfig(CONFIG_FLOAT_RATE_XP_QUEST));
13506 if (getLevel() < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
13507 GiveXP( XP , NULL );
13508 else
13510 uint32 money = uint32(pQuest->GetRewMoneyMaxLevel() * sWorld.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY));
13511 ModifyMoney( money );
13512 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, money);
13515 // Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
13516 if (pQuest->GetRewOrReqMoney())
13518 ModifyMoney( pQuest->GetRewOrReqMoney() );
13520 if (pQuest->GetRewOrReqMoney() > 0)
13521 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney());
13524 // honor reward
13525 if (pQuest->GetRewHonorAddition())
13526 RewardHonor(NULL, 0, MaNGOS::Honor::hk_honor_at_level(getLevel(), pQuest->GetRewHonorAddition()));
13528 // title reward
13529 if (pQuest->GetCharTitleId())
13531 if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
13532 SetTitle(titleEntry);
13535 if (pQuest->GetBonusTalents())
13537 m_questRewardTalentCount+=pQuest->GetBonusTalents();
13538 InitTalentForLevel();
13541 // Send reward mail
13542 if (uint32 mail_template_id = pQuest->GetRewMailTemplateId())
13543 MailDraft(mail_template_id).SendMailTo(this, questGiver, MAIL_CHECK_MASK_HAS_BODY, pQuest->GetRewMailDelaySecs());
13545 if (pQuest->IsDaily())
13547 SetDailyQuestStatus(quest_id);
13548 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, 1);
13551 if (pQuest->IsWeekly())
13552 SetWeeklyQuestStatus(quest_id);
13554 if (!pQuest->IsRepeatable())
13555 SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE);
13556 else
13557 SetQuestStatus(quest_id, QUEST_STATUS_NONE);
13559 q_status.m_rewarded = true;
13560 if (q_status.uState != QUEST_NEW)
13561 q_status.uState = QUEST_CHANGED;
13563 if (announce)
13564 SendQuestReward( pQuest, XP, questGiver );
13566 // cast spells after mark quest complete (some spells have quest completed state reqyurements in spell_area data)
13567 if (pQuest->GetRewSpellCast() > 0)
13568 CastSpell( this, pQuest->GetRewSpellCast(), true);
13569 else if ( pQuest->GetRewSpell() > 0)
13570 CastSpell( this, pQuest->GetRewSpell(), true);
13572 if (pQuest->GetZoneOrSort() > 0)
13573 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE, pQuest->GetZoneOrSort());
13574 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT);
13575 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, pQuest->GetQuestId());
13577 uint32 zone = 0;
13578 uint32 area = 0;
13580 // remove auras from spells with quest reward state limitations
13581 SpellAreaForQuestMapBounds saEndBounds = sSpellMgr.GetSpellAreaForQuestEndMapBounds(quest_id);
13582 if(saEndBounds.first != saEndBounds.second)
13584 GetZoneAndAreaId(zone,area);
13586 for(SpellAreaForAreaMap::const_iterator itr = saEndBounds.first; itr != saEndBounds.second; ++itr)
13587 if(!itr->second->IsFitToRequirements(this,zone,area))
13588 RemoveAurasDueToSpell(itr->second->spellId);
13591 // Some spells applied at quest reward
13592 SpellAreaForQuestMapBounds saBounds = sSpellMgr.GetSpellAreaForQuestMapBounds(quest_id,false);
13593 if(saBounds.first != saBounds.second)
13595 if(!zone || !area)
13596 GetZoneAndAreaId(zone,area);
13598 for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
13599 if(itr->second->autocast && itr->second->IsFitToRequirements(this,zone,area))
13600 if (!HasAura(itr->second->spellId, EFFECT_INDEX_0))
13601 CastSpell(this,itr->second->spellId,true);
13604 //lets remove flag for delayed teleports
13605 SetCanDelayTeleport(false);
13608 void Player::FailQuest(uint32 questId)
13610 if (Quest const* pQuest = sObjectMgr.GetQuestTemplate(questId))
13612 SetQuestStatus(questId, QUEST_STATUS_FAILED);
13614 uint16 log_slot = FindQuestSlot(questId);
13616 if (log_slot < MAX_QUEST_LOG_SIZE)
13618 SetQuestSlotTimer(log_slot, 1);
13619 SetQuestSlotState(log_slot, QUEST_STATE_FAIL);
13622 if (pQuest->HasFlag(QUEST_MANGOS_FLAGS_TIMED))
13624 QuestStatusData& q_status = mQuestStatus[questId];
13626 RemoveTimedQuest(questId);
13627 q_status.m_timer = 0;
13629 SendQuestTimerFailed(questId);
13631 else
13632 SendQuestFailed(questId);
13636 bool Player::SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg )
13638 int32 zoneOrSort = qInfo->GetZoneOrSort();
13639 int32 skillOrClass = qInfo->GetSkillOrClass();
13641 // skip zone zoneOrSort and 0 case skillOrClass
13642 if( zoneOrSort >= 0 && skillOrClass == 0 )
13643 return true;
13645 int32 questSort = -zoneOrSort;
13646 uint8 reqSortClass = ClassByQuestSort(questSort);
13648 // check class sort cases in zoneOrSort
13649 if( reqSortClass != 0 && getClass() != reqSortClass)
13651 if( msg )
13652 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13653 return false;
13656 // check class
13657 if( skillOrClass < 0 )
13659 uint8 reqClass = -int32(skillOrClass);
13660 if(getClass() != reqClass)
13662 if( msg )
13663 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13664 return false;
13667 // check skill
13668 else if( skillOrClass > 0 )
13670 uint32 reqSkill = skillOrClass;
13671 if( GetSkillValue( reqSkill ) < qInfo->GetRequiredSkillValue() )
13673 if( msg )
13674 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13675 return false;
13679 return true;
13682 bool Player::SatisfyQuestLevel( Quest const* qInfo, bool msg )
13684 if( getLevel() < qInfo->GetMinLevel() )
13686 if( msg )
13687 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13688 return false;
13690 return true;
13693 bool Player::SatisfyQuestLog( bool msg )
13695 // exist free slot
13696 if( FindQuestSlot(0) < MAX_QUEST_LOG_SIZE )
13697 return true;
13699 if( msg )
13701 WorldPacket data( SMSG_QUESTLOG_FULL, 0 );
13702 GetSession()->SendPacket( &data );
13703 DEBUG_LOG( "WORLD: Sent SMSG_QUESTLOG_FULL" );
13705 return false;
13708 bool Player::SatisfyQuestPreviousQuest( Quest const* qInfo, bool msg )
13710 // No previous quest (might be first quest in a series)
13711 if( qInfo->prevQuests.empty())
13712 return true;
13714 for(Quest::PrevQuests::const_iterator iter = qInfo->prevQuests.begin(); iter != qInfo->prevQuests.end(); ++iter )
13716 uint32 prevId = abs(*iter);
13718 QuestStatusMap::const_iterator i_prevstatus = mQuestStatus.find( prevId );
13719 Quest const* qPrevInfo = sObjectMgr.GetQuestTemplate(prevId);
13721 if( qPrevInfo && i_prevstatus != mQuestStatus.end() )
13723 // If any of the positive previous quests completed, return true
13724 if( *iter > 0 && i_prevstatus->second.m_rewarded )
13726 // skip one-from-all exclusive group
13727 if(qPrevInfo->GetExclusiveGroup() >= 0)
13728 return true;
13730 // each-from-all exclusive group ( < 0)
13731 // can be start if only all quests in prev quest exclusive group completed and rewarded
13732 ObjectMgr::ExclusiveQuestGroups::const_iterator iter2 = sObjectMgr.mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
13733 ObjectMgr::ExclusiveQuestGroups::const_iterator end = sObjectMgr.mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
13735 ASSERT(iter2!=end); // always must be found if qPrevInfo->ExclusiveGroup != 0
13737 for(; iter2 != end; ++iter2)
13739 uint32 exclude_Id = iter2->second;
13741 // skip checked quest id, only state of other quests in group is interesting
13742 if(exclude_Id == prevId)
13743 continue;
13745 QuestStatusMap::const_iterator i_exstatus = mQuestStatus.find( exclude_Id );
13747 // alternative quest from group also must be completed and rewarded(reported)
13748 if( i_exstatus == mQuestStatus.end() || !i_exstatus->second.m_rewarded )
13750 if( msg )
13751 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13752 return false;
13755 return true;
13757 // If any of the negative previous quests active, return true
13758 if( *iter < 0 && (i_prevstatus->second.m_status == QUEST_STATUS_INCOMPLETE
13759 || (i_prevstatus->second.m_status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(prevId))))
13761 // skip one-from-all exclusive group
13762 if(qPrevInfo->GetExclusiveGroup() >= 0)
13763 return true;
13765 // each-from-all exclusive group ( < 0)
13766 // can be start if only all quests in prev quest exclusive group active
13767 ObjectMgr::ExclusiveQuestGroups::const_iterator iter2 = sObjectMgr.mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
13768 ObjectMgr::ExclusiveQuestGroups::const_iterator end = sObjectMgr.mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
13770 ASSERT(iter2!=end); // always must be found if qPrevInfo->ExclusiveGroup != 0
13772 for(; iter2 != end; ++iter2)
13774 uint32 exclude_Id = iter2->second;
13776 // skip checked quest id, only state of other quests in group is interesting
13777 if(exclude_Id == prevId)
13778 continue;
13780 QuestStatusMap::const_iterator i_exstatus = mQuestStatus.find( exclude_Id );
13782 // alternative quest from group also must be active
13783 if( i_exstatus == mQuestStatus.end() ||
13784 i_exstatus->second.m_status != QUEST_STATUS_INCOMPLETE &&
13785 (i_prevstatus->second.m_status != QUEST_STATUS_COMPLETE || GetQuestRewardStatus(prevId)) )
13787 if( msg )
13788 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13789 return false;
13792 return true;
13797 // Has only positive prev. quests in non-rewarded state
13798 // and negative prev. quests in non-active state
13799 if( msg )
13800 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13802 return false;
13805 bool Player::SatisfyQuestRace( Quest const* qInfo, bool msg )
13807 uint32 reqraces = qInfo->GetRequiredRaces();
13808 if ( reqraces == 0 )
13809 return true;
13810 if( (reqraces & getRaceMask()) == 0 )
13812 if( msg )
13813 SendCanTakeQuestResponse( INVALIDREASON_QUEST_FAILED_WRONG_RACE );
13814 return false;
13816 return true;
13819 bool Player::SatisfyQuestReputation( Quest const* qInfo, bool msg )
13821 uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); //Min required rep
13822 if(fIdMin && GetReputationMgr().GetReputation(fIdMin) < qInfo->GetRequiredMinRepValue())
13824 if( msg )
13825 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13826 return false;
13829 uint32 fIdMax = qInfo->GetRequiredMaxRepFaction(); //Max required rep
13830 if(fIdMax && GetReputationMgr().GetReputation(fIdMax) >= qInfo->GetRequiredMaxRepValue())
13832 if( msg )
13833 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13834 return false;
13837 return true;
13840 bool Player::SatisfyQuestStatus( Quest const* qInfo, bool msg )
13842 QuestStatusMap::const_iterator itr = mQuestStatus.find( qInfo->GetQuestId() );
13843 if ( itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE )
13845 if( msg )
13846 SendCanTakeQuestResponse( INVALIDREASON_QUEST_ALREADY_ON );
13847 return false;
13849 return true;
13852 bool Player::SatisfyQuestTimed(Quest const* qInfo, bool msg)
13854 if (!m_timedquests.empty() && qInfo->HasFlag(QUEST_MANGOS_FLAGS_TIMED))
13856 if (msg)
13857 SendCanTakeQuestResponse(INVALIDREASON_QUEST_ONLY_ONE_TIMED);
13859 return false;
13861 return true;
13864 bool Player::SatisfyQuestExclusiveGroup( Quest const* qInfo, bool msg )
13866 // non positive exclusive group, if > 0 then can be start if any other quest in exclusive group already started/completed
13867 if (qInfo->GetExclusiveGroup() <= 0)
13868 return true;
13870 ObjectMgr::ExclusiveQuestGroups::const_iterator iter = sObjectMgr.mExclusiveQuestGroups.lower_bound(qInfo->GetExclusiveGroup());
13871 ObjectMgr::ExclusiveQuestGroups::const_iterator end = sObjectMgr.mExclusiveQuestGroups.upper_bound(qInfo->GetExclusiveGroup());
13873 ASSERT(iter!=end); // always must be found if qInfo->ExclusiveGroup != 0
13875 for(; iter != end; ++iter)
13877 uint32 exclude_Id = iter->second;
13879 // skip checked quest id, only state of other quests in group is interesting
13880 if (exclude_Id == qInfo->GetQuestId())
13881 continue;
13883 // not allow have daily quest if daily quest from exclusive group already recently completed
13884 Quest const* Nquest = sObjectMgr.GetQuestTemplate(exclude_Id);
13885 if (!SatisfyQuestDay(Nquest, false) || !SatisfyQuestWeek(Nquest, false))
13887 if( msg )
13888 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13889 return false;
13892 QuestStatusMap::iterator i_exstatus = mQuestStatus.find( exclude_Id );
13894 // alternative quest already started or completed
13895 if (i_exstatus != mQuestStatus.end()
13896 && (i_exstatus->second.m_status == QUEST_STATUS_COMPLETE || i_exstatus->second.m_status == QUEST_STATUS_INCOMPLETE))
13898 if( msg )
13899 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13900 return false;
13903 return true;
13906 bool Player::SatisfyQuestNextChain( Quest const* qInfo, bool msg )
13908 if(!qInfo->GetNextQuestInChain())
13909 return true;
13911 // next quest in chain already started or completed
13912 QuestStatusMap::const_iterator itr = mQuestStatus.find( qInfo->GetNextQuestInChain() );
13913 if( itr != mQuestStatus.end()
13914 && (itr->second.m_status == QUEST_STATUS_COMPLETE || itr->second.m_status == QUEST_STATUS_INCOMPLETE) )
13916 if( msg )
13917 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13918 return false;
13921 // check for all quests further up the chain
13922 // only necessary if there are quest chains with more than one quest that can be skipped
13923 //return SatisfyQuestNextChain( qInfo->GetNextQuestInChain(), msg );
13924 return true;
13927 bool Player::SatisfyQuestPrevChain( Quest const* qInfo, bool msg )
13929 // No previous quest in chain
13930 if( qInfo->prevChainQuests.empty())
13931 return true;
13933 for(Quest::PrevChainQuests::const_iterator iter = qInfo->prevChainQuests.begin(); iter != qInfo->prevChainQuests.end(); ++iter )
13935 uint32 prevId = *iter;
13937 QuestStatusMap::const_iterator i_prevstatus = mQuestStatus.find( prevId );
13939 if( i_prevstatus != mQuestStatus.end() )
13941 // If any of the previous quests in chain active, return false
13942 if( i_prevstatus->second.m_status == QUEST_STATUS_INCOMPLETE
13943 || (i_prevstatus->second.m_status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(prevId)))
13945 if( msg )
13946 SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
13947 return false;
13951 // check for all quests further down the chain
13952 // only necessary if there are quest chains with more than one quest that can be skipped
13953 //if( !SatisfyQuestPrevChain( prevId, msg ) )
13954 // return false;
13957 // No previous quest in chain active
13958 return true;
13961 bool Player::SatisfyQuestDay( Quest const* qInfo, bool msg )
13963 if (!qInfo->IsDaily())
13964 return true;
13966 bool have_slot = false;
13967 for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
13969 uint32 id = GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx);
13970 if (qInfo->GetQuestId()==id)
13971 return false;
13973 if(!id)
13974 have_slot = true;
13977 if (!have_slot)
13979 if( msg )
13980 SendCanTakeQuestResponse( INVALIDREASON_DAILY_QUESTS_REMAINING );
13981 return false;
13984 return true;
13987 bool Player::SatisfyQuestWeek( Quest const* qInfo, bool msg )
13989 if (!qInfo->IsWeekly() || m_weeklyquests.empty())
13990 return true;
13992 // if not found in cooldown list
13993 return m_weeklyquests.find(qInfo->GetQuestId()) == m_weeklyquests.end();
13996 bool Player::GiveQuestSourceItem( Quest const *pQuest )
13998 uint32 srcitem = pQuest->GetSrcItemId();
13999 if (srcitem > 0)
14001 uint32 count = pQuest->GetSrcItemCount();
14002 if( count <= 0 )
14003 count = 1;
14005 ItemPosCountVec dest;
14006 uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, srcitem, count );
14007 if (msg == EQUIP_ERR_OK)
14009 Item * item = StoreNewItem(dest, srcitem, true);
14010 SendNewItem(item, count, true, false);
14011 return true;
14013 // player already have max amount required item, just report success
14014 else if (msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
14015 return true;
14016 else
14017 SendEquipError( msg, NULL, NULL, srcitem );
14018 return false;
14021 return true;
14024 bool Player::TakeQuestSourceItem( uint32 quest_id, bool msg )
14026 Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id);
14027 if( qInfo )
14029 uint32 srcitem = qInfo->GetSrcItemId();
14030 if( srcitem > 0 )
14032 uint32 count = qInfo->GetSrcItemCount();
14033 if( count <= 0 )
14034 count = 1;
14036 // exist one case when destroy source quest item not possible:
14037 // non un-equippable item (equipped non-empty bag, for example)
14038 uint8 res = CanUnequipItems(srcitem,count);
14039 if(res != EQUIP_ERR_OK)
14041 if(msg)
14042 SendEquipError( res, NULL, NULL, srcitem );
14043 return false;
14046 DestroyItemCount(srcitem, count, true, true);
14049 return true;
14052 bool Player::GetQuestRewardStatus( uint32 quest_id ) const
14054 Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id);
14055 if( qInfo )
14057 // for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
14058 QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
14059 if( itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE
14060 && !qInfo->IsRepeatable() )
14061 return itr->second.m_rewarded;
14063 return false;
14065 return false;
14068 QuestStatus Player::GetQuestStatus( uint32 quest_id ) const
14070 if( quest_id )
14072 QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
14073 if( itr != mQuestStatus.end() )
14074 return itr->second.m_status;
14076 return QUEST_STATUS_NONE;
14079 bool Player::CanShareQuest(uint32 quest_id) const
14081 Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id);
14082 if( qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE) )
14084 QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
14085 if( itr != mQuestStatus.end() )
14086 return itr->second.m_status == QUEST_STATUS_NONE || itr->second.m_status == QUEST_STATUS_INCOMPLETE;
14088 return false;
14091 void Player::SetQuestStatus(uint32 quest_id, QuestStatus status)
14093 if (sObjectMgr.GetQuestTemplate(quest_id))
14095 QuestStatusData& q_status = mQuestStatus[quest_id];
14097 q_status.m_status = status;
14099 if (q_status.uState != QUEST_NEW)
14100 q_status.uState = QUEST_CHANGED;
14103 UpdateForQuestWorldObjects();
14106 // not used in MaNGOS, but used in scripting code
14107 uint32 Player::GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry)
14109 Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id);
14110 if( !qInfo )
14111 return 0;
14113 for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
14114 if ( qInfo->ReqCreatureOrGOId[j] == entry )
14115 return mQuestStatus[quest_id].m_creatureOrGOcount[j];
14117 return 0;
14120 void Player::AdjustQuestReqItemCount( Quest const* pQuest, QuestStatusData& questStatusData )
14122 if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
14124 for(int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
14126 uint32 reqitemcount = pQuest->ReqItemCount[i];
14127 if( reqitemcount != 0 )
14129 uint32 curitemcount = GetItemCount(pQuest->ReqItemId[i], true);
14131 questStatusData.m_itemcount[i] = std::min(curitemcount, reqitemcount);
14132 if (questStatusData.uState != QUEST_NEW) questStatusData.uState = QUEST_CHANGED;
14138 uint16 Player::FindQuestSlot( uint32 quest_id ) const
14140 for ( uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
14141 if ( GetQuestSlotQuestId(i) == quest_id )
14142 return i;
14144 return MAX_QUEST_LOG_SIZE;
14147 void Player::AreaExploredOrEventHappens( uint32 questId )
14149 if( questId )
14151 uint16 log_slot = FindQuestSlot( questId );
14152 if( log_slot < MAX_QUEST_LOG_SIZE)
14154 QuestStatusData& q_status = mQuestStatus[questId];
14156 if(!q_status.m_explored)
14158 q_status.m_explored = true;
14159 if (q_status.uState != QUEST_NEW)
14160 q_status.uState = QUEST_CHANGED;
14163 if( CanCompleteQuest( questId ) )
14164 CompleteQuest( questId );
14168 //not used in mangosd, function for external script library
14169 void Player::GroupEventHappens( uint32 questId, WorldObject const* pEventObject )
14171 if( Group *pGroup = GetGroup() )
14173 for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
14175 Player *pGroupGuy = itr->getSource();
14177 // for any leave or dead (with not released body) group member at appropriate distance
14178 if( pGroupGuy && pGroupGuy->IsAtGroupRewardDistance(pEventObject) && !pGroupGuy->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) )
14179 pGroupGuy->AreaExploredOrEventHappens(questId);
14182 else
14183 AreaExploredOrEventHappens(questId);
14186 void Player::ItemAddedQuestCheck( uint32 entry, uint32 count )
14188 for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
14190 uint32 questid = GetQuestSlotQuestId(i);
14191 if ( questid == 0 )
14192 continue;
14194 QuestStatusData& q_status = mQuestStatus[questid];
14196 if ( q_status.m_status != QUEST_STATUS_INCOMPLETE )
14197 continue;
14199 Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid);
14200 if( !qInfo || !qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
14201 continue;
14203 for (int j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
14205 uint32 reqitem = qInfo->ReqItemId[j];
14206 if ( reqitem == entry )
14208 uint32 reqitemcount = qInfo->ReqItemCount[j];
14209 uint32 curitemcount = q_status.m_itemcount[j];
14210 if ( curitemcount < reqitemcount )
14212 uint32 additemcount = ( curitemcount + count <= reqitemcount ? count : reqitemcount - curitemcount);
14213 q_status.m_itemcount[j] += additemcount;
14214 if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
14216 SendQuestUpdateAddItem( qInfo, j, additemcount );
14218 if ( CanCompleteQuest( questid ) )
14219 CompleteQuest( questid );
14220 return;
14224 UpdateForQuestWorldObjects();
14227 void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count )
14229 for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
14231 uint32 questid = GetQuestSlotQuestId(i);
14232 if(!questid)
14233 continue;
14234 Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid);
14235 if ( !qInfo )
14236 continue;
14237 if( !qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
14238 continue;
14240 for (int j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
14242 uint32 reqitem = qInfo->ReqItemId[j];
14243 if ( reqitem == entry )
14245 QuestStatusData& q_status = mQuestStatus[questid];
14247 uint32 reqitemcount = qInfo->ReqItemCount[j];
14248 uint32 curitemcount;
14249 if( q_status.m_status != QUEST_STATUS_COMPLETE )
14250 curitemcount = q_status.m_itemcount[j];
14251 else
14252 curitemcount = GetItemCount(entry, true);
14253 if ( curitemcount < reqitemcount + count )
14255 uint32 remitemcount = ( curitemcount <= reqitemcount ? count : count + reqitemcount - curitemcount);
14256 q_status.m_itemcount[j] = curitemcount - remitemcount;
14257 if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
14259 IncompleteQuest( questid );
14261 return;
14265 UpdateForQuestWorldObjects();
14268 void Player::KilledMonster( CreatureInfo const* cInfo, ObjectGuid guid )
14270 if(cInfo->Entry)
14271 KilledMonsterCredit(cInfo->Entry, guid);
14273 for(int i = 0; i < MAX_KILL_CREDIT; ++i)
14274 if(cInfo->KillCredit[i])
14275 KilledMonsterCredit(cInfo->KillCredit[i], guid);
14278 void Player::KilledMonsterCredit( uint32 entry, ObjectGuid guid )
14280 uint32 addkillcount = 1;
14281 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, entry, addkillcount);
14282 for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
14284 uint32 questid = GetQuestSlotQuestId(i);
14285 if(!questid)
14286 continue;
14288 Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid);
14289 if (!qInfo)
14290 continue;
14291 // just if !ingroup || !noraidgroup || raidgroup
14292 QuestStatusData& q_status = mQuestStatus[questid];
14293 if (q_status.m_status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->IsAllowedInRaid()))
14295 if (qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST))
14297 for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
14299 // skip GO activate objective or none
14300 if (qInfo->ReqCreatureOrGOId[j] <=0)
14301 continue;
14303 // skip Cast at creature objective
14304 if (qInfo->ReqSpell[j] !=0 )
14305 continue;
14307 uint32 reqkill = qInfo->ReqCreatureOrGOId[j];
14309 if (reqkill == entry)
14311 uint32 reqkillcount = qInfo->ReqCreatureOrGOCount[j];
14312 uint32 curkillcount = q_status.m_creatureOrGOcount[j];
14313 if (curkillcount < reqkillcount)
14315 q_status.m_creatureOrGOcount[j] = curkillcount + addkillcount;
14316 if (q_status.uState != QUEST_NEW)
14317 q_status.uState = QUEST_CHANGED;
14319 SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curkillcount, addkillcount);
14322 if (CanCompleteQuest( questid ))
14323 CompleteQuest( questid );
14325 // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
14326 continue;
14334 void Player::CastedCreatureOrGO( uint32 entry, ObjectGuid guid, uint32 spell_id )
14336 bool isCreature = guid.IsCreature();
14338 uint32 addCastCount = 1;
14339 for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
14341 uint32 questid = GetQuestSlotQuestId(i);
14342 if(!questid)
14343 continue;
14345 Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid);
14346 if ( !qInfo )
14347 continue;
14349 QuestStatusData& q_status = mQuestStatus[questid];
14351 if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
14353 if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST ) )
14355 for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
14357 // skip kill creature objective (0) or wrong spell casts
14358 if(qInfo->ReqSpell[j] != spell_id )
14359 continue;
14361 uint32 reqTarget = 0;
14363 if(isCreature)
14365 // creature activate objectives
14366 if(qInfo->ReqCreatureOrGOId[j] > 0)
14367 // checked at quest_template loading
14368 reqTarget = qInfo->ReqCreatureOrGOId[j];
14370 else
14372 // GO activate objective
14373 if(qInfo->ReqCreatureOrGOId[j] < 0)
14374 // checked at quest_template loading
14375 reqTarget = - qInfo->ReqCreatureOrGOId[j];
14378 // other not this creature/GO related objectives
14379 if( reqTarget != entry )
14380 continue;
14382 uint32 reqCastCount = qInfo->ReqCreatureOrGOCount[j];
14383 uint32 curCastCount = q_status.m_creatureOrGOcount[j];
14384 if ( curCastCount < reqCastCount )
14386 q_status.m_creatureOrGOcount[j] = curCastCount + addCastCount;
14387 if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
14389 SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curCastCount, addCastCount);
14392 if ( CanCompleteQuest( questid ) )
14393 CompleteQuest( questid );
14395 // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
14396 break;
14403 void Player::TalkedToCreature( uint32 entry, ObjectGuid guid )
14405 uint32 addTalkCount = 1;
14406 for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
14408 uint32 questid = GetQuestSlotQuestId(i);
14409 if(!questid)
14410 continue;
14412 Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid);
14413 if ( !qInfo )
14414 continue;
14416 QuestStatusData& q_status = mQuestStatus[questid];
14418 if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
14420 if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO ) )
14422 for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
14424 // skip spell casts and Gameobject objectives
14425 if(qInfo->ReqSpell[j] > 0 || qInfo->ReqCreatureOrGOId[j] < 0)
14426 continue;
14428 uint32 reqTarget = 0;
14430 if(qInfo->ReqCreatureOrGOId[j] > 0) // creature activate objectives
14431 // checked at quest_template loading
14432 reqTarget = qInfo->ReqCreatureOrGOId[j];
14433 else
14434 continue;
14436 if ( reqTarget == entry )
14438 uint32 reqTalkCount = qInfo->ReqCreatureOrGOCount[j];
14439 uint32 curTalkCount = q_status.m_creatureOrGOcount[j];
14440 if ( curTalkCount < reqTalkCount )
14442 q_status.m_creatureOrGOcount[j] = curTalkCount + addTalkCount;
14443 if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
14445 SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curTalkCount, addTalkCount);
14447 if ( CanCompleteQuest( questid ) )
14448 CompleteQuest( questid );
14450 // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
14451 continue;
14459 void Player::MoneyChanged( uint32 count )
14461 for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
14463 uint32 questid = GetQuestSlotQuestId(i);
14464 if (!questid)
14465 continue;
14467 Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid);
14468 if( qInfo && qInfo->GetRewOrReqMoney() < 0 )
14470 QuestStatusData& q_status = mQuestStatus[questid];
14472 if( q_status.m_status == QUEST_STATUS_INCOMPLETE )
14474 if(int32(count) >= -qInfo->GetRewOrReqMoney())
14476 if ( CanCompleteQuest( questid ) )
14477 CompleteQuest( questid );
14480 else if( q_status.m_status == QUEST_STATUS_COMPLETE )
14482 if(int32(count) < -qInfo->GetRewOrReqMoney())
14483 IncompleteQuest( questid );
14489 void Player::ReputationChanged(FactionEntry const* factionEntry )
14491 for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
14493 if(uint32 questid = GetQuestSlotQuestId(i))
14495 if(Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid))
14497 if(qInfo->GetRepObjectiveFaction() == factionEntry->ID )
14499 QuestStatusData& q_status = mQuestStatus[questid];
14500 if( q_status.m_status == QUEST_STATUS_INCOMPLETE )
14502 if(GetReputationMgr().GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue())
14503 if ( CanCompleteQuest( questid ) )
14504 CompleteQuest( questid );
14506 else if( q_status.m_status == QUEST_STATUS_COMPLETE )
14508 if(GetReputationMgr().GetReputation(factionEntry) < qInfo->GetRepObjectiveValue())
14509 IncompleteQuest( questid );
14517 bool Player::HasQuestForItem( uint32 itemid ) const
14519 for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
14521 uint32 questid = GetQuestSlotQuestId(i);
14522 if ( questid == 0 )
14523 continue;
14525 QuestStatusMap::const_iterator qs_itr = mQuestStatus.find(questid);
14526 if(qs_itr == mQuestStatus.end())
14527 continue;
14529 QuestStatusData const& q_status = qs_itr->second;
14531 if (q_status.m_status == QUEST_STATUS_INCOMPLETE)
14533 Quest const* qinfo = sObjectMgr.GetQuestTemplate(questid);
14534 if(!qinfo)
14535 continue;
14537 // hide quest if player is in raid-group and quest is no raid quest
14538 if (GetGroup() && GetGroup()->isRaidGroup() && qinfo->IsAllowedInRaid() && !InBattleGround())
14539 continue;
14541 // There should be no mixed ReqItem/ReqSource drop
14542 // This part for ReqItem drop
14543 for (int j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
14545 if(itemid == qinfo->ReqItemId[j] && q_status.m_itemcount[j] < qinfo->ReqItemCount[j] )
14546 return true;
14548 // This part - for ReqSource
14549 for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
14551 // examined item is a source item
14552 if (qinfo->ReqSourceId[j] == itemid)
14554 ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(itemid);
14556 // 'unique' item
14557 if (pProto->MaxCount && (int32)GetItemCount(itemid,true) < pProto->MaxCount)
14558 return true;
14560 // allows custom amount drop when not 0
14561 if (qinfo->ReqSourceCount[j])
14563 if (GetItemCount(itemid,true) < qinfo->ReqSourceCount[j])
14564 return true;
14565 } else if ((int32)GetItemCount(itemid,true) < pProto->Stackable)
14566 return true;
14571 return false;
14574 void Player::SendQuestComplete( uint32 quest_id )
14576 if( quest_id )
14578 WorldPacket data( SMSG_QUESTUPDATE_COMPLETE, 4 );
14579 data << uint32(quest_id);
14580 GetSession()->SendPacket( &data );
14581 DEBUG_LOG( "WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = %u", quest_id );
14585 void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGiver )
14587 uint32 questid = pQuest->GetQuestId();
14588 DEBUG_LOG( "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questid );
14589 WorldPacket data( SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4) );
14590 data << uint32(questid);
14592 if ( getLevel() < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL) )
14594 data << uint32(XP);
14595 data << uint32(pQuest->GetRewOrReqMoney());
14597 else
14599 data << uint32(0);
14600 data << uint32(pQuest->GetRewOrReqMoney() + int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY)));
14603 data << uint32(10*MaNGOS::Honor::hk_honor_at_level(getLevel(), pQuest->GetRewHonorAddition()));
14604 data << uint32(pQuest->GetBonusTalents()); // bonus talents
14605 data << uint32(0); // arena points
14606 GetSession()->SendPacket( &data );
14608 if (pQuest->GetQuestCompleteScript() != 0)
14609 GetMap()->ScriptsStart(sQuestEndScripts, pQuest->GetQuestCompleteScript(), questGiver, this);
14612 void Player::SendQuestFailed( uint32 quest_id )
14614 if( quest_id )
14616 WorldPacket data( SMSG_QUESTGIVER_QUEST_FAILED, 4+4 );
14617 data << uint32(quest_id);
14618 data << uint32(0); // failed reason (4 for inventory is full)
14619 GetSession()->SendPacket( &data );
14620 DEBUG_LOG("WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED");
14624 void Player::SendQuestTimerFailed( uint32 quest_id )
14626 if( quest_id )
14628 WorldPacket data( SMSG_QUESTUPDATE_FAILEDTIMER, 4 );
14629 data << uint32(quest_id);
14630 GetSession()->SendPacket( &data );
14631 DEBUG_LOG("WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
14635 void Player::SendCanTakeQuestResponse( uint32 msg )
14637 WorldPacket data( SMSG_QUESTGIVER_QUEST_INVALID, 4 );
14638 data << uint32(msg);
14639 GetSession()->SendPacket( &data );
14640 DEBUG_LOG("WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID");
14643 void Player::SendQuestConfirmAccept(const Quest* pQuest, Player* pReceiver)
14645 if (pReceiver)
14647 std::string strTitle = pQuest->GetTitle();
14649 int loc_idx = pReceiver->GetSession()->GetSessionDbLocaleIndex();
14651 if (loc_idx >= 0)
14653 if (const QuestLocale* pLocale = sObjectMgr.GetQuestLocale(pQuest->GetQuestId()))
14655 if ((int32)pLocale->Title.size() > loc_idx && !pLocale->Title[loc_idx].empty())
14656 strTitle = pLocale->Title[loc_idx];
14660 WorldPacket data(SMSG_QUEST_CONFIRM_ACCEPT, (4 + strTitle.size() + 8));
14661 data << uint32(pQuest->GetQuestId());
14662 data << strTitle;
14663 data << uint64(GetGUID());
14664 pReceiver->GetSession()->SendPacket(&data);
14666 DEBUG_LOG("WORLD: Sent SMSG_QUEST_CONFIRM_ACCEPT");
14670 void Player::SendPushToPartyResponse( Player *pPlayer, uint32 msg )
14672 if( pPlayer )
14674 WorldPacket data( MSG_QUEST_PUSH_RESULT, (8+1) );
14675 data << uint64(pPlayer->GetGUID());
14676 data << uint8(msg); // valid values: 0-8
14677 GetSession()->SendPacket( &data );
14678 DEBUG_LOG("WORLD: Sent MSG_QUEST_PUSH_RESULT");
14682 void Player::SendQuestUpdateAddItem( Quest const* /*pQuest*/, uint32 /*item_idx*/, uint32 /*count*/ )
14684 WorldPacket data( SMSG_QUESTUPDATE_ADD_ITEM, 0 );
14685 DEBUG_LOG( "WORLD: Sent SMSG_QUESTUPDATE_ADD_ITEM" );
14686 //data << pQuest->ReqItemId[item_idx];
14687 //data << count;
14688 GetSession()->SendPacket( &data );
14691 void Player::SendQuestUpdateAddCreatureOrGo( Quest const* pQuest, ObjectGuid guid, uint32 creatureOrGO_idx, uint32 old_count, uint32 add_count )
14693 ASSERT(old_count + add_count < 65536 && "mob/GO count store in 16 bits 2^16 = 65536 (0..65536)");
14695 int32 entry = pQuest->ReqCreatureOrGOId[ creatureOrGO_idx ];
14696 if (entry < 0)
14697 // client expected gameobject template id in form (id|0x80000000)
14698 entry = (-entry) | 0x80000000;
14700 WorldPacket data( SMSG_QUESTUPDATE_ADD_KILL, (4*4+8) );
14701 DEBUG_LOG( "WORLD: Sent SMSG_QUESTUPDATE_ADD_KILL" );
14702 data << uint32(pQuest->GetQuestId());
14703 data << uint32(entry);
14704 data << uint32(old_count + add_count);
14705 data << uint32(pQuest->ReqCreatureOrGOCount[ creatureOrGO_idx ]);
14706 data << guid;
14707 GetSession()->SendPacket(&data);
14709 uint16 log_slot = FindQuestSlot( pQuest->GetQuestId() );
14710 if( log_slot < MAX_QUEST_LOG_SIZE)
14711 SetQuestSlotCounter(log_slot,creatureOrGO_idx,GetQuestSlotCounter(log_slot,creatureOrGO_idx)+add_count);
14714 /*********************************************************/
14715 /*** LOAD SYSTEM ***/
14716 /*********************************************************/
14718 void Player::_LoadDeclinedNames(QueryResult* result)
14720 if(!result)
14721 return;
14723 if(m_declinedname)
14724 delete m_declinedname;
14726 m_declinedname = new DeclinedName;
14727 Field *fields = result->Fetch();
14728 for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
14729 m_declinedname->name[i] = fields[i].GetCppString();
14731 delete result;
14734 void Player::_LoadArenaTeamInfo(QueryResult *result)
14736 // arenateamid, played_week, played_season, personal_rating
14737 memset((void*)&m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1], 0, sizeof(uint32) * MAX_ARENA_SLOT * ARENA_TEAM_END);
14738 if (!result)
14739 return;
14743 Field *fields = result->Fetch();
14745 uint32 arenateamid = fields[0].GetUInt32();
14746 uint32 played_week = fields[1].GetUInt32();
14747 uint32 played_season = fields[2].GetUInt32();
14748 uint32 wons_season = fields[3].GetUInt32();
14749 uint32 personal_rating = fields[4].GetUInt32();
14751 ArenaTeam* aTeam = sObjectMgr.GetArenaTeamById(arenateamid);
14752 if(!aTeam)
14754 sLog.outError("Player::_LoadArenaTeamInfo: couldn't load arenateam %u", arenateamid);
14755 continue;
14757 uint8 arenaSlot = aTeam->GetSlot();
14759 SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_ID, arenateamid);
14760 SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_TYPE, aTeam->GetType());
14761 SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_MEMBER, (aTeam->GetCaptain() == GetGUID()) ? 0 : 1);
14762 SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_WEEK, played_week);
14763 SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_SEASON, played_season);
14764 SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_WINS_SEASON, wons_season);
14765 SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_PERSONAL_RATING, personal_rating);
14767 } while (result->NextRow());
14768 delete result;
14771 void Player::_LoadEquipmentSets(QueryResult *result)
14773 // SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, 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));
14774 if (!result)
14775 return;
14777 uint32 count = 0;
14780 Field *fields = result->Fetch();
14782 EquipmentSet eqSet;
14784 eqSet.Guid = fields[0].GetUInt64();
14785 uint32 index = fields[1].GetUInt32();
14786 eqSet.Name = fields[2].GetCppString();
14787 eqSet.IconName = fields[3].GetCppString();
14788 eqSet.state = EQUIPMENT_SET_UNCHANGED;
14790 for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
14791 eqSet.Items[i] = fields[4+i].GetUInt32();
14793 m_EquipmentSets[index] = eqSet;
14795 ++count;
14797 if(count >= MAX_EQUIPMENT_SET_INDEX) // client limit
14798 break;
14799 } while (result->NextRow());
14800 delete result;
14803 void Player::_LoadBGData(QueryResult* result)
14805 if (!result)
14806 return;
14808 // Expecting only one row
14809 Field *fields = result->Fetch();
14810 /* bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */
14811 m_bgData.bgInstanceID = fields[0].GetUInt32();
14812 m_bgData.bgTeam = fields[1].GetUInt32();
14813 m_bgData.joinPos = WorldLocation(fields[6].GetUInt32(), // Map
14814 fields[2].GetFloat(), // X
14815 fields[3].GetFloat(), // Y
14816 fields[4].GetFloat(), // Z
14817 fields[5].GetFloat()); // Orientation
14818 m_bgData.taxiPath[0] = fields[7].GetUInt32();
14819 m_bgData.taxiPath[1] = fields[8].GetUInt32();
14820 m_bgData.mountSpell = fields[9].GetUInt32();
14822 delete result;
14825 bool Player::LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid)
14827 QueryResult *result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,taxi_path FROM characters WHERE guid = '%u'",GUID_LOPART(guid));
14828 if(!result)
14829 return false;
14831 Field *fields = result->Fetch();
14833 x = fields[0].GetFloat();
14834 y = fields[1].GetFloat();
14835 z = fields[2].GetFloat();
14836 o = fields[3].GetFloat();
14837 mapid = fields[4].GetUInt32();
14838 in_flight = !fields[5].GetCppString().empty();
14840 delete result;
14841 return true;
14844 void Player::_LoadIntoDataField(const char* data, uint32 startOffset, uint32 count)
14846 if(!data)
14847 return;
14849 Tokens tokens = StrSplit(data, " ");
14851 if(tokens.size() != count)
14852 return;
14854 Tokens::iterator iter;
14855 uint32 index;
14856 for (iter = tokens.begin(), index = 0; index < count; ++iter, ++index)
14858 m_uint32Values[startOffset + index] = atol((*iter).c_str());
14862 bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
14864 // 0 1 2 3 4 5 6 7 8 9 10 11
14865 //SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags,"
14866 // 12 13 14 15 16 17 18 19 20 21 22 23 24
14867 //"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost,"
14868 // 25 26 27 28 29 30 31 32 33 34 35 36 37 38
14869 //"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty,"
14870 // 39 40 41 42 43 44 45 46 47 48 49
14871 //"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk,"
14872 // 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
14873 //"health, power1, power2, power3, power4, power5, power6, power7, specCount, activeSpec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
14874 QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM);
14876 if(!result)
14878 sLog.outError("Player (GUID: %u) not found in table `characters`, can't load. ",guid);
14879 return false;
14882 Field *fields = result->Fetch();
14884 uint32 dbAccountId = fields[1].GetUInt32();
14886 // check if the character's account in the db and the logged in account match.
14887 // player should be able to load/delete character only with correct account!
14888 if( dbAccountId != GetSession()->GetAccountId() )
14890 sLog.outError("Player (GUID: %u) loading from wrong account (is: %u, should be: %u)",guid,GetSession()->GetAccountId(),dbAccountId);
14891 delete result;
14892 return false;
14895 Object::_Create( guid, 0, HIGHGUID_PLAYER );
14897 m_name = fields[2].GetCppString();
14899 // check name limitations
14900 if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS ||
14901 (GetSession()->GetSecurity() == SEC_PLAYER && sObjectMgr.IsReservedName(m_name)))
14903 delete result;
14904 CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid ='%u'", uint32(AT_LOGIN_RENAME),guid);
14905 return false;
14908 // overwrite possible wrong/corrupted guid
14909 SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
14911 // overwrite some data fields
14912 uint32 bytes0 = 0;
14913 bytes0 |= fields[3].GetUInt8(); // race
14914 bytes0 |= fields[4].GetUInt8() << 8; // class
14915 bytes0 |= fields[5].GetUInt8() << 16; // gender
14916 SetUInt32Value(UNIT_FIELD_BYTES_0, bytes0);
14918 SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8());
14919 SetUInt32Value(PLAYER_XP, fields[7].GetUInt32());
14921 _LoadIntoDataField(fields[60].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE);
14922 _LoadIntoDataField(fields[63].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2);
14924 SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE);
14925 SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f);
14926 SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f);
14928 uint32 money = fields[8].GetUInt32();
14929 if(money > MAX_MONEY_AMOUNT)
14930 money = MAX_MONEY_AMOUNT;
14931 SetMoney(money);
14933 SetUInt32Value(PLAYER_BYTES, fields[9].GetUInt32());
14934 SetUInt32Value(PLAYER_BYTES_2, fields[10].GetUInt32());
14935 SetUInt32Value(PLAYER_BYTES_3, (fields[49].GetUInt16() & 0xFFFE) | fields[5].GetUInt8());
14936 SetUInt32Value(PLAYER_FLAGS, fields[11].GetUInt32());
14937 SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[48].GetInt32());
14939 SetUInt64Value(PLAYER_FIELD_KNOWN_CURRENCIES, fields[47].GetUInt64());
14941 SetUInt32Value(PLAYER_AMMO_ID, fields[62].GetUInt32());
14942 SetByteValue(PLAYER_FIELD_BYTES, 2, fields[64].GetUInt8());
14944 InitDisplayIds();
14946 // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
14947 for(uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
14949 SetUInt64Value( PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), 0 );
14950 SetVisibleItemSlot(slot, NULL);
14952 if (m_items[slot])
14954 delete m_items[slot];
14955 m_items[slot] = NULL;
14959 DEBUG_FILTER_LOG(LOG_FILTER_PLAYER_STATS, "Load Basic value of player %s is: ", m_name.c_str());
14960 outDebugStatsValues();
14962 //Need to call it to initialize m_team (m_team can be calculated from race)
14963 //Other way is to saves m_team into characters table.
14964 setFactionForRace(getRace());
14965 SetCharm(NULL);
14967 // load home bind and check in same time class/race pair, it used later for restore broken positions
14968 if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND)))
14970 delete result;
14971 return false;
14974 InitPrimaryProfessions(); // to max set before any spell loaded
14976 // init saved position, and fix it later if problematic
14977 uint32 transGUID = fields[30].GetUInt32();
14978 Relocate(fields[12].GetFloat(),fields[13].GetFloat(),fields[14].GetFloat(),fields[16].GetFloat());
14979 SetLocationMapId(fields[15].GetUInt32());
14981 uint32 difficulty = fields[38].GetUInt32();
14982 if(difficulty >= MAX_DUNGEON_DIFFICULTY)
14983 difficulty = DUNGEON_DIFFICULTY_NORMAL;
14984 SetDungeonDifficulty(Difficulty(difficulty)); // may be changed in _LoadGroup
14986 _LoadGroup(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP));
14988 _LoadArenaTeamInfo(holder->GetResult(PLAYER_LOGIN_QUERY_LOADARENAINFO));
14990 uint32 arena_currency = fields[39].GetUInt32();
14991 if (arena_currency > sWorld.getConfig(CONFIG_UINT32_MAX_ARENA_POINTS))
14992 arena_currency = sWorld.getConfig(CONFIG_UINT32_MAX_ARENA_POINTS);
14994 SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, arena_currency);
14996 // check arena teams integrity
14997 for(uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot)
14999 uint32 arena_team_id = GetArenaTeamId(arena_slot);
15000 if(!arena_team_id)
15001 continue;
15003 if(ArenaTeam * at = sObjectMgr.GetArenaTeamById(arena_team_id))
15004 if(at->HaveMember(GetGUID()))
15005 continue;
15007 // arena team not exist or not member, cleanup fields
15008 for(int j = 0; j < ARENA_TEAM_END; ++j)
15009 SetArenaTeamInfoField(arena_slot, ArenaTeamInfoType(j), 0);
15012 uint32 honor_currency = fields[40].GetUInt32();
15013 if (honor_currency > sWorld.getConfig(CONFIG_UINT32_MAX_HONOR_POINTS))
15014 honor_currency = sWorld.getConfig(CONFIG_UINT32_MAX_HONOR_POINTS);
15015 SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, honor_currency);
15017 SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, fields[41].GetUInt32());
15018 SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, fields[42].GetUInt32());
15019 SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, fields[43].GetUInt32());
15020 SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[44].GetUInt16());
15021 SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[45].GetUInt16());
15023 _LoadBoundInstances(holder->GetResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES));
15025 if(!IsPositionValid())
15027 sLog.outError("Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
15028 RelocateToHomebind();
15030 transGUID = 0;
15032 m_movementInfo.ClearTransportData();
15035 _LoadBGData(holder->GetResult(PLAYER_LOGIN_QUERY_LOADBGDATA));
15037 if(m_bgData.bgInstanceID) //saved in BattleGround
15039 BattleGround *currentBg = sBattleGroundMgr.GetBattleGround(m_bgData.bgInstanceID, BATTLEGROUND_TYPE_NONE);
15041 bool player_at_bg = currentBg && currentBg->IsPlayerInBattleGround(GetGUID());
15043 if(player_at_bg && currentBg->GetStatus() != STATUS_WAIT_LEAVE)
15045 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(currentBg->GetTypeID(), currentBg->GetArenaType());
15046 AddBattleGroundQueueId(bgQueueTypeId);
15048 m_bgData.bgTypeID = currentBg->GetTypeID();
15050 //join player to battleground group
15051 currentBg->EventPlayerLoggedIn(this, GetGUID());
15052 currentBg->AddOrSetPlayerToCorrectBgGroup(this, GetGUID(), m_bgData.bgTeam);
15054 SetInviteForBattleGroundQueueType(bgQueueTypeId,currentBg->GetInstanceID());
15056 else
15058 // leave bg
15059 if (player_at_bg)
15060 currentBg->RemovePlayerAtLeave(GetGUID(), false, true);
15062 // move to bg enter point
15063 const WorldLocation& _loc = GetBattleGroundEntryPoint();
15064 SetLocationMapId(_loc.mapid);
15065 Relocate(_loc.coord_x, _loc.coord_y, _loc.coord_z, _loc.orientation);
15067 // We are not in BG anymore
15068 m_bgData.bgInstanceID = 0;
15071 else
15073 MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId());
15074 // if server restart after player save in BG or area
15075 // player can have current coordinates in to BG/Arena map, fix this
15076 if(!mapEntry || mapEntry->IsBattleGroundOrArena())
15078 const WorldLocation& _loc = GetBattleGroundEntryPoint();
15079 SetLocationMapId(_loc.mapid);
15080 Relocate(_loc.coord_x, _loc.coord_y, _loc.coord_z, _loc.orientation);
15084 if (transGUID != 0)
15086 m_movementInfo.SetTransportData(ObjectGuid(HIGHGUID_MO_TRANSPORT,transGUID), fields[26].GetFloat(), fields[27].GetFloat(), fields[28].GetFloat(), fields[29].GetFloat(), 0, -1);
15088 if( !MaNGOS::IsValidMapCoord(
15089 GetPositionX() + m_movementInfo.GetTransportPos()->x, GetPositionY() + m_movementInfo.GetTransportPos()->y,
15090 GetPositionZ() + m_movementInfo.GetTransportPos()->z, GetOrientation() + m_movementInfo.GetTransportPos()->o) ||
15091 // transport size limited
15092 m_movementInfo.GetTransportPos()->x > 50 || m_movementInfo.GetTransportPos()->y > 50 || m_movementInfo.GetTransportPos()->z > 50 )
15094 sLog.outError("Player (guidlow %d) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
15095 guid, GetPositionX() + m_movementInfo.GetTransportPos()->x, GetPositionY() + m_movementInfo.GetTransportPos()->y,
15096 GetPositionZ() + m_movementInfo.GetTransportPos()->z, GetOrientation() + m_movementInfo.GetTransportPos()->o);
15098 RelocateToHomebind();
15100 m_movementInfo.ClearTransportData();
15102 transGUID = 0;
15106 if (transGUID != 0)
15108 for (MapManager::TransportSet::const_iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter)
15110 if( (*iter)->GetGUIDLow() == transGUID)
15112 MapEntry const* transMapEntry = sMapStore.LookupEntry((*iter)->GetMapId());
15113 // client without expansion support
15114 if(GetSession()->Expansion() < transMapEntry->Expansion())
15116 DEBUG_LOG("Player %s using client without required expansion tried login at transport at non accessible map %u", GetName(), (*iter)->GetMapId());
15117 break;
15120 m_transport = *iter;
15121 m_transport->AddPassenger(this);
15122 SetLocationMapId(m_transport->GetMapId());
15123 break;
15127 if(!m_transport)
15129 sLog.outError("Player (guidlow %d) have problems with transport guid (%u). Teleport to default race/class locations.",
15130 guid,transGUID);
15132 RelocateToHomebind();
15134 m_movementInfo.ClearTransportData();
15136 transGUID = 0;
15139 else // not transport case
15141 MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId());
15142 // client without expansion support
15143 if(GetSession()->Expansion() < mapEntry->Expansion())
15145 DEBUG_LOG("Player %s using client without required expansion tried login at non accessible map %u", GetName(), GetMapId());
15146 RelocateToHomebind();
15150 // NOW player must have valid map
15151 // load the player's map here if it's not already loaded
15152 SetMap(sMapMgr.CreateMap(GetMapId(), this));
15154 // if the player is in an instance and it has been reset in the meantime teleport him to the entrance
15155 if(GetInstanceId() && !sInstanceSaveMgr.GetInstanceSave(GetInstanceId()))
15157 AreaTrigger const* at = sObjectMgr.GetMapEntranceTrigger(GetMapId());
15158 if(at)
15159 Relocate(at->target_X, at->target_Y, at->target_Z, at->target_Orientation);
15160 else
15161 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());
15164 SaveRecallPosition();
15166 time_t now = time(NULL);
15167 time_t logoutTime = time_t(fields[22].GetUInt64());
15169 // since last logout (in seconds)
15170 uint32 time_diff = uint32(now - logoutTime);
15172 // set value, including drunk invisibility detection
15173 // calculate sobering. after 15 minutes logged out, the player will be sober again
15174 float soberFactor;
15175 if(time_diff > 15*MINUTE)
15176 soberFactor = 0;
15177 else
15178 soberFactor = 1-time_diff/(15.0f*MINUTE);
15179 uint16 newDrunkenValue = uint16(soberFactor*(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE));
15180 SetDrunkValue(newDrunkenValue);
15182 m_cinematic = fields[18].GetUInt32();
15183 m_Played_time[PLAYED_TIME_TOTAL]= fields[19].GetUInt32();
15184 m_Played_time[PLAYED_TIME_LEVEL]= fields[20].GetUInt32();
15186 m_resetTalentsCost = fields[24].GetUInt32();
15187 m_resetTalentsTime = time_t(fields[25].GetUInt64());
15189 // reserve some flags
15190 uint32 old_safe_flags = GetUInt32Value(PLAYER_FLAGS) & ( PLAYER_FLAGS_HIDE_CLOAK | PLAYER_FLAGS_HIDE_HELM );
15192 if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM) )
15193 SetUInt32Value(PLAYER_FLAGS, 0 | old_safe_flags);
15195 m_taxi.LoadTaxiMask( fields[17].GetString() ); // must be before InitTaxiNodesForLevel
15197 uint32 extraflags = fields[31].GetUInt32();
15199 m_stableSlots = fields[32].GetUInt32();
15200 if(m_stableSlots > MAX_PET_STABLES)
15202 sLog.outError("Player can have not more %u stable slots, but have in DB %u",MAX_PET_STABLES,uint32(m_stableSlots));
15203 m_stableSlots = MAX_PET_STABLES;
15206 m_atLoginFlags = fields[33].GetUInt32();
15208 // Honor system
15209 // Update Honor kills data
15210 m_lastHonorUpdateTime = logoutTime;
15211 UpdateHonorFields();
15213 m_deathExpireTime = (time_t)fields[36].GetUInt64();
15214 if(m_deathExpireTime > now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP)
15215 m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP-1;
15217 std::string taxi_nodes = fields[37].GetCppString();
15219 // clear channel spell data (if saved at channel spell casting)
15220 SetChannelObjectGUID(0);
15221 SetUInt32Value(UNIT_CHANNEL_SPELL,0);
15223 // clear charm/summon related fields
15224 SetCharm(NULL);
15225 SetPet(NULL);
15226 SetTargetGUID(0);
15227 SetChannelObjectGUID(0);
15228 SetCharmerGUID(0);
15229 SetOwnerGUID(0);
15230 SetCreatorGUID(0);
15232 // reset some aura modifiers before aura apply
15233 SetFarSightGUID(0);
15234 SetUInt32Value(PLAYER_TRACK_CREATURES, 0 );
15235 SetUInt32Value(PLAYER_TRACK_RESOURCES, 0 );
15237 // cleanup aura list explicitly before skill load where some spells can be applied
15238 RemoveAllAuras();
15240 // make sure the unit is considered out of combat for proper loading
15241 ClearInCombat();
15243 // make sure the unit is considered not in duel for proper loading
15244 SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
15245 SetUInt32Value(PLAYER_DUEL_TEAM, 0);
15247 // reset stats before loading any modifiers
15248 InitStatsForLevel();
15249 InitGlyphsForLevel();
15250 InitTaxiNodesForLevel();
15251 InitRunes();
15253 // rest bonus can only be calculated after InitStatsForLevel()
15254 m_rest_bonus = fields[21].GetFloat();
15256 if(time_diff > 0)
15258 //speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour)
15259 float bubble0 = 0.031f;
15260 //speed collect rest bonus in offline, in logout, in tavern, city (section/in hour)
15261 float bubble1 = 0.125f;
15262 float bubble = fields[23].GetUInt32() > 0
15263 ? bubble1*sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_TAVERN_OR_CITY)
15264 : bubble0*sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_WILDERNESS);
15266 SetRestBonus(GetRestBonus()+ time_diff*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble);
15269 // load skills after InitStatsForLevel because it triggering aura apply also
15270 _LoadSkills(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSKILLS));
15272 // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
15274 // Mail
15275 _LoadMails(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILS));
15276 _LoadMailedItems(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILEDITEMS));
15277 UpdateNextMailTimeAndUnreads();
15279 m_specsCount = fields[58].GetUInt8();
15280 m_activeSpec = fields[59].GetUInt8();
15282 _LoadGlyphs(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGLYPHS));
15284 _LoadAuras(holder->GetResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff);
15285 ApplyGlyphs(true);
15287 // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
15288 if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) )
15289 m_deathState = DEAD;
15291 _LoadSpells(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS));
15293 // after spell load, learn rewarded spell if need also
15294 _LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
15295 _LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS));
15296 _LoadWeeklyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS));
15298 _LoadTalents(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTALENTS));
15300 // after spell and quest load
15301 InitTalentForLevel();
15302 learnDefaultSpells();
15304 // must be before inventory (some items required reputation check)
15305 m_reputationMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADREPUTATION));
15307 _LoadInventory(holder->GetResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff);
15309 // update items with duration and realtime
15310 UpdateItemDuration(time_diff, true);
15312 _LoadActions(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACTIONS));
15314 m_social = sSocialMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSOCIALLIST), GetGUIDLow());
15316 // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES
15317 // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded
15318 uint32 curTitle = fields[46].GetUInt32();
15319 if (curTitle && !HasTitle(curTitle))
15320 curTitle = 0;
15322 SetUInt32Value(PLAYER_CHOSEN_TITLE, curTitle);
15324 // Not finish taxi flight path
15325 if(m_bgData.HasTaxiPath())
15327 m_taxi.ClearTaxiDestinations();
15328 for (int i = 0; i < 2; ++i)
15329 m_taxi.AddTaxiDestination(m_bgData.taxiPath[i]);
15331 else if(!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes,GetTeam()))
15333 // problems with taxi path loading
15334 TaxiNodesEntry const* nodeEntry = NULL;
15335 if(uint32 node_id = m_taxi.GetTaxiSource())
15336 nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
15338 if(!nodeEntry) // don't know taxi start node, to homebind
15340 sLog.outError("Character %u have wrong data in taxi destination list, teleport to homebind.",GetGUIDLow());
15341 RelocateToHomebind();
15343 else // have start node, to it
15345 sLog.outError("Character %u have too short taxi destination list, teleport to original node.",GetGUIDLow());
15346 SetLocationMapId(nodeEntry->map_id);
15347 Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z,0.0f);
15350 //we can be relocated from taxi and still have an outdated Map pointer!
15351 //so we need to get a new Map pointer!
15352 SetMap(sMapMgr.CreateMap(GetMapId(), this));
15353 SaveRecallPosition(); // save as recall also to prevent recall and fall from sky
15355 m_taxi.ClearTaxiDestinations();
15358 if(uint32 node_id = m_taxi.GetTaxiSource())
15360 // save source node as recall coord to prevent recall and fall from sky
15361 TaxiNodesEntry const* nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
15362 ASSERT(nodeEntry); // checked in m_taxi.LoadTaxiDestinationsFromString
15363 m_recallMap = nodeEntry->map_id;
15364 m_recallX = nodeEntry->x;
15365 m_recallY = nodeEntry->y;
15366 m_recallZ = nodeEntry->z;
15368 // flight will started later
15371 // has to be called after last Relocate() in Player::LoadFromDB
15372 SetFallInformation(0, GetPositionZ());
15374 _LoadSpellCooldowns(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS));
15376 // Spell code allow apply any auras to dead character in load time in aura/spell/item loading
15377 // Do now before stats re-calculation cleanup for ghost state unexpected auras
15378 if(!isAlive())
15379 RemoveAllAurasOnDeath();
15381 //apply all stat bonuses from items and auras
15382 SetCanModifyStats(true);
15383 UpdateAllStats();
15385 // restore remembered power/health values (but not more max values)
15386 uint32 savedhealth = fields[50].GetUInt32();
15387 SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth);
15388 for(uint32 i = 0; i < MAX_POWERS; ++i)
15390 uint32 savedpower = fields[51+i].GetUInt32();
15391 SetPower(Powers(i),savedpower > GetMaxPower(Powers(i)) ? GetMaxPower(Powers(i)) : savedpower);
15394 DEBUG_FILTER_LOG(LOG_FILTER_PLAYER_STATS, "The value of player %s after load item and aura is: ", m_name.c_str());
15395 outDebugStatsValues();
15397 // all fields read
15398 delete result;
15400 // GM state
15401 if(GetSession()->GetSecurity() > SEC_PLAYER)
15403 switch(sWorld.getConfig(CONFIG_UINT32_GM_LOGIN_STATE))
15405 default:
15406 case 0: break; // disable
15407 case 1: SetGameMaster(true); break; // enable
15408 case 2: // save state
15409 if(extraflags & PLAYER_EXTRA_GM_ON)
15410 SetGameMaster(true);
15411 break;
15414 switch(sWorld.getConfig(CONFIG_UINT32_GM_VISIBLE_STATE))
15416 default:
15417 case 0: SetGMVisible(false); break; // invisible
15418 case 1: break; // visible
15419 case 2: // save state
15420 if(extraflags & PLAYER_EXTRA_GM_INVISIBLE)
15421 SetGMVisible(false);
15422 break;
15425 switch(sWorld.getConfig(CONFIG_UINT32_GM_ACCEPT_TICKETS))
15427 default:
15428 case 0: break; // disable
15429 case 1: SetAcceptTicket(true); break; // enable
15430 case 2: // save state
15431 if(extraflags & PLAYER_EXTRA_GM_ACCEPT_TICKETS)
15432 SetAcceptTicket(true);
15433 break;
15436 switch(sWorld.getConfig(CONFIG_UINT32_GM_CHAT))
15438 default:
15439 case 0: break; // disable
15440 case 1: SetGMChat(true); break; // enable
15441 case 2: // save state
15442 if(extraflags & PLAYER_EXTRA_GM_CHAT)
15443 SetGMChat(true);
15444 break;
15447 switch(sWorld.getConfig(CONFIG_UINT32_GM_WISPERING_TO))
15449 default:
15450 case 0: break; // disable
15451 case 1: SetAcceptWhispers(true); break; // enable
15452 case 2: // save state
15453 if(extraflags & PLAYER_EXTRA_ACCEPT_WHISPERS)
15454 SetAcceptWhispers(true);
15455 break;
15459 _LoadDeclinedNames(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES));
15461 m_achievementMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS), holder->GetResult(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS));
15462 m_achievementMgr.CheckAllAchievementCriteria();
15464 _LoadEquipmentSets(holder->GetResult(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS));
15466 return true;
15469 bool Player::isAllowedToLoot(Creature* creature)
15471 if(Player* recipient = creature->GetLootRecipient())
15473 if (recipient == this)
15474 return true;
15476 if (Group* otherGroup = recipient->GetGroup())
15478 Group* thisGroup = GetGroup();
15479 if (!thisGroup)
15480 return false;
15482 return thisGroup == otherGroup;
15484 return false;
15486 else
15487 // prevent other players from looting if the recipient got disconnected
15488 return !creature->HasLootRecipient();
15491 void Player::_LoadActions(QueryResult *result)
15493 for(int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
15494 m_actionButtons[i].clear();
15496 //QueryResult *result = CharacterDatabase.PQuery("SELECT spec, button,action,type FROM character_action WHERE guid = '%u' ORDER BY button",GetGUIDLow());
15498 if(result)
15502 Field *fields = result->Fetch();
15504 uint8 spec = fields[0].GetUInt8();
15505 uint8 button = fields[1].GetUInt8();
15506 uint32 action = fields[2].GetUInt32();
15507 uint8 type = fields[3].GetUInt8();
15509 if(ActionButton* ab = addActionButton(spec, button, action, type))
15510 ab->uState = ACTIONBUTTON_UNCHANGED;
15511 else
15513 sLog.outError( " ...at loading, and will deleted in DB also");
15515 // Will deleted in DB at next save (it can create data until save but marked as deleted)
15516 m_actionButtons[spec][button].uState = ACTIONBUTTON_DELETED;
15519 while( result->NextRow() );
15521 delete result;
15525 void Player::_LoadAuras(QueryResult *result, uint32 timediff)
15527 //RemoveAllAuras(); -- some spells casted before aura load, for example in LoadSkills, aura list explcitly cleaned early
15529 //QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'",GetGUIDLow());
15531 if(result)
15535 Field *fields = result->Fetch();
15536 uint64 caster_guid = fields[0].GetUInt64();
15537 uint32 spellid = fields[1].GetUInt32();
15538 SpellEffectIndex effindex = SpellEffectIndex(fields[2].GetUInt32());
15539 uint32 stackcount = fields[3].GetUInt32();
15540 int32 damage = fields[4].GetInt32();
15541 int32 maxduration = fields[5].GetInt32();
15542 int32 remaintime = fields[6].GetInt32();
15543 int32 remaincharges = fields[7].GetInt32();
15545 SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
15546 if (!spellproto)
15548 sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex);
15549 continue;
15552 if (effindex >= MAX_EFFECT_INDEX)
15554 sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex);
15555 continue;
15558 // negative effects should continue counting down after logout
15559 if (remaintime != -1 && !IsPositiveEffect(spellid, effindex))
15561 if (remaintime/IN_MILLISECONDS <= int32(timediff))
15562 continue;
15564 remaintime -= timediff*IN_MILLISECONDS;
15567 // prevent wrong values of remaincharges
15568 if (spellproto->procCharges)
15570 if (remaincharges <= 0 || remaincharges > (int32)spellproto->procCharges)
15571 remaincharges = spellproto->procCharges;
15573 else
15574 remaincharges = 0;
15577 for(uint32 i = 0; i < stackcount; ++i)
15579 Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL);
15580 if (!damage)
15581 damage = aura->GetModifier()->m_amount;
15583 // reset stolen single target auras
15584 if (caster_guid != GetGUID() && aura->IsSingleTarget())
15585 aura->SetIsSingleTarget(false);
15587 aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges);
15588 AddAura(aura);
15589 DETAIL_LOG("Added aura spellid %u, effect %u", spellproto->Id, effindex);
15592 while( result->NextRow() );
15594 delete result;
15597 if(getClass() == CLASS_WARRIOR && !HasAuraType(SPELL_AURA_MOD_SHAPESHIFT))
15598 CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true);
15601 void Player::_LoadGlyphs(QueryResult *result)
15603 if(!result)
15604 return;
15606 // 0 1 2
15607 // "SELECT spec, slot, glyph FROM character_glyphs WHERE guid='%u'"
15611 Field *fields = result->Fetch();
15612 uint8 spec = fields[0].GetUInt8();
15613 uint8 slot = fields[1].GetUInt8();
15614 uint32 glyph = fields[2].GetUInt32();
15616 GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph);
15617 if(!gp)
15619 sLog.outError("Player %s has not existing glyph entry %u on index %u, spec %u", m_name.c_str(), glyph, slot, spec);
15620 continue;
15623 GlyphSlotEntry const *gs = sGlyphSlotStore.LookupEntry(GetGlyphSlot(slot));
15624 if (!gs)
15626 sLog.outError("Player %s has not existing glyph slot entry %u on index %u, spec %u", m_name.c_str(), GetGlyphSlot(slot), slot, spec);
15627 continue;
15630 if(gp->TypeFlags != gs->TypeFlags)
15632 sLog.outError("Player %s has glyph with typeflags %u in slot with typeflags %u, removing.", m_name.c_str(), gp->TypeFlags, gs->TypeFlags);
15633 continue;
15636 m_glyphs[spec][slot].id = glyph;
15638 } while( result->NextRow() );
15640 delete result;
15645 void Player::LoadCorpse()
15647 if( isAlive() )
15649 sObjectAccessor.ConvertCorpseForPlayer(GetGUID());
15651 else
15653 if(Corpse *corpse = GetCorpse())
15655 ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, corpse && !sMapStore.LookupEntry(corpse->GetMapId())->Instanceable() );
15657 else
15659 //Prevent Dead Player login without corpse
15660 ResurrectPlayer(0.5f);
15665 void Player::_LoadInventory(QueryResult *result, uint32 timediff)
15667 //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());
15668 std::map<uint64, Bag*> bagMap; // fast guid lookup for bags
15669 //NOTE: the "order by `bag`" is important because it makes sure
15670 //the bagMap is filled before items in the bags are loaded
15671 //NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?)
15672 //expected to be equipped before offhand items (TODO: fixme)
15674 uint32 zone = GetZoneId();
15676 if (result)
15678 std::list<Item*> problematicItems;
15680 // prevent items from being added to the queue when stored
15681 m_itemUpdateQueueBlocked = true;
15684 Field *fields = result->Fetch();
15685 uint32 bag_guid = fields[2].GetUInt32();
15686 uint8 slot = fields[3].GetUInt8();
15687 uint32 item_guid = fields[4].GetUInt32();
15688 uint32 item_id = fields[5].GetUInt32();
15690 ItemPrototype const * proto = ObjectMgr::GetItemPrototype(item_id);
15692 if(!proto)
15694 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
15695 CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid);
15696 sLog.outError( "Player::_LoadInventory: Player %s has an unknown item (id: #%u) in inventory, deleted.", GetName(),item_id );
15697 continue;
15700 Item *item = NewItemOrBag(proto);
15702 if(!item->LoadFromDB(item_guid, GetGUID(), result))
15704 sLog.outError( "Player::_LoadInventory: Player %s has broken item (id: #%u) in inventory, deleted.", GetName(),item_id );
15705 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
15706 item->FSetState(ITEM_REMOVED);
15707 item->SaveToDB(); // it also deletes item object !
15708 continue;
15711 // not allow have in alive state item limited to another map/zone
15712 if(isAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(),zone) )
15714 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
15715 item->FSetState(ITEM_REMOVED);
15716 item->SaveToDB(); // it also deletes item object !
15717 continue;
15720 // "Conjured items disappear if you are logged out for more than 15 minutes"
15721 if (timediff > 15*MINUTE && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED))
15723 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
15724 item->FSetState(ITEM_REMOVED);
15725 item->SaveToDB(); // it also deletes item object !
15726 continue;
15729 bool success = true;
15731 // the item/bag is not in a bag
15732 if (!bag_guid)
15734 item->SetContainer( NULL );
15735 item->SetSlot(slot);
15737 if( IsInventoryPos( INVENTORY_SLOT_BAG_0, slot ) )
15739 ItemPosCountVec dest;
15740 if( CanStoreItem( INVENTORY_SLOT_BAG_0, slot, dest, item, false ) == EQUIP_ERR_OK )
15741 item = StoreItem(dest, item, true);
15742 else
15743 success = false;
15745 else if( IsEquipmentPos( INVENTORY_SLOT_BAG_0, slot ) )
15747 uint16 dest;
15748 if( CanEquipItem( slot, dest, item, false, false ) == EQUIP_ERR_OK )
15749 QuickEquipItem(dest, item);
15750 else
15751 success = false;
15753 else if( IsBankPos( INVENTORY_SLOT_BAG_0, slot ) )
15755 ItemPosCountVec dest;
15756 if( CanBankItem( INVENTORY_SLOT_BAG_0, slot, dest, item, false, false ) == EQUIP_ERR_OK )
15757 item = BankItem(dest, item, true);
15758 else
15759 success = false;
15762 if(success)
15764 // store bags that may contain items in them
15765 if(item->IsBag() && IsBagPos(item->GetPos()))
15766 bagMap[item_guid] = (Bag*)item;
15769 // the item/bag in a bag
15770 else
15772 item->SetSlot(NULL_SLOT);
15773 // the item is in a bag, find the bag
15774 std::map<uint64, Bag*>::const_iterator itr = bagMap.find(bag_guid);
15775 if(itr != bagMap.end() && slot < itr->second->GetBagSize())
15777 ItemPosCountVec dest;
15778 if( CanStoreItem( itr->second->GetSlot(), slot, dest, item, false ) == EQUIP_ERR_OK )
15779 item = StoreItem(dest, item, true);
15780 else
15781 success = false;
15783 else
15784 success = false;
15787 // item's state may have changed after stored
15788 if (success)
15790 item->SetState(ITEM_UNCHANGED, this);
15792 // recharged mana gem
15793 if (timediff > 15*MINUTE && proto->ItemLimitCategory ==ITEM_LIMIT_CATEGORY_MANA_GEM)
15794 item->RestoreCharges();
15796 else
15798 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_guid, item_id, bag_guid, slot);
15799 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
15800 problematicItems.push_back(item);
15802 } while (result->NextRow());
15804 delete result;
15805 m_itemUpdateQueueBlocked = false;
15807 // send by mail problematic items
15808 while(!problematicItems.empty())
15810 std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM);
15812 // fill mail
15813 MailDraft draft(subject, "There's were problems with equipping item(s).");
15815 for(int i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i)
15817 Item* item = problematicItems.front();
15818 problematicItems.pop_front();
15820 draft.AddItem(item);
15823 draft.SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED);
15826 //if(isAlive())
15827 _ApplyAllItemMods();
15830 // load mailed item which should receive current player
15831 void Player::_LoadMailedItems(QueryResult *result)
15833 // data needs to be at first place for Item::LoadFromDB
15834 // 0 1 2 3 4
15835 // "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)
15836 if(!result)
15837 return;
15841 Field *fields = result->Fetch();
15842 uint32 mail_id = fields[2].GetUInt32();
15843 uint32 item_guid_low = fields[3].GetUInt32();
15844 uint32 item_template = fields[4].GetUInt32();
15846 Mail* mail = GetMail(mail_id);
15847 if(!mail)
15848 continue;
15849 mail->AddItem(item_guid_low, item_template);
15851 ItemPrototype const *proto = ObjectMgr::GetItemPrototype(item_template);
15853 if(!proto)
15855 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);
15856 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low);
15857 CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid_low);
15858 continue;
15861 Item *item = NewItemOrBag(proto);
15863 if(!item->LoadFromDB(item_guid_low, 0, result))
15865 sLog.outError( "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, item_guid_low);
15866 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low);
15867 item->FSetState(ITEM_REMOVED);
15868 item->SaveToDB(); // it also deletes item object !
15869 continue;
15872 AddMItem(item);
15873 } while (result->NextRow());
15875 delete result;
15878 void Player::_LoadMails(QueryResult *result)
15880 m_mail.clear();
15881 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
15882 //"SELECT id,messageType,sender,receiver,subject,body,has_items,expire_time,deliver_time,money,cod,checked,stationery,mailTemplateId FROM mail WHERE receiver = '%u' ORDER BY id DESC", GetGUIDLow()
15883 if(!result)
15884 return;
15888 Field *fields = result->Fetch();
15889 Mail *m = new Mail;
15890 m->messageID = fields[0].GetUInt32();
15891 m->messageType = fields[1].GetUInt8();
15892 m->sender = fields[2].GetUInt32();
15893 m->receiver = fields[3].GetUInt32();
15894 m->subject = fields[4].GetCppString();
15895 m->body = fields[5].GetCppString();
15896 bool has_items = fields[6].GetBool();
15897 m->expire_time = (time_t)fields[7].GetUInt64();
15898 m->deliver_time = (time_t)fields[8].GetUInt64();
15899 m->money = fields[9].GetUInt32();
15900 m->COD = fields[10].GetUInt32();
15901 m->checked = fields[11].GetUInt32();
15902 m->stationery = fields[12].GetUInt8();
15903 m->mailTemplateId = fields[13].GetInt16();
15905 if(m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId))
15907 sLog.outError( "Player::_LoadMail - Mail (%u) have not existed MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId);
15908 m->mailTemplateId = 0;
15911 m->state = MAIL_STATE_UNCHANGED;
15913 m_mail.push_back(m);
15914 } while( result->NextRow() );
15915 delete result;
15918 void Player::LoadPet()
15920 // fixme: the pet should still be loaded if the player is not in world
15921 // just not added to the map
15922 if(IsInWorld())
15924 Pet *pet = new Pet;
15925 if(!pet->LoadPetFromDB(this, 0, 0, true))
15926 delete pet;
15930 void Player::_LoadQuestStatus(QueryResult *result)
15932 mQuestStatus.clear();
15934 uint32 slot = 0;
15936 //// 0 1 2 3 4 5 6 7 8 9 10 11 12
15937 //QueryResult *result = CharacterDatabase.PQuery("SELECT quest, status, rewarded, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3, itemcount4 FROM character_queststatus WHERE guid = '%u'", GetGUIDLow());
15939 if(result)
15943 Field *fields = result->Fetch();
15945 uint32 quest_id = fields[0].GetUInt32();
15946 // used to be new, no delete?
15947 Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id);
15948 if( pQuest )
15950 // find or create
15951 QuestStatusData& questStatusData = mQuestStatus[quest_id];
15953 uint32 qstatus = fields[1].GetUInt32();
15954 if(qstatus < MAX_QUEST_STATUS)
15955 questStatusData.m_status = QuestStatus(qstatus);
15956 else
15958 questStatusData.m_status = QUEST_STATUS_NONE;
15959 sLog.outError("Player %s have invalid quest %d status (%d), replaced by QUEST_STATUS_NONE(0).",GetName(),quest_id,qstatus);
15962 questStatusData.m_rewarded = ( fields[2].GetUInt8() > 0 );
15963 questStatusData.m_explored = ( fields[3].GetUInt8() > 0 );
15965 time_t quest_time = time_t(fields[4].GetUInt64());
15967 if( pQuest->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) && !GetQuestRewardStatus(quest_id) && questStatusData.m_status != QUEST_STATUS_NONE )
15969 AddTimedQuest( quest_id );
15971 if (quest_time <= sWorld.GetGameTime())
15972 questStatusData.m_timer = 1;
15973 else
15974 questStatusData.m_timer = uint32(quest_time - sWorld.GetGameTime()) * IN_MILLISECONDS;
15976 else
15977 quest_time = 0;
15979 questStatusData.m_creatureOrGOcount[0] = fields[5].GetUInt32();
15980 questStatusData.m_creatureOrGOcount[1] = fields[6].GetUInt32();
15981 questStatusData.m_creatureOrGOcount[2] = fields[7].GetUInt32();
15982 questStatusData.m_creatureOrGOcount[3] = fields[8].GetUInt32();
15983 questStatusData.m_itemcount[0] = fields[9].GetUInt32();
15984 questStatusData.m_itemcount[1] = fields[10].GetUInt32();
15985 questStatusData.m_itemcount[2] = fields[11].GetUInt32();
15986 questStatusData.m_itemcount[3] = fields[12].GetUInt32();
15988 questStatusData.uState = QUEST_UNCHANGED;
15990 // add to quest log
15991 if (slot < MAX_QUEST_LOG_SIZE &&
15992 ((questStatusData.m_status == QUEST_STATUS_INCOMPLETE ||
15993 questStatusData.m_status == QUEST_STATUS_COMPLETE ||
15994 questStatusData.m_status == QUEST_STATUS_FAILED) &&
15995 (!questStatusData.m_rewarded || pQuest->IsRepeatable())))
15997 SetQuestSlot(slot, quest_id, uint32(quest_time));
15999 if (questStatusData.m_status == QUEST_STATUS_COMPLETE)
16000 SetQuestSlotState(slot, QUEST_STATE_COMPLETE);
16002 if (questStatusData.m_status == QUEST_STATUS_FAILED)
16003 SetQuestSlotState(slot, QUEST_STATE_FAIL);
16005 for(uint8 idx = 0; idx < QUEST_OBJECTIVES_COUNT; ++idx)
16006 if(questStatusData.m_creatureOrGOcount[idx])
16007 SetQuestSlotCounter(slot, idx, questStatusData.m_creatureOrGOcount[idx]);
16009 ++slot;
16012 if(questStatusData.m_rewarded)
16014 // learn rewarded spell if unknown
16015 learnQuestRewardedSpells(pQuest);
16017 // set rewarded title if any
16018 if(pQuest->GetCharTitleId())
16020 if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
16021 SetTitle(titleEntry);
16024 if(pQuest->GetBonusTalents())
16025 m_questRewardTalentCount += pQuest->GetBonusTalents();
16028 DEBUG_LOG("Quest status is {%u} for quest {%u} for player (GUID: %u)", questStatusData.m_status, quest_id, GetGUIDLow());
16031 while( result->NextRow() );
16033 delete result;
16036 // clear quest log tail
16037 for ( uint16 i = slot; i < MAX_QUEST_LOG_SIZE; ++i )
16038 SetQuestSlot(i, 0);
16041 void Player::_LoadDailyQuestStatus(QueryResult *result)
16043 for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
16044 SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,0);
16046 //QueryResult *result = CharacterDatabase.PQuery("SELECT quest FROM character_queststatus_daily WHERE guid = '%u'", GetGUIDLow());
16048 if(result)
16050 uint32 quest_daily_idx = 0;
16054 if(quest_daily_idx >= PLAYER_MAX_DAILY_QUESTS) // max amount with exist data in query
16056 sLog.outError("Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`",GetGUIDLow());
16057 break;
16060 Field *fields = result->Fetch();
16062 uint32 quest_id = fields[0].GetUInt32();
16064 Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id);
16065 if( !pQuest )
16066 continue;
16068 SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,quest_id);
16069 ++quest_daily_idx;
16071 DEBUG_LOG("Daily quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
16073 while( result->NextRow() );
16075 delete result;
16078 m_DailyQuestChanged = false;
16081 void Player::_LoadWeeklyQuestStatus(QueryResult *result)
16083 m_weeklyquests.clear();
16085 //QueryResult *result = CharacterDatabase.PQuery("SELECT quest FROM character_queststatus_weekly WHERE guid = '%u'", GetGUIDLow());
16087 if (result)
16091 Field *fields = result->Fetch();
16093 uint32 quest_id = fields[0].GetUInt32();
16095 Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id);
16096 if (!pQuest)
16097 continue;
16099 m_weeklyquests.insert(quest_id);
16101 DEBUG_LOG("Weekly quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
16103 while( result->NextRow() );
16105 delete result;
16107 m_WeeklyQuestChanged = false;
16110 void Player::_LoadSpells(QueryResult *result)
16112 //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,active,disabled FROM character_spell WHERE guid = '%u'",GetGUIDLow());
16114 if(result)
16118 Field *fields = result->Fetch();
16120 uint32 spell_id = fields[0].GetUInt32();
16122 // skip talents & drop unneeded data
16123 if(GetTalentSpellPos(spell_id))
16125 sLog.outError("Player::_LoadSpells: Player (GUID: %u) has talent spell in character_spell, removing it.", GetGUIDLow(), spell_id);
16126 CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id);
16127 continue;
16130 addSpell(spell_id, fields[1].GetBool(), false, false, fields[2].GetBool());
16132 while( result->NextRow() );
16134 delete result;
16138 void Player::_LoadTalents(QueryResult *result)
16140 //QueryResult *result = CharacterDatabase.PQuery("SELECT talent_id, current_rank, spec FROM character_talent WHERE guid = '%u'",GetGUIDLow());
16141 if (result)
16145 Field *fields = result->Fetch();
16147 uint32 talent_id = fields[0].GetUInt32();
16148 TalentEntry const *talentInfo = sTalentStore.LookupEntry( talent_id );
16150 if (!talentInfo)
16152 sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent_id: %u , this talent will be deleted from character_talent",GetGUIDLow(), talent_id );
16153 CharacterDatabase.PExecute("DELETE FROM character_talent WHERE talent_id = '%u'", talent_id);
16154 continue;
16157 TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
16159 if (!talentTabInfo)
16161 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 );
16162 CharacterDatabase.PExecute("DELETE FROM character_talent WHERE talent_id = '%u'", talent_id);
16163 continue;
16166 // prevent load talent for different class (cheating)
16167 if ((getClassMask() & talentTabInfo->ClassMask) == 0)
16169 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 );
16170 CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND talent_id = '%u'", GetGUIDLow(), talent_id);
16171 continue;
16174 uint32 currentRank = fields[1].GetUInt32();
16176 if (currentRank > MAX_TALENT_RANK || talentInfo->RankID[currentRank] == 0)
16178 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 );
16179 CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND talent_id = '%u'", GetGUIDLow(), talent_id);
16180 continue;
16183 uint32 spec = fields[2].GetUInt32();
16185 if (spec > MAX_TALENT_SPEC_COUNT)
16187 sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent spec: %u, spec will be deleted from character_talent", GetGUIDLow(), spec);
16188 CharacterDatabase.PExecute("DELETE FROM character_talent WHERE spec = '%u' ", spec);
16189 continue;
16192 if (spec >= m_specsCount)
16194 sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent spec: %u , this spec will be deleted from character_talent.", GetGUIDLow(), spec);
16195 CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND spec = '%u' ", GetGUIDLow(), spec);
16196 continue;
16199 if (m_activeSpec == spec)
16200 addSpell(talentInfo->RankID[currentRank], true,false,false,false);
16201 else
16203 PlayerTalent talent;
16204 talent.currentRank = currentRank;
16205 talent.m_talentEntry = talentInfo;
16206 talent.state = PLAYERSPELL_UNCHANGED;
16207 m_talents[spec][talentInfo->TalentID] = talent;
16210 while (result->NextRow());
16211 delete result;
16214 void Player::_LoadGroup(QueryResult *result)
16216 //QueryResult *result = CharacterDatabase.PQuery("SELECT groupId FROM group_member WHERE memberGuid='%u'", GetGUIDLow());
16217 if (result)
16219 uint32 groupId = (*result)[0].GetUInt32();
16220 delete result;
16222 if (Group* group = sObjectMgr.GetGroupById(groupId))
16224 uint8 subgroup = group->GetMemberGroup(GetGUID());
16225 SetGroup(group, subgroup);
16226 if (getLevel() >= LEVELREQUIREMENT_HEROIC)
16228 // the group leader may change the instance difficulty while the player is offline
16229 SetDungeonDifficulty(group->GetDungeonDifficulty());
16230 SetRaidDifficulty(group->GetRaidDifficulty());
16236 void Player::_LoadBoundInstances(QueryResult *result)
16238 for(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
16239 m_boundInstances[i].clear();
16241 Group *group = GetGroup();
16243 //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));
16244 if(result)
16248 Field *fields = result->Fetch();
16249 bool perm = fields[1].GetBool();
16250 uint32 mapId = fields[2].GetUInt32();
16251 uint32 instanceId = fields[0].GetUInt32();
16252 uint8 difficulty = fields[3].GetUInt8();
16254 time_t resetTime = (time_t)fields[4].GetUInt64();
16255 // the resettime for normal instances is only saved when the InstanceSave is unloaded
16256 // so the value read from the DB may be wrong here but only if the InstanceSave is loaded
16257 // and in that case it is not used
16259 MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
16260 if(!mapEntry || !mapEntry->IsDungeon())
16262 sLog.outError("_LoadBoundInstances: player %s(%d) has bind to not existed or not dungeon map %d", GetName(), GetGUIDLow(), mapId);
16263 CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'", GetGUIDLow(), instanceId);
16264 continue;
16267 if(difficulty >= MAX_DIFFICULTY)
16269 sLog.outError("_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u", GetName(), GetGUIDLow(), difficulty, mapId);
16270 CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'", GetGUIDLow(), instanceId);
16271 continue;
16274 MapDifficulty const* mapDiff = GetMapDifficultyData(mapId,Difficulty(difficulty));
16275 if(!mapDiff)
16277 sLog.outError("_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u", GetName(), GetGUIDLow(), difficulty, mapId);
16278 CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'", GetGUIDLow(), instanceId);
16279 continue;
16282 if(!perm && group)
16284 sLog.outError("_LoadBoundInstances: player %s(%d) is in group %d but has a non-permanent character bind to map %d,%d,%d", GetName(), GetGUIDLow(), GUID_LOPART(group->GetLeaderGUID()), mapId, instanceId, difficulty);
16285 CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'", GetGUIDLow(), instanceId);
16286 continue;
16289 // since non permanent binds are always solo bind, they can always be reset
16290 InstanceSave *save = sInstanceSaveMgr.AddInstanceSave(mapId, instanceId, Difficulty(difficulty), resetTime, !perm, true);
16291 if(save) BindToInstance(save, perm, true);
16292 } while(result->NextRow());
16293 delete result;
16297 InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty)
16299 // some instances only have one difficulty
16300 MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty);
16301 if(!mapDiff)
16302 return NULL;
16304 BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
16305 if(itr != m_boundInstances[difficulty].end())
16306 return &itr->second;
16307 else
16308 return NULL;
16311 void Player::UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload)
16313 BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
16314 UnbindInstance(itr, difficulty, unload);
16317 void Player::UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficulty, bool unload)
16319 if(itr != m_boundInstances[difficulty].end())
16321 if(!unload) CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), itr->second.save->GetInstanceId());
16322 itr->second.save->RemovePlayer(this); // save can become invalid
16323 m_boundInstances[difficulty].erase(itr++);
16327 InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent, bool load)
16329 if(save)
16331 InstancePlayerBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
16332 if(bind.save)
16334 // update the save when the group kills a boss
16335 if(permanent != bind.perm || save != bind.save)
16336 if(!load) CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GetGUIDLow(), bind.save->GetInstanceId());
16338 else
16339 if(!load) CharacterDatabase.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')", GetGUIDLow(), save->GetInstanceId(), permanent);
16341 if(bind.save != save)
16343 if(bind.save) bind.save->RemovePlayer(this);
16344 save->AddPlayer(this);
16347 if(permanent) save->SetCanReset(false);
16349 bind.save = save;
16350 bind.perm = permanent;
16351 if(!load) DEBUG_LOG("Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", GetName(), GetGUIDLow(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
16352 return &bind;
16354 else
16355 return NULL;
16358 void Player::SendRaidInfo()
16360 uint32 counter = 0;
16362 WorldPacket data(SMSG_RAID_INSTANCE_INFO, 4);
16364 size_t p_counter = data.wpos();
16365 data << uint32(counter); // placeholder
16367 time_t now = time(NULL);
16369 for(int i = 0; i < MAX_DIFFICULTY; ++i)
16371 for (BoundInstancesMap::const_iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
16373 if(itr->second.perm)
16375 InstanceSave *save = itr->second.save;
16376 data << uint32(save->GetMapId()); // map id
16377 data << uint32(save->GetDifficulty()); // difficulty
16378 data << uint64(save->GetInstanceId()); // instance id
16379 data << uint8(1); // expired = 0
16380 data << uint8(0); // extended = 1
16381 data << uint32(save->GetResetTime() - now); // reset time
16382 ++counter;
16386 data.put<uint32>(p_counter, counter);
16387 GetSession()->SendPacket(&data);
16391 - called on every successful teleportation to a map
16393 void Player::SendSavedInstances()
16395 bool hasBeenSaved = false;
16396 WorldPacket data;
16398 for(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
16400 for (BoundInstancesMap::const_iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
16402 if(itr->second.perm) // only permanent binds are sent
16404 hasBeenSaved = true;
16405 break;
16410 //Send opcode 811. true or false means, whether you have current raid/heroic instances
16411 data.Initialize(SMSG_UPDATE_INSTANCE_OWNERSHIP);
16412 data << uint32(hasBeenSaved);
16413 GetSession()->SendPacket(&data);
16415 if(!hasBeenSaved)
16416 return;
16418 for(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
16420 for (BoundInstancesMap::const_iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
16422 if(itr->second.perm)
16424 data.Initialize(SMSG_UPDATE_LAST_INSTANCE);
16425 data << uint32(itr->second.save->GetMapId());
16426 GetSession()->SendPacket(&data);
16432 /// convert the player's binds to the group
16433 void Player::ConvertInstancesToGroup(Player *player, Group *group, uint64 player_guid)
16435 bool has_binds = false;
16436 bool has_solo = false;
16438 if(player) { player_guid = player->GetGUID(); if(!group) group = player->GetGroup(); }
16439 ASSERT(player_guid);
16441 // copy all binds to the group, when changing leader it's assumed the character
16442 // will not have any solo binds
16444 if(player)
16446 for(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
16448 for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();)
16450 has_binds = true;
16451 if(group) group->BindToInstance(itr->second.save, itr->second.perm, true);
16452 // permanent binds are not removed
16453 if(!itr->second.perm)
16455 // increments itr in call
16456 player->UnbindInstance(itr, Difficulty(i), true);
16457 has_solo = true;
16459 else
16460 ++itr;
16465 // if the player's not online we don't know what binds it has
16466 if(!player || !group || has_binds)
16467 CharacterDatabase.PExecute("INSERT INTO group_instance SELECT guid, instance, permanent FROM character_instance WHERE guid = '%u'", GUID_LOPART(player_guid));
16469 // the following should not get executed when changing leaders
16470 if(!player || has_solo)
16471 CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND permanent = 0", GUID_LOPART(player_guid));
16474 bool Player::_LoadHomeBind(QueryResult *result)
16476 PlayerInfo const *info = sObjectMgr.GetPlayerInfo(getRace(), getClass());
16477 if(!info)
16479 sLog.outError("Player have incorrect race/class pair. Can't be loaded.");
16480 return false;
16483 bool ok = false;
16484 //QueryResult *result = CharacterDatabase.PQuery("SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(playerGuid));
16485 if (result)
16487 Field *fields = result->Fetch();
16488 m_homebindMapId = fields[0].GetUInt32();
16489 m_homebindAreaId = fields[1].GetUInt16();
16490 m_homebindX = fields[2].GetFloat();
16491 m_homebindY = fields[3].GetFloat();
16492 m_homebindZ = fields[4].GetFloat();
16493 delete result;
16495 MapEntry const* bindMapEntry = sMapStore.LookupEntry(m_homebindMapId);
16497 // accept saved data only for valid position (and non instanceable), and accessable
16498 if( MapManager::IsValidMapCoord(m_homebindMapId,m_homebindX,m_homebindY,m_homebindZ) &&
16499 !bindMapEntry->Instanceable() && GetSession()->Expansion() >= bindMapEntry->Expansion())
16501 ok = true;
16503 else
16504 CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'", GetGUIDLow());
16507 if(!ok)
16509 m_homebindMapId = info->mapId;
16510 m_homebindAreaId = info->areaId;
16511 m_homebindX = info->positionX;
16512 m_homebindY = info->positionY;
16513 m_homebindZ = info->positionZ;
16515 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);
16518 DEBUG_LOG("Setting player home position: mapid is: %u, zoneid is %u, X is %f, Y is %f, Z is %f",
16519 m_homebindMapId, m_homebindAreaId, m_homebindX, m_homebindY, m_homebindZ);
16521 return true;
16524 /*********************************************************/
16525 /*** SAVE SYSTEM ***/
16526 /*********************************************************/
16528 void Player::SaveToDB()
16530 // we should assure this: ASSERT((m_nextSave != sWorld.getConfig(CONFIG_UINT32_INTERVAL_SAVE)));
16531 // delay auto save at any saves (manual, in code, or autosave)
16532 m_nextSave = sWorld.getConfig(CONFIG_UINT32_INTERVAL_SAVE);
16534 //lets allow only players in world to be saved
16535 if(IsBeingTeleportedFar())
16537 ScheduleDelayedOperation(DELAYED_SAVE_PLAYER);
16538 return;
16541 // first save/honor gain after midnight will also update the player's honor fields
16542 UpdateHonorFields();
16544 DEBUG_FILTER_LOG(LOG_FILTER_PLAYER_STATS, "The value of player %s at save: ", m_name.c_str());
16545 outDebugStatsValues();
16547 CharacterDatabase.BeginTransaction();
16549 CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",GetGUIDLow());
16551 std::string sql_name = m_name;
16552 CharacterDatabase.escape_string(sql_name);
16554 std::ostringstream ss;
16555 ss << "INSERT INTO characters (guid,account,name,race,class,gender,level,xp,money,playerBytes,playerBytes2,playerFlags,"
16556 "map, dungeon_difficulty, position_x, position_y, position_z, orientation, "
16557 "taximask, online, cinematic, "
16558 "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, "
16559 "trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, "
16560 "death_expire_time, taxi_path, arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, "
16561 "todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, health, power1, power2, power3, "
16562 "power4, power5, power6, power7, specCount, activeSpec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars) VALUES ("
16563 << GetGUIDLow() << ", "
16564 << GetSession()->GetAccountId() << ", '"
16565 << sql_name << "', "
16566 << (uint32)getRace() << ", "
16567 << (uint32)getClass() << ", "
16568 << (uint32)getGender() << ", "
16569 << getLevel() << ", "
16570 << GetUInt32Value(PLAYER_XP) << ", "
16571 << GetMoney() << ", "
16572 << GetUInt32Value(PLAYER_BYTES) << ", "
16573 << GetUInt32Value(PLAYER_BYTES_2) << ", "
16574 << GetUInt32Value(PLAYER_FLAGS) << ", ";
16576 if(!IsBeingTeleported())
16578 ss << GetMapId() << ", "
16579 << (uint32)GetDungeonDifficulty() << ", "
16580 << finiteAlways(GetPositionX()) << ", "
16581 << finiteAlways(GetPositionY()) << ", "
16582 << finiteAlways(GetPositionZ()) << ", "
16583 << finiteAlways(GetOrientation()) << ", ";
16585 else
16587 ss << GetTeleportDest().mapid << ", "
16588 << (uint32)GetDungeonDifficulty() << ", "
16589 << finiteAlways(GetTeleportDest().coord_x) << ", "
16590 << finiteAlways(GetTeleportDest().coord_y) << ", "
16591 << finiteAlways(GetTeleportDest().coord_z) << ", "
16592 << finiteAlways(GetTeleportDest().orientation) << ", ";
16595 ss << m_taxi << ", "; // string with TaxiMaskSize numbers
16597 ss << (IsInWorld() ? 1 : 0) << ", ";
16599 ss << m_cinematic << ", ";
16601 ss << m_Played_time[PLAYED_TIME_TOTAL] << ", ";
16602 ss << m_Played_time[PLAYED_TIME_LEVEL] << ", ";
16604 ss << finiteAlways(m_rest_bonus) << ", ";
16605 ss << (uint64)time(NULL) << ", ";
16606 ss << (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0) << ", ";
16607 //save, far from tavern/city
16608 //save, but in tavern/city
16609 ss << m_resetTalentsCost << ", ";
16610 ss << (uint64)m_resetTalentsTime << ", ";
16612 ss << finiteAlways(m_movementInfo.GetTransportPos()->x) << ", ";
16613 ss << finiteAlways(m_movementInfo.GetTransportPos()->y) << ", ";
16614 ss << finiteAlways(m_movementInfo.GetTransportPos()->z) << ", ";
16615 ss << finiteAlways(m_movementInfo.GetTransportPos()->o) << ", ";
16616 if (m_transport)
16617 ss << m_transport->GetGUIDLow();
16618 else
16619 ss << "0";
16620 ss << ", ";
16622 ss << m_ExtraFlags << ", ";
16624 ss << uint32(m_stableSlots) << ", "; // to prevent save uint8 as char
16626 ss << uint32(m_atLoginFlags) << ", ";
16628 ss << GetZoneId() << ", ";
16630 ss << (uint64)m_deathExpireTime << ", '";
16632 ss << m_taxi.SaveTaxiDestinationsToString() << "', ";
16634 ss << GetArenaPoints() << ", ";
16636 ss << GetHonorPoints() << ", ";
16638 ss << GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION) << ", ";
16640 ss << GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION) << ", ";
16642 ss << GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS) << ", ";
16644 ss << GetUInt16Value(PLAYER_FIELD_KILLS, 0) << ", ";
16646 ss << GetUInt16Value(PLAYER_FIELD_KILLS, 1) << ", ";
16648 ss << GetUInt32Value(PLAYER_CHOSEN_TITLE) << ", ";
16650 ss << GetUInt64Value(PLAYER_FIELD_KNOWN_CURRENCIES) << ", ";
16652 // FIXME: at this moment send to DB as unsigned, including unit32(-1)
16653 ss << GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX) << ", ";
16655 ss << (uint16)(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE) << ", ";
16657 ss << GetHealth();
16659 for(uint32 i = 0; i < MAX_POWERS; ++i)
16660 ss << "," << GetPower(Powers(i));
16662 ss << ", ";
16663 ss << uint32(m_specsCount) << ", ";
16664 ss << uint32(m_activeSpec) << ", '";
16665 for(uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i )
16667 ss << GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + i) << " ";
16670 ss << "', '";
16671 for(uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i )
16673 ss << GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + i) << " ";
16676 ss << "',";
16677 ss << GetUInt32Value(PLAYER_AMMO_ID) << ", '";
16678 for(uint32 i = 0; i < KNOWN_TITLES_SIZE*2; ++i )
16680 ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << " ";
16682 ss << "',";
16683 ss << uint32(GetByteValue(PLAYER_FIELD_BYTES, 2));
16684 ss << ")";
16686 CharacterDatabase.Execute( ss.str().c_str() );
16688 if (m_mailsUpdated) //save mails only when needed
16689 _SaveMail();
16691 _SaveBGData();
16692 _SaveInventory();
16693 _SaveQuestStatus();
16694 _SaveDailyQuestStatus();
16695 _SaveWeeklyQuestStatus();
16696 _SaveSpells();
16697 _SaveSpellCooldowns();
16698 _SaveActions();
16699 _SaveAuras();
16700 _SaveSkills();
16701 m_achievementMgr.SaveToDB();
16702 m_reputationMgr.SaveToDB();
16703 _SaveEquipmentSets();
16704 GetSession()->SaveTutorialsData(); // changed only while character in game
16705 _SaveGlyphs();
16706 _SaveTalents();
16708 CharacterDatabase.CommitTransaction();
16710 // check if stats should only be saved on logout
16711 // save stats can be out of transaction
16712 if (m_session->isLogingOut() || !sWorld.getConfig(CONFIG_BOOL_STATS_SAVE_ONLY_ON_LOGOUT))
16713 _SaveStats();
16715 // save pet (hunter pet level and experience and all type pets health/mana).
16716 if (Pet* pet = GetPet())
16717 pet->SavePetToDB(PET_SAVE_AS_CURRENT);
16720 // fast save function for item/money cheating preventing - save only inventory and money state
16721 void Player::SaveInventoryAndGoldToDB()
16723 _SaveInventory();
16724 SaveGoldToDB();
16727 void Player::SaveGoldToDB()
16729 CharacterDatabase.PExecute("UPDATE characters SET money = '%u' WHERE guid = '%u'", GetMoney(), GetGUIDLow());
16732 void Player::_SaveActions()
16734 for(int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
16736 for(ActionButtonList::iterator itr = m_actionButtons[i].begin(); itr != m_actionButtons[i].end(); )
16738 switch (itr->second.uState)
16740 case ACTIONBUTTON_NEW:
16741 CharacterDatabase.PExecute("INSERT INTO character_action (guid,spec, button,action,type) VALUES ('%u', '%u', '%u', '%u', '%u')",
16742 GetGUIDLow(), i, (uint32)itr->first, (uint32)itr->second.GetAction(), (uint32)itr->second.GetType() );
16743 itr->second.uState = ACTIONBUTTON_UNCHANGED;
16744 ++itr;
16745 break;
16746 case ACTIONBUTTON_CHANGED:
16747 CharacterDatabase.PExecute("UPDATE character_action SET action = '%u', type = '%u' WHERE guid= '%u' AND button= '%u' AND spec = '%u'",
16748 (uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), GetGUIDLow(), (uint32)itr->first, i );
16749 itr->second.uState = ACTIONBUTTON_UNCHANGED;
16750 ++itr;
16751 break;
16752 case ACTIONBUTTON_DELETED:
16753 CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u' AND button = '%u' AND spec = '%u'", GetGUIDLow(), (uint32)itr->first, i);
16754 m_actionButtons[i].erase(itr++);
16755 break;
16756 default:
16757 ++itr;
16758 break;
16764 void Player::_SaveAuras()
16766 CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",GetGUIDLow());
16768 AuraMap const& auras = GetAuras();
16770 if (auras.empty())
16771 return;
16773 spellEffectPair lastEffectPair = auras.begin()->first;
16774 uint32 stackCounter = 1;
16776 /* copied following sql-code partly from achievementmgr */
16777 bool first_round = true;
16778 std::ostringstream ss;
16779 for(AuraMap::const_iterator itr = auras.begin(); ; ++itr)
16781 if(itr == auras.end() || lastEffectPair != itr->first)
16783 AuraMap::const_iterator itr2 = itr;
16784 // save previous spellEffectPair to db
16785 itr2--;
16787 //skip all auras from spells that are passive
16788 //do not save single target auras (unless they were cast by the player)
16789 if (!itr2->second->IsPassive() && (itr2->second->GetCasterGUID() == GetGUID() || !itr2->second->IsSingleTarget()))
16791 if (first_round)
16793 ss << "INSERT INTO character_aura (guid,caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges)VALUES ";
16794 first_round = false;
16796 // next new/changed record prefix
16797 else
16798 ss << ", ";
16800 ss << "("<< GetGUIDLow() << "," << itr2->second->GetCasterGUID() << ","
16801 << (uint32)itr2->second->GetId() << "," << (uint32)itr2->second->GetEffIndex() << ","
16802 << stackCounter << "," << itr2->second->GetModifier()->m_amount << ","
16803 <<int(itr2->second->GetAuraMaxDuration()) << "," << int(itr2->second->GetAuraDuration()) << ","
16804 << int(itr2->second->GetAuraCharges()) << ")";
16807 if(itr == auras.end())
16808 break;
16811 if (lastEffectPair == itr->first)
16812 stackCounter++;
16813 else
16815 lastEffectPair = itr->first;
16816 stackCounter = 1;
16820 // if something changed execute
16821 if (!first_round)
16822 CharacterDatabase.Execute( ss.str().c_str() );
16825 void Player::_SaveGlyphs()
16828 for (uint8 spec = 0; spec < m_specsCount; ++spec)
16830 for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
16832 switch(m_glyphs[spec][slot].uState)
16834 case GLYPH_NEW:
16835 CharacterDatabase.PExecute("INSERT INTO character_glyphs (guid, spec, slot, glyph) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), spec, slot, m_glyphs[spec][slot].GetId());
16836 break;
16837 case GLYPH_CHANGED:
16838 CharacterDatabase.PExecute("UPDATE character_glyphs SET glyph = '%u' WHERE guid='%u' AND spec = '%u' AND slot = '%u'", m_glyphs[spec][slot].GetId(), GetGUIDLow(), spec, slot);
16839 break;
16840 case GLYPH_DELETED:
16841 CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE guid='%u' AND spec = '%u' AND slot = '%u'",GetGUIDLow(), spec, slot);
16842 break;
16843 case GLYPH_UNCHANGED:
16844 break;
16846 m_glyphs[spec][slot].uState = GLYPH_UNCHANGED;
16851 void Player::_SaveInventory()
16853 // force items in buyback slots to new state
16854 // and remove those that aren't already
16855 for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; ++i)
16857 Item *item = m_items[i];
16858 if (!item || item->GetState() == ITEM_NEW) continue;
16859 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item->GetGUIDLow());
16860 CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item->GetGUIDLow());
16861 m_items[i]->FSetState(ITEM_NEW);
16864 // update enchantment durations
16865 for(EnchantDurationList::const_iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();++itr)
16867 itr->item->SetEnchantmentDuration(itr->slot,itr->leftduration);
16870 // if no changes
16871 if (m_itemUpdateQueue.empty()) return;
16873 // do not save if the update queue is corrupt
16874 bool error = false;
16875 for(size_t i = 0; i < m_itemUpdateQueue.size(); ++i)
16877 Item *item = m_itemUpdateQueue[i];
16878 if(!item || item->GetState() == ITEM_REMOVED) continue;
16879 Item *test = GetItemByPos( item->GetBagSlot(), item->GetSlot());
16881 if (test == NULL)
16883 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());
16884 error = true;
16886 else if (test != item)
16888 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());
16889 error = true;
16893 if (error)
16895 sLog.outError("Player::_SaveInventory - one or more errors occurred save aborted!");
16896 ChatHandler(this).SendSysMessage(LANG_ITEM_SAVE_FAILED);
16897 return;
16900 for(size_t i = 0; i < m_itemUpdateQueue.size(); ++i)
16902 Item *item = m_itemUpdateQueue[i];
16903 if(!item) continue;
16905 Bag *container = item->GetContainer();
16906 uint32 bag_guid = container ? container->GetGUIDLow() : 0;
16908 switch(item->GetState())
16910 case ITEM_NEW:
16911 CharacterDatabase.PExecute("INSERT INTO character_inventory (guid,bag,slot,item,item_template) VALUES ('%u', '%u', '%u', '%u', '%u')", GetGUIDLow(), bag_guid, item->GetSlot(), item->GetGUIDLow(), item->GetEntry());
16912 break;
16913 case ITEM_CHANGED:
16914 CharacterDatabase.PExecute("UPDATE character_inventory SET guid='%u', bag='%u', slot='%u', item_template='%u' WHERE item='%u'", GetGUIDLow(), bag_guid, item->GetSlot(), item->GetEntry(), item->GetGUIDLow());
16915 break;
16916 case ITEM_REMOVED:
16917 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item->GetGUIDLow());
16918 break;
16919 case ITEM_UNCHANGED:
16920 break;
16923 item->SaveToDB(); // item have unchanged inventory record and can be save standalone
16925 m_itemUpdateQueue.clear();
16928 void Player::_SaveMail()
16930 for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
16932 Mail *m = (*itr);
16933 if (m->state == MAIL_STATE_CHANGED)
16935 CharacterDatabase.PExecute("UPDATE mail SET has_items = '%u',expire_time = '" UI64FMTD "', deliver_time = '" UI64FMTD "',money = '%u',cod = '%u',checked = '%u' WHERE id = '%u'",
16936 m->HasItems() ? 1 : 0, (uint64)m->expire_time, (uint64)m->deliver_time, m->money, m->COD, m->checked, m->messageID);
16937 if(m->removedItems.size())
16939 for(std::vector<uint32>::const_iterator itr2 = m->removedItems.begin(); itr2 != m->removedItems.end(); ++itr2)
16940 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", *itr2);
16941 m->removedItems.clear();
16943 m->state = MAIL_STATE_UNCHANGED;
16945 else if (m->state == MAIL_STATE_DELETED)
16947 if (m->HasItems())
16948 for(std::vector<MailItemInfo>::const_iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
16949 CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
16951 CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
16952 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", m->messageID);
16956 //deallocate deleted mails...
16957 for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); )
16959 if ((*itr)->state == MAIL_STATE_DELETED)
16961 Mail* m = *itr;
16962 m_mail.erase(itr);
16963 delete m;
16964 itr = m_mail.begin();
16966 else
16967 ++itr;
16970 m_mailsUpdated = false;
16973 void Player::_SaveQuestStatus()
16975 // we don't need transactions here.
16976 for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
16978 switch (i->second.uState)
16980 case QUEST_NEW :
16981 CharacterDatabase.PExecute("INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4) "
16982 "VALUES ('%u', '%u', '%u', '%u', '%u', '" UI64FMTD "', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
16983 GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / IN_MILLISECONDS+ sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]);
16984 break;
16985 case QUEST_CHANGED :
16986 CharacterDatabase.PExecute("UPDATE character_queststatus SET status = '%u',rewarded = '%u',explored = '%u',timer = '" UI64FMTD "',mobcount1 = '%u',mobcount2 = '%u',mobcount3 = '%u',mobcount4 = '%u',itemcount1 = '%u',itemcount2 = '%u',itemcount3 = '%u',itemcount4 = '%u' WHERE guid = '%u' AND quest = '%u' ",
16987 i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / IN_MILLISECONDS + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first );
16988 break;
16989 case QUEST_UNCHANGED:
16990 break;
16992 i->second.uState = QUEST_UNCHANGED;
16996 void Player::_SaveDailyQuestStatus()
16998 if (!m_DailyQuestChanged)
16999 return;
17001 // we don't need transactions here.
17002 CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'",GetGUIDLow());
17003 for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
17004 if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
17005 CharacterDatabase.PExecute("INSERT INTO character_queststatus_daily (guid,quest) VALUES ('%u', '%u')",
17006 GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx));
17008 m_DailyQuestChanged = false;
17011 void Player::_SaveWeeklyQuestStatus()
17013 if (!m_WeeklyQuestChanged || m_weeklyquests.empty())
17014 return;
17016 // we don't need transactions here.
17017 CharacterDatabase.PExecute("DELETE FROM character_queststatus_weekly WHERE guid = '%u'",GetGUIDLow());
17019 for (QuestSet::const_iterator iter = m_weeklyquests.begin(); iter != m_weeklyquests.end(); ++iter)
17021 uint32 quest_id = *iter;
17023 CharacterDatabase.PExecute("INSERT INTO character_queststatus_weekly (guid,quest) VALUES ('%u', '%u')", GetGUIDLow(), quest_id);
17026 m_WeeklyQuestChanged = false;
17029 void Player::_SaveSkills()
17031 // we don't need transactions here.
17032 for( SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); )
17034 if(itr->second.uState == SKILL_UNCHANGED)
17036 ++itr;
17037 continue;
17040 if(itr->second.uState == SKILL_DELETED)
17042 CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u' AND skill = '%u' ", GetGUIDLow(), itr->first );
17043 mSkillStatus.erase(itr++);
17044 continue;
17047 uint32 valueData = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos));
17048 uint16 value = SKILL_VALUE(valueData);
17049 uint16 max = SKILL_MAX(valueData);
17051 switch (itr->second.uState)
17053 case SKILL_NEW:
17054 CharacterDatabase.PExecute("INSERT INTO character_skills (guid, skill, value, max) VALUES ('%u', '%u', '%u', '%u')",
17055 GetGUIDLow(), itr->first, value, max);
17056 break;
17057 case SKILL_CHANGED:
17058 CharacterDatabase.PExecute("UPDATE character_skills SET value = '%u',max = '%u'WHERE guid = '%u' AND skill = '%u' ",
17059 value, max, GetGUIDLow(), itr->first );
17060 break;
17062 itr->second.uState = SKILL_UNCHANGED;
17064 ++itr;
17068 void Player::_SaveSpells()
17070 for (PlayerSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end();)
17072 uint32 talentCosts = GetTalentSpellCost(itr->first);
17074 if (!talentCosts)
17076 if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
17077 CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first);
17079 // add only changed/new not dependent spells
17080 if (!itr->second.dependent && (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED))
17081 CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,active,disabled) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second.active ? 1 : 0,itr->second.disabled ? 1 : 0);
17084 if (itr->second.state == PLAYERSPELL_REMOVED)
17085 m_spells.erase(itr++);
17086 else
17088 itr->second.state = PLAYERSPELL_UNCHANGED;
17089 ++itr;
17095 void Player::_SaveTalents()
17097 for (int32 i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
17099 for (PlayerTalentMap::iterator itr = m_talents[i].begin(); itr != m_talents[i].end();)
17101 if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
17102 CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' and talent_id = '%u' and spec = '%u'", GetGUIDLow(),itr->first, i);
17104 // add only changed/new talents
17105 if (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED)
17106 CharacterDatabase.PExecute("INSERT INTO character_talent (guid, talent_id, current_rank , spec) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second.currentRank, i);
17108 if (itr->second.state == PLAYERSPELL_REMOVED)
17109 m_talents[i].erase(itr++);
17110 else
17112 itr->second.state = PLAYERSPELL_UNCHANGED;
17113 ++itr;
17119 // save player stats -- only for external usage
17120 // real stats will be recalculated on player login
17121 void Player::_SaveStats()
17123 // check if stat saving is enabled and if char level is high enough
17124 if(!sWorld.getConfig(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE) || getLevel() < sWorld.getConfig(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE))
17125 return;
17127 CharacterDatabase.PExecute("DELETE FROM character_stats WHERE guid = '%u'", GetGUIDLow());
17128 std::ostringstream ss;
17129 ss << "INSERT INTO character_stats (guid, maxhealth, maxpower1, maxpower2, maxpower3, maxpower4, maxpower5, maxpower6, maxpower7, "
17130 "strength, agility, stamina, intellect, spirit, armor, resHoly, resFire, resNature, resFrost, resShadow, resArcane, "
17131 "blockPct, dodgePct, parryPct, critPct, rangedCritPct, spellCritPct, attackPower, rangedAttackPower, spellPower) VALUES ("
17132 << GetGUIDLow() << ", "
17133 << GetMaxHealth() << ", ";
17134 for(int i = 0; i < MAX_POWERS; ++i)
17135 ss << GetMaxPower(Powers(i)) << ", ";
17136 for(int i = 0; i < MAX_STATS; ++i)
17137 ss << GetStat(Stats(i)) << ", ";
17138 // armor + school resistances
17139 for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
17140 ss << GetResistance(SpellSchools(i)) << ",";
17141 ss << GetFloatValue(PLAYER_BLOCK_PERCENTAGE) << ", "
17142 << GetFloatValue(PLAYER_DODGE_PERCENTAGE) << ", "
17143 << GetFloatValue(PLAYER_PARRY_PERCENTAGE) << ", "
17144 << GetFloatValue(PLAYER_CRIT_PERCENTAGE) << ", "
17145 << GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE) << ", "
17146 << GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1) << ", "
17147 << GetUInt32Value(UNIT_FIELD_ATTACK_POWER) << ", "
17148 << GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER) << ", "
17149 << GetBaseSpellPowerBonus() << ")";
17150 CharacterDatabase.Execute( ss.str().c_str() );
17153 void Player::outDebugStatsValues() const
17155 // optimize disabled debug output
17156 if(!sLog.HasLogLevelOrHigher(LOG_LVL_DEBUG) || (sLog.getLogFilter() & LOG_FILTER_PLAYER_STATS)!=0)
17157 return;
17159 sLog.outDebug("HP is: \t\t\t%u\t\tMP is: \t\t\t%u",GetMaxHealth(), GetMaxPower(POWER_MANA));
17160 sLog.outDebug("AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f",GetStat(STAT_AGILITY), GetStat(STAT_STRENGTH));
17161 sLog.outDebug("INTELLECT is: \t\t%f\t\tSPIRIT is: \t\t%f",GetStat(STAT_INTELLECT), GetStat(STAT_SPIRIT));
17162 sLog.outDebug("STAMINA is: \t\t%f",GetStat(STAT_STAMINA));
17163 sLog.outDebug("Armor is: \t\t%u\t\tBlock is: \t\t%f",GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE));
17164 sLog.outDebug("HolyRes is: \t\t%u\t\tFireRes is: \t\t%u",GetResistance(SPELL_SCHOOL_HOLY), GetResistance(SPELL_SCHOOL_FIRE));
17165 sLog.outDebug("NatureRes is: \t\t%u\t\tFrostRes is: \t\t%u",GetResistance(SPELL_SCHOOL_NATURE), GetResistance(SPELL_SCHOOL_FROST));
17166 sLog.outDebug("ShadowRes is: \t\t%u\t\tArcaneRes is: \t\t%u",GetResistance(SPELL_SCHOOL_SHADOW), GetResistance(SPELL_SCHOOL_ARCANE));
17167 sLog.outDebug("MIN_DAMAGE is: \t\t%f\tMAX_DAMAGE is: \t\t%f",GetFloatValue(UNIT_FIELD_MINDAMAGE), GetFloatValue(UNIT_FIELD_MAXDAMAGE));
17168 sLog.outDebug("MIN_OFFHAND_DAMAGE is: \t%f\tMAX_OFFHAND_DAMAGE is: \t%f",GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE), GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE));
17169 sLog.outDebug("MIN_RANGED_DAMAGE is: \t%f\tMAX_RANGED_DAMAGE is: \t%f",GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE), GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE));
17170 sLog.outDebug("ATTACK_TIME is: \t%u\t\tRANGE_ATTACK_TIME is: \t%u",GetAttackTime(BASE_ATTACK), GetAttackTime(RANGED_ATTACK));
17173 /*********************************************************/
17174 /*** FLOOD FILTER SYSTEM ***/
17175 /*********************************************************/
17177 void Player::UpdateSpeakTime()
17179 // ignore chat spam protection for GMs in any mode
17180 if(GetSession()->GetSecurity() > SEC_PLAYER)
17181 return;
17183 time_t current = time (NULL);
17184 if(m_speakTime > current)
17186 uint32 max_count = sWorld.getConfig(CONFIG_UINT32_CHATFLOOD_MESSAGE_COUNT);
17187 if(!max_count)
17188 return;
17190 ++m_speakCount;
17191 if(m_speakCount >= max_count)
17193 // prevent overwrite mute time, if message send just before mutes set, for example.
17194 time_t new_mute = current + sWorld.getConfig(CONFIG_UINT32_CHATFLOOD_MUTE_TIME);
17195 if(GetSession()->m_muteTime < new_mute)
17196 GetSession()->m_muteTime = new_mute;
17198 m_speakCount = 0;
17201 else
17202 m_speakCount = 0;
17204 m_speakTime = current + sWorld.getConfig(CONFIG_UINT32_CHATFLOOD_MESSAGE_DELAY);
17207 bool Player::CanSpeak() const
17209 return GetSession()->m_muteTime <= time (NULL);
17212 /*********************************************************/
17213 /*** LOW LEVEL FUNCTIONS:Notifiers ***/
17214 /*********************************************************/
17216 void Player::SendAttackSwingNotInRange()
17218 WorldPacket data(SMSG_ATTACKSWING_NOTINRANGE, 0);
17219 GetSession()->SendPacket( &data );
17222 void Player::SavePositionInDB(uint32 mapid, float x,float y,float z,float o,uint32 zone,uint64 guid)
17224 std::ostringstream ss;
17225 ss << "UPDATE characters SET position_x='"<<x<<"',position_y='"<<y
17226 << "',position_z='"<<z<<"',orientation='"<<o<<"',map='"<<mapid
17227 << "',zone='"<<zone<<"',trans_x='0',trans_y='0',trans_z='0',"
17228 << "transguid='0',taxi_path='' WHERE guid='"<< GUID_LOPART(guid) <<"'";
17229 DEBUG_LOG("%s", ss.str().c_str());
17230 CharacterDatabase.Execute(ss.str().c_str());
17233 void Player::SetUInt32ValueInArray(Tokens& tokens,uint16 index, uint32 value)
17235 char buf[11];
17236 snprintf(buf,11,"%u",value);
17238 if(index >= tokens.size())
17239 return;
17241 tokens[index] = buf;
17244 void Player::Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair)
17246 // 0
17247 QueryResult* result = CharacterDatabase.PQuery("SELECT playerBytes2 FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
17248 if(!result)
17249 return;
17251 Field* fields = result->Fetch();
17253 uint32 player_bytes2 = fields[0].GetUInt32();
17254 player_bytes2 &= ~0xFF;
17255 player_bytes2 |= facialHair;
17257 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_LOPART(guid));
17259 delete result;
17262 void Player::SendAttackSwingDeadTarget()
17264 WorldPacket data(SMSG_ATTACKSWING_DEADTARGET, 0);
17265 GetSession()->SendPacket( &data );
17268 void Player::SendAttackSwingCantAttack()
17270 WorldPacket data(SMSG_ATTACKSWING_CANT_ATTACK, 0);
17271 GetSession()->SendPacket( &data );
17274 void Player::SendAttackSwingCancelAttack()
17276 WorldPacket data(SMSG_CANCEL_COMBAT, 0);
17277 GetSession()->SendPacket( &data );
17280 void Player::SendAttackSwingBadFacingAttack()
17282 WorldPacket data(SMSG_ATTACKSWING_BADFACING, 0);
17283 GetSession()->SendPacket( &data );
17286 void Player::SendAutoRepeatCancel(Unit *target)
17288 WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, target->GetPackGUID().size());
17289 data << target->GetPackGUID(); // may be it's target guid
17290 GetSession()->SendPacket( &data );
17293 void Player::SendExplorationExperience(uint32 Area, uint32 Experience)
17295 WorldPacket data( SMSG_EXPLORATION_EXPERIENCE, 8 );
17296 data << uint32(Area);
17297 data << uint32(Experience);
17298 GetSession()->SendPacket(&data);
17301 void Player::SendDungeonDifficulty(bool IsInGroup)
17303 uint8 val = 0x00000001;
17304 WorldPacket data(MSG_SET_DUNGEON_DIFFICULTY, 12);
17305 data << uint32(GetDungeonDifficulty());
17306 data << uint32(val);
17307 data << uint32(IsInGroup);
17308 GetSession()->SendPacket(&data);
17311 void Player::SendRaidDifficulty(bool IsInGroup)
17313 uint8 val = 0x00000001;
17314 WorldPacket data(MSG_SET_RAID_DIFFICULTY, 12);
17315 data << uint32(GetRaidDifficulty());
17316 data << uint32(val);
17317 data << uint32(IsInGroup);
17318 GetSession()->SendPacket(&data);
17321 void Player::SendResetFailedNotify(uint32 mapid)
17323 WorldPacket data(SMSG_RESET_FAILED_NOTIFY, 4);
17324 data << uint32(mapid);
17325 GetSession()->SendPacket(&data);
17328 /// Reset all solo instances and optionally send a message on success for each
17329 void Player::ResetInstances(uint8 method, bool isRaid)
17331 // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN
17333 // we assume that when the difficulty changes, all instances that can be reset will be
17334 Difficulty diff = GetDifficulty(isRaid);
17336 for (BoundInstancesMap::iterator itr = m_boundInstances[diff].begin(); itr != m_boundInstances[diff].end();)
17338 InstanceSave *p = itr->second.save;
17339 const MapEntry *entry = sMapStore.LookupEntry(itr->first);
17340 if(!entry || entry->IsRaid() != isRaid || !p->CanReset())
17342 ++itr;
17343 continue;
17346 if(method == INSTANCE_RESET_ALL)
17348 // the "reset all instances" method can only reset normal maps
17349 if(entry->map_type == MAP_RAID || diff == DUNGEON_DIFFICULTY_HEROIC)
17351 ++itr;
17352 continue;
17356 // if the map is loaded, reset it
17357 Map *map = sMapMgr.FindMap(p->GetMapId(), p->GetInstanceId());
17358 if(map && map->IsDungeon())
17359 ((InstanceMap*)map)->Reset(method);
17361 // since this is a solo instance there should not be any players inside
17362 if(method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
17363 SendResetInstanceSuccess(p->GetMapId());
17365 p->DeleteFromDB();
17366 m_boundInstances[diff].erase(itr++);
17368 // the following should remove the instance save from the manager and delete it as well
17369 p->RemovePlayer(this);
17373 void Player::SendResetInstanceSuccess(uint32 MapId)
17375 WorldPacket data(SMSG_INSTANCE_RESET, 4);
17376 data << uint32(MapId);
17377 GetSession()->SendPacket(&data);
17380 void Player::SendResetInstanceFailed(uint32 reason, uint32 MapId)
17382 // TODO: find what other fail reasons there are besides players in the instance
17383 WorldPacket data(SMSG_INSTANCE_RESET_FAILED, 4);
17384 data << uint32(reason);
17385 data << uint32(MapId);
17386 GetSession()->SendPacket(&data);
17389 /*********************************************************/
17390 /*** Update timers ***/
17391 /*********************************************************/
17393 ///checks the 15 afk reports per 5 minutes limit
17394 void Player::UpdateAfkReport(time_t currTime)
17396 if(m_bgData.bgAfkReportedTimer <= currTime)
17398 m_bgData.bgAfkReportedCount = 0;
17399 m_bgData.bgAfkReportedTimer = currTime+5*MINUTE;
17403 void Player::UpdateContestedPvP(uint32 diff)
17405 if(!m_contestedPvPTimer||isInCombat())
17406 return;
17407 if(m_contestedPvPTimer <= diff)
17409 ResetContestedPvP();
17411 else
17412 m_contestedPvPTimer -= diff;
17415 void Player::UpdatePvPFlag(time_t currTime)
17417 if(!IsPvP())
17418 return;
17419 if(pvpInfo.endTimer == 0 || currTime < (pvpInfo.endTimer + 300))
17420 return;
17422 UpdatePvP(false);
17425 void Player::UpdateDuelFlag(time_t currTime)
17427 if(!duel || duel->startTimer == 0 ||currTime < duel->startTimer + 3)
17428 return;
17430 SetUInt32Value(PLAYER_DUEL_TEAM, 1);
17431 duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 2);
17433 duel->startTimer = 0;
17434 duel->startTime = currTime;
17435 duel->opponent->duel->startTimer = 0;
17436 duel->opponent->duel->startTime = currTime;
17439 void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
17441 if (!pet)
17442 pet = GetPet();
17444 if (!pet || pet->GetOwnerGUID() != GetGUID())
17445 return;
17447 // not save secondary permanent pet as current
17448 if (pet && m_temporaryUnsummonedPetNumber && m_temporaryUnsummonedPetNumber != pet->GetCharmInfo()->GetPetNumber() && mode == PET_SAVE_AS_CURRENT)
17449 mode = PET_SAVE_NOT_IN_SLOT;
17451 if (returnreagent && pet && mode != PET_SAVE_AS_CURRENT)
17453 //returning of reagents only for players, so best done here
17454 uint32 spellId = pet ? pet->GetUInt32Value(UNIT_CREATED_BY_SPELL) : m_oldpetspell;
17455 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
17457 if(spellInfo)
17459 for(uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i)
17461 if(spellInfo->Reagent[i] > 0)
17463 ItemPosCountVec dest; //for succubus, voidwalker, felhunter and felguard credit soulshard when despawn reason other than death (out of range, logout)
17464 uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->Reagent[i], spellInfo->ReagentCount[i] );
17465 if( msg == EQUIP_ERR_OK )
17467 Item* item = StoreNewItem( dest, spellInfo->Reagent[i], true);
17468 if(IsInWorld())
17469 SendNewItem(item,spellInfo->ReagentCount[i],true,false);
17476 // only if current pet in slot
17477 switch(pet->getPetType())
17479 case MINI_PET:
17480 m_miniPet = 0;
17481 break;
17482 case GUARDIAN_PET:
17483 RemoveGuardian(pet);
17484 break;
17485 default:
17486 if (GetPetGUID() == pet->GetGUID())
17487 SetPet(NULL);
17488 break;
17491 pet->CombatStop();
17493 pet->SavePetToDB(mode);
17495 pet->AddObjectToRemoveList();
17496 pet->m_removed = true;
17498 if (pet->isControlled())
17500 RemovePetActionBar();
17502 if(GetGroup())
17503 SetGroupUpdateFlag(GROUP_UPDATE_PET);
17507 void Player::RemoveMiniPet()
17509 if (Pet* pet = GetMiniPet())
17511 pet->Remove(PET_SAVE_AS_DELETED);
17512 m_miniPet = 0;
17516 Pet* Player::GetMiniPet()
17518 if (!m_miniPet)
17519 return NULL;
17521 return GetMap()->GetPet(m_miniPet);
17524 void Player::BuildPlayerChat(WorldPacket *data, uint8 msgtype, const std::string& text, uint32 language) const
17526 *data << (uint8)msgtype;
17527 *data << (uint32)language;
17528 *data << (uint64)GetGUID();
17529 *data << (uint32)language; //language 2.1.0 ?
17530 *data << (uint64)GetGUID();
17531 *data << (uint32)(text.length()+1);
17532 *data << text;
17533 *data << (uint8)chatTag();
17536 void Player::Say(const std::string& text, const uint32 language)
17538 WorldPacket data(SMSG_MESSAGECHAT, 200);
17539 BuildPlayerChat(&data, CHAT_MSG_SAY, text, language);
17540 SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_SAY),true);
17543 void Player::Yell(const std::string& text, const uint32 language)
17545 WorldPacket data(SMSG_MESSAGECHAT, 200);
17546 BuildPlayerChat(&data, CHAT_MSG_YELL, text, language);
17547 SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_YELL),true);
17550 void Player::TextEmote(const std::string& text)
17552 WorldPacket data(SMSG_MESSAGECHAT, 200);
17553 BuildPlayerChat(&data, CHAT_MSG_EMOTE, text, LANG_UNIVERSAL);
17554 SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_TEXTEMOTE),true, !sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_CHAT) );
17557 void Player::Whisper(const std::string& text, uint32 language,uint64 receiver)
17559 if (language != LANG_ADDON) // if not addon data
17560 language = LANG_UNIVERSAL; // whispers should always be readable
17562 Player *rPlayer = sObjectMgr.GetPlayer(receiver);
17564 // when player you are whispering to is dnd, he cannot receive your message, unless you are in gm mode
17565 if(!rPlayer->isDND() || isGameMaster())
17567 WorldPacket data(SMSG_MESSAGECHAT, 200);
17568 BuildPlayerChat(&data, CHAT_MSG_WHISPER, text, language);
17569 rPlayer->GetSession()->SendPacket(&data);
17571 // not send confirmation for addon messages
17572 if (language != LANG_ADDON)
17574 data.Initialize(SMSG_MESSAGECHAT, 200);
17575 rPlayer->BuildPlayerChat(&data, CHAT_MSG_WHISPER_INFORM, text, language);
17576 GetSession()->SendPacket(&data);
17579 else
17581 // announce to player that player he is whispering to is dnd and cannot receive his message
17582 ChatHandler(this).PSendSysMessage(LANG_PLAYER_DND, rPlayer->GetName(), rPlayer->dndMsg.c_str());
17585 if(!isAcceptWhispers())
17587 SetAcceptWhispers(true);
17588 ChatHandler(this).SendSysMessage(LANG_COMMAND_WHISPERON);
17591 // announce to player that player he is whispering to is afk
17592 if(rPlayer->isAFK())
17593 ChatHandler(this).PSendSysMessage(LANG_PLAYER_AFK, rPlayer->GetName(), rPlayer->afkMsg.c_str());
17595 // if player whisper someone, auto turn of dnd to be able to receive an answer
17596 if(isDND() && !rPlayer->isGameMaster())
17597 ToggleDND();
17600 void Player::PetSpellInitialize()
17602 Pet* pet = GetPet();
17604 if(!pet)
17605 return;
17607 DEBUG_LOG("Pet Spells Groups");
17609 CharmInfo *charmInfo = pet->GetCharmInfo();
17611 WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1);
17612 data << uint64(pet->GetGUID());
17613 data << uint16(pet->GetCreatureInfo()->family); // creature family (required for pet talents)
17614 data << uint32(0);
17615 data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0);
17617 // action bar loop
17618 charmInfo->BuildActionBar(&data);
17620 size_t spellsCountPos = data.wpos();
17622 // spells count
17623 uint8 addlist = 0;
17624 data << uint8(addlist); // placeholder
17626 if (pet->IsPermanentPetFor(this))
17628 // spells loop
17629 for (PetSpellMap::const_iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
17631 if(itr->second.state == PETSPELL_REMOVED)
17632 continue;
17634 data << uint32(MAKE_UNIT_ACTION_BUTTON(itr->first,itr->second.active));
17635 ++addlist;
17639 data.put<uint8>(spellsCountPos, addlist);
17641 uint8 cooldownsCount = pet->m_CreatureSpellCooldowns.size() + pet->m_CreatureCategoryCooldowns.size();
17642 data << uint8(cooldownsCount);
17644 time_t curTime = time(NULL);
17646 for(CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureSpellCooldowns.begin(); itr != pet->m_CreatureSpellCooldowns.end(); ++itr)
17648 time_t cooldown = (itr->second > curTime) ? (itr->second - curTime) * IN_MILLISECONDS : 0;
17650 data << uint32(itr->first); // spellid
17651 data << uint16(0); // spell category?
17652 data << uint32(cooldown); // cooldown
17653 data << uint32(0); // category cooldown
17656 for(CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureCategoryCooldowns.begin(); itr != pet->m_CreatureCategoryCooldowns.end(); ++itr)
17658 time_t cooldown = (itr->second > curTime) ? (itr->second - curTime) * IN_MILLISECONDS : 0;
17660 data << uint32(itr->first); // spellid
17661 data << uint16(0); // spell category?
17662 data << uint32(0); // cooldown
17663 data << uint32(cooldown); // category cooldown
17666 GetSession()->SendPacket(&data);
17669 void Player::SendPetGUIDs()
17671 if(!GetPetGUID())
17672 return;
17674 // Later this function might get modified for multiple guids
17675 WorldPacket data(SMSG_PET_GUIDS, 12);
17676 data << uint32(1); // count
17677 data << uint64(GetPetGUID());
17678 GetSession()->SendPacket(&data);
17681 void Player::PossessSpellInitialize()
17683 Unit* charm = GetCharm();
17685 if(!charm)
17686 return;
17688 CharmInfo *charmInfo = charm->GetCharmInfo();
17690 if(!charmInfo)
17692 sLog.outError("Player::PossessSpellInitialize(): charm (GUID: %u TypeId: %u) has no charminfo!", charm->GetGUIDLow(),charm->GetTypeId());
17693 return;
17696 WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1);
17697 data << uint64(charm->GetGUID());
17698 data << uint16(0);
17699 data << uint32(0);
17700 data << uint32(0);
17702 charmInfo->BuildActionBar(&data);
17704 data << uint8(0); // spells count
17705 data << uint8(0); // cooldowns count
17707 GetSession()->SendPacket(&data);
17710 void Player::CharmSpellInitialize()
17712 Unit* charm = GetCharm();
17714 if(!charm)
17715 return;
17717 CharmInfo *charmInfo = charm->GetCharmInfo();
17718 if(!charmInfo)
17720 sLog.outError("Player::CharmSpellInitialize(): the player's charm (GUID: %u TypeId: %u) has no charminfo!", charm->GetGUIDLow(),charm->GetTypeId());
17721 return;
17724 uint8 addlist = 0;
17726 if(charm->GetTypeId() != TYPEID_PLAYER)
17728 CreatureInfo const *cinfo = ((Creature*)charm)->GetCreatureInfo();
17730 if(cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK)
17732 for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
17734 if(charmInfo->GetCharmSpell(i)->GetAction())
17735 ++addlist;
17740 WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+4*addlist+1);
17741 data << uint64(charm->GetGUID());
17742 data << uint16(0);
17743 data << uint32(0);
17745 if(charm->GetTypeId() != TYPEID_PLAYER)
17746 data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0);
17747 else
17748 data << uint8(0) << uint8(0) << uint16(0);
17750 charmInfo->BuildActionBar(&data);
17752 data << uint8(addlist);
17754 if(addlist)
17756 for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
17758 CharmSpellEntry *cspell = charmInfo->GetCharmSpell(i);
17759 if(cspell->GetAction())
17760 data << uint32(cspell->packedData);
17764 data << uint8(0); // cooldowns count
17766 GetSession()->SendPacket(&data);
17769 void Player::RemovePetActionBar()
17771 WorldPacket data(SMSG_PET_SPELLS, 8);
17772 data << uint64(0);
17773 SendDirectMessage(&data);
17776 bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell)
17778 if (!mod || !spellInfo)
17779 return false;
17781 if(mod->charges == -1 && mod->lastAffected ) // marked as expired but locked until spell casting finish
17783 // prevent apply to any spell except spell that trigger expire
17784 if(spell)
17786 if(mod->lastAffected != spell)
17787 return false;
17789 else if(mod->lastAffected != FindCurrentSpellBySpellId(spellInfo->Id))
17790 return false;
17793 return mod->isAffectedOnSpell(spellInfo);
17796 void Player::AddSpellMod(SpellModifier* mod, bool apply)
17798 uint16 Opcode= (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER;
17800 for(int eff = 0; eff < 96; ++eff)
17802 uint64 _mask = 0;
17803 uint32 _mask2= 0;
17805 if (eff < 64)
17806 _mask = uint64(1) << (eff - 0);
17807 else
17808 _mask2= uint32(1) << (eff - 64);
17810 if ( mod->mask & _mask || mod->mask2 & _mask2)
17812 int32 val = 0;
17813 for (SpellModList::const_iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr)
17815 if ((*itr)->type == mod->type && ((*itr)->mask & _mask || (*itr)->mask2 & _mask2))
17816 val += (*itr)->value;
17818 val += apply ? mod->value : -(mod->value);
17819 WorldPacket data(Opcode, (1+1+4));
17820 data << uint8(eff);
17821 data << uint8(mod->op);
17822 data << int32(val);
17823 SendDirectMessage(&data);
17827 if (apply)
17828 m_spellMods[mod->op].push_back(mod);
17829 else
17831 if (mod->charges == -1)
17832 --m_SpellModRemoveCount;
17833 m_spellMods[mod->op].remove(mod);
17834 delete mod;
17838 void Player::RemoveSpellMods(Spell const* spell)
17840 if(!spell || (m_SpellModRemoveCount == 0))
17841 return;
17843 for(int i=0;i<MAX_SPELLMOD;++i)
17845 for (SpellModList::const_iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();)
17847 SpellModifier *mod = *itr;
17848 ++itr;
17850 if (mod && mod->charges == -1 && (mod->lastAffected == spell || mod->lastAffected==NULL))
17852 RemoveAurasDueToSpell(mod->spellId);
17853 if (m_spellMods[i].empty())
17854 break;
17855 else
17856 itr = m_spellMods[i].begin();
17862 // send Proficiency
17863 void Player::SendProficiency(uint8 pr1, uint32 pr2)
17865 WorldPacket data(SMSG_SET_PROFICIENCY, 8);
17866 data << uint8(pr1) << uint32(pr2);
17867 GetSession()->SendPacket (&data);
17870 void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type)
17872 QueryResult *result = NULL;
17873 if(type == 10)
17874 result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid));
17875 else
17876 result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
17877 if(result)
17879 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.
17880 { // and SendPetitionQueryOpcode reads data from the DB
17881 Field *fields = result->Fetch();
17882 uint64 ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
17883 uint64 petitionguid = MAKE_NEW_GUID(fields[1].GetUInt32(), 0, HIGHGUID_ITEM);
17885 // send update if charter owner in game
17886 Player* owner = sObjectMgr.GetPlayer(ownerguid);
17887 if(owner)
17888 owner->GetSession()->SendPetitionQueryOpcode(petitionguid);
17890 } while ( result->NextRow() );
17892 delete result;
17894 if(type==10)
17895 CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid));
17896 else
17897 CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
17900 CharacterDatabase.BeginTransaction();
17901 if(type == 10)
17903 CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u'", GUID_LOPART(guid));
17904 CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u'", GUID_LOPART(guid));
17906 else
17908 CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
17909 CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
17911 CharacterDatabase.CommitTransaction();
17914 void Player::LeaveAllArenaTeams(uint64 guid)
17916 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'", GUID_LOPART(guid));
17917 if(!result)
17918 return;
17922 Field *fields = result->Fetch();
17923 uint32 at_id = fields[0].GetUInt32();
17924 if(at_id != 0)
17926 ArenaTeam * at = sObjectMgr.GetArenaTeamById(at_id);
17927 if(at)
17928 at->DelMember(guid);
17930 } while (result->NextRow());
17932 delete result;
17935 void Player::SetRestBonus (float rest_bonus_new)
17937 // Prevent resting on max level
17938 if(getLevel() >= sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
17939 rest_bonus_new = 0;
17941 if(rest_bonus_new < 0)
17942 rest_bonus_new = 0;
17944 float rest_bonus_max = (float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)*1.5f/2.0f;
17946 if(rest_bonus_new > rest_bonus_max)
17947 m_rest_bonus = rest_bonus_max;
17948 else
17949 m_rest_bonus = rest_bonus_new;
17951 // update data for client
17952 if(m_rest_bonus>10)
17953 SetByteValue(PLAYER_BYTES_2, 3, 0x01); // Set Reststate = Rested
17954 else if(m_rest_bonus<=1)
17955 SetByteValue(PLAYER_BYTES_2, 3, 0x02); // Set Reststate = Normal
17957 //RestTickUpdate
17958 SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus));
17961 void Player::HandleStealthedUnitsDetection()
17963 std::list<Unit*> stealthedUnits;
17965 MaNGOS::AnyStealthedCheck u_check;
17966 MaNGOS::UnitListSearcher<MaNGOS::AnyStealthedCheck > searcher(this,stealthedUnits, u_check);
17967 Cell::VisitAllObjects(this, searcher, MAX_PLAYER_STEALTH_DETECT_RANGE);
17969 WorldObject const* viewPoint = GetViewPoint();
17971 for (std::list<Unit*>::const_iterator i = stealthedUnits.begin(); i != stealthedUnits.end(); ++i)
17973 if((*i)==this)
17974 continue;
17976 bool hasAtClient = HaveAtClient((*i));
17977 bool hasDetected = (*i)->isVisibleForOrDetect(this, viewPoint, true);
17979 if (hasDetected)
17981 if(!hasAtClient)
17983 ObjectGuid i_guid = (*i)->GetGUID();
17984 (*i)->SendCreateUpdateToPlayer(this);
17985 m_clientGUIDs.insert(i_guid);
17987 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));
17989 // target aura duration for caster show only if target exist at caster client
17990 // send data at target visibility change (adding to client)
17991 if((*i)!=this && (*i)->isType(TYPEMASK_UNIT))
17992 SendAurasForTarget(*i);
17995 else
17997 if(hasAtClient)
17999 (*i)->DestroyForPlayer(this);
18000 m_clientGUIDs.erase((*i)->GetGUID());
18006 bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc /*= NULL*/, uint32 spellid /*= 0*/)
18008 if(nodes.size() < 2)
18009 return false;
18011 // 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
18012 if(GetSession()->isLogingOut() || isInCombat())
18014 WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
18015 data << uint32(ERR_TAXIPLAYERBUSY);
18016 GetSession()->SendPacket(&data);
18017 return false;
18020 if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
18021 return false;
18023 // taximaster case
18024 if(npc)
18026 // not let cheating with start flight mounted
18027 if(IsMounted())
18029 WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
18030 data << uint32(ERR_TAXIPLAYERALREADYMOUNTED);
18031 GetSession()->SendPacket(&data);
18032 return false;
18035 if( m_ShapeShiftFormSpellId && m_form != FORM_BATTLESTANCE && m_form != FORM_BERSERKERSTANCE && m_form != FORM_DEFENSIVESTANCE && m_form != FORM_SHADOW )
18037 WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
18038 data << uint32(ERR_TAXIPLAYERSHAPESHIFTED);
18039 GetSession()->SendPacket(&data);
18040 return false;
18043 // 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
18044 if(IsNonMeleeSpellCasted(false))
18046 WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
18047 data << uint32(ERR_TAXIPLAYERBUSY);
18048 GetSession()->SendPacket(&data);
18049 return false;
18052 // cast case or scripted call case
18053 else
18055 RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
18057 if( m_ShapeShiftFormSpellId && m_form != FORM_BATTLESTANCE && m_form != FORM_BERSERKERSTANCE && m_form != FORM_DEFENSIVESTANCE && m_form != FORM_SHADOW )
18058 RemoveAurasDueToSpell(m_ShapeShiftFormSpellId);
18060 if (Spell* spell = GetCurrentSpell(CURRENT_GENERIC_SPELL))
18061 if (spell->m_spellInfo->Id != spellid)
18062 InterruptSpell(CURRENT_GENERIC_SPELL,false);
18064 InterruptSpell(CURRENT_AUTOREPEAT_SPELL,false);
18066 if (Spell* spell = GetCurrentSpell(CURRENT_CHANNELED_SPELL))
18067 if (spell->m_spellInfo->Id != spellid)
18068 InterruptSpell(CURRENT_CHANNELED_SPELL,true);
18071 uint32 sourcenode = nodes[0];
18073 // starting node too far away (cheat?)
18074 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(sourcenode);
18075 if (!node)
18077 WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
18078 data << uint32(ERR_TAXINOSUCHPATH);
18079 GetSession()->SendPacket(&data);
18080 return false;
18083 // check node starting pos data set case if provided
18084 if (node->x != 0.0f || node->y != 0.0f || node->z != 0.0f)
18086 if (node->map_id != GetMapId() ||
18087 (node->x - GetPositionX())*(node->x - GetPositionX())+
18088 (node->y - GetPositionY())*(node->y - GetPositionY())+
18089 (node->z - GetPositionZ())*(node->z - GetPositionZ()) >
18090 (2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE))
18092 WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
18093 data << uint32(ERR_TAXITOOFARAWAY);
18094 GetSession()->SendPacket(&data);
18095 return false;
18098 // node must have pos if taxi master case (npc != NULL)
18099 else if (npc)
18101 WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
18102 data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR);
18103 GetSession()->SendPacket(&data);
18104 return false;
18107 // Prepare to flight start now
18109 // stop combat at start taxi flight if any
18110 CombatStop();
18112 // stop trade (client cancel trade at taxi map open but cheating tools can be used for reopen it)
18113 TradeCancel(true);
18115 // clean not finished taxi path if any
18116 m_taxi.ClearTaxiDestinations();
18118 // 0 element current node
18119 m_taxi.AddTaxiDestination(sourcenode);
18121 // fill destinations path tail
18122 uint32 sourcepath = 0;
18123 uint32 totalcost = 0;
18125 uint32 prevnode = sourcenode;
18126 uint32 lastnode = 0;
18128 for(uint32 i = 1; i < nodes.size(); ++i)
18130 uint32 path, cost;
18132 lastnode = nodes[i];
18133 sObjectMgr.GetTaxiPath(prevnode, lastnode, path, cost);
18135 if(!path)
18137 m_taxi.ClearTaxiDestinations();
18138 return false;
18141 totalcost += cost;
18143 if(prevnode == sourcenode)
18144 sourcepath = path;
18146 m_taxi.AddTaxiDestination(lastnode);
18148 prevnode = lastnode;
18151 // get mount model (in case non taximaster (npc==NULL) allow more wide lookup)
18152 uint32 mount_display_id = sObjectMgr.GetTaxiMountDisplayId(sourcenode, GetTeam(), npc == NULL);
18154 // in spell case allow 0 model
18155 if ((mount_display_id == 0 && spellid == 0) || sourcepath == 0)
18157 WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
18158 data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR);
18159 GetSession()->SendPacket(&data);
18160 m_taxi.ClearTaxiDestinations();
18161 return false;
18164 uint32 money = GetMoney();
18166 if (npc)
18167 totalcost = (uint32)ceil(totalcost*GetReputationPriceDiscount(npc));
18169 if(money < totalcost)
18171 WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
18172 data << uint32(ERR_TAXINOTENOUGHMONEY);
18173 GetSession()->SendPacket(&data);
18174 m_taxi.ClearTaxiDestinations();
18175 return false;
18178 //Checks and preparations done, DO FLIGHT
18179 ModifyMoney(-(int32)totalcost);
18180 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost);
18181 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN, 1);
18183 // prevent stealth flight
18184 RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
18186 WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
18187 data << uint32(ERR_TAXIOK);
18188 GetSession()->SendPacket(&data);
18190 DEBUG_LOG("WORLD: Sent SMSG_ACTIVATETAXIREPLY");
18192 GetSession()->SendDoFlight(mount_display_id, sourcepath);
18194 return true;
18197 bool Player::ActivateTaxiPathTo( uint32 taxi_path_id, uint32 spellid /*= 0*/ )
18199 TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(taxi_path_id);
18200 if(!entry)
18201 return false;
18203 std::vector<uint32> nodes;
18205 nodes.resize(2);
18206 nodes[0] = entry->from;
18207 nodes[1] = entry->to;
18209 return ActivateTaxiPathTo(nodes,NULL,spellid);
18212 void Player::ContinueTaxiFlight()
18214 uint32 sourceNode = m_taxi.GetTaxiSource();
18215 if (!sourceNode)
18216 return;
18218 DEBUG_LOG( "WORLD: Restart character %u taxi flight", GetGUIDLow() );
18220 uint32 mountDisplayId = sObjectMgr.GetTaxiMountDisplayId(sourceNode, GetTeam(),true);
18221 uint32 path = m_taxi.GetCurrentTaxiPath();
18223 // search appropriate start path node
18224 uint32 startNode = 0;
18226 TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path];
18228 float distPrev = MAP_SIZE*MAP_SIZE;
18229 float distNext =
18230 (nodeList[0].x-GetPositionX())*(nodeList[0].x-GetPositionX())+
18231 (nodeList[0].y-GetPositionY())*(nodeList[0].y-GetPositionY())+
18232 (nodeList[0].z-GetPositionZ())*(nodeList[0].z-GetPositionZ());
18234 for(uint32 i = 1; i < nodeList.size(); ++i)
18236 TaxiPathNodeEntry const& node = nodeList[i];
18237 TaxiPathNodeEntry const& prevNode = nodeList[i-1];
18239 // skip nodes at another map
18240 if (node.mapid != GetMapId())
18241 continue;
18243 distPrev = distNext;
18245 distNext =
18246 (node.x-GetPositionX())*(node.x-GetPositionX())+
18247 (node.y-GetPositionY())*(node.y-GetPositionY())+
18248 (node.z-GetPositionZ())*(node.z-GetPositionZ());
18250 float distNodes =
18251 (node.x-prevNode.x)*(node.x-prevNode.x)+
18252 (node.y-prevNode.y)*(node.y-prevNode.y)+
18253 (node.z-prevNode.z)*(node.z-prevNode.z);
18255 if (distNext + distPrev < distNodes)
18257 startNode = i;
18258 break;
18262 GetSession()->SendDoFlight(mountDisplayId, path, startNode);
18265 void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs )
18267 // last check 2.0.10
18268 WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+m_spells.size()*8);
18269 data << uint64(GetGUID());
18270 data << uint8(0x0); // flags (0x1, 0x2)
18271 time_t curTime = time(NULL);
18272 for(PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
18274 if (itr->second.state == PLAYERSPELL_REMOVED)
18275 continue;
18276 uint32 unSpellId = itr->first;
18277 SpellEntry const *spellInfo = sSpellStore.LookupEntry(unSpellId);
18278 if (!spellInfo)
18280 ASSERT(spellInfo);
18281 continue;
18284 // Not send cooldown for this spells
18285 if (spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
18286 continue;
18288 if((idSchoolMask & GetSpellSchoolMask(spellInfo)) && GetSpellCooldownDelay(unSpellId) < unTimeMs )
18290 data << uint32(unSpellId);
18291 data << uint32(unTimeMs); // in m.secs
18292 AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/IN_MILLISECONDS);
18295 GetSession()->SendPacket(&data);
18298 void Player::InitDataForForm(bool reapplyMods)
18300 SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(m_form);
18301 if(ssEntry && ssEntry->attackSpeed)
18303 SetAttackTime(BASE_ATTACK,ssEntry->attackSpeed);
18304 SetAttackTime(OFF_ATTACK,ssEntry->attackSpeed);
18305 SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
18307 else
18308 SetRegularAttackTime();
18310 switch(m_form)
18312 case FORM_CAT:
18314 if(getPowerType()!=POWER_ENERGY)
18315 setPowerType(POWER_ENERGY);
18316 break;
18318 case FORM_BEAR:
18319 case FORM_DIREBEAR:
18321 if(getPowerType()!=POWER_RAGE)
18322 setPowerType(POWER_RAGE);
18323 break;
18325 default: // 0, for example
18327 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(getClass());
18328 if(cEntry && cEntry->powerType < MAX_POWERS && uint32(getPowerType()) != cEntry->powerType)
18329 setPowerType(Powers(cEntry->powerType));
18330 break;
18334 // update auras at form change, ignore this at mods reapply (.reset stats/etc) when form not change.
18335 if (!reapplyMods)
18336 UpdateEquipSpellsAtFormChange();
18338 UpdateAttackPowerAndDamage();
18339 UpdateAttackPowerAndDamage(true);
18342 void Player::InitDisplayIds()
18344 PlayerInfo const *info = sObjectMgr.GetPlayerInfo(getRace(), getClass());
18345 if(!info)
18347 sLog.outError("Player %u has incorrect race/class pair. Can't init display ids.", GetGUIDLow());
18348 return;
18351 uint8 gender = getGender();
18352 switch(gender)
18354 case GENDER_FEMALE:
18355 SetDisplayId(info->displayId_f );
18356 SetNativeDisplayId(info->displayId_f );
18357 break;
18358 case GENDER_MALE:
18359 SetDisplayId(info->displayId_m );
18360 SetNativeDisplayId(info->displayId_m );
18361 break;
18362 default:
18363 sLog.outError("Invalid gender %u for player",gender);
18364 return;
18368 // Return true is the bought item has a max count to force refresh of window by caller
18369 bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot)
18371 // cheating attempt
18372 if (count < 1) count = 1;
18374 if (!isAlive())
18375 return false;
18377 ItemPrototype const *pProto = ObjectMgr::GetItemPrototype( item );
18378 if (!pProto)
18380 SendBuyError( BUY_ERR_CANT_FIND_ITEM, NULL, item, 0);
18381 return false;
18384 Creature *pCreature = GetNPCIfCanInteractWith(vendorguid,UNIT_NPC_FLAG_VENDOR);
18385 if (!pCreature)
18387 DEBUG_LOG( "WORLD: BuyItemFromVendor - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
18388 SendBuyError( BUY_ERR_DISTANCE_TOO_FAR, NULL, item, 0);
18389 return false;
18392 VendorItemData const* vItems = pCreature->GetVendorItems();
18393 if(!vItems || vItems->Empty())
18395 SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
18396 return false;
18399 if (vendorslot >= vItems->GetItemCount())
18401 SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
18402 return false;
18405 VendorItem const* crItem = vItems->GetItem(vendorslot);
18406 if(!crItem || crItem->item != item) // store diff item (cheating)
18408 SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
18409 return false;
18413 // check current item amount if it limited
18414 if (crItem->maxcount != 0)
18416 if (pCreature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count )
18418 SendBuyError( BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0);
18419 return false;
18423 if (uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank)
18425 SendBuyError( BUY_ERR_REPUTATION_REQUIRE, pCreature, item, 0);
18426 return false;
18429 if (uint32 extendedCostId = crItem->GetExtendedCostId())
18431 ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(extendedCostId);
18432 if (!iece)
18434 sLog.outError("Item %u have wrong ExtendedCost field value %u", pProto->ItemId, extendedCostId);
18435 return false;
18438 // honor points price
18439 if (GetHonorPoints() < (iece->reqhonorpoints * count))
18441 SendEquipError(EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS, NULL, NULL);
18442 return false;
18445 // arena points price
18446 if (GetArenaPoints() < (iece->reqarenapoints * count))
18448 SendEquipError(EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS, NULL, NULL);
18449 return false;
18452 // item base price
18453 for (uint8 i = 0; i < 5; ++i)
18455 if(iece->reqitem[i] && !HasItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count)))
18457 SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
18458 return false;
18462 // check for personal arena rating requirement
18463 if( GetMaxPersonalArenaRatingRequirement(iece->reqarenaslot) < iece->reqpersonalarenarating )
18465 // probably not the proper equip err
18466 SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK,NULL,NULL);
18467 return false;
18471 uint32 price = crItem->IsExcludeMoneyPrice() ? 0 : pProto->BuyPrice * count;
18473 // reputation discount
18474 if (price)
18475 price = uint32(floor(price * GetReputationPriceDiscount(pCreature)));
18477 if (GetMoney() < price)
18479 SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, item, 0);
18480 return false;
18483 if ((bag == NULL_BAG && slot == NULL_SLOT) || IsInventoryPos(bag, slot))
18485 ItemPosCountVec dest;
18486 uint8 msg = CanStoreNewItem( bag, slot, dest, item, pProto->BuyCount * count );
18487 if (msg != EQUIP_ERR_OK)
18489 SendEquipError( msg, NULL, NULL, item );
18490 return false;
18493 ModifyMoney( -(int32)price );
18494 if (uint32 extendedCostId = crItem->GetExtendedCostId())
18496 ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(extendedCostId);
18497 if (iece->reqhonorpoints)
18498 ModifyHonorPoints( - int32(iece->reqhonorpoints * count));
18499 if (iece->reqarenapoints)
18500 ModifyArenaPoints( - int32(iece->reqarenapoints * count));
18501 for (uint8 i = 0; i < 5; ++i)
18503 if (iece->reqitem[i])
18504 DestroyItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count), true);
18508 if (Item *it = StoreNewItem( dest, item, true ))
18510 uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count);
18512 WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
18513 data << uint64(pCreature->GetGUID());
18514 data << uint32(vendorslot+1); // numbered from 1 at client
18515 data << uint32(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
18516 data << uint32(count);
18517 GetSession()->SendPacket(&data);
18519 SendNewItem(it, pProto->BuyCount*count, true, false, false);
18522 else if (IsEquipmentPos(bag, slot))
18524 if (pProto->BuyCount * count != 1)
18526 SendEquipError( EQUIP_ERR_ITEM_CANT_BE_EQUIPPED, NULL, NULL );
18527 return false;
18530 uint16 dest;
18531 uint8 msg = CanEquipNewItem( slot, dest, item, false );
18532 if (msg != EQUIP_ERR_OK)
18534 SendEquipError( msg, NULL, NULL, item );
18535 return false;
18538 ModifyMoney( -(int32)price );
18539 if (uint32 extendedCostId = crItem->GetExtendedCostId())
18541 ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(extendedCostId);
18542 if (iece->reqhonorpoints)
18543 ModifyHonorPoints( - int32(iece->reqhonorpoints));
18544 if (iece->reqarenapoints)
18545 ModifyArenaPoints( - int32(iece->reqarenapoints));
18546 for (uint8 i = 0; i < 5; ++i)
18548 if(iece->reqitem[i])
18549 DestroyItemCount(iece->reqitem[i], iece->reqitemcount[i], true);
18553 if (Item *it = EquipNewItem( dest, item, true ))
18555 uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count);
18557 WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
18558 data << uint64(pCreature->GetGUID());
18559 data << uint32(vendorslot + 1); // numbered from 1 at client
18560 data << uint32(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
18561 data << uint32(count);
18562 GetSession()->SendPacket(&data);
18564 SendNewItem(it, pProto->BuyCount*count, true, false, false);
18566 AutoUnequipOffhandIfNeed();
18569 else
18571 SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
18572 return false;
18575 return crItem->maxcount != 0;
18578 uint32 Player::GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot)
18580 // returns the maximal personal arena rating that can be used to purchase items requiring this condition
18581 // the personal rating of the arena team must match the required limit as well
18582 // so return max[in arenateams](min(personalrating[teamtype], teamrating[teamtype]))
18583 uint32 max_personal_rating = 0;
18584 for(int i = minarenaslot; i < MAX_ARENA_SLOT; ++i)
18586 if(ArenaTeam * at = sObjectMgr.GetArenaTeamById(GetArenaTeamId(i)))
18588 uint32 p_rating = GetArenaPersonalRating(i);
18589 uint32 t_rating = at->GetRating();
18590 p_rating = p_rating < t_rating ? p_rating : t_rating;
18591 if(max_personal_rating < p_rating)
18592 max_personal_rating = p_rating;
18595 return max_personal_rating;
18598 void Player::UpdateHomebindTime(uint32 time)
18600 // GMs never get homebind timer online
18601 if (m_InstanceValid || isGameMaster())
18603 if(m_HomebindTimer) // instance valid, but timer not reset
18605 // hide reminder
18606 WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
18607 data << uint32(0);
18608 data << uint32(ERR_RAID_GROUP_NONE); // error used only when timer = 0
18609 GetSession()->SendPacket(&data);
18611 // instance is valid, reset homebind timer
18612 m_HomebindTimer = 0;
18614 else if (m_HomebindTimer > 0)
18616 if (time >= m_HomebindTimer)
18618 // teleport to nearest graveyard
18619 RepopAtGraveyard();
18621 else
18622 m_HomebindTimer -= time;
18624 else
18626 // instance is invalid, start homebind timer
18627 m_HomebindTimer = 60000;
18628 // send message to player
18629 WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
18630 data << uint32(m_HomebindTimer);
18631 data << uint32(ERR_RAID_GROUP_NONE); // error used only when timer = 0
18632 GetSession()->SendPacket(&data);
18633 DEBUG_LOG("PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName(),GetGUIDLow());
18637 void Player::UpdatePvP(bool state, bool ovrride)
18639 if(!state || ovrride)
18641 SetPvP(state);
18642 pvpInfo.endTimer = 0;
18644 else
18646 if(pvpInfo.endTimer != 0)
18647 pvpInfo.endTimer = time(NULL);
18648 else
18649 SetPvP(state);
18653 void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown)
18655 // init cooldown values
18656 uint32 cat = 0;
18657 int32 rec = -1;
18658 int32 catrec = -1;
18660 // some special item spells without correct cooldown in SpellInfo
18661 // cooldown information stored in item prototype
18662 // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client.
18664 if(itemId)
18666 if(ItemPrototype const* proto = ObjectMgr::GetItemPrototype(itemId))
18668 for(int idx = 0; idx < 5; ++idx)
18670 if(proto->Spells[idx].SpellId == spellInfo->Id)
18672 cat = proto->Spells[idx].SpellCategory;
18673 rec = proto->Spells[idx].SpellCooldown;
18674 catrec = proto->Spells[idx].SpellCategoryCooldown;
18675 break;
18681 // if no cooldown found above then base at DBC data
18682 if(rec < 0 && catrec < 0)
18684 cat = spellInfo->Category;
18685 rec = spellInfo->RecoveryTime;
18686 catrec = spellInfo->CategoryRecoveryTime;
18689 time_t curTime = time(NULL);
18691 time_t catrecTime;
18692 time_t recTime;
18694 // overwrite time for selected category
18695 if(infinityCooldown)
18697 // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped)
18698 // but not allow ignore until reset or re-login
18699 catrecTime = catrec > 0 ? curTime+infinityCooldownDelay : 0;
18700 recTime = rec > 0 ? curTime+infinityCooldownDelay : catrecTime;
18702 else
18704 // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
18705 // prevent 0 cooldowns set by another way
18706 if (rec <= 0 && catrec <= 0 && (cat == 76 || IsAutoRepeatRangedSpell(spellInfo) && spellInfo->Id != SPELL_ID_AUTOSHOT))
18707 rec = GetAttackTime(RANGED_ATTACK);
18709 // Now we have cooldown data (if found any), time to apply mods
18710 if(rec > 0)
18711 ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell);
18713 if(catrec > 0)
18714 ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell);
18716 // replace negative cooldowns by 0
18717 if (rec < 0) rec = 0;
18718 if (catrec < 0) catrec = 0;
18720 // no cooldown after applying spell mods
18721 if( rec == 0 && catrec == 0)
18722 return;
18724 catrecTime = catrec ? curTime+catrec/IN_MILLISECONDS : 0;
18725 recTime = rec ? curTime+rec/IN_MILLISECONDS : catrecTime;
18728 // self spell cooldown
18729 if(recTime > 0)
18730 AddSpellCooldown(spellInfo->Id, itemId, recTime);
18732 // category spells
18733 if (cat && catrec > 0)
18735 SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat);
18736 if(i_scstore != sSpellCategoryStore.end())
18738 for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
18740 if(*i_scset == spellInfo->Id) // skip main spell, already handled above
18741 continue;
18743 AddSpellCooldown(*i_scset, itemId, catrecTime);
18749 void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
18751 SpellCooldown sc;
18752 sc.end = end_time;
18753 sc.itemid = itemid;
18754 m_spellCooldowns[spellid] = sc;
18757 void Player::SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId, Spell* spell)
18759 // start cooldowns at server side, if any
18760 AddSpellAndCategoryCooldowns(spellInfo, itemId, spell);
18762 // Send activate cooldown timer (possible 0) at client side
18763 WorldPacket data(SMSG_COOLDOWN_EVENT, (4+8));
18764 data << uint32(spellInfo->Id);
18765 data << uint64(GetGUID());
18766 SendDirectMessage(&data);
18769 void Player::UpdatePotionCooldown(Spell* spell)
18771 // no potion used in combat or still in combat
18772 if(!m_lastPotionId || isInCombat())
18773 return;
18775 // Call not from spell cast, send cooldown event for item spells if no in combat
18776 if(!spell)
18778 // spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions)
18779 if(ItemPrototype const* proto = ObjectMgr::GetItemPrototype(m_lastPotionId))
18780 for(int idx = 0; idx < 5; ++idx)
18781 if(proto->Spells[idx].SpellId && proto->Spells[idx].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
18782 if(SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[idx].SpellId))
18783 SendCooldownEvent(spellInfo,m_lastPotionId);
18785 // from spell cases (m_lastPotionId set in Spell::SendSpellCooldown)
18786 else
18787 SendCooldownEvent(spell->m_spellInfo,m_lastPotionId,spell);
18789 m_lastPotionId = 0;
18792 //slot to be excluded while counting
18793 bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot)
18795 if(!enchantmentcondition)
18796 return true;
18798 SpellItemEnchantmentConditionEntry const *Condition = sSpellItemEnchantmentConditionStore.LookupEntry(enchantmentcondition);
18800 if(!Condition)
18801 return true;
18803 uint8 curcount[4] = {0, 0, 0, 0};
18805 //counting current equipped gem colors
18806 for(uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
18808 if(i == slot)
18809 continue;
18810 Item *pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
18811 if(pItem2 && !pItem2->IsBroken() && pItem2->GetProto()->Socket[0].Color)
18813 for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
18815 uint32 enchant_id = pItem2->GetEnchantmentId(EnchantmentSlot(enchant_slot));
18816 if(!enchant_id)
18817 continue;
18819 SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
18820 if(!enchantEntry)
18821 continue;
18823 uint32 gemid = enchantEntry->GemID;
18824 if(!gemid)
18825 continue;
18827 ItemPrototype const* gemProto = sItemStorage.LookupEntry<ItemPrototype>(gemid);
18828 if(!gemProto)
18829 continue;
18831 GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties);
18832 if(!gemProperty)
18833 continue;
18835 uint8 GemColor = gemProperty->color;
18837 for(uint8 b = 0, tmpcolormask = 1; b < 4; b++, tmpcolormask <<= 1)
18839 if(tmpcolormask & GemColor)
18840 ++curcount[b];
18846 bool activate = true;
18848 for(int i = 0; i < 5; ++i)
18850 if(!Condition->Color[i])
18851 continue;
18853 uint32 _cur_gem = curcount[Condition->Color[i] - 1];
18855 // if have <CompareColor> use them as count, else use <value> from Condition
18856 uint32 _cmp_gem = Condition->CompareColor[i] ? curcount[Condition->CompareColor[i] - 1]: Condition->Value[i];
18858 switch(Condition->Comparator[i])
18860 case 2: // requires less <color> than (<value> || <comparecolor>) gems
18861 activate &= (_cur_gem < _cmp_gem) ? true : false;
18862 break;
18863 case 3: // requires more <color> than (<value> || <comparecolor>) gems
18864 activate &= (_cur_gem > _cmp_gem) ? true : false;
18865 break;
18866 case 5: // requires at least <color> than (<value> || <comparecolor>) gems
18867 activate &= (_cur_gem >= _cmp_gem) ? true : false;
18868 break;
18872 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");
18874 return activate;
18877 void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply)
18879 //cycle all equipped items
18880 for(uint32 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
18882 //enchants for the slot being socketed are handled by Player::ApplyItemMods
18883 if(slot == exceptslot)
18884 continue;
18886 Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot );
18888 if(!pItem || !pItem->GetProto()->Socket[0].Color)
18889 continue;
18891 for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
18893 uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
18894 if(!enchant_id)
18895 continue;
18897 SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
18898 if(!enchantEntry)
18899 continue;
18901 uint32 condition = enchantEntry->EnchantmentCondition;
18902 if(condition)
18904 //was enchant active with/without item?
18905 bool wasactive = EnchantmentFitsRequirements(condition, apply ? exceptslot : -1);
18906 //should it now be?
18907 if(wasactive != EnchantmentFitsRequirements(condition, apply ? -1 : exceptslot))
18909 // ignore item gem conditions
18910 //if state changed, (dis)apply enchant
18911 ApplyEnchantment(pItem, EnchantmentSlot(enchant_slot), !wasactive, true, true);
18918 //if false -> then toggled off if was on| if true -> toggled on if was off AND meets requirements
18919 void Player::ToggleMetaGemsActive(uint8 exceptslot, bool apply)
18921 //cycle all equipped items
18922 for(int slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
18924 //enchants for the slot being socketed are handled by WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
18925 if(slot == exceptslot)
18926 continue;
18928 Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot );
18930 if(!pItem || !pItem->GetProto()->Socket[0].Color) //if item has no sockets or no item is equipped go to next item
18931 continue;
18933 //cycle all (gem)enchants
18934 for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
18936 uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
18937 if(!enchant_id) //if no enchant go to next enchant(slot)
18938 continue;
18940 SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
18941 if(!enchantEntry)
18942 continue;
18944 //only metagems to be (de)activated, so only enchants with condition
18945 uint32 condition = enchantEntry->EnchantmentCondition;
18946 if(condition)
18947 ApplyEnchantment(pItem,EnchantmentSlot(enchant_slot), apply);
18952 void Player::SetBattleGroundEntryPoint()
18954 // Taxi path store
18955 if (!m_taxi.empty())
18957 m_bgData.mountSpell = 0;
18958 m_bgData.taxiPath[0] = m_taxi.GetTaxiSource();
18959 m_bgData.taxiPath[1] = m_taxi.GetTaxiDestination();
18961 // On taxi we don't need check for dungeon
18962 m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
18963 return;
18965 else
18967 m_bgData.ClearTaxiPath();
18969 // Mount spell id storing
18970 if (IsMounted())
18972 AuraList const& auras = GetAurasByType(SPELL_AURA_MOUNTED);
18973 if (!auras.empty())
18974 m_bgData.mountSpell = (*auras.begin())->GetId();
18976 else
18977 m_bgData.mountSpell = 0;
18979 // If map is dungeon find linked graveyard
18980 if(GetMap()->IsDungeon())
18982 if (const WorldSafeLocsEntry* entry = sObjectMgr.GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam()))
18984 m_bgData.joinPos = WorldLocation(entry->map_id, entry->x, entry->y, entry->z, 0.0f);
18985 return;
18987 else
18988 sLog.outError("SetBattleGroundEntryPoint: Dungeon map %u has no linked graveyard, setting home location as entry point.", GetMapId());
18990 // If new entry point is not BG or arena set it
18991 else if (!GetMap()->IsBattleGroundOrArena())
18993 m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
18994 return;
18998 // In error cases use homebind position
18999 m_bgData.joinPos = WorldLocation(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, 0.0f);
19002 void Player::LeaveBattleground(bool teleportToEntryPoint)
19004 if(BattleGround *bg = GetBattleGround())
19006 bg->RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true);
19008 // call after remove to be sure that player resurrected for correct cast
19009 if( bg->isBattleGround() && !isGameMaster() && sWorld.getConfig(CONFIG_BOOL_BATTLEGROUND_CAST_DESERTER) )
19011 if( bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN )
19013 //lets check if player was teleported from BG and schedule delayed Deserter spell cast
19014 if(IsBeingTeleportedFar())
19016 ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER);
19017 return;
19020 CastSpell(this, 26013, true); // Deserter
19026 bool Player::CanJoinToBattleground() const
19028 // check Deserter debuff
19029 if(GetDummyAura(26013))
19030 return false;
19032 return true;
19035 bool Player::CanReportAfkDueToLimit()
19037 // a player can complain about 15 people per 5 minutes
19038 if(m_bgData.bgAfkReportedCount++ >= 15)
19039 return false;
19041 return true;
19044 ///This player has been blamed to be inactive in a battleground
19045 void Player::ReportedAfkBy(Player* reporter)
19047 BattleGround *bg = GetBattleGround();
19048 if(!bg || bg != reporter->GetBattleGround() || GetTeam() != reporter->GetTeam())
19049 return;
19051 // check if player has 'Idle' or 'Inactive' debuff
19052 if(m_bgData.bgAfkReporter.find(reporter->GetGUIDLow()) == m_bgData.bgAfkReporter.end() && !HasAura(43680, EFFECT_INDEX_0) && !HasAura(43681, EFFECT_INDEX_0) && reporter->CanReportAfkDueToLimit())
19054 m_bgData.bgAfkReporter.insert(reporter->GetGUIDLow());
19055 // 3 players have to complain to apply debuff
19056 if(m_bgData.bgAfkReporter.size() >= 3)
19058 // cast 'Idle' spell
19059 CastSpell(this, 43680, true);
19060 m_bgData.bgAfkReporter.clear();
19065 WorldObject const* Player::GetViewPoint() const
19067 if(uint64 far_sight = GetFarSight())
19069 WorldObject const* viewPoint = GetMap()->GetWorldObject(far_sight);
19070 return viewPoint ? viewPoint : this; // always expected not NULL
19072 else
19073 return this;
19076 bool Player::IsVisibleInGridForPlayer( Player* pl ) const
19078 // gamemaster in GM mode see all, including ghosts
19079 if(pl->isGameMaster() && GetSession()->GetSecurity() <= pl->GetSession()->GetSecurity())
19080 return true;
19082 // It seems in battleground everyone sees everyone, except the enemy-faction ghosts
19083 if (InBattleGround())
19085 if (!(isAlive() || m_deathTimer > 0) && !IsFriendlyTo(pl) )
19086 return false;
19087 return true;
19090 // Live player see live player or dead player with not realized corpse
19091 if(pl->isAlive() || pl->m_deathTimer > 0)
19093 return isAlive() || m_deathTimer > 0;
19096 // Ghost see other friendly ghosts, that's for sure
19097 if(!(isAlive() || m_deathTimer > 0) && IsFriendlyTo(pl))
19098 return true;
19100 // Dead player see live players near own corpse
19101 if(isAlive())
19103 Corpse *corpse = pl->GetCorpse();
19104 if(corpse)
19106 // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level
19107 if(corpse->IsWithinDistInMap(this,(20+25)*sWorld.getConfig(CONFIG_FLOAT_RATE_CREATURE_AGGRO)))
19108 return true;
19112 // and not see any other
19113 return false;
19116 bool Player::IsVisibleGloballyFor( Player* u ) const
19118 if(!u)
19119 return false;
19121 // Always can see self
19122 if (u==this)
19123 return true;
19125 // Visible units, always are visible for all players
19126 if (GetVisibility() == VISIBILITY_ON)
19127 return true;
19129 // GMs are visible for higher gms (or players are visible for gms)
19130 if (u->GetSession()->GetSecurity() > SEC_PLAYER)
19131 return GetSession()->GetSecurity() <= u->GetSession()->GetSecurity();
19133 // non faction visibility non-breakable for non-GMs
19134 if (GetVisibility() == VISIBILITY_OFF)
19135 return false;
19137 // non-gm stealth/invisibility not hide from global player lists
19138 return true;
19141 template<class T>
19142 inline void BeforeVisibilityDestroy(T* /*t*/, Player* /*p*/)
19146 template<>
19147 inline void BeforeVisibilityDestroy<Creature>(Creature* t, Player* p)
19149 if (p->GetPetGUID()==t->GetGUID() && ((Creature*)t)->isPet())
19150 ((Pet*)t)->Remove(PET_SAVE_NOT_IN_SLOT, true);
19153 void Player::UpdateVisibilityOf(WorldObject const* viewPoint, WorldObject* target)
19155 if(HaveAtClient(target))
19157 if(!target->isVisibleForInState(this, viewPoint, true))
19159 if (target->GetTypeId()==TYPEID_UNIT)
19160 BeforeVisibilityDestroy<Creature>((Creature*)target,this);
19162 ObjectGuid t_guid = target->GetGUID();
19164 target->DestroyForPlayer(this);
19165 m_clientGUIDs.erase(t_guid);
19167 DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES, "%s out of range for player %u. Distance = %f",t_guid.GetString().c_str(),GetGUIDLow(),GetDistance(target));
19170 else
19172 if(target->isVisibleForInState(this, viewPoint, false))
19174 target->SendCreateUpdateToPlayer(this);
19175 if(target->GetTypeId()!=TYPEID_GAMEOBJECT||!((GameObject*)target)->IsTransport())
19176 m_clientGUIDs.insert(target->GetObjectGuid());
19178 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));
19180 // target aura duration for caster show only if target exist at caster client
19181 // send data at target visibility change (adding to client)
19182 if(target!=this && target->isType(TYPEMASK_UNIT))
19183 SendAurasForTarget((Unit*)target);
19185 if(target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->isAlive())
19186 ((Creature*)target)->SendMonsterMoveWithSpeedToCurrentDestination(this);
19191 template<class T>
19192 inline void UpdateVisibilityOf_helper(ObjectGuidSet& s64, T* target)
19194 s64.insert(target->GetGUID());
19197 template<>
19198 inline void UpdateVisibilityOf_helper(ObjectGuidSet& s64, GameObject* target)
19200 if(!target->IsTransport())
19201 s64.insert(target->GetGUID());
19204 template<class T>
19205 void Player::UpdateVisibilityOf(WorldObject const* viewPoint, T* target, UpdateData& data, UpdateDataMapType& /*data_updates*/, std::set<WorldObject*>& visibleNow)
19207 if(HaveAtClient(target))
19209 if(!target->isVisibleForInState(this,viewPoint,true))
19211 BeforeVisibilityDestroy<T>(target,this);
19213 ObjectGuid t_guid = target->GetObjectGuid();
19215 target->BuildOutOfRangeUpdateBlock(&data);
19216 m_clientGUIDs.erase(t_guid);
19218 DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES, "%s is out of range for %s. Distance = %f", t_guid.GetString().c_str(), GetObjectGuid().GetString().c_str(), GetDistance(target));
19221 else
19223 if(target->isVisibleForInState(this,viewPoint,false))
19225 visibleNow.insert(target);
19226 target->BuildCreateUpdateBlockForPlayer(&data, this);
19227 UpdateVisibilityOf_helper(m_clientGUIDs,target);
19229 DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES, "%s is visible now for %s. Distance = %f", target->GetObjectGuid().GetString().c_str(), GetObjectGuid().GetString().c_str(), GetDistance(target));
19234 template void Player::UpdateVisibilityOf(WorldObject const* viewPoint, Player* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
19235 template void Player::UpdateVisibilityOf(WorldObject const* viewPoint, Creature* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
19236 template void Player::UpdateVisibilityOf(WorldObject const* viewPoint, Corpse* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
19237 template void Player::UpdateVisibilityOf(WorldObject const* viewPoint, GameObject* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
19238 template void Player::UpdateVisibilityOf(WorldObject const* viewPoint, DynamicObject* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
19240 void Player::InitPrimaryProfessions()
19242 SetFreePrimaryProfessions(sWorld.getConfig(CONFIG_UINT32_MAX_PRIMARY_TRADE_SKILL));
19245 void Player::SendComboPoints()
19247 Unit *combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget);
19248 if (combotarget)
19250 WorldPacket data(SMSG_UPDATE_COMBO_POINTS, combotarget->GetPackGUID().size()+1);
19251 data << combotarget->GetPackGUID();
19252 data << uint8(m_comboPoints);
19253 GetSession()->SendPacket(&data);
19257 void Player::AddComboPoints(Unit* target, int8 count)
19259 if(!count)
19260 return;
19262 // without combo points lost (duration checked in aura)
19263 RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS);
19265 if(target->GetGUID() == m_comboTarget)
19267 m_comboPoints += count;
19269 else
19271 if(m_comboTarget)
19272 if(Unit* target2 = ObjectAccessor::GetUnit(*this,m_comboTarget))
19273 target2->RemoveComboPointHolder(GetGUIDLow());
19275 m_comboTarget = target->GetGUID();
19276 m_comboPoints = count;
19278 target->AddComboPointHolder(GetGUIDLow());
19281 if (m_comboPoints > 5) m_comboPoints = 5;
19282 if (m_comboPoints < 0) m_comboPoints = 0;
19284 SendComboPoints();
19287 void Player::ClearComboPoints()
19289 if(!m_comboTarget)
19290 return;
19292 // without combopoints lost (duration checked in aura)
19293 RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS);
19295 m_comboPoints = 0;
19297 SendComboPoints();
19299 if(Unit* target = ObjectAccessor::GetUnit(*this,m_comboTarget))
19300 target->RemoveComboPointHolder(GetGUIDLow());
19302 m_comboTarget = 0;
19305 void Player::SetGroup(Group *group, int8 subgroup)
19307 if(group == NULL)
19308 m_group.unlink();
19309 else
19311 // never use SetGroup without a subgroup unless you specify NULL for group
19312 ASSERT(subgroup >= 0);
19313 m_group.link(group, this);
19314 m_group.setSubGroup((uint8)subgroup);
19318 void Player::SendInitialPacketsBeforeAddToMap()
19320 GetSocial()->SendSocialList();
19322 // Homebind
19323 WorldPacket data(SMSG_BINDPOINTUPDATE, 5*4);
19324 data << m_homebindX << m_homebindY << m_homebindZ;
19325 data << (uint32) m_homebindMapId;
19326 data << (uint32) m_homebindAreaId;
19327 GetSession()->SendPacket(&data);
19329 // SMSG_SET_PROFICIENCY
19330 // SMSG_SET_PCT_SPELL_MODIFIER
19331 // SMSG_SET_FLAT_SPELL_MODIFIER
19333 SendTalentsInfoData(false);
19335 data.Initialize(SMSG_INSTANCE_DIFFICULTY, 4+4);
19336 data << uint32(GetMap()->GetDifficulty());
19337 data << uint32(0);
19338 GetSession()->SendPacket(&data);
19340 SendInitialSpells();
19342 data.Initialize(SMSG_SEND_UNLEARN_SPELLS, 4);
19343 data << uint32(0); // count, for(count) uint32;
19344 GetSession()->SendPacket(&data);
19346 SendInitialActionButtons();
19347 m_reputationMgr.SendInitialReputations();
19349 if(!isAlive())
19350 SendCorpseReclaimDelay(true);
19352 SendInitWorldStates(GetZoneId(), GetAreaId());
19354 SendEquipmentSetList();
19356 m_achievementMgr.SendAllAchievementData();
19358 data.Initialize(SMSG_LOGIN_SETTIMESPEED, 4 + 4 + 4);
19359 data << uint32(secsToTimeBitFields(sWorld.GetGameTime()));
19360 data << (float)0.01666667f; // game speed
19361 data << uint32(0); // added in 3.1.2
19362 GetSession()->SendPacket( &data );
19364 // SMSG_TALENTS_INFO x 2 for pet (unspent points and talents in separate packets...)
19365 // SMSG_PET_GUIDS
19366 // SMSG_POWER_UPDATE
19368 // set fly flag if in fly form or taxi flight to prevent visually drop at ground in showup moment
19369 if(HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED) || HasAuraType(SPELL_AURA_FLY) || isInFlight())
19370 m_movementInfo.AddMovementFlag(MOVEFLAG_FLYING);
19372 m_mover = this;
19375 void Player::SendInitialPacketsAfterAddToMap()
19377 // update zone
19378 uint32 newzone, newarea;
19379 GetZoneAndAreaId(newzone,newarea);
19380 UpdateZone(newzone,newarea); // also call SendInitWorldStates();
19382 ResetTimeSync();
19383 SendTimeSync();
19385 CastSpell(this, 836, true); // LOGINEFFECT
19387 // set some aura effects that send packet to player client after add player to map
19388 // SendMessageToSet not send it to player not it map, only for aura that not changed anything at re-apply
19389 // same auras state lost at far teleport, send it one more time in this case also
19390 static const AuraType auratypes[] =
19392 SPELL_AURA_MOD_FEAR, SPELL_AURA_TRANSFORM, SPELL_AURA_WATER_WALK,
19393 SPELL_AURA_FEATHER_FALL, SPELL_AURA_HOVER, SPELL_AURA_SAFE_FALL,
19394 SPELL_AURA_FLY, SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED, SPELL_AURA_NONE
19396 for(AuraType const* itr = &auratypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr)
19398 Unit::AuraList const& auraList = GetAurasByType(*itr);
19399 if(!auraList.empty())
19400 auraList.front()->ApplyModifier(true,true);
19403 if(HasAuraType(SPELL_AURA_MOD_STUN))
19404 SetMovement(MOVE_ROOT);
19406 // manual send package (have code in ApplyModifier(true,true); that don't must be re-applied.
19407 if(HasAuraType(SPELL_AURA_MOD_ROOT))
19409 WorldPacket data2(SMSG_FORCE_MOVE_ROOT, 10);
19410 data2 << GetPackGUID();
19411 data2 << (uint32)2;
19412 SendMessageToSet(&data2,true);
19415 SendAurasForTarget(this);
19416 SendEnchantmentDurations(); // must be after add to map
19417 SendItemDurations(); // must be after add to map
19420 void Player::SendUpdateToOutOfRangeGroupMembers()
19422 if (m_groupUpdateMask == GROUP_UPDATE_FLAG_NONE)
19423 return;
19424 if(Group* group = GetGroup())
19425 group->UpdatePlayerOutOfRange(this);
19427 m_groupUpdateMask = GROUP_UPDATE_FLAG_NONE;
19428 m_auraUpdateMask = 0;
19429 if(Pet *pet = GetPet())
19430 pet->ResetAuraUpdateMask();
19433 void Player::SendTransferAborted(uint32 mapid, uint8 reason, uint8 arg)
19435 WorldPacket data(SMSG_TRANSFER_ABORTED, 4+2);
19436 data << uint32(mapid);
19437 data << uint8(reason); // transfer abort reason
19438 switch(reason)
19440 case TRANSFER_ABORT_INSUF_EXPAN_LVL:
19441 case TRANSFER_ABORT_DIFFICULTY:
19442 case TRANSFER_ABORT_UNIQUE_MESSAGE:
19443 data << uint8(arg);
19444 break;
19446 GetSession()->SendPacket(&data);
19449 void Player::SendInstanceResetWarning( uint32 mapid, Difficulty difficulty, uint32 time )
19451 // type of warning, based on the time remaining until reset
19452 uint32 type;
19453 if(time > 3600)
19454 type = RAID_INSTANCE_WELCOME;
19455 else if(time > 900 && time <= 3600)
19456 type = RAID_INSTANCE_WARNING_HOURS;
19457 else if(time > 300 && time <= 900)
19458 type = RAID_INSTANCE_WARNING_MIN;
19459 else
19460 type = RAID_INSTANCE_WARNING_MIN_SOON;
19462 WorldPacket data(SMSG_RAID_INSTANCE_MESSAGE, 4+4+4+4);
19463 data << uint32(type);
19464 data << uint32(mapid);
19465 data << uint32(difficulty); // difficulty
19466 data << uint32(time);
19467 if(type == RAID_INSTANCE_WELCOME)
19469 data << uint8(0); // is your (1)
19470 data << uint8(0); // is extended (1), ignored if prev field is 0
19472 GetSession()->SendPacket(&data);
19475 void Player::ApplyEquipCooldown( Item * pItem )
19477 for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
19479 _Spell const& spellData = pItem->GetProto()->Spells[i];
19481 // no spell
19482 if( !spellData.SpellId )
19483 continue;
19485 // wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown)
19486 if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE )
19487 continue;
19489 AddSpellCooldown(spellData.SpellId, pItem->GetEntry(), time(NULL) + 30);
19491 WorldPacket data(SMSG_ITEM_COOLDOWN, 12);
19492 data << pItem->GetGUID();
19493 data << uint32(spellData.SpellId);
19494 GetSession()->SendPacket(&data);
19498 void Player::resetSpells()
19500 // not need after this call
19501 if(HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
19502 RemoveAtLoginFlag(AT_LOGIN_RESET_SPELLS,true);
19504 // make full copy of map (spells removed and marked as deleted at another spell remove
19505 // and we can't use original map for safe iterative with visit each spell at loop end
19506 PlayerSpellMap smap = GetSpellMap();
19508 for(PlayerSpellMap::const_iterator iter = smap.begin();iter != smap.end(); ++iter)
19509 removeSpell(iter->first,false,false); // only iter->first can be accessed, object by iter->second can be deleted already
19511 learnDefaultSpells();
19512 learnQuestRewardedSpells();
19515 void Player::learnDefaultSpells()
19517 // learn default race/class spells
19518 PlayerInfo const *info = sObjectMgr.GetPlayerInfo(getRace(),getClass());
19519 for (PlayerCreateInfoSpells::const_iterator itr = info->spell.begin(); itr!=info->spell.end(); ++itr)
19521 uint32 tspell = *itr;
19522 DEBUG_LOG("PLAYER (Class: %u Race: %u): Adding initial spell, id = %u",uint32(getClass()),uint32(getRace()), tspell);
19523 if(!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add
19524 addSpell(tspell, true, true, true, false);
19525 else // but send in normal spell in game learn case
19526 learnSpell(tspell, true);
19530 void Player::learnQuestRewardedSpells(Quest const* quest)
19532 uint32 spell_id = quest->GetRewSpellCast();
19534 // skip quests without rewarded spell
19535 if( !spell_id )
19536 return;
19538 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
19539 if(!spellInfo)
19540 return;
19542 // check learned spells state
19543 bool found = false;
19544 for(int i=0; i < MAX_EFFECT_INDEX; ++i)
19546 if(spellInfo->Effect[i] == SPELL_EFFECT_LEARN_SPELL && !HasSpell(spellInfo->EffectTriggerSpell[i]))
19548 found = true;
19549 break;
19553 // skip quests with not teaching spell or already known spell
19554 if(!found)
19555 return;
19557 // prevent learn non first rank unknown profession and second specialization for same profession)
19558 uint32 learned_0 = spellInfo->EffectTriggerSpell[EFFECT_INDEX_0];
19559 if( sSpellMgr.GetSpellRank(learned_0) > 1 && !HasSpell(learned_0) )
19561 // not have first rank learned (unlearned prof?)
19562 uint32 first_spell = sSpellMgr.GetFirstSpellInChain(learned_0);
19563 if( !HasSpell(first_spell) )
19564 return;
19566 SpellEntry const *learnedInfo = sSpellStore.LookupEntry(learned_0);
19567 if(!learnedInfo)
19568 return;
19570 // specialization
19571 if (learnedInfo->Effect[EFFECT_INDEX_0] == SPELL_EFFECT_TRADE_SKILL && learnedInfo->Effect[EFFECT_INDEX_1] == 0)
19573 // search other specialization for same prof
19574 for(PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
19576 if(itr->second.state == PLAYERSPELL_REMOVED || itr->first==learned_0)
19577 continue;
19579 SpellEntry const *itrInfo = sSpellStore.LookupEntry(itr->first);
19580 if(!itrInfo)
19581 return;
19583 // compare only specializations
19584 if (itrInfo->Effect[EFFECT_INDEX_0] != SPELL_EFFECT_TRADE_SKILL || itrInfo->Effect[EFFECT_INDEX_1] != 0)
19585 continue;
19587 // compare same chain spells
19588 if(sSpellMgr.GetFirstSpellInChain(itr->first) != first_spell)
19589 continue;
19591 // now we have 2 specialization, learn possible only if found is lesser specialization rank
19592 if(!sSpellMgr.IsHighRankOfSpell(learned_0,itr->first))
19593 return;
19598 CastSpell( this, spell_id, true);
19601 void Player::learnQuestRewardedSpells()
19603 // learn spells received from quest completing
19604 for(QuestStatusMap::const_iterator itr = mQuestStatus.begin(); itr != mQuestStatus.end(); ++itr)
19606 // skip no rewarded quests
19607 if(!itr->second.m_rewarded)
19608 continue;
19610 Quest const* quest = sObjectMgr.GetQuestTemplate(itr->first);
19611 if( !quest )
19612 continue;
19614 learnQuestRewardedSpells(quest);
19618 void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value )
19620 uint32 raceMask = getRaceMask();
19621 uint32 classMask = getClassMask();
19622 for (uint32 j = 0; j<sSkillLineAbilityStore.GetNumRows(); ++j)
19624 SkillLineAbilityEntry const *pAbility = sSkillLineAbilityStore.LookupEntry(j);
19625 if (!pAbility || pAbility->skillId!=skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
19626 continue;
19627 // Check race if set
19628 if (pAbility->racemask && !(pAbility->racemask & raceMask))
19629 continue;
19630 // Check class if set
19631 if (pAbility->classmask && !(pAbility->classmask & classMask))
19632 continue;
19634 if (sSpellStore.LookupEntry(pAbility->spellId))
19636 // need unlearn spell
19637 if (skill_value < pAbility->req_skill_value)
19638 removeSpell(pAbility->spellId);
19639 // need learn
19640 else if (!IsInWorld())
19641 addSpell(pAbility->spellId, true, true, true, false);
19642 else
19643 learnSpell(pAbility->spellId, true);
19648 void Player::SendAurasForTarget(Unit *target)
19650 if(target->GetVisibleAuras()->empty()) // speedup things
19651 return;
19653 WorldPacket data(SMSG_AURA_UPDATE_ALL);
19654 data << target->GetPackGUID();
19656 Unit::VisibleAuraMap const *visibleAuras = target->GetVisibleAuras();
19657 for(Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr)
19659 for(int j = 0; j < MAX_EFFECT_INDEX; ++j)
19661 if(Aura *aura = target->GetAura(itr->second, SpellEffectIndex(j)))
19663 data << uint8(aura->GetAuraSlot());
19664 data << uint32(aura->GetId());
19666 if(aura->GetId())
19668 uint8 auraFlags = aura->GetAuraFlags();
19669 // flags
19670 data << uint8(auraFlags);
19671 // level
19672 data << uint8(aura->GetAuraLevel());
19673 // charges
19674 if (aura->GetAuraCharges())
19675 data << uint8(aura->GetAuraCharges() * aura->GetStackAmount());
19676 else
19677 data << uint8(aura->GetStackAmount());
19679 if(!(auraFlags & AFLAG_NOT_CASTER)) // packed GUID of caster
19681 data.appendPackGUID(aura->GetCasterGUID());
19684 if(auraFlags & AFLAG_DURATION) // include aura duration
19686 data << uint32(aura->GetAuraMaxDuration());
19687 data << uint32(aura->GetAuraDuration());
19690 break;
19695 GetSession()->SendPacket(&data);
19698 void Player::SetDailyQuestStatus( uint32 quest_id )
19700 for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
19702 if(!GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
19704 SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,quest_id);
19705 m_DailyQuestChanged = true;
19706 break;
19711 void Player::SetWeeklyQuestStatus( uint32 quest_id )
19713 m_weeklyquests.insert(quest_id);
19714 m_WeeklyQuestChanged = true;
19717 void Player::ResetDailyQuestStatus()
19719 for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
19720 SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,0);
19722 // DB data deleted in caller
19723 m_DailyQuestChanged = false;
19726 void Player::ResetWeeklyQuestStatus()
19728 if (m_weeklyquests.empty())
19729 return;
19731 m_weeklyquests.clear();
19732 // DB data deleted in caller
19733 m_WeeklyQuestChanged = false;
19736 BattleGround* Player::GetBattleGround() const
19738 if(GetBattleGroundId()==0)
19739 return NULL;
19741 return sBattleGroundMgr.GetBattleGround(GetBattleGroundId(), m_bgData.bgTypeID);
19744 bool Player::InArena() const
19746 BattleGround *bg = GetBattleGround();
19747 if(!bg || !bg->isArena())
19748 return false;
19750 return true;
19753 bool Player::GetBGAccessByLevel(BattleGroundTypeId bgTypeId) const
19755 // get a template bg instead of running one
19756 BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
19757 if(!bg)
19758 return false;
19760 // limit check leel to dbc compatible level range
19761 uint32 level = getLevel();
19762 if (level > DEFAULT_MAX_LEVEL)
19763 level = DEFAULT_MAX_LEVEL;
19765 if(level < bg->GetMinLevel() || level > bg->GetMaxLevel())
19766 return false;
19768 return true;
19771 float Player::GetReputationPriceDiscount( Creature const* pCreature ) const
19773 FactionTemplateEntry const* vendor_faction = pCreature->getFactionTemplateEntry();
19774 if(!vendor_faction || !vendor_faction->faction)
19775 return 1.0f;
19777 ReputationRank rank = GetReputationRank(vendor_faction->faction);
19778 if(rank <= REP_NEUTRAL)
19779 return 1.0f;
19781 return 1.0f - 0.05f* (rank - REP_NEUTRAL);
19784 bool Player::IsSpellFitByClassAndRace( uint32 spell_id ) const
19786 uint32 racemask = getRaceMask();
19787 uint32 classmask = getClassMask();
19789 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spell_id);
19790 if (bounds.first==bounds.second)
19791 return true;
19793 for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
19795 // skip wrong race skills
19796 if (_spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0)
19797 continue;
19799 // skip wrong class skills
19800 if (_spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0)
19801 continue;
19803 return true;
19806 return false;
19809 bool Player::HasQuestForGO(int32 GOId) const
19811 for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
19813 uint32 questid = GetQuestSlotQuestId(i);
19814 if ( questid == 0 )
19815 continue;
19817 QuestStatusMap::const_iterator qs_itr = mQuestStatus.find(questid);
19818 if(qs_itr == mQuestStatus.end())
19819 continue;
19821 QuestStatusData const& qs = qs_itr->second;
19823 if (qs.m_status == QUEST_STATUS_INCOMPLETE)
19825 Quest const* qinfo = sObjectMgr.GetQuestTemplate(questid);
19826 if(!qinfo)
19827 continue;
19829 if(GetGroup() && GetGroup()->isRaidGroup() && qinfo->IsAllowedInRaid())
19830 continue;
19832 for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
19834 if (qinfo->ReqCreatureOrGOId[j]>=0) //skip non GO case
19835 continue;
19837 if((-1)*GOId == qinfo->ReqCreatureOrGOId[j] && qs.m_creatureOrGOcount[j] < qinfo->ReqCreatureOrGOCount[j])
19838 return true;
19842 return false;
19845 void Player::UpdateForQuestWorldObjects()
19847 if(m_clientGUIDs.empty())
19848 return;
19850 UpdateData udata;
19851 WorldPacket packet;
19852 for(ObjectGuidSet::const_iterator itr=m_clientGUIDs.begin(); itr!=m_clientGUIDs.end(); ++itr)
19854 if (itr->IsGameobject())
19856 if (GameObject *obj = GetMap()->GetGameObject(*itr))
19857 obj->BuildValuesUpdateBlockForPlayer(&udata,this);
19859 else if (itr->IsCreatureOrVehicle())
19861 Creature *obj = GetMap()->GetCreatureOrPetOrVehicle(*itr);
19862 if(!obj)
19863 continue;
19865 // check if this unit requires quest specific flags
19866 if(!obj->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_SPELLCLICK))
19867 continue;
19869 SpellClickInfoMapBounds clickPair = sObjectMgr.GetSpellClickInfoMapBounds(obj->GetEntry());
19870 for(SpellClickInfoMap::const_iterator _itr = clickPair.first; _itr != clickPair.second; ++_itr)
19872 if(_itr->second.questStart || _itr->second.questEnd)
19874 obj->BuildCreateUpdateBlockForPlayer(&udata,this);
19875 break;
19880 udata.BuildPacket(&packet);
19881 GetSession()->SendPacket(&packet);
19884 void Player::SummonIfPossible(bool agree)
19886 if(!agree)
19888 m_summon_expire = 0;
19889 return;
19892 // expire and auto declined
19893 if(m_summon_expire < time(NULL))
19894 return;
19896 // stop taxi flight at summon
19897 if(isInFlight())
19899 GetMotionMaster()->MovementExpired();
19900 m_taxi.ClearTaxiDestinations();
19903 // drop flag at summon
19904 // 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
19905 if(BattleGround *bg = GetBattleGround())
19906 bg->EventPlayerDroppedFlag(this);
19908 m_summon_expire = 0;
19910 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS, 1);
19912 TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z,GetOrientation());
19915 void Player::RemoveItemDurations( Item *item )
19917 for(ItemDurationList::iterator itr = m_itemDuration.begin();itr != m_itemDuration.end(); ++itr)
19919 if(*itr==item)
19921 m_itemDuration.erase(itr);
19922 break;
19927 void Player::AddItemDurations( Item *item )
19929 if(item->GetUInt32Value(ITEM_FIELD_DURATION))
19931 m_itemDuration.push_back(item);
19932 item->SendTimeUpdate(this);
19936 void Player::AutoUnequipOffhandIfNeed()
19938 Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND );
19939 if(!offItem)
19940 return;
19942 // need unequip offhand for 2h-weapon without TitanGrip (in any from hands)
19943 if (CanTitanGrip() || (offItem->GetProto()->InventoryType != INVTYPE_2HWEAPON && !IsTwoHandUsed()))
19944 return;
19946 ItemPosCountVec off_dest;
19947 uint8 off_msg = CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false );
19948 if( off_msg == EQUIP_ERR_OK )
19950 RemoveItem(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
19951 StoreItem( off_dest, offItem, true );
19953 else
19955 MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
19956 CharacterDatabase.BeginTransaction();
19957 offItem->DeleteFromInventoryDB(); // deletes item from character's inventory
19958 offItem->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
19959 CharacterDatabase.CommitTransaction();
19961 std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM);
19962 MailDraft(subject, "There's were problems with equipping this item.").AddItem(offItem).SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED);
19966 bool Player::HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem)
19968 if(spellInfo->EquippedItemClass < 0)
19969 return true;
19971 // scan other equipped items for same requirements (mostly 2 daggers/etc)
19972 // for optimize check 2 used cases only
19973 switch(spellInfo->EquippedItemClass)
19975 case ITEM_CLASS_WEAPON:
19977 for(int i= EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i)
19978 if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
19979 if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
19980 return true;
19981 break;
19983 case ITEM_CLASS_ARMOR:
19985 // tabard not have dependent spells
19986 for(int i= EQUIPMENT_SLOT_START; i< EQUIPMENT_SLOT_MAINHAND; ++i)
19987 if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
19988 if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
19989 return true;
19991 // shields can be equipped to offhand slot
19992 if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
19993 if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
19994 return true;
19996 // ranged slot can have some armor subclasses
19997 if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
19998 if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
19999 return true;
20001 break;
20003 default:
20004 sLog.outError("HasItemFitToSpellReqirements: Not handled spell requirement for item class %u",spellInfo->EquippedItemClass);
20005 break;
20008 return false;
20011 bool Player::CanNoReagentCast(SpellEntry const* spellInfo) const
20013 // don't take reagents for spells with SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP
20014 if (spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP &&
20015 HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))
20016 return true;
20018 // Check no reagent use mask
20019 uint64 noReagentMask_0_1 = GetUInt64Value(PLAYER_NO_REAGENT_COST_1);
20020 uint32 noReagentMask_2 = GetUInt32Value(PLAYER_NO_REAGENT_COST_1+2);
20021 if (spellInfo->SpellFamilyFlags & noReagentMask_0_1 ||
20022 spellInfo->SpellFamilyFlags2 & noReagentMask_2)
20023 return true;
20025 return false;
20028 void Player::RemoveItemDependentAurasAndCasts( Item * pItem )
20030 AuraMap& auras = GetAuras();
20031 for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); )
20033 Aura* aura = itr->second;
20035 // skip passive (passive item dependent spells work in another way) and not self applied auras
20036 SpellEntry const* spellInfo = aura->GetSpellProto();
20037 if(aura->IsPassive() || aura->GetCasterGUID()!=GetGUID())
20039 ++itr;
20040 continue;
20043 // skip if not item dependent or have alternative item
20044 if(HasItemFitToSpellReqirements(spellInfo,pItem))
20046 ++itr;
20047 continue;
20050 // no alt item, remove aura, restart check
20051 RemoveAurasDueToSpell(aura->GetId());
20052 itr = auras.begin();
20055 // currently casted spells can be dependent from item
20056 for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i)
20057 if (Spell* spell = GetCurrentSpell(CurrentSpellTypes(i)))
20058 if (spell->getState()!=SPELL_STATE_DELAYED && !HasItemFitToSpellReqirements(spell->m_spellInfo,pItem) )
20059 InterruptSpell(CurrentSpellTypes(i));
20062 uint32 Player::GetResurrectionSpellId()
20064 // search priceless resurrection possibilities
20065 uint32 prio = 0;
20066 uint32 spell_id = 0;
20067 AuraList const& dummyAuras = GetAurasByType(SPELL_AURA_DUMMY);
20068 for(AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
20070 // Soulstone Resurrection // prio: 3 (max, non death persistent)
20071 if( prio < 2 && (*itr)->GetSpellProto()->SpellVisual[0] == 99 && (*itr)->GetSpellProto()->SpellIconID == 92 )
20073 switch((*itr)->GetId())
20075 case 20707: spell_id = 3026; break; // rank 1
20076 case 20762: spell_id = 20758; break; // rank 2
20077 case 20763: spell_id = 20759; break; // rank 3
20078 case 20764: spell_id = 20760; break; // rank 4
20079 case 20765: spell_id = 20761; break; // rank 5
20080 case 27239: spell_id = 27240; break; // rank 6
20081 case 47883: spell_id = 47882; break; // rank 7
20082 default:
20083 sLog.outError("Unhandled spell %u: S.Resurrection",(*itr)->GetId());
20084 continue;
20087 prio = 3;
20089 // Twisting Nether // prio: 2 (max)
20090 else if((*itr)->GetId()==23701 && roll_chance_i(10))
20092 prio = 2;
20093 spell_id = 23700;
20097 // Reincarnation (passive spell) // prio: 1
20098 // Glyph of Renewed Life remove reagent requiremnnt
20099 if (prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169) && (HasItemCount(17030,1) || HasAura(58059, EFFECT_INDEX_0)))
20100 spell_id = 21169;
20102 return spell_id;
20105 // Used in triggers for check "Only to targets that grant experience or honor" req
20106 bool Player::isHonorOrXPTarget(Unit* pVictim) const
20108 uint32 v_level = pVictim->getLevel();
20109 uint32 k_grey = MaNGOS::XP::GetGrayLevel(getLevel());
20111 // Victim level less gray level
20112 if(v_level<=k_grey)
20113 return false;
20115 if(pVictim->GetTypeId() == TYPEID_UNIT)
20117 if (((Creature*)pVictim)->isTotem() ||
20118 ((Creature*)pVictim)->isPet() ||
20119 ((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL)
20120 return false;
20122 return true;
20125 void Player::RewardSinglePlayerAtKill(Unit* pVictim)
20127 bool PvP = pVictim->isCharmedOwnedByPlayerOrPlayer();
20128 uint32 xp = PvP ? 0 : MaNGOS::XP::Gain(this, pVictim);
20130 // honor can be in PvP and !PvP (racial leader) cases
20131 RewardHonor(pVictim,1);
20133 // xp and reputation only in !PvP case
20134 if(!PvP)
20136 RewardReputation(pVictim,1);
20137 GiveXP(xp, pVictim);
20139 if(Pet* pet = GetPet())
20140 pet->GivePetXP(xp);
20142 // normal creature (not pet/etc) can be only in !PvP case
20143 if(pVictim->GetTypeId()==TYPEID_UNIT)
20144 KilledMonster(((Creature*)pVictim)->GetCreatureInfo(), pVictim->GetObjectGuid());
20148 void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewardSource)
20150 uint64 creature_guid = pRewardSource->GetTypeId()==TYPEID_UNIT ? pRewardSource->GetGUID() : uint64(0);
20152 // prepare data for near group iteration
20153 if(Group *pGroup = GetGroup())
20155 for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
20157 Player* pGroupGuy = itr->getSource();
20158 if(!pGroupGuy)
20159 continue;
20161 if(!pGroupGuy->IsAtGroupRewardDistance(pRewardSource))
20162 continue; // member (alive or dead) or his corpse at req. distance
20164 // quest objectives updated only for alive group member or dead but with not released body
20165 if(pGroupGuy->isAlive()|| !pGroupGuy->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
20166 pGroupGuy->KilledMonsterCredit(creature_id, creature_guid);
20169 else // if (!pGroup)
20170 KilledMonsterCredit(creature_id, creature_guid);
20173 bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const
20175 if (pRewardSource->IsWithinDistInMap(this,sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE)))
20176 return true;
20178 if (isAlive())
20179 return false;
20181 Corpse* corpse = GetCorpse();
20182 if (!corpse)
20183 return false;
20185 return pRewardSource->IsWithinDistInMap(corpse,sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE));
20188 uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
20190 Item* item = GetWeaponForAttack(attType,true,true);
20192 // unarmed only with base attack
20193 if(attType != BASE_ATTACK && !item)
20194 return 0;
20196 // weapon skill or (unarmed for base attack)
20197 uint32 skill = item ? item->GetSkill() : uint32(SKILL_UNARMED);
20198 return GetBaseSkillValue(skill);
20201 void Player::ResurectUsingRequestData()
20203 /// Teleport before resurrecting by player, otherwise the player might get attacked from creatures near his corpse
20204 if(IS_PLAYER_GUID(m_resurrectGUID))
20205 TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation());
20207 //we cannot resurrect player when we triggered far teleport
20208 //player will be resurrected upon teleportation
20209 if(IsBeingTeleportedFar())
20211 ScheduleDelayedOperation(DELAYED_RESURRECT_PLAYER);
20212 return;
20215 ResurrectPlayer(0.0f,false);
20217 if(GetMaxHealth() > m_resurrectHealth)
20218 SetHealth( m_resurrectHealth );
20219 else
20220 SetHealth( GetMaxHealth() );
20222 if(GetMaxPower(POWER_MANA) > m_resurrectMana)
20223 SetPower(POWER_MANA, m_resurrectMana );
20224 else
20225 SetPower(POWER_MANA, GetMaxPower(POWER_MANA) );
20227 SetPower(POWER_RAGE, 0 );
20229 SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY) );
20231 SpawnCorpseBones();
20234 void Player::SetClientControl(Unit* target, uint8 allowMove)
20236 WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size()+1);
20237 data << target->GetPackGUID();
20238 data << uint8(allowMove);
20239 GetSession()->SendPacket(&data);
20242 void Player::UpdateZoneDependentAuras( uint32 newZone )
20244 // Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update
20245 SpellAreaForAreaMapBounds saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(newZone);
20246 for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
20247 if(itr->second->autocast && itr->second->IsFitToRequirements(this,newZone,0))
20248 if (!HasAura(itr->second->spellId, EFFECT_INDEX_0))
20249 CastSpell(this,itr->second->spellId,true);
20252 void Player::UpdateAreaDependentAuras( uint32 newArea )
20254 // remove auras from spells with area limitations
20255 for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
20257 // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date
20258 if(sSpellMgr.GetSpellAllowedInLocationError(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea,this) != SPELL_CAST_OK)
20259 RemoveAura(iter);
20260 else
20261 ++iter;
20264 // some auras applied at subzone enter
20265 SpellAreaForAreaMapBounds saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(newArea);
20266 for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
20267 if(itr->second->autocast && itr->second->IsFitToRequirements(this,m_zoneUpdateId,newArea))
20268 if (!HasAura(itr->second->spellId, EFFECT_INDEX_0))
20269 CastSpell(this,itr->second->spellId,true);
20272 uint32 Player::GetCorpseReclaimDelay(bool pvp) const
20274 if ((pvp && !sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVP)) ||
20275 (!pvp && !sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVE) ))
20277 return copseReclaimDelay[0];
20280 time_t now = time(NULL);
20281 // 0..2 full period
20282 uint32 count = (now < m_deathExpireTime) ? uint32((m_deathExpireTime - now)/DEATH_EXPIRE_STEP) : 0;
20283 return copseReclaimDelay[count];
20286 void Player::UpdateCorpseReclaimDelay()
20288 bool pvp = m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH;
20290 if ((pvp && !sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVP)) ||
20291 (!pvp && !sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVE) ))
20292 return;
20294 time_t now = time(NULL);
20295 if(now < m_deathExpireTime)
20297 // full and partly periods 1..3
20298 uint32 count = uint32((m_deathExpireTime - now)/DEATH_EXPIRE_STEP +1);
20299 if(count < MAX_DEATH_COUNT)
20300 m_deathExpireTime = now+(count+1)*DEATH_EXPIRE_STEP;
20301 else
20302 m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP;
20304 else
20305 m_deathExpireTime = now+DEATH_EXPIRE_STEP;
20308 void Player::SendCorpseReclaimDelay(bool load)
20310 Corpse* corpse = GetCorpse();
20311 if(!corpse)
20312 return;
20314 uint32 delay;
20315 if(load)
20317 if(corpse->GetGhostTime() > m_deathExpireTime)
20318 return;
20320 bool pvp = corpse->GetType()==CORPSE_RESURRECTABLE_PVP;
20322 uint32 count;
20323 if( pvp && sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
20324 !pvp && sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
20326 count = uint32(m_deathExpireTime-corpse->GetGhostTime())/DEATH_EXPIRE_STEP;
20327 if(count>=MAX_DEATH_COUNT)
20328 count = MAX_DEATH_COUNT-1;
20330 else
20331 count=0;
20333 time_t expected_time = corpse->GetGhostTime()+copseReclaimDelay[count];
20335 time_t now = time(NULL);
20336 if(now >= expected_time)
20337 return;
20339 delay = uint32(expected_time-now);
20341 else
20342 delay = GetCorpseReclaimDelay(corpse->GetType()==CORPSE_RESURRECTABLE_PVP);
20344 //! corpse reclaim delay 30 * 1000ms or longer at often deaths
20345 WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4);
20346 data << uint32(delay*IN_MILLISECONDS);
20347 GetSession()->SendPacket( &data );
20350 Player* Player::GetNextRandomRaidMember(float radius)
20352 Group *pGroup = GetGroup();
20353 if(!pGroup)
20354 return NULL;
20356 std::vector<Player*> nearMembers;
20357 nearMembers.reserve(pGroup->GetMembersCount());
20359 for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
20361 Player* Target = itr->getSource();
20363 // IsHostileTo check duel and controlled by enemy
20364 if( Target && Target != this && IsWithinDistInMap(Target, radius) &&
20365 !Target->HasInvisibilityAura() && !IsHostileTo(Target) )
20366 nearMembers.push_back(Target);
20369 if (nearMembers.empty())
20370 return NULL;
20372 uint32 randTarget = urand(0,nearMembers.size()-1);
20373 return nearMembers[randTarget];
20376 PartyResult Player::CanUninviteFromGroup() const
20378 const Group* grp = GetGroup();
20379 if(!grp)
20380 return ERR_NOT_IN_GROUP;
20382 if(!grp->IsLeader(GetGUID()) && !grp->IsAssistant(GetGUID()))
20383 return ERR_NOT_LEADER;
20385 if(InBattleGround())
20386 return ERR_INVITE_RESTRICTED;
20388 return ERR_PARTY_RESULT_OK;
20391 void Player::SetBattleGroundRaid(Group* group, int8 subgroup)
20393 //we must move references from m_group to m_originalGroup
20394 SetOriginalGroup(GetGroup(), GetSubGroup());
20396 m_group.unlink();
20397 m_group.link(group, this);
20398 m_group.setSubGroup((uint8)subgroup);
20401 void Player::RemoveFromBattleGroundRaid()
20403 //remove existing reference
20404 m_group.unlink();
20405 if( Group* group = GetOriginalGroup() )
20407 m_group.link(group, this);
20408 m_group.setSubGroup(GetOriginalSubGroup());
20410 SetOriginalGroup(NULL);
20413 void Player::SetOriginalGroup(Group *group, int8 subgroup)
20415 if( group == NULL )
20416 m_originalGroup.unlink();
20417 else
20419 // never use SetOriginalGroup without a subgroup unless you specify NULL for group
20420 ASSERT(subgroup >= 0);
20421 m_originalGroup.link(group, this);
20422 m_originalGroup.setSubGroup((uint8)subgroup);
20426 void Player::UpdateUnderwaterState( Map* m, float x, float y, float z )
20428 GridMapLiquidData liquid_status;
20429 GridMapLiquidStatus res = m->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status);
20430 if (!res)
20432 m_MirrorTimerFlags &= ~(UNDERWATER_INWATER|UNDERWATER_INLAVA|UNDERWATER_INSLIME|UNDERWATER_INDARKWATER);
20433 // Small hack for enable breath in WMO
20434 if (IsInWater())
20435 m_MirrorTimerFlags|=UNDERWATER_INWATER;
20436 return;
20439 // All liquids type - check under water position
20440 if (liquid_status.type&(MAP_LIQUID_TYPE_WATER|MAP_LIQUID_TYPE_OCEAN|MAP_LIQUID_TYPE_MAGMA|MAP_LIQUID_TYPE_SLIME))
20442 if ( res & LIQUID_MAP_UNDER_WATER)
20443 m_MirrorTimerFlags |= UNDERWATER_INWATER;
20444 else
20445 m_MirrorTimerFlags &= ~UNDERWATER_INWATER;
20448 // Allow travel in dark water on taxi or transport
20449 if ((liquid_status.type & MAP_LIQUID_TYPE_DARK_WATER) && !isInFlight() && !GetTransport())
20450 m_MirrorTimerFlags |= UNDERWATER_INDARKWATER;
20451 else
20452 m_MirrorTimerFlags &= ~UNDERWATER_INDARKWATER;
20454 // in lava check, anywhere in lava level
20455 if (liquid_status.type&MAP_LIQUID_TYPE_MAGMA)
20457 if (res & (LIQUID_MAP_UNDER_WATER|LIQUID_MAP_IN_WATER|LIQUID_MAP_WATER_WALK))
20458 m_MirrorTimerFlags |= UNDERWATER_INLAVA;
20459 else
20460 m_MirrorTimerFlags &= ~UNDERWATER_INLAVA;
20462 // in slime check, anywhere in slime level
20463 if (liquid_status.type&MAP_LIQUID_TYPE_SLIME)
20465 if (res & (LIQUID_MAP_UNDER_WATER|LIQUID_MAP_IN_WATER|LIQUID_MAP_WATER_WALK))
20466 m_MirrorTimerFlags |= UNDERWATER_INSLIME;
20467 else
20468 m_MirrorTimerFlags &= ~UNDERWATER_INSLIME;
20472 void Player::SetCanParry( bool value )
20474 if(m_canParry==value)
20475 return;
20477 m_canParry = value;
20478 UpdateParryPercentage();
20481 void Player::SetCanBlock( bool value )
20483 if(m_canBlock==value)
20484 return;
20486 m_canBlock = value;
20487 UpdateBlockPercentage();
20490 bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const
20492 for(ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr)
20493 if(itr->pos == pos)
20494 return true;
20496 return false;
20499 bool Player::CanUseBattleGroundObject()
20501 // TODO : some spells gives player ForceReaction to one faction (ReputationMgr::ApplyForceReaction)
20502 // maybe gameobject code should handle that ForceReaction usage
20503 // BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet
20504 return ( //InBattleGround() && // in battleground - not need, check in other cases
20505 //!IsMounted() && - not correct, player is dismounted when he clicks on flag
20506 //player cannot use object when he is invulnerable (immune)
20507 !isTotalImmune() && // not totally immune
20508 //i'm not sure if these two are correct, because invisible players should get visible when they click on flag
20509 !HasStealthAura() && // not stealthed
20510 !HasInvisibilityAura() && // not invisible
20511 !HasAura(SPELL_RECENTLY_DROPPED_FLAG, EFFECT_INDEX_0) &&// can't pickup
20512 isAlive() // live player
20516 bool Player::CanCaptureTowerPoint()
20518 return ( !HasStealthAura() && // not stealthed
20519 !HasInvisibilityAura() && // not invisible
20520 isAlive() // live player
20524 uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair)
20526 uint32 level = getLevel();
20528 if(level > GT_MAX_LEVEL)
20529 level = GT_MAX_LEVEL; // max level in this dbc
20531 uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2);
20532 uint8 haircolor = GetByteValue(PLAYER_BYTES, 3);
20533 uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0);
20535 if((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair))
20536 return 0;
20538 GtBarberShopCostBaseEntry const *bsc = sGtBarberShopCostBaseStore.LookupEntry(level - 1);
20540 if(!bsc) // shouldn't happen
20541 return 0xFFFFFFFF;
20543 float cost = 0;
20545 if(hairstyle != newhairstyle)
20546 cost += bsc->cost; // full price
20548 if((haircolor != newhaircolor) && (hairstyle == newhairstyle))
20549 cost += bsc->cost * 0.5f; // +1/2 of price
20551 if(facialhair != newfacialhair)
20552 cost += bsc->cost * 0.75f; // +3/4 of price
20554 return uint32(cost);
20557 void Player::InitGlyphsForLevel()
20559 for(uint32 i = 0; i < sGlyphSlotStore.GetNumRows(); ++i)
20560 if(GlyphSlotEntry const * gs = sGlyphSlotStore.LookupEntry(i))
20561 if(gs->Order)
20562 SetGlyphSlot(gs->Order - 1, gs->Id);
20564 uint32 level = getLevel();
20565 uint32 value = 0;
20567 // 0x3F = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 for 80 level
20568 if(level >= 15)
20569 value |= (0x01 | 0x02);
20570 if(level >= 30)
20571 value |= 0x08;
20572 if(level >= 50)
20573 value |= 0x04;
20574 if(level >= 70)
20575 value |= 0x10;
20576 if(level >= 80)
20577 value |= 0x20;
20579 SetUInt32Value(PLAYER_GLYPHS_ENABLED, value);
20582 void Player::ApplyGlyph(uint8 slot, bool apply)
20584 if (uint32 glyph = GetGlyph(slot))
20586 if(GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph))
20588 if(apply)
20590 CastSpell(this, gp->SpellId, true);
20591 SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, glyph);
20593 else
20595 RemoveAurasDueToSpell(gp->SpellId);
20596 SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, 0);
20602 void Player::ApplyGlyphs(bool apply)
20604 for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
20605 ApplyGlyph(i,apply);
20608 void Player::EnterVehicle(Vehicle *vehicle)
20610 VehicleEntry const *ve = sVehicleStore.LookupEntry(vehicle->GetVehicleId());
20611 if(!ve)
20612 return;
20614 VehicleSeatEntry const *veSeat = sVehicleSeatStore.LookupEntry(ve->m_seatID[0]);
20615 if(!veSeat)
20616 return;
20618 vehicle->SetCharmerGUID(GetGUID());
20619 vehicle->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
20620 vehicle->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
20621 vehicle->setFaction(getFaction());
20623 SetCharm(vehicle); // charm
20624 SetFarSightGUID(vehicle->GetGUID()); // set view
20626 SetClientControl(vehicle, 1); // redirect controls to vehicle
20627 SetMover(vehicle);
20629 WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0);
20630 GetSession()->SendPacket(&data);
20632 data.Initialize(MSG_MOVE_TELEPORT_ACK, 30);
20633 data << GetPackGUID();
20634 data << uint32(0); // counter?
20635 data << uint32(MOVEFLAG_ONTRANSPORT); // transport
20636 data << uint16(0); // special flags
20637 data << uint32(getMSTime()); // time
20638 data << vehicle->GetPositionX(); // x
20639 data << vehicle->GetPositionY(); // y
20640 data << vehicle->GetPositionZ(); // z
20641 data << vehicle->GetOrientation(); // o
20642 // transport part, TODO: load/calculate seat offsets
20643 data << uint64(vehicle->GetGUID()); // transport guid
20644 data << float(veSeat->m_attachmentOffsetX); // transport offsetX
20645 data << float(veSeat->m_attachmentOffsetY); // transport offsetY
20646 data << float(veSeat->m_attachmentOffsetZ); // transport offsetZ
20647 data << float(0); // transport orientation
20648 data << uint32(getMSTime()); // transport time
20649 data << uint8(0); // seat
20650 // end of transport part
20651 data << uint32(0); // fall time
20652 GetSession()->SendPacket(&data);
20654 data.Initialize(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1);
20655 data << uint64(vehicle->GetGUID());
20656 data << uint16(0);
20657 data << uint32(0);
20658 data << uint32(0x00000101);
20660 for(uint32 i = 0; i < 10; ++i)
20661 data << uint16(0) << uint8(0) << uint8(i+8);
20663 data << uint8(0);
20664 data << uint8(0);
20665 GetSession()->SendPacket(&data);
20668 void Player::ExitVehicle(Vehicle *vehicle)
20670 vehicle->SetCharmerGUID(0);
20671 vehicle->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
20672 vehicle->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
20673 vehicle->setFaction((GetTeam() == ALLIANCE) ? vehicle->GetCreatureInfo()->faction_A : vehicle->GetCreatureInfo()->faction_H);
20675 SetCharm(NULL);
20676 SetFarSightGUID(0);
20678 SetClientControl(vehicle, 0);
20679 SetMover(NULL);
20681 WorldPacket data(MSG_MOVE_TELEPORT_ACK, 30);
20682 data << GetPackGUID();
20683 data << uint32(0); // counter?
20684 data << uint32(MOVEFLAG_ROOT); // fly unk
20685 data << uint16(MOVEFLAG2_UNK4); // special flags
20686 data << uint32(getMSTime()); // time
20687 data << vehicle->GetPositionX(); // x
20688 data << vehicle->GetPositionY(); // y
20689 data << vehicle->GetPositionZ(); // z
20690 data << vehicle->GetOrientation(); // o
20691 data << uint32(0); // fall time
20692 GetSession()->SendPacket(&data);
20694 RemovePetActionBar();
20696 // maybe called at dummy aura remove?
20697 // CastSpell(this, 45472, true); // Parachute
20700 bool Player::isTotalImmune()
20702 AuraList const& immune = GetAurasByType(SPELL_AURA_SCHOOL_IMMUNITY);
20704 uint32 immuneMask = 0;
20705 for(AuraList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr)
20707 immuneMask |= (*itr)->GetModifier()->m_miscvalue;
20708 if( immuneMask & SPELL_SCHOOL_MASK_ALL ) // total immunity
20709 return true;
20711 return false;
20714 bool Player::HasTitle(uint32 bitIndex)
20716 if (bitIndex > MAX_TITLE_INDEX)
20717 return false;
20719 uint32 fieldIndexOffset = bitIndex / 32;
20720 uint32 flag = 1 << (bitIndex % 32);
20721 return HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
20724 void Player::SetTitle(CharTitlesEntry const* title, bool lost)
20726 uint32 fieldIndexOffset = title->bit_index / 32;
20727 uint32 flag = 1 << (title->bit_index % 32);
20729 if(lost)
20731 if(!HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag))
20732 return;
20734 RemoveFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
20736 else
20738 if(HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag))
20739 return;
20741 SetFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
20744 WorldPacket data(SMSG_TITLE_EARNED, 4 + 4);
20745 data << uint32(title->bit_index);
20746 data << uint32(lost ? 0 : 1); // 1 - earned, 0 - lost
20747 GetSession()->SendPacket(&data);
20750 void Player::ConvertRune(uint8 index, RuneType newType)
20752 SetCurrentRune(index, newType);
20754 WorldPacket data(SMSG_CONVERT_RUNE, 2);
20755 data << uint8(index);
20756 data << uint8(newType);
20757 GetSession()->SendPacket(&data);
20760 void Player::ResyncRunes(uint8 count)
20762 WorldPacket data(SMSG_RESYNC_RUNES, count * 2);
20763 for(uint32 i = 0; i < count; ++i)
20765 data << uint8(GetCurrentRune(i)); // rune type
20766 data << uint8(255 - ((GetRuneCooldown(i) / REGEN_TIME_FULL) * 51)); // passed cooldown time (0-255)
20768 GetSession()->SendPacket(&data);
20771 void Player::AddRunePower(uint8 index)
20773 WorldPacket data(SMSG_ADD_RUNE_POWER, 4);
20774 data << uint32(1 << index); // mask (0x00-0x3F probably)
20775 GetSession()->SendPacket(&data);
20778 static RuneType runeSlotTypes[MAX_RUNES] = {
20779 /*0*/ RUNE_BLOOD,
20780 /*1*/ RUNE_BLOOD,
20781 /*2*/ RUNE_UNHOLY,
20782 /*3*/ RUNE_UNHOLY,
20783 /*4*/ RUNE_FROST,
20784 /*5*/ RUNE_FROST
20787 void Player::InitRunes()
20789 if(getClass() != CLASS_DEATH_KNIGHT)
20790 return;
20792 m_runes = new Runes;
20794 m_runes->runeState = 0;
20796 for(uint32 i = 0; i < MAX_RUNES; ++i)
20798 SetBaseRune(i, runeSlotTypes[i]); // init base types
20799 SetCurrentRune(i, runeSlotTypes[i]); // init current types
20800 SetRuneCooldown(i, 0); // reset cooldowns
20801 m_runes->SetRuneState(i);
20804 for(uint32 i = 0; i < NUM_RUNE_TYPES; ++i)
20805 SetFloatValue(PLAYER_RUNE_REGEN_1 + i, 0.1f);
20809 bool Player::IsBaseRuneSlotsOnCooldown( RuneType runeType ) const
20811 for(uint32 i = 0; i < MAX_RUNES; ++i)
20812 if (GetBaseRune(i) == runeType && GetRuneCooldown(i) == 0)
20813 return false;
20815 return true;
20818 void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast)
20820 Loot loot;
20821 loot.FillLoot (loot_id,store,this,true);
20823 uint32 max_slot = loot.GetMaxSlotInLootFor(this);
20824 for(uint32 i = 0; i < max_slot; ++i)
20826 LootItem* lootItem = loot.LootItemInSlot(i,this);
20828 ItemPosCountVec dest;
20829 uint8 msg = CanStoreNewItem (bag,slot,dest,lootItem->itemid,lootItem->count);
20830 if(msg != EQUIP_ERR_OK && slot != NULL_SLOT)
20831 msg = CanStoreNewItem( bag, NULL_SLOT,dest,lootItem->itemid,lootItem->count);
20832 if( msg != EQUIP_ERR_OK && bag != NULL_BAG)
20833 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT,dest,lootItem->itemid,lootItem->count);
20834 if(msg != EQUIP_ERR_OK)
20836 SendEquipError( msg, NULL, NULL, lootItem->itemid );
20837 continue;
20840 Item* pItem = StoreNewItem (dest,lootItem->itemid,true,lootItem->randomPropertyId);
20841 SendNewItem(pItem, lootItem->count, false, false, broadcast);
20845 uint32 Player::CalculateTalentsPoints() const
20847 uint32 base_talent = getLevel() < 10 ? 0 : getLevel()-9;
20849 if(getClass() != CLASS_DEATH_KNIGHT)
20850 return uint32(base_talent * sWorld.getConfig(CONFIG_FLOAT_RATE_TALENT));
20852 uint32 talentPointsForLevel = getLevel() < 56 ? 0 : getLevel() - 55;
20853 talentPointsForLevel += m_questRewardTalentCount;
20855 if(talentPointsForLevel > base_talent)
20856 talentPointsForLevel = base_talent;
20858 return uint32(talentPointsForLevel * sWorld.getConfig(CONFIG_FLOAT_RATE_TALENT));
20861 bool Player::IsKnowHowFlyIn(uint32 mapid, uint32 zone, uint32 area) const
20863 // continent checked in SpellMgr::GetSpellAllowedInLocationError at cast and area update
20864 uint32 v_map = GetVirtualMapForMapAndZone(mapid, zone);
20866 // don't allow flying in Dalaran except Krasus' Landing
20867 return (v_map != 571 || HasSpell(54197)) && (zone != 4395 || area == 4564); // Cold Weather Flying
20870 struct DoPlayerLearnSpell
20872 DoPlayerLearnSpell(Player& _player) : player(_player) {}
20873 void operator() (uint32 spell_id) { player.learnSpell(spell_id, false); }
20874 Player& player;
20877 void Player::learnSpellHighRank(uint32 spellid)
20879 learnSpell(spellid, false);
20881 DoPlayerLearnSpell worker(*this);
20882 sSpellMgr.doForHighRanks(spellid, worker);
20885 void Player::_LoadSkills(QueryResult *result)
20887 // 0 1 2
20888 // SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid));
20890 uint32 count = 0;
20891 if (result)
20895 Field *fields = result->Fetch();
20897 uint16 skill = fields[0].GetUInt16();
20898 uint16 value = fields[1].GetUInt16();
20899 uint16 max = fields[2].GetUInt16();
20901 SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(skill);
20902 if(!pSkill)
20904 sLog.outError("Character %u has skill %u that does not exist.", GetGUIDLow(), skill);
20905 continue;
20908 // set fixed skill ranges
20909 switch(GetSkillRangeType(pSkill,false))
20911 case SKILL_RANGE_LANGUAGE: // 300..300
20912 value = max = 300;
20913 break;
20914 case SKILL_RANGE_MONO: // 1..1, grey monolite bar
20915 value = max = 1;
20916 break;
20917 default:
20918 break;
20921 if(value == 0)
20923 sLog.outError("Character %u has skill %u with value 0. Will be deleted.", GetGUIDLow(), skill);
20924 CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u' AND skill = '%u' ", GetGUIDLow(), skill );
20925 continue;
20928 SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill,0));
20929 SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count),MAKE_SKILL_VALUE(value, max));
20930 SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count),0);
20932 mSkillStatus.insert(SkillStatusMap::value_type(skill, SkillStatusData(count, SKILL_UNCHANGED)));
20934 learnSkillRewardedSpells(skill, value);
20936 ++count;
20938 if(count >= PLAYER_MAX_SKILLS) // client limit
20940 sLog.outError("Character %u has more than %u skills.", GetGUIDLow(), PLAYER_MAX_SKILLS);
20941 break;
20943 } while (result->NextRow());
20944 delete result;
20947 for (; count < PLAYER_MAX_SKILLS; ++count)
20949 SetUInt32Value(PLAYER_SKILL_INDEX(count), 0);
20950 SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count),0);
20951 SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count),0);
20954 // special settings
20955 if(getClass()==CLASS_DEATH_KNIGHT)
20957 uint32 base_level = std::min(getLevel(),sWorld.getConfig (CONFIG_UINT32_START_HEROIC_PLAYER_LEVEL));
20958 if(base_level < 1)
20959 base_level = 1;
20960 uint32 base_skill = (base_level-1)*5; // 270 at starting level 55
20961 if(base_skill < 1)
20962 base_skill = 1; // skill mast be known and then > 0 in any case
20964 if(GetPureSkillValue (SKILL_FIRST_AID) < base_skill)
20965 SetSkill(SKILL_FIRST_AID, base_skill, base_skill);
20966 if(GetPureSkillValue (SKILL_AXES) < base_skill)
20967 SetSkill(SKILL_AXES, base_skill, base_skill);
20968 if(GetPureSkillValue (SKILL_DEFENSE) < base_skill)
20969 SetSkill(SKILL_DEFENSE, base_skill, base_skill);
20970 if(GetPureSkillValue (SKILL_POLEARMS) < base_skill)
20971 SetSkill(SKILL_POLEARMS, base_skill, base_skill);
20972 if(GetPureSkillValue (SKILL_SWORDS) < base_skill)
20973 SetSkill(SKILL_SWORDS, base_skill, base_skill);
20974 if(GetPureSkillValue (SKILL_2H_AXES) < base_skill)
20975 SetSkill(SKILL_2H_AXES, base_skill, base_skill);
20976 if(GetPureSkillValue (SKILL_2H_SWORDS) < base_skill)
20977 SetSkill(SKILL_2H_SWORDS, base_skill, base_skill);
20978 if(GetPureSkillValue (SKILL_UNARMED) < base_skill)
20979 SetSkill(SKILL_UNARMED, base_skill, base_skill);
20983 uint32 Player::GetPhaseMaskForSpawn() const
20985 uint32 phase = PHASEMASK_NORMAL;
20986 if(!isGameMaster())
20987 phase = GetPhaseMask();
20988 else
20990 AuraList const& phases = GetAurasByType(SPELL_AURA_PHASE);
20991 if(!phases.empty())
20992 phase = phases.front()->GetMiscValue();
20995 // some aura phases include 1 normal map in addition to phase itself
20996 if(uint32 n_phase = phase & ~PHASEMASK_NORMAL)
20997 return n_phase;
20999 return PHASEMASK_NORMAL;
21002 uint8 Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) const
21004 ItemPrototype const* pProto = pItem->GetProto();
21006 // proto based limitations
21007 if(uint8 res = CanEquipUniqueItem(pProto,eslot,limit_count))
21008 return res;
21010 // check unique-equipped on gems
21011 for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
21013 uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
21014 if(!enchant_id)
21015 continue;
21016 SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
21017 if(!enchantEntry)
21018 continue;
21020 ItemPrototype const* pGem = ObjectMgr::GetItemPrototype(enchantEntry->GemID);
21021 if(!pGem)
21022 continue;
21024 // include for check equip another gems with same limit category for not equipped item (and then not counted)
21025 uint32 gem_limit_count = !pItem->IsEquipped() && pGem->ItemLimitCategory
21026 ? pItem->GetGemCountWithLimitCategory(pGem->ItemLimitCategory) : 1;
21028 if(uint8 res = CanEquipUniqueItem(pGem, eslot,gem_limit_count))
21029 return res;
21032 return EQUIP_ERR_OK;
21035 uint8 Player::CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_slot, uint32 limit_count) const
21037 // check unique-equipped on item
21038 if (itemProto->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED)
21040 // there is an equip limit on this item
21041 if(HasItemOrGemWithIdEquipped(itemProto->ItemId,1,except_slot))
21042 return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
21045 // check unique-equipped limit
21046 if (itemProto->ItemLimitCategory)
21048 ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(itemProto->ItemLimitCategory);
21049 if(!limitEntry)
21050 return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
21052 // NOTE: limitEntry->mode not checked because if item have have-limit then it applied and to equip case
21054 if(limit_count > limitEntry->maxCount)
21055 return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS;
21057 // there is an equip limit on this item
21058 if(HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory,limitEntry->maxCount-limit_count+1,except_slot))
21059 return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS;
21062 return EQUIP_ERR_OK;
21065 void Player::HandleFall(MovementInfo const& movementInfo)
21067 // calculate total z distance of the fall
21068 float z_diff = m_lastFallZ - movementInfo.GetPos()->z;
21069 DEBUG_LOG("zDiff = %f", z_diff);
21071 //Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored
21072 // 14.57 can be calculated by resolving damageperc formula below to 0
21073 if (z_diff >= 14.57f && !isDead() && !isGameMaster() &&
21074 !HasAuraType(SPELL_AURA_HOVER) && !HasAuraType(SPELL_AURA_FEATHER_FALL) &&
21075 !HasAuraType(SPELL_AURA_FLY) && !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL) )
21077 //Safe fall, fall height reduction
21078 int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL);
21080 float damageperc = 0.018f*(z_diff-safe_fall)-0.2426f;
21082 if(damageperc >0 )
21084 uint32 damage = (uint32)(damageperc * GetMaxHealth()*sWorld.getConfig(CONFIG_FLOAT_RATE_DAMAGE_FALL));
21086 float height = movementInfo.GetPos()->z;
21087 UpdateGroundPositionZ(movementInfo.GetPos()->x, movementInfo.GetPos()->y, height);
21089 if (damage > 0)
21091 //Prevent fall damage from being more than the player maximum health
21092 if (damage > GetMaxHealth())
21093 damage = GetMaxHealth();
21095 // Gust of Wind
21096 if (GetDummyAura(43621))
21097 damage = GetMaxHealth()/2;
21099 uint32 original_health = GetHealth();
21100 uint32 final_damage = EnvironmentalDamage(DAMAGE_FALL, damage);
21102 // recheck alive, might have died of EnvironmentalDamage, avoid cases when player die in fact like Spirit of Redemption case
21103 if (isAlive() && final_damage < original_health)
21104 GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING, uint32(z_diff*100));
21107 //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction
21108 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);
21113 void Player::UpdateAchievementCriteria( AchievementCriteriaTypes type, uint32 miscvalue1/*=0*/, uint32 miscvalue2/*=0*/, Unit *unit/*=NULL*/, uint32 time/*=0*/ )
21115 GetAchievementMgr().UpdateAchievementCriteria(type, miscvalue1,miscvalue2,unit,time);
21118 void Player::LearnTalent(uint32 talentId, uint32 talentRank)
21120 uint32 CurTalentPoints = GetFreeTalentPoints();
21122 if(CurTalentPoints == 0)
21123 return;
21125 if (talentRank >= MAX_TALENT_RANK)
21126 return;
21128 TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentId );
21130 if(!talentInfo)
21131 return;
21133 TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
21135 if(!talentTabInfo)
21136 return;
21138 // prevent learn talent for different class (cheating)
21139 if( (getClassMask() & talentTabInfo->ClassMask) == 0 )
21140 return;
21142 // find current max talent rank
21143 uint32 curtalent_maxrank = 0;
21144 PlayerTalentMap::iterator itr = m_talents[m_activeSpec].find(talentId);
21145 if (itr != m_talents[m_activeSpec].end() && itr->second.state != PLAYERSPELL_REMOVED)
21146 curtalent_maxrank = itr->second.currentRank + 1;
21148 // we already have same or higher talent rank learned
21149 if(curtalent_maxrank >= (talentRank + 1))
21150 return;
21152 // check if we have enough talent points
21153 if(CurTalentPoints < (talentRank - curtalent_maxrank + 1))
21154 return;
21156 // Check if it requires another talent
21157 if (talentInfo->DependsOn > 0)
21159 if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
21161 bool hasEnoughRank = false;
21162 PlayerTalentMap::iterator dependsOnTalent = m_talents[m_activeSpec].find(depTalentInfo->TalentID);
21163 if (dependsOnTalent != m_talents[m_activeSpec].end() && dependsOnTalent->second.state != PLAYERSPELL_REMOVED)
21165 PlayerTalent depTalent = (*dependsOnTalent).second;
21166 if (depTalent.currentRank >= talentInfo->DependsOnRank)
21167 hasEnoughRank = true;
21170 if (!hasEnoughRank)
21171 return;
21175 // Find out how many points we have in this field
21176 uint32 spentPoints = 0;
21178 uint32 tTab = talentInfo->TalentTab;
21179 if (talentInfo->Row > 0)
21181 for (PlayerTalentMap::const_iterator iter = m_talents[m_activeSpec].begin(); iter != m_talents[m_activeSpec].end(); ++iter)
21182 if (iter->second.state != PLAYERSPELL_REMOVED && iter->second.m_talentEntry->TalentTab == tTab)
21183 spentPoints += iter->second.currentRank + 1;
21186 // not have required min points spent in talent tree
21187 if(spentPoints < (talentInfo->Row * MAX_TALENT_RANK))
21188 return;
21190 // spell not set in talent.dbc
21191 uint32 spellid = talentInfo->RankID[talentRank];
21192 if( spellid == 0 )
21194 sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
21195 return;
21198 // already known
21199 if(HasSpell(spellid))
21200 return;
21202 // learn! (other talent ranks will unlearned at learning)
21203 learnSpell(spellid, false);
21204 DETAIL_LOG("TalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
21207 void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
21209 Pet *pet = GetPet();
21211 if(!pet)
21212 return;
21214 if(petGuid != pet->GetGUID())
21215 return;
21217 uint32 CurTalentPoints = pet->GetFreeTalentPoints();
21219 if(CurTalentPoints == 0)
21220 return;
21222 if (talentRank >= MAX_PET_TALENT_RANK)
21223 return;
21225 TalentEntry const *talentInfo = sTalentStore.LookupEntry(talentId);
21227 if(!talentInfo)
21228 return;
21230 TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
21232 if(!talentTabInfo)
21233 return;
21235 CreatureInfo const *ci = pet->GetCreatureInfo();
21237 if(!ci)
21238 return;
21240 CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
21242 if(!pet_family)
21243 return;
21245 if(pet_family->petTalentType < 0) // not hunter pet
21246 return;
21248 // prevent learn talent for different family (cheating)
21249 if(!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
21250 return;
21252 // find current max talent rank
21253 int32 curtalent_maxrank = 0;
21254 for(int32 k = MAX_TALENT_RANK-1; k > -1; --k)
21256 if(talentInfo->RankID[k] && pet->HasSpell(talentInfo->RankID[k]))
21258 curtalent_maxrank = k + 1;
21259 break;
21263 // we already have same or higher talent rank learned
21264 if(curtalent_maxrank >= int32(talentRank + 1))
21265 return;
21267 // check if we have enough talent points
21268 if(CurTalentPoints < (talentRank - curtalent_maxrank + 1))
21269 return;
21271 // Check if it requires another talent
21272 if (talentInfo->DependsOn > 0)
21274 if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
21276 bool hasEnoughRank = false;
21277 for (int i = talentInfo->DependsOnRank; i < MAX_TALENT_RANK; ++i)
21279 if (depTalentInfo->RankID[i] != 0)
21280 if (pet->HasSpell(depTalentInfo->RankID[i]))
21281 hasEnoughRank = true;
21283 if (!hasEnoughRank)
21284 return;
21288 // Find out how many points we have in this field
21289 uint32 spentPoints = 0;
21291 uint32 tTab = talentInfo->TalentTab;
21292 if (talentInfo->Row > 0)
21294 unsigned int numRows = sTalentStore.GetNumRows();
21295 for (unsigned int i = 0; i < numRows; ++i) // Loop through all talents.
21297 // Someday, someone needs to revamp
21298 const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i);
21299 if (tmpTalent) // the way talents are tracked
21301 if (tmpTalent->TalentTab == tTab)
21303 for (int j = 0; j < MAX_TALENT_RANK; ++j)
21305 if (tmpTalent->RankID[j] != 0)
21307 if (pet->HasSpell(tmpTalent->RankID[j]))
21309 spentPoints += j + 1;
21318 // not have required min points spent in talent tree
21319 if(spentPoints < (talentInfo->Row * MAX_PET_TALENT_RANK))
21320 return;
21322 // spell not set in talent.dbc
21323 uint32 spellid = talentInfo->RankID[talentRank];
21324 if( spellid == 0 )
21326 sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
21327 return;
21330 // already known
21331 if(pet->HasSpell(spellid))
21332 return;
21334 // learn! (other talent ranks will unlearned at learning)
21335 pet->learnSpell(spellid);
21336 DETAIL_LOG("PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
21339 void Player::UpdateKnownCurrencies(uint32 itemId, bool apply)
21341 if(CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId))
21343 if(apply)
21344 SetFlag64(PLAYER_FIELD_KNOWN_CURRENCIES, (UI64LIT(1) << (ctEntry->BitIndex - 1)));
21345 else
21346 RemoveFlag64(PLAYER_FIELD_KNOWN_CURRENCIES, (UI64LIT(1) << (ctEntry->BitIndex - 1)));
21350 void Player::UpdateFallInformationIfNeed( MovementInfo const& minfo,uint16 opcode )
21352 if (m_lastFallTime >= minfo.GetFallTime() || m_lastFallZ <= minfo.GetPos()->z || opcode == MSG_MOVE_FALL_LAND)
21353 SetFallInformation(minfo.GetFallTime(), minfo.GetPos()->z);
21356 void Player::UnsummonPetTemporaryIfAny()
21358 Pet* pet = GetPet();
21359 if(!pet)
21360 return;
21362 if(!m_temporaryUnsummonedPetNumber && pet->isControlled() && !pet->isTemporarySummoned() )
21364 m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
21365 m_oldpetspell = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL);
21368 RemovePet(pet, PET_SAVE_AS_CURRENT);
21371 void Player::ResummonPetTemporaryUnSummonedIfAny()
21373 if(!m_temporaryUnsummonedPetNumber)
21374 return;
21376 // not resummon in not appropriate state
21377 if(IsPetNeedBeTemporaryUnsummoned())
21378 return;
21380 if(GetPetGUID())
21381 return;
21383 Pet* NewPet = new Pet;
21384 if(!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
21385 delete NewPet;
21387 m_temporaryUnsummonedPetNumber = 0;
21390 bool Player::canSeeSpellClickOn(Creature const *c) const
21392 if(!c->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_SPELLCLICK))
21393 return false;
21395 SpellClickInfoMapBounds clickPair = sObjectMgr.GetSpellClickInfoMapBounds(c->GetEntry());
21396 for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr)
21397 if(itr->second.IsFitToRequirements(this))
21398 return true;
21400 return false;
21403 void Player::BuildPlayerTalentsInfoData(WorldPacket *data)
21405 *data << uint32(GetFreeTalentPoints()); // unspentTalentPoints
21406 *data << uint8(m_specsCount); // talent group count (0, 1 or 2)
21407 *data << uint8(m_activeSpec); // talent group index (0 or 1)
21409 if(m_specsCount)
21411 // loop through all specs (only 1 for now)
21412 for(uint32 specIdx = 0; specIdx < m_specsCount; ++specIdx)
21414 uint8 talentIdCount = 0;
21415 size_t pos = data->wpos();
21416 *data << uint8(talentIdCount); // [PH], talentIdCount
21418 // find class talent tabs (all players have 3 talent tabs)
21419 uint32 const* talentTabIds = GetTalentTabPages(getClass());
21421 for(uint32 i = 0; i < 3; ++i)
21423 uint32 talentTabId = talentTabIds[i];
21424 for(PlayerTalentMap::iterator iter = m_talents[specIdx].begin(); iter != m_talents[specIdx].end(); ++iter)
21426 PlayerTalent talent = (*iter).second;
21428 if (talent.state == PLAYERSPELL_REMOVED)
21429 continue;
21431 // skip another tab talents
21432 if(talent.m_talentEntry->TalentTab != talentTabId)
21433 continue;
21435 *data << uint32(talent.m_talentEntry->TalentID); // Talent.dbc
21436 *data << uint8(talent.currentRank); // talentMaxRank (0-4)
21438 ++talentIdCount;
21442 data->put<uint8>(pos, talentIdCount); // put real count
21444 *data << uint8(MAX_GLYPH_SLOT_INDEX); // glyphs count
21446 // GlyphProperties.dbc
21447 for(uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
21448 *data << uint16(m_glyphs[specIdx][i].GetId());
21453 void Player::BuildPetTalentsInfoData(WorldPacket *data)
21455 uint32 unspentTalentPoints = 0;
21456 size_t pointsPos = data->wpos();
21457 *data << uint32(unspentTalentPoints); // [PH], unspentTalentPoints
21459 uint8 talentIdCount = 0;
21460 size_t countPos = data->wpos();
21461 *data << uint8(talentIdCount); // [PH], talentIdCount
21463 Pet *pet = GetPet();
21464 if(!pet)
21465 return;
21467 unspentTalentPoints = pet->GetFreeTalentPoints();
21469 data->put<uint32>(pointsPos, unspentTalentPoints); // put real points
21471 CreatureInfo const *ci = pet->GetCreatureInfo();
21472 if(!ci)
21473 return;
21475 CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
21476 if(!pet_family || pet_family->petTalentType < 0)
21477 return;
21479 for(uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId)
21481 TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentTabId );
21482 if(!talentTabInfo)
21483 continue;
21485 if(!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
21486 continue;
21488 for(uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
21490 TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
21491 if(!talentInfo)
21492 continue;
21494 // skip another tab talents
21495 if(talentInfo->TalentTab != talentTabId)
21496 continue;
21498 // find max talent rank
21499 int32 curtalent_maxrank = -1;
21500 for(int32 k = 4; k > -1; --k)
21502 if(talentInfo->RankID[k] && pet->HasSpell(talentInfo->RankID[k]))
21504 curtalent_maxrank = k;
21505 break;
21509 // not learned talent
21510 if(curtalent_maxrank < 0)
21511 continue;
21513 *data << uint32(talentInfo->TalentID); // Talent.dbc
21514 *data << uint8(curtalent_maxrank); // talentMaxRank (0-4)
21516 ++talentIdCount;
21519 data->put<uint8>(countPos, talentIdCount); // put real count
21521 break;
21525 void Player::SendTalentsInfoData(bool pet)
21527 WorldPacket data(SMSG_TALENTS_INFO, 50);
21528 data << uint8(pet ? 1 : 0);
21529 if(pet)
21530 BuildPetTalentsInfoData(&data);
21531 else
21532 BuildPlayerTalentsInfoData(&data);
21533 GetSession()->SendPacket(&data);
21536 void Player::BuildEnchantmentsInfoData(WorldPacket *data)
21538 uint32 slotUsedMask = 0;
21539 size_t slotUsedMaskPos = data->wpos();
21540 *data << uint32(slotUsedMask); // slotUsedMask < 0x80000
21542 for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
21544 Item *item = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
21546 if(!item)
21547 continue;
21549 slotUsedMask |= (1 << i);
21551 *data << uint32(item->GetEntry()); // item entry
21553 uint16 enchantmentMask = 0;
21554 size_t enchantmentMaskPos = data->wpos();
21555 *data << uint16(enchantmentMask); // enchantmentMask < 0x1000
21557 for(uint32 j = 0; j < MAX_ENCHANTMENT_SLOT; ++j)
21559 uint32 enchId = item->GetEnchantmentId(EnchantmentSlot(j));
21561 if(!enchId)
21562 continue;
21564 enchantmentMask |= (1 << j);
21566 *data << uint16(enchId); // enchantmentId?
21569 data->put<uint16>(enchantmentMaskPos, enchantmentMask);
21571 *data << uint16(0); // ?
21572 *data << uint8(0); // PGUID!
21573 *data << uint32(0); // seed?
21576 data->put<uint32>(slotUsedMaskPos, slotUsedMask);
21579 void Player::SendEquipmentSetList()
21581 uint32 count = 0;
21582 WorldPacket data(SMSG_EQUIPMENT_SET_LIST, 4);
21583 size_t count_pos = data.wpos();
21584 data << uint32(count); // count placeholder
21585 for(EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
21587 if(itr->second.state==EQUIPMENT_SET_DELETED)
21588 continue;
21589 data.appendPackGUID(itr->second.Guid);
21590 data << uint32(itr->first);
21591 data << itr->second.Name;
21592 data << itr->second.IconName;
21593 for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
21594 data.appendPackGUID(MAKE_NEW_GUID(itr->second.Items[i], 0, HIGHGUID_ITEM));
21596 ++count; // client have limit but it checked at loading and set
21598 data.put<uint32>(count_pos, count);
21599 GetSession()->SendPacket(&data);
21602 void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset)
21604 if(eqset.Guid != 0)
21606 bool found = false;
21608 for(EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
21610 if((itr->second.Guid == eqset.Guid) && (itr->first == index))
21612 found = true;
21613 break;
21617 if(!found) // something wrong...
21619 sLog.outError("Player %s tried to save equipment set "UI64FMTD" (index %u), but that equipment set not found!", GetName(), eqset.Guid, index);
21620 return;
21624 EquipmentSet& eqslot = m_EquipmentSets[index];
21626 EquipmentSetUpdateState old_state = eqslot.state;
21628 eqslot = eqset;
21630 if(eqset.Guid == 0)
21632 eqslot.Guid = sObjectMgr.GenerateEquipmentSetGuid();
21634 WorldPacket data(SMSG_EQUIPMENT_SET_SAVED, 4 + 1);
21635 data << uint32(index);
21636 data.appendPackGUID(eqslot.Guid);
21637 GetSession()->SendPacket(&data);
21640 eqslot.state = old_state == EQUIPMENT_SET_NEW ? EQUIPMENT_SET_NEW : EQUIPMENT_SET_CHANGED;
21643 void Player::_SaveEquipmentSets()
21645 for(EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end();)
21647 uint32 index = itr->first;
21648 EquipmentSet& eqset = itr->second;
21649 switch(eqset.state)
21651 case EQUIPMENT_SET_UNCHANGED:
21652 ++itr;
21653 break; // nothing do
21654 case EQUIPMENT_SET_CHANGED:
21656 // prevent SQL injection
21657 std::string db_IconName = eqset.IconName;
21658 std::string db_Name = eqset.Name;
21659 CharacterDatabase.escape_string(db_IconName);
21660 CharacterDatabase.escape_string(db_Name);
21661 CharacterDatabase.PExecute("UPDATE character_equipmentsets SET name='%s', iconname='%s', item0='%u', item1='%u', item2='%u', item3='%u', item4='%u', item5='%u', item6='%u', item7='%u', item8='%u', item9='%u', item10='%u', item11='%u', item12='%u', item13='%u', item14='%u', item15='%u', item16='%u', item17='%u', item18='%u' WHERE guid='%u' AND setguid='"UI64FMTD"' AND setindex='%u'",
21662 db_Name.c_str(), db_IconName.c_str(), eqset.Items[0], eqset.Items[1], eqset.Items[2], eqset.Items[3], eqset.Items[4], eqset.Items[5], eqset.Items[6], eqset.Items[7],
21663 eqset.Items[8], eqset.Items[9], eqset.Items[10], eqset.Items[11], eqset.Items[12], eqset.Items[13], eqset.Items[14], eqset.Items[15], eqset.Items[16], eqset.Items[17], eqset.Items[18], GetGUIDLow(), eqset.Guid, index);
21664 eqset.state = EQUIPMENT_SET_UNCHANGED;
21665 ++itr;
21666 break;
21668 case EQUIPMENT_SET_NEW:
21670 // prevent SQL injection
21671 std::string db_IconName = eqset.IconName;
21672 std::string db_Name = eqset.Name;
21673 CharacterDatabase.escape_string(db_IconName);
21674 CharacterDatabase.escape_string(db_Name);
21675 CharacterDatabase.PExecute("INSERT INTO character_equipmentsets VALUES ('%u', '"UI64FMTD"', '%u', '%s', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
21676 GetGUIDLow(), eqset.Guid, index, db_Name.c_str(), db_IconName.c_str(), eqset.Items[0], eqset.Items[1], eqset.Items[2], eqset.Items[3], eqset.Items[4], eqset.Items[5], eqset.Items[6], eqset.Items[7],
21677 eqset.Items[8], eqset.Items[9], eqset.Items[10], eqset.Items[11], eqset.Items[12], eqset.Items[13], eqset.Items[14], eqset.Items[15], eqset.Items[16], eqset.Items[17], eqset.Items[18]);
21678 eqset.state = EQUIPMENT_SET_UNCHANGED;
21679 ++itr;
21680 break;
21682 case EQUIPMENT_SET_DELETED:
21683 CharacterDatabase.PExecute("DELETE FROM character_equipmentsets WHERE setguid="UI64FMTD, eqset.Guid);
21684 m_EquipmentSets.erase(itr++);
21685 break;
21690 void Player::_SaveBGData()
21692 CharacterDatabase.PExecute("DELETE FROM character_battleground_data WHERE guid='%u'", GetGUIDLow());
21693 if (m_bgData.bgInstanceID)
21695 /* guid, bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */
21696 CharacterDatabase.PExecute("INSERT INTO character_battleground_data VALUES ('%u', '%u', '%u', '%f', '%f', '%f', '%f', '%u', '%u', '%u', '%u')",
21697 GetGUIDLow(), m_bgData.bgInstanceID, m_bgData.bgTeam, m_bgData.joinPos.coord_x, m_bgData.joinPos.coord_y, m_bgData.joinPos.coord_z,
21698 m_bgData.joinPos.orientation, m_bgData.joinPos.mapid, m_bgData.taxiPath[0], m_bgData.taxiPath[1], m_bgData.mountSpell);
21702 void Player::DeleteEquipmentSet(uint64 setGuid)
21704 for(EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
21706 if(itr->second.Guid == setGuid)
21708 if(itr->second.state == EQUIPMENT_SET_NEW)
21709 m_EquipmentSets.erase(itr);
21710 else
21711 itr->second.state = EQUIPMENT_SET_DELETED;
21712 break;
21717 void Player::ActivateSpec(uint8 specNum)
21719 if(GetActiveSpec() == specNum)
21720 return;
21722 if(specNum >= GetSpecsCount())
21723 return;
21725 UnsummonPetTemporaryIfAny();
21727 ApplyGlyphs(false);
21729 // copy of new talent spec (we will use it as model for converting current tlanet state to new)
21730 PlayerTalentMap tempSpec = m_talents[specNum];
21732 // copy old spec talents to new one, must be before spec switch to have previous spec num(as m_activeSpec)
21733 m_talents[specNum] = m_talents[m_activeSpec];
21735 SetActiveSpec(specNum);
21737 // remove all talent spells that don't exist in next spec but exist in old
21738 for (PlayerTalentMap::iterator specIter = m_talents[m_activeSpec].begin(); specIter != m_talents[m_activeSpec].end();)
21740 PlayerTalent& talent = (*specIter).second;
21742 if (talent.state == PLAYERSPELL_REMOVED)
21744 ++specIter;
21745 continue;
21748 PlayerTalentMap::iterator iterTempSpec = tempSpec.find(specIter->first);
21750 // remove any talent rank if talent not listed in temp spec
21751 if (iterTempSpec == tempSpec.end() || iterTempSpec->second.state == PLAYERSPELL_REMOVED)
21753 TalentEntry const *talentInfo = talent.m_talentEntry;
21755 for(int r = 0; r < MAX_TALENT_RANK; ++r)
21756 if (talentInfo->RankID[r])
21757 removeSpell(talentInfo->RankID[r],!IsPassiveSpell(talentInfo->RankID[r]),false);
21759 specIter = m_talents[m_activeSpec].begin();
21761 else
21762 ++specIter;
21765 // now new spec data have only talents (maybe different rank) as in temp spec data, sync ranks then.
21766 for (PlayerTalentMap::const_iterator tempIter = tempSpec.begin(); tempIter != tempSpec.end(); ++tempIter)
21768 PlayerTalent const& talent = (*tempIter).second;
21770 // removed state talent already unlearned in prev. loop
21771 // but we need restore it if it deleted for finish removed-marked data in DB
21772 if (talent.state == PLAYERSPELL_REMOVED)
21774 m_talents[m_activeSpec][tempIter->first] = talent;
21775 continue;
21778 uint32 talentSpellId = talent.m_talentEntry->RankID[talent.currentRank];
21780 // learn talent spells if they not in new spec (old spec copy)
21781 // and if they have different rank
21782 PlayerTalentMap::iterator specIter = m_talents[m_activeSpec].find(tempIter->first);
21783 if (specIter != m_talents[m_activeSpec].end() && specIter->second.state != PLAYERSPELL_REMOVED)
21785 if ((*specIter).second.currentRank != talent.currentRank)
21786 learnSpell(talentSpellId, false);
21788 else
21789 learnSpell(talentSpellId, false);
21791 // sync states - original state is changed in addSpell that learnSpell calls
21792 specIter = m_talents[m_activeSpec].find(tempIter->first);
21793 if (specIter != m_talents[m_activeSpec].end())
21794 (*specIter).second.state = talent.state;
21795 else
21797 sLog.outError("ActivateSpec: Talent spell %u expected to learned at spec switch but not listed in talents at final check!", talentSpellId);
21799 // attempt resync DB state (deleted lost spell from DB)
21800 if (talent.state != PLAYERSPELL_NEW)
21802 PlayerTalent& talentNew = m_talents[m_activeSpec][tempIter->first];
21803 talentNew = talent;
21804 talentNew.state = PLAYERSPELL_REMOVED;
21809 InitTalentForLevel();
21811 // recheck action buttons (not checked at loading/spec copy)
21812 ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec];
21813 for(ActionButtonList::const_iterator itr = currentActionButtonList.begin(); itr != currentActionButtonList.end(); ++itr)
21814 if (itr->second.uState != ACTIONBUTTON_DELETED)
21815 // remove broken without any output (it can be not correct because talents not copied at spec creating)
21816 if (!IsActionButtonDataValid(itr->first,itr->second.GetAction(),itr->second.GetType(), this, false))
21817 removeActionButton(m_activeSpec,itr->first);
21819 ResummonPetTemporaryUnSummonedIfAny();
21821 ApplyGlyphs(true);
21823 SendInitialActionButtons();
21825 Powers pw = getPowerType();
21826 if(pw != POWER_MANA)
21827 SetPower(POWER_MANA, 0);
21829 SetPower(pw, 0);
21832 void Player::UpdateSpecCount(uint8 count)
21834 uint8 curCount = GetSpecsCount();
21835 if (curCount == count)
21836 return;
21838 // maybe current spec data must be copied to 0 spec?
21839 if (m_activeSpec >= count)
21840 ActivateSpec(0);
21842 // copy spec data from new specs
21843 if (count > curCount)
21845 // copy action buttons from active spec (more easy in this case iterate first by button)
21846 ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec];
21848 for(ActionButtonList::const_iterator itr = currentActionButtonList.begin(); itr != currentActionButtonList.end(); ++itr)
21850 if (itr->second.uState != ACTIONBUTTON_DELETED)
21852 for(uint8 spec = curCount; spec < count; ++spec)
21853 addActionButton(spec,itr->first,itr->second.GetAction(),itr->second.GetType());
21857 // delete spec data for removed specs
21858 else if (count < curCount)
21860 // delete action buttons for removed spec
21861 for(uint8 spec = count; spec < curCount; ++spec)
21863 // delete action buttons for removed spec
21864 for(uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button)
21865 removeActionButton(spec,button);
21869 SetSpecsCount(count);
21871 SendTalentsInfoData(false);
21874 void Player::RemoveAtLoginFlag( AtLoginFlags f, bool in_db_also /*= false*/ )
21876 m_atLoginFlags &= ~f;
21878 if(in_db_also)
21879 CharacterDatabase.PExecute("UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'", uint32(f), GetGUIDLow());
21882 void Player::SendClearCooldown( uint32 spell_id, Unit* target )
21884 WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8);
21885 data << uint32(spell_id);
21886 data << uint64(target->GetGUID());
21887 SendDirectMessage(&data);
21890 void Player::BuildTeleportAckMsg( WorldPacket *data, float x, float y, float z, float ang ) const
21892 data->Initialize(MSG_MOVE_TELEPORT_ACK, 41);
21893 *data << GetPackGUID();
21894 *data << uint32(0); // this value increments every time
21895 *data << uint32(m_movementInfo.GetMovementFlags()); // movement flags
21896 *data << uint16(0); // 2.3.0
21897 *data << uint32(getMSTime()); // time
21898 *data << x;
21899 *data << y;
21900 *data << z;
21901 *data << ang;
21902 *data << uint32(0);
21905 bool Player::HasMovementFlag( MovementFlags f ) const
21907 return m_movementInfo.HasMovementFlag(f);
21910 void Player::SetFarSightGUID( uint64 guid )
21912 if(GetFarSight() == guid)
21913 return;
21915 SetUInt64Value(PLAYER_FARSIGHT, guid);
21917 // need triggering load grids around new view point
21918 UpdateVisibilityForPlayer();
21921 void Player::UpdateVisibilityForPlayer()
21923 WorldObject const* viewPoint = GetViewPoint();
21924 Map* m = GetMap();
21926 CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
21927 Cell cell(p);
21929 m->UpdateObjectVisibility(this, cell, p);
21931 if (this != viewPoint)
21933 CellPair pView(MaNGOS::ComputeCellPair(viewPoint->GetPositionX(), viewPoint->GetPositionY()));
21934 Cell cellView(pView);
21936 m->UpdateObjectsVisibilityFor(this, cellView, pView);
21938 else
21939 m->UpdateObjectsVisibilityFor(this, cell, p);
21942 void Player::ResetTimeSync()
21944 m_timeSyncCounter = 0;
21945 m_timeSyncTimer = 0;
21946 m_timeSyncClient = 0;
21947 m_timeSyncServer = getMSTime();
21950 void Player::SendTimeSync()
21952 WorldPacket data(SMSG_TIME_SYNC_REQ, 4);
21953 data << uint32(m_timeSyncCounter++);
21954 GetSession()->SendPacket(&data);
21956 // Schedule next sync in 10 sec
21957 m_timeSyncTimer = 10000;
21958 m_timeSyncServer = getMSTime();
21961 void Player::SendDuelCountdown(uint32 counter)
21963 WorldPacket data(SMSG_DUEL_COUNTDOWN, 4);
21964 data << uint32(counter); // seconds
21965 GetSession()->SendPacket(&data);
21968 bool Player::IsImmunedToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex index) const
21970 switch(spellInfo->Effect[index])
21972 case SPELL_EFFECT_ATTACK_ME:
21973 return true;
21974 default:
21975 break;
21977 switch(spellInfo->EffectApplyAuraName[index])
21979 case SPELL_AURA_MOD_TAUNT:
21980 return true;
21981 default:
21982 break;
21984 return Unit::IsImmunedToSpellEffect(spellInfo, index);
21987 void Player::SetHomebindToLocation(WorldLocation const& loc, uint32 area_id)
21989 m_homebindMapId = loc.mapid;
21990 m_homebindAreaId = area_id;
21991 m_homebindX = loc.coord_x;
21992 m_homebindY = loc.coord_y;
21993 m_homebindZ = loc.coord_z;
21995 // update sql homebind
21996 CharacterDatabase.PExecute("UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'",
21997 m_homebindMapId, m_homebindAreaId, m_homebindX, m_homebindY, m_homebindZ, GetGUIDLow());
22000 Object* Player::GetObjectByTypeMask(ObjectGuid guid, TypeMask typemask)
22002 switch(guid.GetHigh())
22004 case HIGHGUID_ITEM:
22005 if (typemask & TYPEMASK_ITEM)
22006 return GetItemByGuid(guid);
22007 break;
22008 case HIGHGUID_PLAYER:
22009 if (GetObjectGuid()==guid)
22010 return this;
22011 if ((typemask & TYPEMASK_PLAYER) && IsInWorld())
22012 return ObjectAccessor::FindPlayer(guid);
22013 break;
22014 case HIGHGUID_GAMEOBJECT:
22015 if ((typemask & TYPEMASK_GAMEOBJECT) && IsInWorld())
22016 return GetMap()->GetGameObject(guid);
22017 break;
22018 case HIGHGUID_UNIT:
22019 if ((typemask & TYPEMASK_UNIT) && IsInWorld())
22020 return GetMap()->GetCreature(guid);
22021 break;
22022 case HIGHGUID_PET:
22023 if ((typemask & TYPEMASK_UNIT) && IsInWorld())
22024 return GetMap()->GetPet(guid);
22025 break;
22026 case HIGHGUID_VEHICLE:
22027 if ((typemask & TYPEMASK_UNIT) && IsInWorld())
22028 return GetMap()->GetVehicle(guid);
22029 break;
22030 case HIGHGUID_DYNAMICOBJECT:
22031 if ((typemask & TYPEMASK_DYNAMICOBJECT) && IsInWorld())
22032 return GetMap()->GetDynamicObject(guid);
22033 break;
22034 case HIGHGUID_TRANSPORT:
22035 case HIGHGUID_CORPSE:
22036 case HIGHGUID_MO_TRANSPORT:
22037 break;
22040 return NULL;
22043 void Player::SetRestType( RestType n_r_type, uint32 areaTriggerId /*= 0*/)
22045 rest_type = n_r_type;
22047 if (rest_type == REST_TYPE_NO)
22049 RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
22051 // Set player to FFA PVP when not in rested environment.
22052 if(sWorld.IsFFAPvPRealm())
22053 SetFFAPvP(true);
22055 else
22057 SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
22059 inn_trigger_id = areaTriggerId;
22060 time_inn_enter = time(NULL);
22062 if(sWorld.IsFFAPvPRealm())
22063 SetFFAPvP(false);