aboutsummaryrefslogtreecommitdiff
path: root/src/game/Player.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/Player.cpp')
-rw-r--r--src/game/Player.cpp373
1 files changed, 348 insertions, 25 deletions
diff --git a/src/game/Player.cpp b/src/game/Player.cpp
index 2f324d7063c..8ce1aca0364 100644
--- a/src/game/Player.cpp
+++ b/src/game/Player.cpp
@@ -424,9 +424,17 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa
m_lastPotionId = 0;
m_activeSpec = 0;
- m_specsCount = 1;
+ m_specsCount = 0;
- for (uint8 i = 0; i < BASEMOD_END; ++i)
+ for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
+ {
+ for (int g = 0; g < MAX_GLYPH_SLOT_INDEX; ++g)
+ m_Glyphs[i][g] = 0;
+
+ m_talents[i] = new PlayerTalentMap();
+ }
+
+ for (int i = 0; i < BASEMOD_END; ++i)
{
m_auraBaseMod[i][FLAT_MOD] = 0.0f;
m_auraBaseMod[i][PCT_MOD] = 1.0f;
@@ -492,6 +500,13 @@ Player::~Player ()
for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
delete itr->second;
+ for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
+ {
+ for (PlayerTalentMap::const_iterator itr = m_talents[i]->begin(); itr != m_talents[i]->end(); ++itr)
+ delete itr->second;
+ delete m_talents[i];
+ }
+
//all mailed items should be deleted, also all mail should be deallocated
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();++itr)
delete *itr;
@@ -2532,6 +2547,12 @@ void Player::InitTalentForLevel()
}
else
{
+ if (level < 40 || m_specsCount == 0)
+ {
+ m_specsCount = 1;
+ m_activeSpec = 0;
+ }
+
uint32 talentPointsForLevel = CalculateTalentsPoints();
// if used more that have then reset
@@ -2847,6 +2868,73 @@ void Player::AddNewMailDeliverTime(time_t deliver_time)
}
}
+bool Player::AddTalent(uint32 spell_id, uint8 spec, bool learning)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+ if (!spellInfo)
+ {
+ // do character spell book cleanup (all characters)
+ if(!IsInWorld() && !learning) // spell load case
+ {
+ sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.",spell_id);
+ CharacterDatabase.PExecute("DELETE FROM character_talent WHERE spell = '%u'",spell_id);
+ }
+ else
+ sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
+
+ return false;
+ }
+
+ if(!SpellMgr::IsSpellValid(spellInfo,this,false))
+ {
+ // do character spell book cleanup (all characters)
+ if(!IsInWorld() && !learning) // spell load case
+ {
+ sLog.outError("Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.",spell_id);
+ CharacterDatabase.PExecute("DELETE FROM character_talent WHERE spell = '%u'",spell_id);
+ }
+ else
+ sLog.outError("Player::addTalent: Broken spell #%u learning not allowed.",spell_id);
+
+ return false;
+ }
+
+ PlayerTalentMap::iterator itr = m_talents[spec]->find(spell_id);
+ if (itr != m_talents[spec]->end())
+ {
+ itr->second->state = PLAYERSPELL_UNCHANGED;
+ }
+ else if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id))
+ {
+ if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
+ {
+ for(int i=0; i < MAX_TALENT_RANK; ++i)
+ {
+ // skip learning spell and no rank spell case
+ uint32 rankSpellId = talentInfo->RankID[i];
+ if(!rankSpellId || rankSpellId==spell_id)
+ continue;
+
+ PlayerTalentMap::iterator itr = m_talents[spec]->find(rankSpellId);
+ if (itr != m_talents[spec]->end())
+ {
+ itr->second->state = PLAYERSPELL_REMOVED;
+ }
+ }
+ }
+
+ PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
+ PlayerTalent *newtalent = new PlayerTalent();
+
+ newtalent->state = state;
+ newtalent->spec = spec;
+
+ (*m_talents[spec])[spell_id] = newtalent;
+ return true;
+ }
+ return false;
+}
+
bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
@@ -3681,7 +3769,7 @@ bool Player::resetTalents(bool no_cost)
if( (getClassMask() & talentTabInfo->ClassMask) == 0 )
continue;
- for (int j = 0; j < MAX_TALENT_RANK; j++)
+ /* for (int j = 0; j < MAX_TALENT_RANK; j++)
{
for(PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();)
{
@@ -3710,6 +3798,12 @@ bool Player::resetTalents(bool no_cost)
else
++itr;
}
+ } */
+ PlayerTalentMap::iterator itr2 = m_talents[m_activeSpec]->begin();
+ for (; itr2 != m_talents[m_activeSpec]->end(); ++itr2)
+ {
+ removeSpell(itr2->first, !IsPassiveSpell(itr2->first),false);
+ itr2->second->state = PLAYERSPELL_REMOVED;
}
}
@@ -3945,6 +4039,12 @@ bool Player::HasSpell(uint32 spell) const
!itr->second->disabled);
}
+bool Player::HasTalent(uint32 spell, uint8 spec) const
+{
+ PlayerTalentMap::const_iterator itr = m_talents[spec]->find(spell);
+ return (itr != m_talents[spec]->end() && itr->second->state != PLAYERSPELL_REMOVED);
+}
+
bool Player::HasActiveSpell(uint32 spell) const
{
PlayerSpellMap::const_iterator itr = m_spells.find(spell);
@@ -5671,12 +5771,12 @@ int16 Player::GetSkillTempBonusValue(uint32 skill) const
return 0;
}
-void Player::SendInitialActionButtons() const
+void Player::SendActionButtons(uint32 spec) const
{
- sLog.outDetail( "Initializing Action Buttons for '%u'", GetGUIDLow() );
+ sLog.outDetail( "Sending Action Buttons for '%u' spec '%u'", GetGUIDLow(), spec );
WorldPacket data(SMSG_ACTION_BUTTONS, 1+(MAX_ACTION_BUTTONS*4));
- data << uint8(0); // can be 0, 1, 2 (talent spec)
+ data << uint8(spec); // can be 0, 1, 2 (talent spec)
for(int button = 0; button < MAX_ACTION_BUTTONS; ++button)
{
ActionButtonList::const_iterator itr = m_actionButtons.find(button);
@@ -5687,7 +5787,7 @@ void Player::SendInitialActionButtons() const
}
GetSession()->SendPacket( &data );
- sLog.outDetail( "Action Buttons for '%u' Initialized", GetGUIDLow() );
+ sLog.outDetail( "Action Buttons for '%u' spec '%u' Sent", GetGUIDLow(), spec );
}
ActionButton* Player::addActionButton(uint8 button, uint32 action, uint8 type)
@@ -14521,8 +14621,8 @@ float Player::GetFloatValueFromDB(uint16 index, uint64 guid)
bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
{
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
- //QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, 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, arena_pending_points FROM characters WHERE guid = '%u'", guid);
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, 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, arena_pending_points, instance_id, speccount, activespec FROM characters WHERE guid = '%u'", guid);
QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM);
if(!result)
@@ -14544,6 +14644,13 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
return false;
}
+ m_specsCount = fields[42].GetUInt32();
+ m_activeSpec = fields[43].GetUInt32();
+
+ // sanity check
+ if (m_specsCount < 2) // Maybe better to check MAX_TALENT_SPECS?
+ m_activeSpec = 0;
+
Object::_Create( guid, 0, HIGHGUID_PLAYER );
m_name = fields[3].GetCppString();
@@ -14964,8 +15071,10 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
//mails are loaded only when needed ;-) - when player in game click on mailbox.
//_LoadMail();
+ _LoadTalents(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTALENTS));
_LoadSpells(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS));
+ _LoadGlyphs(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGLYPHS));
_LoadAuras(holder->GetResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff);
_LoadGlyphAuras();
// add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
@@ -16124,7 +16233,7 @@ void Player::SaveToDB()
"taximask, online, cinematic, "
"totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, "
"trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, "
- "death_expire_time, taxi_path, arena_pending_points, latency) VALUES ("
+ "death_expire_time, taxi_path, arena_pending_points, latency, speccount, activespec) VALUES ("
<< GetGUIDLow() << ", "
<< GetSession()->GetAccountId() << ", '"
<< sql_name << "', "
@@ -16207,6 +16316,10 @@ void Player::SaveToDB()
ss << m_taxi.SaveTaxiDestinationsToString() << "', ";
ss << "'0', "; // arena_pending_points
ss << GetSession()->GetLatency();
+ ss << ", ";
+ ss << uint32(m_specsCount);
+ ss << ", ";
+ ss << uint32(m_activeSpec);
ss << ")";
CharacterDatabase.Execute( ss.str().c_str() );
@@ -16218,6 +16331,7 @@ void Player::SaveToDB()
_SaveInventory();
_SaveQuestStatus();
_SaveDailyQuestStatus();
+ _SaveTalents();
_SaveSpells();
_SaveSpellCooldowns();
_SaveActions();
@@ -16226,6 +16340,7 @@ void Player::SaveToDB()
m_reputationMgr.SaveToDB();
_SaveEquipmentSets();
GetSession()->SaveTutorialsData(); // changed only while character in game
+ _SaveGlyphs();
CharacterDatabase.CommitTransaction();
@@ -16253,14 +16368,14 @@ void Player::_SaveActions()
switch (itr->second.uState)
{
case ACTIONBUTTON_NEW:
- CharacterDatabase.PExecute("INSERT INTO character_action (guid,button,action,type) VALUES ('%u', '%u', '%u', '%u')",
- GetGUIDLow(), (uint32)itr->first, (uint32)itr->second.GetAction(), (uint32)itr->second.GetType() );
+ CharacterDatabase.PExecute("INSERT INTO character_action (guid,spec,button,action,type) VALUES ('%u', '%u', '%u', '%u', '%u')",
+ GetGUIDLow(), (uint32)m_activeSpec, (uint32)itr->first, (uint32)itr->second.GetAction(), (uint32)itr->second.GetType() );
itr->second.uState = ACTIONBUTTON_UNCHANGED;
++itr;
break;
case ACTIONBUTTON_CHANGED:
- CharacterDatabase.PExecute("UPDATE character_action SET action = '%u', type = '%u' WHERE guid= '%u' AND button= '%u' ",
- (uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), GetGUIDLow(), (uint32)itr->first );
+ CharacterDatabase.PExecute("UPDATE character_action SET action = '%u', type = '%u' WHERE guid = '%u' AND button = '%u' AND spec = '%u'",
+ (uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), GetGUIDLow(), (uint32)itr->first, (uint32)m_activeSpec);
itr->second.uState = ACTIONBUTTON_UNCHANGED;
++itr;
break;
@@ -20944,7 +21059,9 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
// learn! (other talent ranks will unlearned at learning)
learnSpell(spellid, false);
- sLog.outDetail("TalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
+ AddTalent(spellid, m_activeSpec, true);
+
+ sLog.outDetail("TalentID: %u Rank: %u Spell: %u Spec: %u\n", talentId, talentRank, spellid, m_activeSpec);
// update free talent points
SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
@@ -21188,7 +21305,7 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket *data)
int32 curtalent_maxrank = -1;
for(int32 k = MAX_TALENT_RANK-1; k > -1; --k)
{
- if(talentInfo->RankID[k] && HasSpell(talentInfo->RankID[k]))
+ if(talentInfo->RankID[k] && HasTalent(talentInfo->RankID[k], specIdx))
{
curtalent_maxrank = k;
break;
@@ -21211,7 +21328,7 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket *data)
*data << uint8(MAX_GLYPH_SLOT_INDEX); // glyphs count
for(uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
- *data << uint16(GetGlyph(i)); // GlyphProperties.dbc
+ *data << uint16(m_Glyphs[specIdx][i]); // GlyphProperties.dbc
}
}
}
@@ -21466,14 +21583,6 @@ void Player::DeleteEquipmentSet(uint64 setGuid)
}
}
-void Player::ActivateSpec(uint32 specNum)
-{
- if(GetActiveSpec() == specNum)
- return;
-
- resetTalents(true);
-}
-
void Player::RemoveAtLoginFlag( AtLoginFlags f, bool in_db_also /*= false*/ )
{
m_atLoginFlags &= ~f;
@@ -21507,3 +21616,217 @@ void Player::SetMap(Map * map)
Unit::SetMap(map);
m_mapRef.link(map, this);
}
+
+void Player::_LoadGlyphs(QueryResult *result)
+{
+ // SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, "SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 from character_glyphs WHERE guid = '%u'", GUID_LOPART(m_guid));
+ if (!result)
+ return;
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint8 spec = fields[0].GetUInt8();
+ if (spec >= m_specsCount)
+ continue;
+
+ m_Glyphs[spec][0] = fields[1].GetUInt32();
+ m_Glyphs[spec][1] = fields[2].GetUInt32();
+ m_Glyphs[spec][2] = fields[3].GetUInt32();
+ m_Glyphs[spec][3] = fields[4].GetUInt32();
+ m_Glyphs[spec][4] = fields[5].GetUInt32();
+ m_Glyphs[spec][5] = fields[6].GetUInt32();
+
+ } while (result->NextRow());
+
+ delete result;
+}
+
+void Player::_SaveGlyphs()
+{
+ CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE guid='%u'",GetGUIDLow());
+ for (uint8 spec = 0; spec < m_specsCount; ++spec)
+ {
+ CharacterDatabase.PExecute("INSERT INTO character_glyphs VALUES('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
+ GetGUIDLow(), spec, m_Glyphs[spec][0], m_Glyphs[spec][1], m_Glyphs[spec][2], m_Glyphs[spec][3], m_Glyphs[spec][4], m_Glyphs[spec][5]);
+ }
+}
+
+void Player::_LoadTalents(QueryResult *result)
+{
+ // SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid = '%u'", GUID_LOPART(m_guid));
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ AddTalent(fields[0].GetUInt32(), fields[1].GetUInt32(), false);
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Player::_SaveTalents()
+{
+ for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
+ {
+ for (PlayerTalentMap::iterator itr = m_talents[i]->begin(), next = m_talents[i]->begin(); itr != m_talents[i]->end();)
+ {
+ if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
+ CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' and spell = '%u' and spec = '%u'", GetGUIDLow(), itr->first, itr->second->spec);
+
+ if (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED)
+ CharacterDatabase.PExecute("INSERT INTO character_talent (guid,spell,spec) VALUES ('%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second->spec);
+
+ if (itr->second->state == PLAYERSPELL_REMOVED)
+ {
+ delete itr->second;
+ m_talents[i]->erase(itr++);
+ }
+ else
+ {
+ itr->second->state = PLAYERSPELL_UNCHANGED;
+ ++itr;
+ }
+ }
+ }
+}
+
+void Player::UpdateSpecCount(uint8 count)
+{
+ if(GetSpecsCount() == count)
+ return;
+
+ if(count == 1)
+ {
+ _SaveActions(); // make sure the button list is cleaned up
+ // active spec becomes only spec?
+ CharacterDatabase.PExecute("DELETE FROM character_action WHERE spec<>'%u' AND guid='%u'",m_activeSpec, GetGUIDLow());
+ CharacterDatabase.PExecute("UPDATE character_action SET spec='%u' WHERE guid='%u'", m_activeSpec, GetGUIDLow());
+ }
+ else if (count == 2)
+ {
+ _SaveActions(); // make sure the button list is cleaned up
+ for(ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end(); ++itr)
+ {
+ CharacterDatabase.PExecute("INSERT INTO character_action (guid,button,action,type,spec) VALUES ('%u', '%u', '%u', '%u', '%u')",
+ GetGUIDLow(), (uint32)itr->first, (uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), count );
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ SetSpecsCount(count);
+
+ SendTalentsInfoData(false);
+}
+
+void Player::ActivateSpec(uint8 spec)
+{
+ if(GetActiveSpec() == spec)
+ return;
+
+ if(GetSpecsCount() != MAX_TALENT_SPECS)
+ return;
+
+ uint32 const* talentTabIds = GetTalentTabPages(getClass());
+
+ for(uint8 i = 0; i < 3; ++i)
+ {
+ uint32 talentTabId = talentTabIds[i];
+
+ for(uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
+ {
+ TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
+ if(!talentInfo)
+ continue;
+
+ // skip another tab talents
+ if(talentInfo->TalentTab != talentTabId)
+ continue;
+
+ // find max talent rank
+ int32 curtalent_maxrank = -1;
+ for(int32 k = 4; k > -1; --k)
+ {
+ if(talentInfo->RankID[k] && HasTalent(talentInfo->RankID[k], m_activeSpec))
+ {
+ removeSpell(talentInfo->RankID[k],true);
+ }
+ }
+ }
+ }
+
+ // set glyphs
+ for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
+ {
+ // remove secondary glyph
+ if(uint32 oldglyph = m_Glyphs[m_activeSpec][slot])
+ {
+ if(GlyphPropertiesEntry const *old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph))
+ {
+ RemoveAurasDueToSpell(old_gp->SpellId);
+ }
+ }
+ }
+
+ SetActiveSpec(spec);
+
+ for(uint8 i = 0; i < 3; ++i)
+ {
+ uint32 talentTabId = talentTabIds[i];
+
+ for(uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
+ {
+ TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
+ if(!talentInfo)
+ continue;
+
+ // skip another tab talents
+ if(talentInfo->TalentTab != talentTabId)
+ continue;
+
+ // find max talent rank
+ int32 curtalent_maxrank = -1;
+ for(int32 k = 4; k > -1; --k)
+ {
+ if(talentInfo->RankID[k] && HasTalent(talentInfo->RankID[k], m_activeSpec))
+ {
+ learnSpell(talentInfo->RankID[k], false);
+ }
+ }
+ }
+ }
+
+ // set glyphs
+ for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
+ {
+ uint32 glyph = m_Glyphs[m_activeSpec][slot];
+ // apply primary glyph
+ if (glyph)
+ {
+ if (GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph))
+ {
+ CastSpell(this, gp->SpellId, true);
+ }
+ }
+ SetGlyph(slot, glyph);
+ }
+
+ InitTalentForLevel();
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT button,action,type FROM character_action WHERE guid = '%u' AND spec = '%u' ORDER BY button", GetGUIDLow(), m_activeSpec);
+ if (result)
+ {
+ _LoadActions(result);
+ }
+ UnsummonPetTemporaryIfAny();
+ SendActionButtons(m_activeSpec);
+ SetPower(getPowerType(), 0);
+}