[12161] Update talents:

Fix talent learn
Fix talent reset
Fix talent inspecting

original author: @Shauren

Signed-off-by: Yaki Khadafi <elsoldollo@gmail.com>
This commit is contained in:
Yaki Khadafi 2012-08-31 12:53:05 +03:00 committed by Antz
parent 578c440902
commit 55ebf1b64f
16 changed files with 284 additions and 109 deletions

View file

@ -416,6 +416,7 @@ Player::Player(WorldSession* session): Unit(), m_mover(this), m_camera(this), m_
m_usedTalentCount = 0;
m_questRewardTalentCount = 0;
m_freeTalentPoints = 0;
m_regenTimer = 0;
m_weaponChangeTimer = 0;
@ -535,6 +536,8 @@ Player::Player(WorldSession* session): Unit(), m_mover(this), m_camera(this), m_
m_activeSpec = 0;
m_specsCount = 1;
for (int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
m_talentsPrimaryTree[i] = 0;
for (int i = 0; i < BASEMOD_END; ++i)
{
@ -2637,6 +2640,12 @@ void Player::UpdateFreeTalentPoints(bool resetIfNeed)
}
else
{
if (m_specsCount == 0)
{
m_specsCount = 1;
m_activeSpec = 0;
}
uint32 talentPointsForLevel = CalculateTalentsPoints();
// if used more that have then reset
@ -3837,6 +3846,17 @@ bool Player::resetTalents(bool no_cost, bool all_specs)
iter = m_talents[m_activeSpec].begin();
}
// Remove spec specific spells
for (uint32 i = 0; i < MAX_TALENT_TABS; ++i)
{
std::vector<uint32> const* specSpells = GetTalentTreePrimarySpells(GetTalentTabPages(getClass())[i]);
if (specSpells)
for (size_t i = 0; i < specSpells->size(); ++i)
removeSpell(specSpells->at(i), true);
}
m_talentsPrimaryTree[m_activeSpec] = 0;
// for not current spec just mark removed all saved to DB case and drop not saved
if (all_specs)
{
@ -15193,11 +15213,11 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
// SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags,"
// 12 13 14 15 16 17 18 19 20 21 22 23 24
//"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost,"
// 25 26 27 28 29 30 31 32 33 34 35 36 37 38
//"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,"
// 39 40 41 42 43 44
// 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
//"resettalents_time, primary_trees, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty,"
// 40 41 42 43 44 45
//"totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk,"
// 45 46 47 48 49 50 51 52 53 54 55 56 57
// 46 47 48 49 50 51 52 53 54 55 56 57 58
//"health, power1, power2, power3, power4, power5, specCount, activeSpec, exploredZones, equipmentCache, knownTitles, actionBars, slot FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM);
@ -15248,8 +15268,8 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8());
SetUInt32Value(PLAYER_XP, fields[7].GetUInt32());
_LoadIntoDataField(fields[53].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE);
_LoadIntoDataField(fields[55].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2);
_LoadIntoDataField(fields[54].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE);
_LoadIntoDataField(fields[56].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2);
InitDisplayIds(); // model, scale and model data
@ -15267,17 +15287,17 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
SetUInt32Value(PLAYER_BYTES, fields[9].GetUInt32());
SetUInt32Value(PLAYER_BYTES_2, fields[10].GetUInt32());
m_drunk = fields[44].GetUInt16();
m_drunk = fields[45].GetUInt16();
SetUInt16Value(PLAYER_BYTES_3, 0, (m_drunk & 0xFFFE) | gender);
SetUInt32Value(PLAYER_FLAGS, fields[11].GetUInt32());
SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[43].GetInt32());
SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[44].GetInt32());
// Action bars state
SetByteValue(PLAYER_FIELD_BYTES, 2, fields[56].GetUInt8());
SetByteValue(PLAYER_FIELD_BYTES, 2, fields[57].GetUInt8());
m_slot = fields[57].GetUInt8();
m_slot = fields[58].GetUInt8();
// cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
@ -15307,11 +15327,11 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
InitPrimaryProfessions(); // to max set before any spell loaded
// init saved position, and fix it later if problematic
uint32 transGUID = fields[30].GetUInt32();
uint32 transGUID = fields[31].GetUInt32();
Relocate(fields[12].GetFloat(), fields[13].GetFloat(), fields[14].GetFloat(), fields[16].GetFloat());
SetLocationMapId(fields[15].GetUInt32());
uint32 difficulty = fields[38].GetUInt32();
uint32 difficulty = fields[39].GetUInt32();
if (difficulty >= MAX_DUNGEON_DIFFICULTY)
difficulty = DUNGEON_DIFFICULTY_NORMAL;
SetDungeonDifficulty(Difficulty(difficulty)); // may be changed in _LoadGroup
@ -15336,9 +15356,9 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
SetArenaTeamInfoField(arena_slot, ArenaTeamInfoType(j), 0);
}
SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, fields[39].GetUInt32());
SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[40].GetUInt16());
SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[41].GetUInt16());
SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, fields[40].GetUInt32());
SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[41].GetUInt16());
SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[42].GetUInt16());
_LoadBoundInstances(holder->GetResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES));
@ -15411,7 +15431,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
if (transGUID != 0)
{
m_movementInfo.SetTransportData(ObjectGuid(HIGHGUID_MO_TRANSPORT, transGUID), fields[26].GetFloat(), fields[27].GetFloat(), fields[28].GetFloat(), fields[29].GetFloat(), 0, -1);
m_movementInfo.SetTransportData(ObjectGuid(HIGHGUID_MO_TRANSPORT, transGUID), fields[27].GetFloat(), fields[28].GetFloat(), fields[29].GetFloat(), fields[30].GetFloat(), 0, -1);
if (!MaNGOS::IsValidMapCoord(
GetPositionX() + m_movementInfo.GetTransportPos()->x, GetPositionY() + m_movementInfo.GetTransportPos()->y,
@ -15524,22 +15544,22 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
m_taxi.LoadTaxiMask(fields[17].GetString()); // must be before InitTaxiNodesForLevel
uint32 extraflags = fields[31].GetUInt32();
uint32 extraflags = fields[32].GetUInt32();
m_stableSlots = fields[32].GetUInt32();
m_stableSlots = fields[33].GetUInt32();
if (m_stableSlots > MAX_PET_STABLES)
{
sLog.outError("Player can have not more %u stable slots, but have in DB %u", MAX_PET_STABLES, uint32(m_stableSlots));
m_stableSlots = MAX_PET_STABLES;
}
m_atLoginFlags = fields[33].GetUInt32();
m_atLoginFlags = fields[34].GetUInt32();
m_deathExpireTime = (time_t)fields[36].GetUInt64();
m_deathExpireTime = (time_t)fields[37].GetUInt64();
if (m_deathExpireTime > now + MAX_DEATH_COUNT * DEATH_EXPIRE_STEP)
m_deathExpireTime = now + MAX_DEATH_COUNT * DEATH_EXPIRE_STEP - 1;
std::string taxi_nodes = fields[37].GetCppString();
std::string taxi_nodes = fields[38].GetCppString();
// clear channel spell data (if saved at channel spell casting)
SetChannelObjectGuid(ObjectGuid());
@ -15601,8 +15621,8 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
_LoadMailedItems(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILEDITEMS));
UpdateNextMailTimeAndUnreads();
m_specsCount = fields[51].GetUInt8();
m_activeSpec = fields[52].GetUInt8();
m_specsCount = fields[52].GetUInt8();
m_activeSpec = fields[53].GetUInt8();
_LoadGlyphs(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGLYPHS));
@ -15643,7 +15663,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
// check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES
// note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded
uint32 curTitle = fields[42].GetUInt32();
uint32 curTitle = fields[43].GetUInt32();
if (curTitle && !HasTitle(curTitle))
curTitle = 0;
@ -15711,19 +15731,33 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
UpdateAllStats();
// restore remembered power/health values (but not more max values)
uint32 savedhealth = fields[45].GetUInt32();
uint32 savedhealth = fields[46].GetUInt32();
SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth);
static_assert(MAX_STORED_POWERS == 5, "Query not updated.");
for (uint32 i = 0; i < MAX_STORED_POWERS; ++i)
{
uint32 savedpower = fields[46 + i].GetUInt32();
uint32 savedpower = fields[47 + i].GetUInt32();
SetPowerByIndex(i, std::min(savedpower, GetMaxPowerByIndex(i)));
}
DEBUG_FILTER_LOG(LOG_FILTER_PLAYER_STATS, "The value of player %s after load item and aura is: ", m_name.c_str());
outDebugStatsValues();
// must be after loading spells and talents
Tokens talentTrees = StrSplit(fields[26].GetString(), " ");
for (uint8 i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
{
if (i >= talentTrees.size())
break;
uint32 talentTree = atol(talentTrees[i].c_str());
if (sTalentTabStore.LookupEntry(talentTree))
m_talentsPrimaryTree[i] = talentTree;
else if (i == m_activeSpec)
SetAtLoginFlag(AT_LOGIN_RESET_TALENTS); // invalid tree, reset talents
}
// all fields read
delete result;
@ -15993,8 +16027,6 @@ void Player::_LoadGlyphs(QueryResult* result)
while (result->NextRow());
delete result;
}
void Player::LoadCorpse()
@ -17034,7 +17066,7 @@ void Player::SaveToDB()
SqlStatement uberInsert = CharacterDatabase.CreateStatement(insChar, "INSERT INTO characters (guid,account,name,race,class,gender,level,xp,money,playerBytes,playerBytes2,playerFlags,"
"map, dungeon_difficulty, position_x, position_y, position_z, orientation, "
"taximask, online, cinematic, "
"totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, "
"totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, primary_trees, "
"trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, "
"death_expire_time, taxi_path, totalKills, "
"todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, "
@ -17042,7 +17074,7 @@ void Player::SaveToDB()
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
"?, ?, ?, ?, ?, ?, "
"?, ?, ?, "
"?, ?, ?, ?, ?, ?, ?, "
"?, ?, ?, ?, ?, ?, ?, ?, "
"?, ?, ?, ?, ?, ?, ?, ?, ?, "
"?, ?, ?, "
"?, ?, ?, ?, ?, ?, ?, ?, ?, "
@ -17098,6 +17130,10 @@ void Player::SaveToDB()
// save, but in tavern/city
uberInsert.addUInt32(m_resetTalentsCost);
uberInsert.addUInt64(uint64(m_resetTalentsTime));
ss.str("");
for (int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
ss << m_talentsPrimaryTree[i] << " ";
uberInsert.addString(ss);
uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->x));
uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->y));
@ -17129,7 +17165,6 @@ void Player::SaveToDB()
uberInsert.addUInt32(GetUInt32Value(PLAYER_CHOSEN_TITLE));
// FIXME: at this moment send to DB as unsigned, including unit32(-1)
uberInsert.addUInt32(GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX));
@ -17358,20 +17393,20 @@ void Player::_SaveGlyphs()
{
SqlStatement stmt = CharacterDatabase.CreateStatement(insertGlyph, "INSERT INTO character_glyphs (guid, spec, slot, glyph) VALUES (?, ?, ?, ?)");
stmt.PExecute(GetGUIDLow(), spec, slot, m_glyphs[spec][slot].GetId());
break;
}
break;
case GLYPH_CHANGED:
{
SqlStatement stmt = CharacterDatabase.CreateStatement(updateGlyph, "UPDATE character_glyphs SET glyph = ? WHERE guid = ? AND spec = ? AND slot = ?");
stmt.PExecute(m_glyphs[spec][slot].GetId(), GetGUIDLow(), spec, slot);
break;
}
break;
case GLYPH_DELETED:
{
SqlStatement stmt = CharacterDatabase.CreateStatement(deleteGlyph, "DELETE FROM character_glyphs WHERE guid = ? AND spec = ? AND slot = ?");
stmt.PExecute(GetGUIDLow(), spec, slot);
break;
}
break;
case GLYPH_UNCHANGED:
break;
}
@ -21382,25 +21417,21 @@ uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 n
void Player::InitGlyphsForLevel()
{
for (uint32 i = 0; i < sGlyphSlotStore.GetNumRows(); ++i)
uint32 slot = 0;
for (uint32 i = 0; i < sGlyphSlotStore.GetNumRows() && slot < MAX_GLYPH_SLOT_INDEX; ++i)
if (GlyphSlotEntry const* gs = sGlyphSlotStore.LookupEntry(i))
if (gs->Order)
SetGlyphSlot(gs->Order - 1, gs->Id);
SetGlyphSlot(slot++, gs->Id);
uint32 level = getLevel();
uint32 value = 0;
// 0x3F = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 for 80 level
if (level >= 15)
value |= (0x01 | 0x02);
if (level >= 30)
value |= 0x08;
if (level >= 25)
value |= 0x01 | 0x02 | 0x40;
if (level >= 50)
value |= 0x04;
if (level >= 70)
value |= 0x10;
if (level >= 80)
value |= 0x20;
value |= 0x04 | 0x08 | 0x80;
if (level >= 75)
value |= 0x10 | 0x20 | 0x100;
SetUInt32Value(PLAYER_GLYPHS_ENABLED, value);
}
@ -21665,10 +21696,30 @@ Item* Player::ConvertItem(Item* item, uint32 newItemId)
uint32 Player::CalculateTalentsPoints() const
{
uint32 base_level = getClass() == CLASS_DEATH_KNIGHT ? 55 : 9;
uint32 base_talent = getLevel() <= base_level ? 0 : getLevel() - base_level;
// this dbc file has entries only up to level 100
NumTalentsAtLevelEntry const* count = sNumTalentsAtLevelStore.LookupEntry(std::min<uint32>(getLevel(), 100));
if (!count)
return 0;
uint32 talentPointsForLevel = base_talent + m_questRewardTalentCount;
float baseForLevel = count->Talents;
if (getClass() != CLASS_DEATH_KNIGHT)
return uint32(baseForLevel * sWorld.getConfig(CONFIG_FLOAT_RATE_TALENT));
// Death Knight starting level
// hardcoded here - number of quest awarded talents is equal to number of talents any other class would have at level 55
if (getLevel() < 55)
return 0;
NumTalentsAtLevelEntry const* dkBase = sNumTalentsAtLevelStore.LookupEntry(55);
if (!dkBase)
return 0;
float talentPointsForLevel = count->Talents - dkBase->Talents;
talentPointsForLevel += float(m_questRewardTalentCount);
if (talentPointsForLevel > baseForLevel)
talentPointsForLevel = baseForLevel;
return uint32(talentPointsForLevel * sWorld.getConfig(CONFIG_FLOAT_RATE_TALENT));
}
@ -21990,29 +22041,29 @@ SpellEntry const* Player::GetKnownTalentRankById(int32 talentId) const
return NULL;
}
void Player::LearnTalent(uint32 talentId, uint32 talentRank)
bool Player::LearnTalent(uint32 talentId, uint32 talentRank)
{
uint32 CurTalentPoints = GetFreeTalentPoints();
if (CurTalentPoints == 0)
return;
return false;
if (talentRank >= MAX_TALENT_RANK)
return;
return false;
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
if (!talentInfo)
return;
return false;
TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
if (!talentTabInfo)
return;
return false;
// prevent learn talent for different class (cheating)
if ((getClassMask() & talentTabInfo->ClassMask) == 0)
return;
return false;
// find current max talent rank
uint32 curtalent_maxrank = 0;
@ -22021,14 +22072,14 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
// we already have same or higher talent rank learned
if (curtalent_maxrank >= (talentRank + 1))
return;
return false;
// check if we have enough talent points
if (CurTalentPoints < (talentRank - curtalent_maxrank + 1))
return;
return false;
// Check if it requires another talent
/*if (talentInfo->DependsOn > 0)
if (talentInfo->DependsOn > 0)
{
if (TalentEntry const* depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
{
@ -22042,40 +22093,75 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
}
if (!hasEnoughRank)
return;
return false;
}
}*/
}
// Find out how many points we have in this field
uint32 spentPoints = 0;
uint32 primaryTreeTalents = 0;
uint32 tTab = talentInfo->TalentTab;
if (talentInfo->Row > 0)
bool isMainTree = m_talentsPrimaryTree[m_activeSpec] == tTab || !m_talentsPrimaryTree[m_activeSpec];
if (talentInfo->Row > 0 || !isMainTree)
{
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.talentEntry->TalentTab == tTab)
spentPoints += iter->second.currentRank + 1;
for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++) // Loop through all talents.
{
if (TalentEntry const* tmpTalent = sTalentStore.LookupEntry(i)) // Someday, someone needs to revamp the way talents are tracked
{
for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++)
{
if (tmpTalent->RankID[rank] != 0)
{
if (HasSpell(tmpTalent->RankID[rank]))
{
if (tmpTalent->TalentTab == tTab)
spentPoints += (rank + 1);
if (tmpTalent->TalentTab == m_talentsPrimaryTree[m_activeSpec])
primaryTreeTalents += (rank + 1);
}
}
}
}
}
}
// not have required min points spent in talent tree
if (spentPoints < (talentInfo->Row * MAX_TALENT_RANK))
return;
return false;
// player has not spent 31 talents in main tree before attempting to learn other tree's talents
if (!isMainTree && primaryTreeTalents < REQ_PRIMARY_TREE_TALENTS)
return false;
// spell not set in talent.dbc
uint32 spellid = talentInfo->RankID[talentRank];
if (spellid == 0)
{
sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
return;
return false;
}
// already known
if (HasSpell(spellid))
return;
return false;
// learn! (other talent ranks will unlearned at learning)
learnSpell(spellid, false);
DETAIL_LOG("TalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
// set talent tree for player
if (!m_talentsPrimaryTree[m_activeSpec])
{
m_talentsPrimaryTree[m_activeSpec] = talentInfo->TalentTab;
std::vector<uint32> const* specSpells = GetTalentTreePrimarySpells(talentInfo->TalentTab);
if (specSpells)
for (size_t i = 0; i < specSpells->size(); ++i)
learnSpell(specSpells->at(i), false);
}
return true;
}
void Player::LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRank)
@ -22142,7 +22228,7 @@ void Player::LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRa
return;
// Check if it requires another talent
/*if (talentInfo->DependsOn > 0)
if (talentInfo->DependsOn > 0)
{
if (TalentEntry const* depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
{
@ -22156,7 +22242,7 @@ void Player::LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRa
if (!hasEnoughRank)
return;
}
}*/
}
// Find out how many points we have in this field
uint32 spentPoints = 0;
@ -22267,9 +22353,13 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket* data)
if (m_specsCount)
{
if (m_specsCount > MAX_TALENT_SPEC_COUNT)
m_specsCount = MAX_TALENT_SPEC_COUNT;
// loop through all specs (only 1 for now)
for (uint32 specIdx = 0; specIdx < m_specsCount; ++specIdx)
{
*data << uint32(m_talentsPrimaryTree[specIdx]);
uint8 talentIdCount = 0;
size_t pos = data->wpos();
*data << uint8(talentIdCount); // [PH], talentIdCount
@ -22277,7 +22367,7 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket* data)
// find class talent tabs (all players have 3 talent tabs)
uint32 const* talentTabIds = GetTalentTabPages(getClass());
for (uint32 i = 0; i < 3; ++i)
for (uint32 i = 0; i < MAX_TALENT_TABS; ++i)
{
uint32 talentTabId = talentTabIds[i];
for (PlayerTalentMap::iterator iter = m_talents[specIdx].begin(); iter != m_talents[specIdx].end(); ++iter)
@ -22636,6 +22726,15 @@ void Player::ActivateSpec(uint8 specNum)
// prevent deletion of action buttons by client at spell unlearn or by player while spec change in progress
SendLockActionButtons();
// Remove spec specific spells
for (uint32 i = 0; i < MAX_TALENT_TABS; ++i)
{
std::vector<uint32> const* specSpells = GetTalentTreePrimarySpells(GetTalentTabPages(getClass())[i]);
if (specSpells)
for (size_t i = 0; i < specSpells->size(); ++i)
removeSpell(specSpells->at(i), true);
}
ApplyGlyphs(false);
// copy of new talent spec (we will use it as model for converting current tlanet state to new)
@ -22738,6 +22837,11 @@ void Player::ActivateSpec(uint8 specNum)
ResummonPetTemporaryUnSummonedIfAny();
std::vector<uint32> const* specSpells = GetTalentTreePrimarySpells(m_talentsPrimaryTree[m_activeSpec]);
if (specSpells)
for (size_t i = 0; i < specSpells->size(); ++i)
learnSpell(specSpells->at(i), false);
ApplyGlyphs(true);
SendInitialActionButtons();
@ -22747,6 +22851,9 @@ void Player::ActivateSpec(uint8 specNum)
SetPower(POWER_MANA, 0);
SetPower(pw, 0);
if (!sTalentTabStore.LookupEntry(m_talentsPrimaryTree[m_activeSpec]))
resetTalents(true);
}
void Player::UpdateSpecCount(uint8 count)