diff options
author | maximius <none@none> | 2009-08-14 17:24:52 -0700 |
---|---|---|
committer | maximius <none@none> | 2009-08-14 17:24:52 -0700 |
commit | 2fd68eab44c1c0ea02d93ea1b5de47f0880f84ea (patch) | |
tree | f411b81aa63bcb7908746676a5461d8332aa63dd /src/game/Player.cpp | |
parent | c7aa2f64e3593f86ddf3ee5c13eb4ea4d8c1db2d (diff) |
*Add Dual Spec support, original by EnderGT, thanks to Thyros and XTElite1 for helping me test :)
--HG--
branch : trunk
Diffstat (limited to 'src/game/Player.cpp')
-rw-r--r-- | src/game/Player.cpp | 373 |
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); +} |