Merge remote branch 'origin/master' into 330

This commit is contained in:
tomrus88 2010-04-03 11:33:45 +04:00
commit d131f137cc
44 changed files with 670 additions and 498 deletions

View file

@ -2533,16 +2533,17 @@ void Player::GiveLevel(uint32 level)
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL);
}
void Player::InitTalentForLevel()
void Player::UpdateFreeTalentPoints(bool resetIfNeed)
{
uint32 level = getLevel();
// talents base at level diff ( talents = level - 9 but some can be used already)
if(level < 10)
if (level < 10)
{
// Remove all talent points
if(m_usedTalentCount > 0) // Free any used talents
if (m_usedTalentCount > 0) // Free any used talents
{
resetTalents(true);
if (resetIfNeed)
resetTalents(true);
SetFreeTalentPoints(0);
}
}
@ -2551,9 +2552,9 @@ void Player::InitTalentForLevel()
uint32 talentPointsForLevel = CalculateTalentsPoints();
// if used more that have then reset
if(m_usedTalentCount > talentPointsForLevel)
if (m_usedTalentCount > talentPointsForLevel)
{
if (GetSession()->GetSecurity() < SEC_ADMINISTRATOR)
if (resetIfNeed && GetSession()->GetSecurity() < SEC_ADMINISTRATOR)
resetTalents(true);
else
SetFreeTalentPoints(0);
@ -2562,8 +2563,13 @@ void Player::InitTalentForLevel()
else
SetFreeTalentPoints(talentPointsForLevel-m_usedTalentCount);
}
}
if(!GetSession()->PlayerLoading())
void Player::InitTalentForLevel()
{
UpdateFreeTalentPoints();
if (!GetSession()->PlayerLoading())
SendTalentsInfoData(false); // update at client
}
@ -3011,10 +3017,12 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
}
}
TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id);
if(!disabled_case) // skip new spell adding if spell already known (disabled spells case)
{
// talent: unlearn all other talent ranks (high and low)
if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id))
if (talentPos)
{
if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
{
@ -3100,11 +3108,45 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
return false;
}
uint32 talentCost = GetTalentSpellCost(spell_id);
if (talentPos)
{
// update talent map
PlayerTalentMap::iterator iter = m_talents[m_activeSpec].find(talentPos->talent_id);
if (iter != m_talents[m_activeSpec].end())
{
// check if ranks different or removed
if ((*iter).second.state == PLAYERSPELL_REMOVED || talentPos->rank != (*iter).second.currentRank)
{
(*iter).second.currentRank = talentPos->rank;
if ((*iter).second.state != PLAYERSPELL_NEW)
(*iter).second.state = PLAYERSPELL_CHANGED;
}
}
else
{
PlayerTalent talent;
talent.currentRank = talentPos->rank;
talent.m_talentEntry = sTalentStore.LookupEntry(talentPos->talent_id);
talent.state = IsInWorld() ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
m_talents[m_activeSpec][talentPos->talent_id] = talent;
}
// update used talent points count
m_usedTalentCount += GetTalentSpellCost(talentPos);
UpdateFreeTalentPoints(false);
}
// update free primary prof.points (if any, can be none in case GM .learn prof. learning)
if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
{
if(sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id))
SetFreePrimaryProfessions(freeProfs-1);
}
// cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
// note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
if (talentCost > 0 && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_LEARN_SPELL))
if (talentPos && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_LEARN_SPELL))
{
// ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
CastSpell(this, spell_id, true);
@ -3121,16 +3163,6 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
return false;
}
// update used talent points count
m_usedTalentCount += talentCost;
// update free primary prof.points (if any, can be none in case GM .learn prof. learning)
if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
{
if(sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id))
SetFreePrimaryProfessions(freeProfs-1);
}
// add dependent skills
uint16 maxskill = GetMaxSkillValueForLevel();
@ -3275,7 +3307,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
// re-search, it can be corrupted in prev loop
itr = m_spells.find(spell_id);
if (itr == m_spells.end())
if (itr == m_spells.end() || itr->second.state == PLAYERSPELL_REMOVED)
return; // already unleared
bool cur_active = itr->second.active;
@ -3302,14 +3334,30 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
if(PetAura const* petSpell = sSpellMgr.GetPetAura(spell_id, SpellEffectIndex(i)))
RemovePetAura(petSpell);
// free talent points
uint32 talentCosts = GetTalentSpellCost(spell_id);
if(talentCosts > 0)
TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id);
if (talentPos)
{
// update talent map
PlayerTalentMap::iterator iter = m_talents[m_activeSpec].find(talentPos->talent_id);
if (iter != m_talents[m_activeSpec].end())
{
if ((*iter).second.state != PLAYERSPELL_NEW)
(*iter).second.state = PLAYERSPELL_REMOVED;
else
m_talents[m_activeSpec].erase(iter);
}
else
sLog.outError("removeSpell: Player (GUID: %u) has talent spell (id: %u) but doesn't have talent",GetGUIDLow(), spell_id );
// free talent points
uint32 talentCosts = GetTalentSpellCost(talentPos);
if(talentCosts < m_usedTalentCount)
m_usedTalentCount -= talentCosts;
else
m_usedTalentCount = 0;
UpdateFreeTalentPoints(false);
}
// update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
@ -3397,7 +3445,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
// if talent then lesser rank also talent and need learn
if (talentCosts)
if (talentPos)
{
if(learn_low_rank)
learnSpell(prev_id, false);
@ -3433,6 +3481,17 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
}
}
if (m_canTitanGrip)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_TITAN_GRIP))
{
m_canTitanGrip = false;
if(sWorld.getConfig(CONFIG_BOOL_OFFHAND_CHECK_AT_TALENTS_RESET))
AutoUnequipOffhandIfNeed();
}
}
// remove from spell book if not replaced by lesser rank
if (!prev_activate && sendUpdate)
{
@ -3613,11 +3672,9 @@ bool Player::resetTalents(bool no_cost)
if(HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS,true);
uint32 talentPointsForLevel = CalculateTalentsPoints();
if (m_usedTalentCount == 0)
{
SetFreeTalentPoints(talentPointsForLevel);
UpdateFreeTalentPoints(false); // for fix if need counter
return false;
}
@ -3634,56 +3691,46 @@ bool Player::resetTalents(bool no_cost)
}
}
for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i)
for (PlayerTalentMap::iterator iter = m_talents[m_activeSpec].begin(); iter != m_talents[m_activeSpec].end();)
{
TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
if (iter->second.state == PLAYERSPELL_REMOVED)
{
++iter;
continue;
}
if (!talentInfo) continue;
TalentEntry const *talentInfo = (*iter).second.m_talentEntry;
if (!talentInfo)
{
iter = m_talents[m_activeSpec].erase(iter);
continue;
}
TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
if(!talentTabInfo)
if (!talentTabInfo)
{
iter = m_talents[m_activeSpec].erase(iter);
continue;
}
// unlearn only talents for character class
// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
// to prevent unexpected lost normal learned spell skip another class talents
if( (getClassMask() & talentTabInfo->ClassMask) == 0 )
if ((getClassMask() & talentTabInfo->ClassMask) == 0)
{
++iter;
continue;
}
for (int j = 0; j < MAX_TALENT_RANK; ++j)
{
for(PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();)
{
if(itr->second.state == PLAYERSPELL_REMOVED || itr->second.disabled)
{
++itr;
continue;
}
if (talentInfo->RankID[j])
removeSpell(talentInfo->RankID[j],!IsPassiveSpell(talentInfo->RankID[j]),false);
// remove learned spells (all ranks)
uint32 itrFirstId = sSpellMgr.GetFirstSpellInChain(itr->first);
// unlearn if first rank is talent or learned by talent
if (itrFirstId == talentInfo->RankID[j])
{
removeSpell(itr->first,!IsPassiveSpell(itr->first),false);
itr = GetSpellMap().begin();
continue;
}
else if (sSpellMgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId))
{
removeSpell(itr->first,!IsPassiveSpell(itr->first));
itr = GetSpellMap().begin();
continue;
}
else
++itr;
}
}
iter = m_talents[m_activeSpec].begin();
}
SetFreeTalentPoints(talentPointsForLevel);
UpdateFreeTalentPoints(false);
if(!no_cost)
{
@ -3704,15 +3751,6 @@ bool Player::resetTalents(bool no_cost)
RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
}
*/
if(m_canTitanGrip)
{
m_canTitanGrip = false;
if(sWorld.getConfig(CONFIG_BOOL_OFFHAND_CHECK_AT_TALENTS_RESET))
AutoUnequipOffhandIfNeed();
}
return true;
}
@ -4120,6 +4158,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'",guid,guid);
@ -4318,7 +4357,7 @@ Corpse* Player::CreateCorpse()
Corpse *corpse = new Corpse( (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE );
SetPvPDeath(false);
if(!corpse->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_CORPSE), this))
if (!corpse->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_CORPSE), this))
{
delete corpse;
return NULL;
@ -4342,11 +4381,11 @@ Corpse* Player::CreateCorpse()
corpse->SetUInt32Value( CORPSE_FIELD_BYTES_2, _cfb2 );
uint32 flags = CORPSE_FLAG_UNK2;
if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
flags |= CORPSE_FLAG_HIDE_HELM;
if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
flags |= CORPSE_FLAG_HIDE_CLOAK;
if(InBattleGround() && !InArena())
if (InBattleGround() && !InArena())
flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia
corpse->SetUInt32Value( CORPSE_FIELD_FLAGS, flags );
@ -4359,7 +4398,7 @@ Corpse* Player::CreateCorpse()
uint32 _cfi;
for (int i = 0; i < EQUIPMENT_SLOT_END; ++i)
{
if(m_items[i])
if (m_items[i])
{
iDisplayID = m_items[i]->GetProto()->DisplayInfoID;
iIventoryType = m_items[i]->GetProto()->InventoryType;
@ -4369,10 +4408,8 @@ Corpse* Player::CreateCorpse()
}
}
// we don't SaveToDB for players in battlegrounds so don't do it for corpses either
const MapEntry *entry = sMapStore.LookupEntry(corpse->GetMapId());
ASSERT(entry);
if(entry->map_type != MAP_BATTLEGROUND)
// we not need saved corpses for BG/arenas
if (!GetMap()->IsBattleGroundOrArena())
corpse->SaveToDB();
// register for player, but not show
@ -5664,10 +5701,10 @@ int16 Player::GetSkillTempBonusValue(uint32 skill) const
void Player::SendInitialActionButtons() const
{
sLog.outDetail( "Initializing Action Buttons for '%u'", GetGUIDLow() );
sLog.outDetail( "Initializing Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec);
WorldPacket data(SMSG_ACTION_BUTTONS, 1+(MAX_ACTION_BUTTONS*4));
data << uint8(1); // can be 0, 1, 2 (talent spec)
data << uint8(1); // talent spec amount (in packet)
ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec];
for(uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button)
{
@ -5679,7 +5716,7 @@ void Player::SendInitialActionButtons() const
}
GetSession()->SendPacket( &data );
sLog.outDetail( "Action Buttons for '%u' Initialized", GetGUIDLow() );
sLog.outDetail( "Action Buttons for '%u' spec '%u' Initialized", GetGUIDLow(), m_activeSpec );
}
bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type, Player* player, bool msg)
@ -15114,6 +15151,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
_LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
_LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS));
_LoadTalents(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTALENTS));
// after spell and quest load
InitTalentForLevel();
learnDefaultSpells();
@ -15906,7 +15945,17 @@ void Player::_LoadSpells(QueryResult *result)
{
Field *fields = result->Fetch();
addSpell(fields[0].GetUInt32(), fields[1].GetBool(), false, false, fields[2].GetBool());
uint32 spell_id = fields[0].GetUInt32();
// skip talents & drop unneeded data
if(GetTalentSpellPos(spell_id))
{
sLog.outError("Player::_LoadSpells: Player (GUID: %u) has talent spell in character_spell, removing it.", GetGUIDLow(), spell_id);
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id);
continue;
}
addSpell(spell_id, fields[1].GetBool(), false, false, fields[2].GetBool());
}
while( result->NextRow() );
@ -15914,6 +15963,82 @@ void Player::_LoadSpells(QueryResult *result)
}
}
void Player::_LoadTalents(QueryResult *result)
{
//QueryResult *result = CharacterDatabase.PQuery("SELECT talent_id, current_rank, spec FROM character_talent WHERE guid = '%u'",GetGUIDLow());
if (result)
{
do
{
Field *fields = result->Fetch();
uint32 talent_id = fields[0].GetUInt32();
TalentEntry const *talentInfo = sTalentStore.LookupEntry( talent_id );
if (!talentInfo)
{
sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent_id: %u , this talent will be deleted from character_talent",GetGUIDLow(), talent_id );
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE talent_id = '%u'", talent_id);
continue;
}
TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
if (!talentTabInfo)
{
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 );
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE talent_id = '%u'", talent_id);
continue;
}
// prevent load talent for different class (cheating)
if ((getClassMask() & talentTabInfo->ClassMask) == 0)
{
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 );
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND talent_id = '%u'", GetGUIDLow(), talent_id);
continue;
}
uint32 currentRank = fields[1].GetUInt32();
if (currentRank > MAX_TALENT_RANK || talentInfo->RankID[currentRank] == 0)
{
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 );
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND talent_id = '%u'", GetGUIDLow(), talent_id);
continue;
}
uint32 spec = fields[2].GetUInt32();
if (spec > MAX_TALENT_SPEC_COUNT)
{
sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent spec: %u, spec will be deleted from character_talent", GetGUIDLow(), spec);
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE spec = '%u' ", spec);
continue;
}
if (spec >= m_specsCount)
{
sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent spec: %u , this spec will be deleted from character_talent.", GetGUIDLow(), spec);
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND spec = '%u' ", GetGUIDLow(), spec);
continue;
}
if (m_activeSpec == spec)
addSpell(talentInfo->RankID[currentRank], true,false,false,false);
else
{
PlayerTalent talent;
talent.currentRank = currentRank;
talent.m_talentEntry = talentInfo;
talent.state = PLAYERSPELL_UNCHANGED;
m_talents[spec][talentInfo->TalentID] = talent;
}
}
while (result->NextRow());
delete result;
}
}
void Player::_LoadGroup(QueryResult *result)
{
//QueryResult *result = CharacterDatabase.PQuery("SELECT groupId FROM group_member WHERE memberGuid='%u'", GetGUIDLow());
@ -16405,6 +16530,7 @@ void Player::SaveToDB()
_SaveEquipmentSets();
GetSession()->SaveTutorialsData(); // changed only while character in game
_SaveGlyphs();
_SaveTalents();
CharacterDatabase.CommitTransaction();
@ -16750,12 +16876,17 @@ void Player::_SaveSpells()
{
for (PlayerSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end();)
{
if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first);
uint32 talentCosts = GetTalentSpellCost(itr->first);
// add only changed/new not dependent spells
if (!itr->second.dependent && (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED))
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);
if (!talentCosts)
{
if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first);
// add only changed/new not dependent spells
if (!itr->second.dependent && (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED))
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);
}
if (itr->second.state == PLAYERSPELL_REMOVED)
m_spells.erase(itr++);
@ -16768,6 +16899,30 @@ void Player::_SaveSpells()
}
}
void Player::_SaveTalents()
{
for (int32 i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
{
for (PlayerTalentMap::iterator itr = 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 talent_id = '%u' and spec = '%u'", GetGUIDLow(),itr->first, i);
// add only changed/new talents
if (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED)
CharacterDatabase.PExecute("INSERT INTO character_talent (guid, talent_id, current_rank , spec) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second.currentRank, i);
if (itr->second.state == PLAYERSPELL_REMOVED)
m_talents[i].erase(itr++);
else
{
itr->second.state = PLAYERSPELL_UNCHANGED;
++itr;
}
}
}
}
void Player::outDebugValues() const
{
if(!sLog.IsOutDebug()) // optimize disabled debug output
@ -20839,14 +20994,9 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
// find current max talent rank
uint32 curtalent_maxrank = 0;
for(int32 k = MAX_TALENT_RANK-1; k > -1; --k)
{
if(talentInfo->RankID[k] && HasSpell(talentInfo->RankID[k]))
{
curtalent_maxrank = k + 1;
break;
}
}
PlayerTalentMap::iterator itr = m_talents[m_activeSpec].find(talentId);
if (itr != m_talents[m_activeSpec].end() && itr->second.state != PLAYERSPELL_REMOVED)
curtalent_maxrank = itr->second.currentRank + 1;
// we already have same or higher talent rank learned
if(curtalent_maxrank >= (talentRank + 1))
@ -20862,12 +21012,14 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
{
bool hasEnoughRank = false;
for (int i = talentInfo->DependsOnRank; i < MAX_TALENT_RANK; ++i)
PlayerTalentMap::iterator dependsOnTalent = m_talents[m_activeSpec].find(depTalentInfo->TalentID);
if (dependsOnTalent != m_talents[m_activeSpec].end() && dependsOnTalent->second.state != PLAYERSPELL_REMOVED)
{
if (depTalentInfo->RankID[i] != 0)
if (HasSpell(depTalentInfo->RankID[i]))
hasEnoughRank = true;
PlayerTalent depTalent = (*dependsOnTalent).second;
if (depTalent.currentRank >= talentInfo->DependsOnRank)
hasEnoughRank = true;
}
if (!hasEnoughRank)
return;
}
@ -20879,28 +21031,9 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
uint32 tTab = talentInfo->TalentTab;
if (talentInfo->Row > 0)
{
unsigned int numRows = sTalentStore.GetNumRows();
for (unsigned int i = 0; i < numRows; ++i) // Loop through all talents.
{
// Someday, someone needs to revamp
const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i);
if (tmpTalent) // the way talents are tracked
{
if (tmpTalent->TalentTab == tTab)
{
for (int j = 0; j < MAX_TALENT_RANK; ++j)
{
if (tmpTalent->RankID[j] != 0)
{
if (HasSpell(tmpTalent->RankID[j]))
{
spentPoints += j + 1;
}
}
}
}
}
}
for (PlayerTalentMap::const_iterator iter = m_talents[m_activeSpec].begin(); iter != m_talents[m_activeSpec].end(); ++iter)
if (iter->second.state != PLAYERSPELL_REMOVED && iter->second.m_talentEntry->TalentTab == tTab)
spentPoints += iter->second.currentRank + 1;
}
// not have required min points spent in talent tree
@ -20922,9 +21055,6 @@ 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);
// update free talent points
SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
}
void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
@ -21057,9 +21187,6 @@ void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
// learn! (other talent ranks will unlearned at learning)
pet->learnSpell(spellid);
sLog.outDetail("PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
// update free talent points
pet->SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
}
void Player::UpdateKnownCurrencies(uint32 itemId, bool apply)
@ -21147,34 +21274,19 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket *data)
for(uint32 i = 0; i < 3; ++i)
{
uint32 talentTabId = talentTabIds[i];
for(uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
for(PlayerTalentMap::iterator iter = m_talents[specIdx].begin(); iter != m_talents[specIdx].end(); ++iter)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
if(!talentInfo)
PlayerTalent talent = (*iter).second;
if (talent.state == PLAYERSPELL_REMOVED)
continue;
// skip another tab talents
if(talentInfo->TalentTab != talentTabId)
if(talent.m_talentEntry->TalentTab != talentTabId)
continue;
// find max talent rank
int32 curtalent_maxrank = -1;
for(int32 k = MAX_TALENT_RANK-1; k > -1; --k)
{
if(talentInfo->RankID[k] && HasSpell(talentInfo->RankID[k]))
{
curtalent_maxrank = k;
break;
}
}
// not learned talent
if(curtalent_maxrank < 0)
continue;
*data << uint32(talentInfo->TalentID); // Talent.dbc
*data << uint8(curtalent_maxrank); // talentMaxRank (0-4)
*data << uint32(talent.m_talentEntry->TalentID); // Talent.dbc
*data << uint8(talent.currentRank); // talentMaxRank (0-4)
++talentIdCount;
}
@ -21449,13 +21561,75 @@ void Player::ActivateSpec(uint8 specNum)
if(specNum >= GetSpecsCount())
return;
// unlearn GetActiveSpec() talents (not learned in specNum);
// learn specNum talents
UnsummonPetTemporaryIfAny();
ApplyGlyphs(false);
// copy of new talent spec (we will use it as model for converting current tlanet state to new)
PlayerTalentMap tempSpec = m_talents[specNum];
// copy old spec talents to new one, must be before spec switch to have previous spec num(as m_activeSpec)
m_talents[specNum] = m_talents[m_activeSpec];
SetActiveSpec(specNum);
// remove all talent spells that don't exist in next spec but exist in old
for (PlayerTalentMap::iterator specIter = m_talents[m_activeSpec].begin(); specIter != m_talents[m_activeSpec].end();)
{
PlayerTalent& talent = (*specIter).second;
if (talent.state == PLAYERSPELL_REMOVED)
{
++specIter;
continue;
}
PlayerTalentMap::iterator iterTempSpec = tempSpec.find(specIter->first);
// remove any talent rank if talent not listed in temp spec
if (iterTempSpec == tempSpec.end() || iterTempSpec->second.state == PLAYERSPELL_REMOVED)
{
for(int r = 0; r < MAX_TALENT_RANK; ++r)
if (talent.m_talentEntry->RankID[r])
removeSpell(talent.m_talentEntry->RankID[r],!IsPassiveSpell(talent.m_talentEntry->RankID[r]),false);
specIter = m_talents[m_activeSpec].begin();
}
else
++specIter;
}
// now new spec data have only talents (maybe different rank) as in temp spec data, sync ranks then.
for (PlayerTalentMap::const_iterator tempIter = tempSpec.begin(); tempIter != tempSpec.end(); ++tempIter)
{
PlayerTalent const& talent = (*tempIter).second;
// removed state talent already unlearned in prev. loop
// but we need restore it if it deleted for finish removed-marked data in DB
if (talent.state == PLAYERSPELL_REMOVED)
{
m_talents[m_activeSpec][tempIter->first] = talent;
continue;
}
// learn talent spells if they not in new spec (old spec copy)
// and if they have different rank
PlayerTalentMap::iterator specIter = m_talents[m_activeSpec].find(tempIter->first);
if (specIter != m_talents[m_activeSpec].end() && specIter->second.state != PLAYERSPELL_REMOVED)
{
if ((*specIter).second.currentRank != talent.currentRank)
learnSpell(talent.m_talentEntry->RankID[talent.currentRank], false);
}
else
learnSpell(talent.m_talentEntry->RankID[talent.currentRank], false);
// sync states - original state is changed in addSpell that learnSpell calls
specIter = m_talents[m_activeSpec].find(tempIter->first);
(*specIter).second.state = talent.state;
}
InitTalentForLevel();
// recheck action buttons (not checked at loading/spec copy)
ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec];
for(ActionButtonList::const_iterator itr = currentActionButtonList.begin(); itr != currentActionButtonList.end(); ++itr)
@ -21464,11 +21638,17 @@ void Player::ActivateSpec(uint8 specNum)
if (!IsActionButtonDataValid(itr->first,itr->second.GetAction(),itr->second.GetType(), this, false))
removeActionButton(m_activeSpec,itr->first);
ResummonPetTemporaryUnSummonedIfAny();
ApplyGlyphs(true);
SendInitialActionButtons();
InitTalentForLevel();
Powers pw = getPowerType();
if(pw != POWER_MANA)
SetPower(POWER_MANA, 0);
SetPower(pw, 0);
}
void Player::UpdateSpecCount(uint8 count)