aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/characters/3.4.x/2023_11_20_00_characters.sql2
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp4
-rw-r--r--src/server/game/DataStores/DBCEnums.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp352
-rw-r--r--src/server/game/Entities/Player/Player.h34
-rw-r--r--src/server/game/Handlers/InspectHandler.cpp2
-rw-r--r--src/server/game/Handlers/SkillHandler.cpp22
-rw-r--r--src/server/game/Handlers/TraitHandler.cpp114
-rw-r--r--src/server/game/Server/Packets/TalentPackets.cpp6
-rw-r--r--src/server/game/Server/Packets/TalentPackets.h13
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp2
-rw-r--r--src/server/game/Server/WorldSession.h2
-rw-r--r--src/server/scripts/Commands/cs_learn.cpp2
13 files changed, 236 insertions, 320 deletions
diff --git a/sql/updates/characters/3.4.x/2023_11_20_00_characters.sql b/sql/updates/characters/3.4.x/2023_11_20_00_characters.sql
new file mode 100644
index 00000000000..3c8be1ba07d
--- /dev/null
+++ b/sql/updates/characters/3.4.x/2023_11_20_00_characters.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `character_talent`
+ ADD COLUMN `talentRank` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `talentId`;
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index a47068200b9..7cd5bb2a8a7 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -150,7 +150,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"appearance17, appearance18, mainHandEnchant, offHandEnchant FROM character_transmog_outfits WHERE guid = ? ORDER BY setindex", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_BGDATA, "SELECT instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell, queueId FROM character_battleground_data WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_GLYPHS, "SELECT talentGroup, glyphId FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_SEL_CHARACTER_TALENTS, "SELECT talentId, talentGroup FROM character_talent WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_TALENTS, "SELECT talentId, talentRank, talentGroup FROM character_talent WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_SKILLS, "SELECT skill, value, max, professionSlot FROM character_skills WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_RANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_BANNED, "SELECT guid FROM character_banned WHERE guid = ? AND active = 1", CONNECTION_ASYNC);
@@ -658,7 +658,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_PETITION_BY_OWNER, "DELETE FROM petition WHERE ownerguid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER, "DELETE FROM petition_sign WHERE ownerguid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHAR_GLYPHS, "INSERT INTO character_glyphs VALUES(?, ?, ?)", CONNECTION_ASYNC);
- PrepareStatement(CHAR_INS_CHAR_TALENT, "INSERT INTO character_talent (guid, talentId, talentGroup) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_TALENT, "INSERT INTO character_talent (guid, talentId, talentRank, talentGroup) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_LIST_SLOT, "UPDATE characters SET slot = ? WHERE guid = ? AND account = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHAR_FISHINGSTEPS, "INSERT INTO character_fishingsteps (guid, fishingSteps) VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_FISHINGSTEPS, "DELETE FROM character_fishingsteps WHERE guid = ?", CONNECTION_ASYNC);
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h
index 0e6d293fc68..0911216fa15 100644
--- a/src/server/game/DataStores/DBCEnums.h
+++ b/src/server/game/DataStores/DBCEnums.h
@@ -2129,6 +2129,7 @@ DEFINE_ENUM_FLAG(SummonPropertiesFlags);
#define MAX_TALENT_TIERS 11
#define MAX_TALENT_COLUMNS 4
+#define MAX_TALENT_RANK 9
enum class TaxiNodeFlags : int32
{
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index a06f5754ca3..88cb94e4aba 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -2414,8 +2414,9 @@ bool Player::IsMaxLevel() const
void Player::InitTalentForLevel()
{
uint8 level = GetLevel();
- int32 talentPoints = CalculateTalentsPoints();
- if (level < MIN_SPECIALIZATION_LEVEL)
+ uint32 talentPoints = CalculateTalentsPoints();
+ uint32 spentTalentPoints = GetSpentTalentPointsCount();
+ if (spentTalentPoints && level < MIN_SPECIALIZATION_LEVEL)
{
// Remove all talent points
ResetTalents(true);
@@ -2424,7 +2425,8 @@ void Player::InitTalentForLevel()
if (GetSpentTalentPointsCount() > talentPoints)
ResetTalents(true);
- SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::MaxTalentTiers), talentPoints);
+ SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::MaxTalentTiers), static_cast<int32>(talentPoints));
+ SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::CharacterPoints), static_cast<int32>(talentPoints - spentTalentPoints));
if (!GetSession()->PlayerLoading())
SendTalentsInfoData(); // update at client
@@ -2709,33 +2711,56 @@ void DeleteSpellFromAllPlayers(uint32 spellId)
CharacterDatabase.Execute(stmt);
}
-bool Player::AddTalent(TalentEntry const* talent, uint8 spec, bool learning)
+bool Player::AddTalent(TalentEntry const* talent, uint16 rank, uint8 talentGroupId, bool learning)
{
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellID, DIFFICULTY_NONE);
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellRank[rank], DIFFICULTY_NONE);
if (!spellInfo)
{
- TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) does not exist.", talent->SpellID);
+ // do character spell book cleanup (all characters)
+ if (!IsInWorld() && !learning) // spell load case
+ {
+ TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) does not exist. Deleting for all characters in `character_spell` and `character_talent`.", talent->SpellRank[rank]);
+ DeleteSpellFromAllPlayers(talent->SpellRank[rank]);
+ }
+ else
+ TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) does not exist", talent->SpellRank[rank]);
+
return false;
}
if (!SpellMgr::IsSpellValid(spellInfo, this, false))
{
- TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) is invalid", talent->SpellID);
+ // do character spell book cleanup (all characters)
+ if (!IsInWorld() && !learning) // spell load case
+ {
+ TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) is invalid. Deleting for all characters in `character_spell` and `character_talent`.", talent->SpellRank[rank]);
+ DeleteSpellFromAllPlayers(talent->SpellRank[rank]);
+ }
+ else
+ TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) is invalid", talent->SpellRank[rank]);
+
return false;
}
- PlayerTalentMap::iterator itr = GetTalentMap(spec)->find(talent->ID);
- if (itr != GetTalentMap(spec)->end())
- itr->second = PLAYERSPELL_UNCHANGED;
- else
- (*GetTalentMap(spec))[talent->ID] = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
-
- if (spec == GetActiveTalentGroup())
+ PlayerTalentMap& talentMap = GetPlayerTalentMap(talentGroupId);
+ auto itr = talentMap.find(talent->ID);
+ if (itr != talentMap.end())
{
- LearnSpell(talent->SpellID, true);
- if (talent->OverridesSpellID)
- AddOverrideSpell(talent->OverridesSpellID, talent->SpellID);
+
+
+ itr->second.State = PLAYERSPELL_UNCHANGED;
+ itr->second.Rank = static_cast<uint8>(rank);
}
+ else
+ talentMap[talent->ID] = { PLAYERSPELL_UNCHANGED, static_cast<uint8>(rank) };
+
+ // Inactive talent groups will only be initialized
+ if (GetActiveTalentGroup() != talentGroupId)
+ return true;
+
+ LearnSpell(spellInfo->Id, true);
+ if (talent->OverridesSpellID)
+ AddOverrideSpell(talent->OverridesSpellID, talent->SpellID);
if (learning)
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2::ChangeTalent);
@@ -2745,11 +2770,20 @@ bool Player::AddTalent(TalentEntry const* talent, uint8 spec, bool learning)
void Player::RemoveTalent(TalentEntry const* talent)
{
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellID, DIFFICULTY_NONE);
+ PlayerTalentMap& talentMap = GetPlayerTalentMap(GetActiveTalentGroup());
+ auto itr = talentMap.find(talent->ID);
+ if (itr == talentMap.end())
+ return;
+
+ uint32 spellId = talent->SpellRank[itr->second.Rank];
+ if (!spellId)
+ return;
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE);
if (!spellInfo)
return;
- RemoveSpell(talent->SpellID, true);
+ RemoveSpell(spellId, true);
// search for spells that the talent teaches and unlearn them
for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
@@ -2759,10 +2793,9 @@ void Player::RemoveTalent(TalentEntry const* talent)
if (talent->OverridesSpellID)
RemoveOverrideSpell(talent->OverridesSpellID, talent->SpellID);
- // if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
- PlayerTalentMap::iterator plrTalent = GetTalentMap(GetActiveTalentGroup())->find(talent->ID);
- if (plrTalent != GetTalentMap(GetActiveTalentGroup())->end())
- plrTalent->second = PLAYERSPELL_REMOVED;
+ // Mark the talent as deleted to it will be deleted upon the next save cycle
+ itr->second.State = PLAYERSPELL_REMOVED;
+ itr->second.Rank = 0;
}
void Player::AddStoredAuraTeleportLocation(uint32 spellId)
@@ -3573,23 +3606,25 @@ bool Player::ResetTalents(bool noCost)
RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT, true);
- for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
+ PlayerTalentMap const& talentMap = GetPlayerTalentMap(GetActiveTalentGroup());
+ for (auto const& pair : talentMap)
{
- TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
- if (!talentInfo)
+ TalentEntry const* talentEntry = sTalentStore.LookupEntry(pair.first);
+ if (!talentEntry)
continue;
+ /*
+ TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TabID);
+ if (!talentTabInfo)
+ 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 (talentInfo->ClassID != GetClass())
- continue;
-
- // skip non-existent talent ranks
- if (talentInfo->SpellID == 0)
+ if ((GetClassMask() & talentTabInfo->ClassMask) == 0)
continue;
+ */
- RemoveTalent(talentInfo);
+ RemoveTalent(talentEntry);
}
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
@@ -3615,6 +3650,8 @@ bool Player::ResetTalents(bool noCost)
}
*/
+ InitTalentForLevel();
+
return true;
}
@@ -3812,12 +3849,6 @@ bool Player::HasSpell(uint32 spell) const
!itr->second.disabled);
}
-bool Player::HasTalent(uint32 talentId, uint8 group) const
-{
- PlayerTalentMap::const_iterator itr = GetTalentMap(group)->find(talentId);
- return (itr != GetTalentMap(group)->end() && itr->second != PLAYERSPELL_REMOVED);
-}
-
bool Player::HasActiveSpell(uint32 spell) const
{
PlayerSpellMap::const_iterator itr = m_spells.find(spell);
@@ -26086,94 +26117,98 @@ bool Player::ModifierTreeSatisfied(uint32 modifierTreeId) const
return m_achievementMgr->ModifierTreeSatisfied(modifierTreeId);
}
-TalentLearnResult Player::LearnTalent(uint32 talentId, int32* spellOnCooldown)
-{
- if (IsInCombat())
- return TALENT_FAILED_AFFECTING_COMBAT;
+static constexpr uint8 NEEDED_TALENT_POINT_PER_TIER = 5;
- if (isDead())
- return TALENT_FAILED_CANT_DO_THAT_RIGHT_NOW;
+bool Player::LearnTalent(uint32 talentId, uint16 requestedRank)
+{
+ // No talent points left to spend, skip learn request
+ if (!m_activePlayerData->CharacterPoints)
+ return false;
- if (GetPrimarySpecialization() == ChrSpecialization::None)
- return TALENT_FAILED_NO_PRIMARY_TREE_SELECTED;
+ if (requestedRank >= MAX_TALENT_RANK)
+ return false;
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
if (!talentInfo)
- return TALENT_FAILED_UNKNOWN;
+ return false;
- if (talentInfo->SpecID && ChrSpecialization(talentInfo->SpecID) != GetPrimarySpecialization())
- return TALENT_FAILED_UNKNOWN;
+ /*
+ TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TabID);
+ if (!talentTabInfo)
+ return;
// prevent learn talent for different class (cheating)
- if (talentInfo->ClassID != GetClass())
- return TALENT_FAILED_UNKNOWN;
-
- // check if we have enough talent points
- if (talentInfo->TierID >= m_activePlayerData->MaxTalentTiers)
- return TALENT_FAILED_UNKNOWN;
+ if (!(GetClassMask() & talentTabInfo->ClassMask))
+ return;
+ */
- // TODO: prevent changing talents that are on cooldown
+ // Check for existing talents
- // Check if there is a different talent for us to learn in selected slot
- // Example situation:
- // Warrior talent row 2 slot 0
- // Talent.dbc has an entry for each specialization
- // but only 2 out of 3 have SpecID != 0
- // We need to make sure that if player is in one of these defined specs he will not learn the other choice
- TalentEntry const* bestSlotMatch = nullptr;
- for (TalentEntry const* talent : sDB2Manager.GetTalentsByPosition(GetClass(), talentInfo->TierID, talentInfo->ColumnIndex))
+ uint32 neededTalentPoints = 0;
+ PlayerTalentMap const& talentMap = GetPlayerTalentMap(GetActiveTalentGroup());
+ auto itr = talentMap.find(talentId);
+ if (itr != talentMap.end())
{
- if (!talent->SpecID)
- bestSlotMatch = talent;
- else if (ChrSpecialization(talent->SpecID) == GetPrimarySpecialization())
- {
- bestSlotMatch = talent;
- break;
- }
+ // We already know this or a higher rank of this talent
+ if (itr->second.Rank >= requestedRank)
+ return false;
+
+ neededTalentPoints = (itr->second.Rank - requestedRank) + 1;
}
+ else
+ neededTalentPoints = requestedRank + 1;
- if (talentInfo != bestSlotMatch)
- return TALENT_FAILED_UNKNOWN;
+ // Not enough talent points to learn the talent at this rank
+ if (neededTalentPoints > static_cast<uint32>(m_activePlayerData->CharacterPoints))
+ return false;
- // Check if player doesn't have any talent in current tier
- for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c)
+ // Check talent dependencies
+ for (size_t i = 0; i < talentInfo->PrereqRank.size(); ++i)
{
- for (TalentEntry const* talent : sDB2Manager.GetTalentsByPosition(GetClass(), talentInfo->TierID, c))
- {
- if (!HasTalent(talent->ID, GetActiveTalentGroup()))
- continue;
+ if (!talentInfo->PrereqTalent[i])
+ continue;
- if (!HasPlayerFlag(PLAYER_FLAGS_RESTING) && !HasUnitFlag2(UNIT_FLAG2_ALLOW_CHANGING_TALENTS))
- return TALENT_FAILED_REST_AREA;
+ itr = talentMap.find(talentInfo->PrereqTalent[i]);
+ if (itr == talentMap.end() || itr->second.Rank < talentInfo->PrereqRank[i])
+ return false;
+ }
- if (GetSpellHistory()->HasCooldown(talent->SpellID))
- {
- *spellOnCooldown = talent->SpellID;
- return TALENT_FAILED_CANT_REMOVE_TALENT;
- }
+ // Find out how many points we have in this field
+ /*
+ uint32 spentPoints = 0;
+ uint32 tTab = talentInfo->TabID;
+ if (talentInfo->TierID > 0)
+ for (TalentEntry const* tmpTalent : sTalentStore) // the way talents are tracked
+ if (tmpTalent->TabID == tTab)
+ for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++)
+ if (tmpTalent->SpellRank[rank] != 0)
+ if (HasSpell(tmpTalent->SpellRank[rank]))
+ spentPoints += (rank + 1);
- RemoveTalent(talent);
- }
- }
+ // not have required min points spent in talent tree
+ if (spentPoints < (talentInfo->TierID * NEEDED_TALENT_POINT_PER_TIER))
+ return;
+ >*/
// spell not set in talent.dbc
- uint32 spellid = talentInfo->SpellID;
- if (!spellid)
+ uint32 spellId = talentInfo->SpellRank[requestedRank];
+ if (spellId == 0)
{
TC_LOG_ERROR("entities.player", "Player::LearnTalent: Talent.dbc has no spellInfo for talent: {} (spell id = 0)", talentId);
- return TALENT_FAILED_UNKNOWN;
+ return false;
}
// already known
- if (HasTalent(talentId, GetActiveTalentGroup()) || HasSpell(spellid))
- return TALENT_FAILED_UNKNOWN;
+ if (HasSpell(spellId))
+ return false;
- if (!AddTalent(talentInfo, GetActiveTalentGroup(), true))
- return TALENT_FAILED_UNKNOWN;
+ AddTalent(talentInfo, requestedRank, GetActiveTalentGroup(), true);
- TC_LOG_DEBUG("misc", "Player::LearnTalent: TalentID: {} Spell: {} Group: {}\n", talentId, spellid, GetActiveTalentGroup());
+ TC_LOG_DEBUG("misc", "Player::LearnTalent: TalentID: {} Spell: {} Group: {}\n", talentId, spellId, uint32(GetActiveTalentGroup()));
- return TALENT_LEARN_OK;
+ // update free talent points
+ SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::CharacterPoints), static_cast<int32>(CalculateTalentsPoints() - GetSpentTalentPointsCount()));
+ return true;
}
void Player::EnablePvpRules(bool dueToCombat /*= false*/)
@@ -26367,39 +26402,37 @@ void Player::SendTalentsInfoData()
uint8 activeGroup = GetActiveTalentGroup();
packet.ActiveGroup = activeGroup;
- packet.UnspentTalentPoints = CalculateTalentsPoints() - GetSpentTalentPointsCount();
+ packet.UnspentTalentPoints = m_activePlayerData->CharacterPoints;
for (uint8 i = 0; i < (1 + GetBonusTalentGroupCount()); ++i)
{
WorldPackets::Talent::TalentGroupInfo& groupInfo = packet.TalentGroupInfos.emplace_back();
groupInfo.SpecID = MAX_SPECIALIZATIONS;
- if (PlayerTalentMap const* talentMap = GetTalentMap(i))
+ PlayerTalentMap const& talentMap = GetPlayerTalentMap(i);
+ groupInfo.Talents.reserve(talentMap.size());
+
+ for (auto const& pair : talentMap)
{
- groupInfo.Talents.reserve(talentMap->size());
+ if (pair.second.State == PLAYERSPELL_REMOVED)
+ continue;
- for (auto const& pair : *talentMap)
+ TalentEntry const* talentInfo = sTalentStore.LookupEntry(pair.first);
+ if (!talentInfo)
{
- if (pair.second == PLAYERSPELL_REMOVED)
- continue;
-
- TalentEntry const* talentInfo = sTalentStore.LookupEntry(pair.first);
- if (!talentInfo)
- {
- TC_LOG_ERROR("entities.player", "Player::SendTalentsInfoData: Player '{}' ({}) has unknown talent id: {}",
- GetName(), GetGUID().ToString(), pair.first);
- continue;
- }
-
- SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID, DIFFICULTY_NONE);
- if (!spellEntry)
- {
- TC_LOG_ERROR("entities.player", "Player::SendTalentsInfoData: Player '{}' ({}) has unknown talent spell: {}",
- GetName(), GetGUID().ToString(), talentInfo->SpellID);
- continue;
- }
+ TC_LOG_ERROR("entities.player", "Player::SendTalentsInfoData: Player '{}' ({}) has unknown talent id: {}",
+ GetName(), GetGUID().ToString(), pair.first);
+ continue;
+ }
- groupInfo.Talents.push_back(uint16(pair.first));
+ SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellRank[pair.second.Rank], DIFFICULTY_NONE);
+ if (!spellEntry)
+ {
+ TC_LOG_ERROR("entities.player", "Player::SendTalentsInfoData: Player '{}' ({}) has unknown talent spell: {}",
+ GetName(), GetGUID().ToString(), talentInfo->SpellID);
+ continue;
}
+
+ groupInfo.Talents.push_back({ pair.first, pair.second.Rank });
}
std::vector<uint32> glyphs = GetGlyphs(activeGroup);
@@ -26676,12 +26709,12 @@ void Player::_SaveGlyphs(CharacterDatabaseTransaction trans) const
void Player::_LoadTalents(PreparedQueryResult result)
{
- // "SELECT talentId, talentGroup FROM character_talent WHERE guid = ?"
+ // "SELECT talentId, talentRank, talentGroup FROM character_talent WHERE guid = ?"
if (result)
{
do
if (TalentEntry const* talent = sTalentStore.LookupEntry((*result)[0].GetUInt32()))
- AddTalent(talent, (*result)[1].GetUInt8(), false);
+ AddTalent(talent, (*result)[1].GetUInt8(), (*result)[2].GetUInt8(), false);
while (result->NextRow());
}
}
@@ -26834,19 +26867,20 @@ void Player::_SaveTalents(CharacterDatabaseTransaction trans)
for (uint8 group = 0; group < MAX_SPECIALIZATIONS; ++group)
{
- PlayerTalentMap* talents = GetTalentMap(group);
- for (auto itr = talents->begin(); itr != talents->end();)
+ PlayerTalentMap& talents = _specializationInfo.Talents[group];
+ for (auto itr = talents.begin(); itr != talents.end();)
{
- if (itr->second == PLAYERSPELL_REMOVED)
+ if (itr->second.State == PLAYERSPELL_REMOVED)
{
- itr = talents->erase(itr);
+ itr = talents.erase(itr);
continue;
}
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_TALENT);
stmt->setUInt64(0, GetGUID().GetCounter());
stmt->setUInt32(1, itr->first);
- stmt->setUInt8(2, group);
+ stmt->setUInt8(2, itr->second.Rank);
+ stmt->setUInt8(3, group);
trans->Append(stmt);
++itr;
}
@@ -26989,37 +27023,31 @@ void Player::ActivateTalentGroup(uint8 talentGroup)
// Let client clear his current Actions
SendActionButtons(2);
- // m_actionButtons.clear() is called in the next _LoadActionButtons
- for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
- {
- TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
- if (!talentInfo)
- 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 (talentInfo->ClassID != GetClass())
- continue;
-
- if (talentInfo->SpellID == 0)
+ PlayerTalentMap const& talentMap = GetPlayerTalentMap(GetActiveTalentGroup());
+ for (auto const& pair : talentMap)
+ {
+ TalentEntry const* talentEntry = sTalentStore.LookupEntry(pair.first);
+ if (!talentEntry)
continue;
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talentInfo->SpellID, DIFFICULTY_NONE);
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talentEntry->SpellRank[pair.second.Rank], DIFFICULTY_NONE);
if (!spellInfo)
continue;
- RemoveSpell(talentInfo->SpellID, true);
+ RemoveSpell(spellInfo->Id, true);
// search for spells that the talent teaches and unlearn them
for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL) && spellEffectInfo.TriggerSpell > 0)
RemoveSpell(spellEffectInfo.TriggerSpell, true);
- if (talentInfo->OverridesSpellID)
- RemoveOverrideSpell(talentInfo->OverridesSpellID, talentInfo->SpellID);
+ if (talentEntry->OverridesSpellID)
+ RemoveOverrideSpell(talentEntry->OverridesSpellID, talentEntry->SpellID);
}
+ // m_actionButtons.clear() is called in the next _LoadActionButtons
+
ApplyTraitConfig(m_activePlayerData->ActiveCombatTraitConfigID, false);
for (uint32 glyphId : GetGlyphs(GetActiveTalentGroup()))
@@ -27028,26 +27056,21 @@ void Player::ActivateTalentGroup(uint8 talentGroup)
SetActiveTalentGroup(talentGroup);
SetPrimarySpecialization(0);
- for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
+ // if the talent can be found in the newly activated PlayerTalentMap
+ PlayerTalentMap const& activatedTalentMap = GetPlayerTalentMap(GetActiveTalentGroup());
+ for (auto const& pair : activatedTalentMap)
{
- TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
- if (!talentInfo)
- continue;
-
- // learn only talents for character class
- if (talentInfo->ClassID != GetClass())
+ TalentEntry const* talentEntry = sTalentStore.LookupEntry(pair.first);
+ if (!talentEntry)
continue;
- if (!talentInfo->SpellID)
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talentEntry->SpellRank[pair.second.Rank], DIFFICULTY_NONE);
+ if (!spellInfo)
continue;
- // if the talent can be found in the newly activated PlayerTalentMap
- if (HasTalent(talentInfo->ID, GetActiveTalentGroup()))
- {
- LearnSpell(talentInfo->SpellID, true); // add the talent to the PlayerSpellMap
- if (talentInfo->OverridesSpellID)
- AddOverrideSpell(talentInfo->OverridesSpellID, talentInfo->SpellID);
- }
+ LearnSpell(spellInfo->Id, true); // add the talent to the PlayerSpellMap
+ if (talentEntry->OverridesSpellID)
+ AddOverrideSpell(talentEntry->OverridesSpellID, talentEntry->SpellID);
}
InitTalentForLevel();
@@ -28732,8 +28755,13 @@ void Player::SetBonusTalentGroupCount(uint8 amount)
uint32 Player::GetSpentTalentPointsCount() const
{
- PlayerTalentMap const& talentMap = _specializationInfo.Talents[GetActiveTalentGroup()];
- return std::count_if(talentMap.begin(), talentMap.end(), [](auto const& pair) { return (pair.second != PLAYERSPELL_REMOVED); });
+ PlayerTalentMap const& talentMap = GetPlayerTalentMap(GetActiveTalentGroup());
+ uint32 count = 0;
+ for (auto const& pair : talentMap)
+ if (pair.second.State != PLAYERSPELL_REMOVED)
+ count += pair.second.Rank + 1;
+
+ return count;
}
ChrSpecializationEntry const* Player::GetPrimarySpecializationEntry() const
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 55e67283755..b3c613fa794 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -264,7 +264,13 @@ struct PlayerCurrency
CurrencyDbFlags Flags;
};
-typedef std::unordered_map<uint32, PlayerSpellState> PlayerTalentMap;
+struct PlayerTalentState
+{
+ PlayerSpellState State = PLAYERSPELL_UNCHANGED;
+ uint8 Rank = 0;
+};
+
+typedef std::unordered_map<uint32, PlayerTalentState> PlayerTalentMap;
typedef std::unordered_map<uint32, PlayerSpell> PlayerSpellMap;
typedef std::unordered_set<SpellModifier*> SpellModContainer;
typedef std::unordered_map<uint32, PlayerCurrency> PlayerCurrenciesMap;
@@ -1047,24 +1053,8 @@ struct GroupUpdateCounter
int32 UpdateSequenceNumber;
};
-enum TalentLearnResult : int32
-{
- TALENT_LEARN_OK = 0,
- TALENT_FAILED_UNKNOWN = 1,
- TALENT_FAILED_NOT_ENOUGH_TALENTS_IN_PRIMARY_TREE = 2,
- TALENT_FAILED_NO_PRIMARY_TREE_SELECTED = 3,
- TALENT_FAILED_CANT_DO_THAT_RIGHT_NOW = 4,
- TALENT_FAILED_AFFECTING_COMBAT = 5,
- TALENT_FAILED_CANT_REMOVE_TALENT = 6,
- TALENT_FAILED_CANT_DO_THAT_CHALLENGE_MODE_ACTIVE = 7,
- TALENT_FAILED_REST_AREA = 8,
- TALENT_FAILED_UNSPENT_TALENT_POINTS = 9,
- TALENT_FAILED_IN_PVP_MATCH = 10
-};
-
struct TC_GAME_API SpecializationInfo
{
-
SpecializationInfo() : ResetTalentsCost(0), ResetTalentsTime(0), ActiveGroup(0), BonusGroups(0) { }
PlayerTalentMap Talents[MAX_SPECIALIZATIONS];
@@ -1818,6 +1808,9 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SetTalentResetCost(uint32 cost) { _specializationInfo.ResetTalentsCost = cost; }
time_t GetTalentResetTime() const { return _specializationInfo.ResetTalentsTime; }
void SetTalentResetTime(time_t time_) { _specializationInfo.ResetTalentsTime = time_; }
+ PlayerTalentMap const& GetPlayerTalentMap(uint8 talentGroupId) const { return _specializationInfo.Talents[talentGroupId]; }
+ PlayerTalentMap& GetPlayerTalentMap(uint8 talentGroupId) { return _specializationInfo.Talents[talentGroupId]; }
+
ChrSpecialization GetPrimarySpecialization() const { return ChrSpecialization(*m_playerData->CurrentSpecID); }
void SetPrimarySpecialization(uint32 spec) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_playerData).ModifyValue(&UF::PlayerData::CurrentSpecID), spec); }
uint8 GetActiveTalentGroup() const { return _specializationInfo.ActiveGroup; }
@@ -1833,9 +1826,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void InitTalentForLevel();
void SendTalentsInfoData();
uint32 CalculateTalentsPoints() const;
- TalentLearnResult LearnTalent(uint32 talentId, int32* spellOnCooldown);
- bool AddTalent(TalentEntry const* talent, uint8 spec, bool learning);
- bool HasTalent(uint32 spell_id, uint8 spec) const;
+ bool LearnTalent(uint32 talentId, uint16 requestedRank);
+ bool AddTalent(TalentEntry const* talent, uint16 rank, uint8 talentGroupId, bool learning);
void RemoveTalent(TalentEntry const* talent);
void EnablePvpRules(bool dueToCombat = false);
@@ -1847,8 +1839,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
// Dual Spec
void ActivateTalentGroup(uint8 talentGroup);
- PlayerTalentMap const* GetTalentMap(uint8 spec) const { return &_specializationInfo.Talents[spec]; }
- PlayerTalentMap* GetTalentMap(uint8 spec) { return &_specializationInfo.Talents[spec]; }
std::vector<uint32> const& GetGlyphs(uint8 spec) const { return _specializationInfo.Glyphs[spec]; }
std::vector<uint32>& GetGlyphs(uint8 spec) { return _specializationInfo.Glyphs[spec]; }
ActionButtonList const& GetActionButtons() const { return m_actionButtons; }
diff --git a/src/server/game/Handlers/InspectHandler.cpp b/src/server/game/Handlers/InspectHandler.cpp
index 865dcc25152..87023704fa5 100644
--- a/src/server/game/Handlers/InspectHandler.cpp
+++ b/src/server/game/Handlers/InspectHandler.cpp
@@ -47,10 +47,12 @@ void WorldSession::HandleInspectOpcode(WorldPackets::Inspect::Inspect& inspect)
if (GetPlayer()->CanBeGameMaster() || sWorld->getIntConfig(CONFIG_TALENTS_INSPECTING) + (GetPlayer()->GetEffectiveTeam() == player->GetEffectiveTeam()) > 1)
{
+ /*
PlayerTalentMap const* talents = player->GetTalentMap(player->GetActiveTalentGroup());
for (PlayerTalentMap::value_type const& v : *talents)
if (v.second != PLAYERSPELL_REMOVED)
inspectResult.Talents.push_back(v.first);
+ */
inspectResult.TalentTraits.Level = player->GetLevel();
inspectResult.TalentTraits.ChrSpecializationID = AsUnderlyingType(player->GetPrimarySpecialization());
diff --git a/src/server/game/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp
index f7447b6ba7a..4316db154d8 100644
--- a/src/server/game/Handlers/SkillHandler.cpp
+++ b/src/server/game/Handlers/SkillHandler.cpp
@@ -28,25 +28,11 @@
void WorldSession::HandleLearnTalentsOpcode(WorldPackets::Talent::LearnTalents& packet)
{
- WorldPackets::Talent::LearnTalentFailed learnTalentFailed;
- bool anythingLearned = false;
- for (uint32 talentId : packet.Talents)
- {
- if (TalentLearnResult result = _player->LearnTalent(talentId, &learnTalentFailed.SpellID))
- {
- if (!learnTalentFailed.Reason)
- learnTalentFailed.Reason = result;
-
- learnTalentFailed.Talents.push_back(talentId);
- }
- else
- anythingLearned = true;
- }
-
- if (learnTalentFailed.Reason)
- SendPacket(learnTalentFailed.Write());
+}
- if (anythingLearned)
+void WorldSession::HandleLearnTalentOpcode(WorldPackets::Talent::LearnTalent& packet)
+{
+ if (_player->LearnTalent(packet.TalentID, packet.RequestedRank))
_player->SendTalentsInfoData();
}
diff --git a/src/server/game/Handlers/TraitHandler.cpp b/src/server/game/Handlers/TraitHandler.cpp
index 1392ecf8ba9..05eb3ce4d21 100644
--- a/src/server/game/Handlers/TraitHandler.cpp
+++ b/src/server/game/Handlers/TraitHandler.cpp
@@ -25,120 +25,6 @@
void WorldSession::HandleTraitsCommitConfig(WorldPackets::Traits::TraitsCommitConfig const& traitsCommitConfig)
{
- int32 configId = traitsCommitConfig.Config.ID;
- UF::TraitConfig const* existingConfig = _player->GetTraitConfig(configId);
- if (!existingConfig)
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, 0, TALENT_FAILED_UNKNOWN).Write());
- return;
- }
-
- if (_player->IsInCombat())
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, 0, TALENT_FAILED_AFFECTING_COMBAT).Write());
- return;
- }
-
- if (_player->GetBattleground() && _player->GetBattleground()->GetStatus() == STATUS_IN_PROGRESS)
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, 0, TALENT_FAILED_IN_PVP_MATCH).Write());
- return;
- }
-
- auto findEntry = [](WorldPackets::Traits::TraitConfig& config, int32 traitNodeId, int32 traitNodeEntryId) -> WorldPackets::Traits::TraitEntry*
- {
- auto entryItr = std::find_if(config.Entries.begin(), config.Entries.end(), [=](WorldPackets::Traits::TraitEntry const& traitEntry)
- {
- return traitEntry.TraitNodeID == traitNodeId && traitEntry.TraitNodeEntryID == traitNodeEntryId;
- });
- return entryItr != config.Entries.end() ? &*entryItr : nullptr;
- };
-
- bool hasRemovedEntries = false;
- WorldPackets::Traits::TraitConfig newConfigState(*existingConfig);
- for (WorldPackets::Traits::TraitEntry const& newEntry : traitsCommitConfig.Config.Entries)
- {
- WorldPackets::Traits::TraitEntry* traitEntry = findEntry(newConfigState, newEntry.TraitNodeID, newEntry.TraitNodeEntryID);
- if (traitEntry && traitEntry->Rank > newEntry.Rank)
- {
- TraitNodeEntry const* traitNode = sTraitNodeStore.LookupEntry(newEntry.TraitNodeID);
- if (!traitNode)
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, 0, TALENT_FAILED_UNKNOWN).Write());
- return;
- }
-
- TraitTreeEntry const* traitTree = sTraitTreeStore.LookupEntry(traitNode->TraitTreeID);
- if (!traitTree)
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, 0, TALENT_FAILED_UNKNOWN).Write());
- return;
- }
-
- if (traitTree->GetFlags().HasFlag(TraitTreeFlag::CannotRefund))
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, 0, TALENT_FAILED_CANT_REMOVE_TALENT).Write());
- return;
- }
-
- TraitNodeEntryEntry const* traitNodeEntry = sTraitNodeEntryStore.LookupEntry(newEntry.TraitNodeEntryID);
- if (!traitNodeEntry)
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, 0, TALENT_FAILED_UNKNOWN).Write());
- return;
- }
-
- TraitDefinitionEntry const* traitDefinition = sTraitDefinitionStore.LookupEntry(traitNodeEntry->TraitDefinitionID);
- if (!traitDefinition)
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, 0, TALENT_FAILED_UNKNOWN).Write());
- return;
- }
-
- if (traitDefinition->SpellID && _player->GetSpellHistory()->HasCooldown(traitDefinition->SpellID))
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, traitDefinition->SpellID, TALENT_FAILED_CANT_REMOVE_TALENT).Write());
- return;
- }
-
- if (traitDefinition->VisibleSpellID && _player->GetSpellHistory()->HasCooldown(traitDefinition->VisibleSpellID))
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, traitDefinition->VisibleSpellID, TALENT_FAILED_CANT_REMOVE_TALENT).Write());
- return;
- }
-
- hasRemovedEntries = true;
- }
-
- if (traitEntry)
- {
- if (newEntry.Rank)
- traitEntry->Rank = newEntry.Rank;
- else
- newConfigState.Entries.erase(std::remove_if(newConfigState.Entries.begin(), newConfigState.Entries.end(), [&newEntry](WorldPackets::Traits::TraitEntry const& traitEntry)
- {
- return traitEntry.TraitNodeID == newEntry.TraitNodeID && traitEntry.TraitNodeEntryID == newEntry.TraitNodeEntryID;
- }), newConfigState.Entries.end());
- }
- else
- newConfigState.Entries.emplace_back() = newEntry;
- }
-
- TraitMgr::LearnResult validationResult = TraitMgr::ValidateConfig(newConfigState, _player, true);
- if (validationResult != TraitMgr::LearnResult::Ok)
- {
- SendPacket(WorldPackets::Traits::TraitConfigCommitFailed(configId, 0, AsUnderlyingType(validationResult)).Write());
- return;
- }
-
- bool needsCastTime = newConfigState.Type == TraitConfigType::Combat && hasRemovedEntries;
-
- if (traitsCommitConfig.SavedLocalIdentifier)
- newConfigState.LocalIdentifier = traitsCommitConfig.SavedLocalIdentifier;
- else if (UF::TraitConfig const* savedConfig = _player->GetTraitConfig(traitsCommitConfig.SavedLocalIdentifier))
- newConfigState.LocalIdentifier = savedConfig->LocalIdentifier;
-
- _player->UpdateTraitConfig(std::move(newConfigState), traitsCommitConfig.SavedConfigID, needsCastTime);
}
void WorldSession::HandleClassTalentsRequestNewConfig(WorldPackets::Traits::ClassTalentsRequestNewConfig& classTalentsRequestNewConfig)
diff --git a/src/server/game/Server/Packets/TalentPackets.cpp b/src/server/game/Server/Packets/TalentPackets.cpp
index d715348d90f..4237f45f7c7 100644
--- a/src/server/game/Server/Packets/TalentPackets.cpp
+++ b/src/server/game/Server/Packets/TalentPackets.cpp
@@ -78,6 +78,12 @@ void WorldPackets::Talent::LearnTalents::Read()
_worldPacket >> Talents[i];
}
+void WorldPackets::Talent::LearnTalent::Read()
+{
+ _worldPacket >> TalentID;
+ _worldPacket >> RequestedRank;
+}
+
WorldPacket const* WorldPackets::Talent::RespecWipeConfirm::Write()
{
_worldPacket << int8(RespecType);
diff --git a/src/server/game/Server/Packets/TalentPackets.h b/src/server/game/Server/Packets/TalentPackets.h
index 494866575ae..9bcd2418ca9 100644
--- a/src/server/game/Server/Packets/TalentPackets.h
+++ b/src/server/game/Server/Packets/TalentPackets.h
@@ -42,7 +42,7 @@ namespace WorldPackets
struct TalentGroupInfo
{
int32 SpecID = 0;
- std::vector<uint16> Talents;
+ std::vector<TalentInfo> Talents;
std::vector<uint16> GlyphIDs;
};
@@ -143,6 +143,17 @@ namespace WorldPackets
int32 SpellID = 0;
std::vector<PvPTalent> Talents;
};
+
+ class LearnTalent final : public ClientPacket
+ {
+ public:
+ LearnTalent(WorldPacket&& packet) : ClientPacket(CMSG_LEARN_TALENT, std::move(packet)) { }
+
+ void Read() override;
+
+ int32 TalentID = 0;
+ uint16 RequestedRank = 0;
+ };
}
}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 9a1279a183d..82b7ddd7c7f 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -557,7 +557,7 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_LEARN_TALENTS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleLearnTalentsOpcode);
DEFINE_HANDLER(CMSG_LEARN_PREVIEW_TALENTS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_LEARN_PREVIEW_TALENTS_PET, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_LEARN_TALENT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
+ DEFINE_HANDLER(CMSG_LEARN_TALENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnTalentOpcode);
DEFINE_HANDLER(CMSG_LEAVE_GROUP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLeaveGroupOpcode);
DEFINE_HANDLER(CMSG_LEAVE_PET_BATTLE_QUEUE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_LFG_LIST_APPLY_TO_GROUP, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 3d29bc83b7e..c974768ff78 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -733,6 +733,7 @@ namespace WorldPackets
namespace Talent
{
+ class LearnTalent;
class LearnTalents;
class LearnPvpTalents;
class ConfirmRespecWipe;
@@ -1514,6 +1515,7 @@ class TC_GAME_API WorldSession
void HandleLearnPvpTalentsOpcode(WorldPackets::Talent::LearnPvpTalents& packet);
void HandleLearnTalentsOpcode(WorldPackets::Talent::LearnTalents& packet);
+ void HandleLearnTalentOpcode(WorldPackets::Talent::LearnTalent& packet);
void HandleConfirmRespecWipeOpcode(WorldPackets::Talent::ConfirmRespecWipe& confirmRespecWipe);
void HandleUnlearnSkillOpcode(WorldPackets::Spells::UnlearnSkill& packet);
void HandleTradeSkillSetFavorite(WorldPackets::Spells::TradeSkillSetFavorite const& tradeSkillSetFavorite);
diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp
index 2986bb3ce13..239acc7505c 100644
--- a/src/server/scripts/Commands/cs_learn.cpp
+++ b/src/server/scripts/Commands/cs_learn.cpp
@@ -183,6 +183,7 @@ public:
static bool HandleLearnAllTalentsCommand(ChatHandler* handler)
{
+ /*
Player* player = handler->GetSession()->GetPlayer();
uint32 playerClass = player->GetClass();
@@ -209,6 +210,7 @@ public:
player->SendTalentsInfoData();
handler->SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS);
+ */
return true;
}