aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRat <gmstreetrat@gmail.com>2014-12-04 19:23:35 +0100
committerRat <gmstreetrat@gmail.com>2014-12-04 19:23:35 +0100
commit08f486bfcf77d007aeb324b5c941433545234b0e (patch)
treed5f4ae067e15e8800c097d0da40f31339e147e8d /src
parentdf2514f0444a1b39f07ed63208f5db3ea4eb5c6d (diff)
Core/Spells: some updates to player spell and talent learning
Diffstat (limited to 'src')
-rw-r--r--src/server/game/DataStores/DBCStores.cpp15
-rw-r--r--src/server/game/DataStores/DBCStores.h2
-rw-r--r--src/server/game/Entities/Player/Player.cpp551
-rw-r--r--src/server/game/Entities/Player/Player.h93
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp2
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp81
-rw-r--r--src/server/game/Globals/ObjectMgr.h2
-rw-r--r--src/server/game/Handlers/SkillHandler.cpp57
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp23
-rw-r--r--src/server/game/Server/Packets/SpellPackets.cpp11
-rw-r--r--src/server/game/Server/Packets/SpellPackets.h10
-rw-r--r--src/server/game/Server/Packets/TalentPackets.cpp14
-rw-r--r--src/server/game/Server/Packets/TalentPackets.h13
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp4
-rw-r--r--src/server/game/Server/Protocol/Opcodes.h4
-rw-r--r--src/server/game/Server/WorldSession.h3
-rw-r--r--src/server/scripts/Commands/cs_learn.cpp4
17 files changed, 532 insertions, 357 deletions
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index cc85b56615b..f23dd49102b 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -518,7 +518,10 @@ void LoadDBCStores(const std::string& dataPath)
SpecializationSpellsEntry const* specSpells = sSpecializationSpellsStore.LookupEntry(i);
if (!specSpells)
continue;
- sSpecializationSpellsBySpecStore[specSpells->SpecID].insert(specSpells);
+ sSpecializationSpellsBySpecStore[specSpells->SpecID].push_back(specSpells);
+
+ if (specSpells->OverridesSpellID)
+ sSpecializationOverrideSpellMap[specSpells->SpecID][specSpells->OverridesSpellID] = specSpells->SpellID;
}
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellStore, dbcPath, "Spell.dbc"/*, &CustomSpellEntryfmt, &CustomSpellEntryIndex*/);
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCategoriesStore, dbcPath, "SpellCategories.dbc");//15595
@@ -1256,16 +1259,16 @@ std::list<uint32> GetSpellsForLevels(uint32 classId, uint32 raceMask, uint32 spe
if (!specializationId)
return spellList;
- SpecializationSpellsMap::const_iterator specIter = sSpecializationSpellsMap.find(specializationId);
- if (specIter != sSpecializationSpellsMap.end())
+ SpecializationSpellsBySpecStore::const_iterator specIter = sSpecializationSpellsBySpecStore.find(specializationId);
+ if (specIter != sSpecializationSpellsBySpecStore.end())
{
- const std::vector<uint32>& learnSpellList = specIter->second;
+ SpecializationSpellsBySpecEntry learnSpellList = specIter->second;
for (int i = 0; i < learnSpellList.size(); i++)
{
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(learnSpellList[i]);
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(learnSpellList[i]->SpellID);
if (!spellInfo)
{
- TC_LOG_ERROR("spells", "GetSpellsForLevels: spell %u not found in spellstore", learnSpellList[i]);
+ TC_LOG_ERROR("spells", "GetSpellsForLevels: spell %u not found in spellstore", learnSpellList[i]->SpellID);
continue;
}
if (spellInfo->SpellLevel <= minLevel || spellInfo->SpellLevel > maxLevel)
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index a6ecadfaf45..633376d85aa 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -93,7 +93,7 @@ typedef std::unordered_multimap<uint32, SkillRaceClassInfoEntry const*> SkillRac
typedef std::pair<SkillRaceClassInfoMap::iterator, SkillRaceClassInfoMap::iterator> SkillRaceClassInfoBounds;
SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_);
-typedef std::set<SpecializationSpellsEntry const*> SpecializationSpellsBySpecEntry;
+typedef std::vector<SpecializationSpellsEntry const*> SpecializationSpellsBySpecEntry;
typedef std::unordered_map<uint32, SpecializationSpellsBySpecEntry> SpecializationSpellsBySpecStore;
typedef ChrSpecializationEntry const* ChrSpecializationByIndexArray[MAX_CLASSES][MAX_SPECIALIZATIONS];
typedef std::unordered_map<uint32, TalentEntry const*> TalentBySpellIDMap;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index d18f8542f16..740c96753ab 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -1122,7 +1122,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac
}
// original spells
- LearnDefaultSpells();
+ LearnDefaultSkills();
LearnCustomSpells();
// original action bar
@@ -2985,11 +2985,39 @@ void Player::GiveLevel(uint8 level)
SetByteFlag(PLAYER_FIELD_BYTES, 1, 0x01);
}
+ std::list<uint32> learnList = GetSpellsForLevels(getClass(), getRaceMask(), GetTalentSpec(GetActiveTalentGroup()), oldLevel, level);
+ for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++)
+ {
+ if (!HasSpell(*iter))
+ LearnSpell(*iter, true);
+ }
+
sScriptMgr->OnPlayerLevelChanged(this, oldLevel);
}
void Player::InitTalentForLevel()
{
+ uint8 level = getLevel();
+ // talents base at level diff (talents = level - 9 but some can be used already)
+ if (level < 15)
+ {
+ // Remove all talent points
+ if (GetUsedTalentCount() > 0) // Free any used talents
+ {
+ ResetTalents(true);
+ }
+ }
+ else
+ {
+ if (level < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL) || GetTalentGroupsCount() == 0)
+ {
+ SetTalentGroupsCount(1);
+ SetActiveTalentGroup(0);
+ }
+ }
+
+ SetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS, CalculateTalentsPoints());
+
if (!GetSession()->PlayerLoading())
SendTalentsInfoData(); // update at client
}
@@ -3267,7 +3295,7 @@ void DeleteSpellFromAllPlayers(uint32 spellId)
}
}
-bool Player::AddTalent(uint32 talentId, uint8 spec)
+bool Player::AddTalent(uint32 talentId, uint8 spec, bool learning)
{
TalentEntry const* talentEntry = sTalentStore.LookupEntry(talentId);
@@ -3309,15 +3337,28 @@ bool Player::AddTalent(uint32 talentId, uint8 spec)
return false;
}
- TalentGroupInfo* talentGroupInfo = GetTalentGroupInfo(spec);
+ PlayerTalentMap::iterator itr = GetTalentMap(spec)->find(talentId);
+ if (itr == GetTalentMap(spec)->end())
+ {
+ //if (GetTalentBySpellID(talentEntry->SpellID))
+ {
+ PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
+ PlayerTalent* newtalent = new PlayerTalent();
- // Check if player already has this talent
- if (talentGroupInfo->HasTalent(talentId))
- return false;
+ newtalent->state = state;
+ newtalent->spec = spec;
- talentGroupInfo->Talents[talentEntry->TierID] = talentId;
+ (*GetTalentMap(spec))[talentId] = newtalent;
- return true;
+ return true;
+ }
+ //else
+ // TC_LOG_ERROR("spells", "Player::addTalent: Talent %u not found in talent store.", talentId);
+ }
+ else
+ itr->second->state = PLAYERSPELL_UNCHANGED;
+
+ return false;
}
bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading /*= false*/, bool fromSkill /*= false*/)
@@ -3421,9 +3462,9 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
SendSupercededSpell(spellId, next_active_spell_id);
else
{
- WorldPacket data(SMSG_REMOVED_SPELL, 4);
- data << uint32(spellId);
- GetSession()->SendPacket(&data);
+ WorldPackets::Spells::SendRemovedSpell removedSpells;
+ removedSpells.Spells.push_back(spellId);
+ GetSession()->SendPacket(removedSpells.Write());
}
}
@@ -3594,11 +3635,15 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
if (HasSkill(pSkill->ID))
continue;
+ SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pSkill->ID, getRace(), getClass());
+ if (!rcEntry)
+ continue;
+
if (_spell_idx->second->AquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN ||
// lockpicking/runeforging special case, not have SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN
((pSkill->ID == SKILL_LOCKPICKING || pSkill->ID == SKILL_RUNEFORGING) && (_spell_idx->second->TrivialSkillLineRankHigh == 0 || _spell_idx->second->TrivialSkillLineRankHigh == 1)))
{
- switch (GetSkillRangeType(pSkill, _spell_idx->second->RaceMask!= 0))
+ switch (GetSkillRangeType(rcEntry))
{
case SKILL_RANGE_LANGUAGE:
SetSkill(pSkill->ID, GetSkillStep(pSkill->ID), 300, 300);
@@ -3690,6 +3735,7 @@ bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const
bool Player::IsCurrentSpecMasterySpell(SpellInfo const* spellInfo) const
{
+
if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec()))
return spellInfo->Id == chrSpec->MasterySpellID[0] || spellInfo->Id == chrSpec->MasterySpellID[1];
@@ -3893,9 +3939,9 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
// remove from spell book if not replaced by lesser rank
if (!prev_activate)
{
- WorldPacket data(SMSG_REMOVED_SPELL, 4);
- data << uint32(spell_id);
- GetSession()->SendPacket(&data);
+ WorldPackets::Spells::SendRemovedSpell removedSpells;
+ removedSpells.Spells.push_back(spell_id);
+ GetSession()->SendPacket(removedSpells.Write());
}
}
@@ -4080,17 +4126,20 @@ uint32 Player::GetNextResetTalentsCost() const
}
}
-bool Player::ResetTalents(bool no_cost)
+bool Player::ResetTalents(bool noCost, bool resetTalents, bool resetSpecialization)
{
- sScriptMgr->OnPlayerTalentsReset(this, no_cost);
+ if (!resetTalents && !resetSpecialization)
+ return false;
+ sScriptMgr->OnPlayerTalentsReset(this, noCost);
+
// not need after this call
if (HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS, true);
uint32 cost = 0;
- if (!no_cost && !sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST))
+ if (!noCost && !sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST))
{
cost = GetNextResetTalentsCost();
@@ -4103,66 +4152,54 @@ bool Player::ResetTalents(bool no_cost)
RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
- uint8 group = GetActiveTalentGroup();
- uint32 specID = GetActiveTalentSpec();
-
- for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
+ if (resetTalents)
{
- TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
+ for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
+ {
+ TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
- if (!talentInfo)
- continue;
+ 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 (getClass() != talentInfo->ClassID)
- 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;
- const SpellInfo* _spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
+ SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
+ if (!spellEntry)
+ continue;
- if (!_spellEntry)
- continue;
+ RemoveSpell(spellEntry->Id, false);
- RemoveSpell(talentInfo->SpellID, true);
+ // search for spells that the talent teaches and unlearn them, 6.x remove?
+ for (SpellEffectInfo const* effect : spellEntry->GetEffectsForDifficulty(DIFFICULTY_NONE))
+ if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
+ RemoveSpell(effect->TriggerSpell, true);
- // search for spells that the talent teaches and unlearn them
- for (SpellEffectInfo const* effect : _spellEntry->GetEffectsForDifficulty(DIFFICULTY_NONE))
- if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
- RemoveSpell(effect->TriggerSpell, true);
+ GetTalentMap(GetActiveTalentGroup())->erase(talentId);
+ }
}
- // Remove all specialization specific spells and give default ones which were overriden
- auto specSpells = sSpecializationSpellsBySpecStore.find(specID);
- if (specSpells != sSpecializationSpellsBySpecStore.end())
+ if (resetSpecialization)
{
- for (auto it = specSpells->second.begin(); it != specSpells->second.end(); ++it)
+ std::list<uint32> learnList = GetSpellsForLevels(0, getRaceMask(), GetTalentSpec(GetActiveTalentGroup()), 0, getLevel());
+ for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++)
{
- SpecializationSpellsEntry const* specSpell = *it;
- if (HasSpell(specSpell->SpellID)) {
- RemoveSpell(specSpell->SpellID, true);
- if (specSpell->OverridesSpellID)
- LearnSpell(specSpell->OverridesSpellID, false);
- }
+ if (HasSpell(*iter))
+ RemoveSpell(*iter, true);
}
+ SetTalentSpec(GetActiveTalentGroup(), 0);
+ SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, 0);
}
- // Unlearn masteries
- if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(specID))
- for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i)
- if (uint32 mastery = chrSpec->MasterySpellID[i])
- RemoveAurasDueToSpell(mastery);
-
- // Reset talents store
- GetTalentGroupInfo(group)->Reset();
- SetTalentSpec(group, 0);
-
SQLTransaction trans = CharacterDatabase.BeginTransaction();
_SaveTalents(trans);
_SaveSpells(trans);
CharacterDatabase.CommitTransaction(trans);
- if (!no_cost)
+ if (!noCost)
{
ModifyMoney(-(int64)cost);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost);
@@ -4183,6 +4220,35 @@ bool Player::ResetTalents(bool no_cost)
return true;
}
+bool Player::RemoveTalent(uint32 talentId)
+{
+ TalentEntry const* talent = sTalentStore.LookupEntry(talentId);
+ if (!talent)
+ return false;
+
+ uint32 spellId = talent->SpellID;
+
+ SpellInfo const* unlearnSpellProto = sSpellMgr->GetSpellInfo(spellId);
+
+ RemoveSpell(spellId, false);
+
+ // 6.x remove?
+ for (SpellEffectInfo const* effect : unlearnSpellProto->GetEffectsForDifficulty(DIFFICULTY_NONE))
+ if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
+ RemoveSpell(effect->TriggerSpell, false);
+
+ GetTalentMap(GetActiveTalentSpec())->erase(talentId);
+
+ // Needs to be executed orthewise the talents will be screwedsx
+ SQLTransaction trans = CharacterDatabase.BeginTransaction();
+ _SaveTalents(trans);
+ _SaveSpells(trans);
+ CharacterDatabase.CommitTransaction(trans);
+
+ SendTalentsInfoData();
+ return true;
+}
+
Mail* Player::GetMail(uint32 id)
{
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
@@ -4249,7 +4315,8 @@ bool Player::HasSpell(uint32 spell) const
bool Player::HasTalent(uint32 talentId, uint8 group)
{
- return GetTalentGroupInfo(group)->HasTalent(talentId);
+ PlayerTalentMap::const_iterator itr = GetTalentMap(group)->find(talentId);
+ return (itr != GetTalentMap(group)->end());
}
bool Player::HasActiveSpell(uint32 spell) const
@@ -5892,14 +5959,14 @@ void Player::UpdateSkillsForLevel()
continue;
uint32 pskill = itr->first;
- SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(pskill);
- if (!pSkill)
+ SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pskill, getRace(), getClass());
+ if (!rcEntry)
continue;
- if (GetSkillRangeType(pSkill, false) != SKILL_RANGE_LEVEL)
+ if (GetSkillRangeType(rcEntry) != SKILL_RANGE_LEVEL)
continue;
- if (IsWeaponSkill(pSkill->ID))
+ if (IsWeaponSkill(rcEntry->SkillID))
continue;
uint16 field = itr->second.pos / 2;
@@ -17415,11 +17482,16 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
break;
uint32 talentSpec = atoul(talentSpecs[i]);
- if (sChrSpecializationStore.LookupEntry(talentSpec))
- SetTalentSpec(i, talentSpec);
- else
- SetAtLoginFlag(AT_LOGIN_RESET_TALENTS);
+ if (talentSpec)
+ {
+ if (sChrSpecializationStore.LookupEntry(talentSpec))
+ SetTalentSpec(i, talentSpec);
+ else
+ SetAtLoginFlag(AT_LOGIN_RESET_TALENTS);
+ }
}
+
+ SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetActiveTalentSpec());
_LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS));
_LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS));
@@ -17442,7 +17514,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
// after spell and quest load
InitTalentForLevel();
- LearnDefaultSpells();
+ LearnDefaultSkills();
LearnCustomSpells();
// must be before inventory (some items required reputation check)
@@ -18486,6 +18558,13 @@ void Player::_LoadSpells(PreparedQueryResult result)
AddSpell((*result)[0].GetUInt32(), (*result)[1].GetBool(), false, false, (*result)[2].GetBool(), true);
while (result->NextRow());
}
+
+ std::list<uint32> learnList = GetSpellsForLevels(getClass(), getRaceMask(), GetActiveTalentSpec(), 0, getLevel());
+ for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++)
+ {
+ if (!HasSpell(*iter))
+ LearnSpell(*iter, true);
+ }
}
void Player::_LoadGroup(PreparedQueryResult result)
@@ -23243,8 +23322,8 @@ void Player::ResetSpells(bool myClassOnly)
else
for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter)
RemoveSpell(iter->first, false, false); // only iter->first can be accessed, object by iter->second can be deleted already
-
- LearnDefaultSpells();
+
+ LearnDefaultSkills();
LearnCustomSpells();
LearnQuestRewardedSpells();
}
@@ -23267,21 +23346,72 @@ void Player::LearnCustomSpells()
}
}
-void Player::LearnDefaultSpells()
+void Player::LearnDefaultSkills()
{
- // learn default race/class spells
+ // learn default race/class skills
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
- for (PlayerCreateInfoSpells::const_iterator itr = info->spells.begin(); itr != info->spells.end(); ++itr)
+ for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
{
- uint32 tspell = *itr;
- TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell);
- if (!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add
- AddSpell(tspell, true, true, true, false);
- else // but send in normal spell in game learn case
- LearnSpell(tspell, true);
+ uint32 skillId = itr->SkillId;
+ if (HasSkill(skillId))
+ continue;
+
+ LearnDefaultSkill(skillId, itr->Rank);
+ }
+}
+
+void Player::LearnDefaultSkill(uint32 skillId, uint16 rank)
+{
+ SkillRaceClassInfoEntry const* rcInfo = GetSkillRaceClassInfo(skillId, getRace(), getClass());
+ if (!rcInfo)
+ return;
+
+ switch (GetSkillRangeType(rcInfo))
+ {
+ case SKILL_RANGE_LANGUAGE:
+ SetSkill(skillId, 0, 300, 300);
+ break;
+ case SKILL_RANGE_LEVEL:
+ {
+ uint16 skillValue = 1;
+ uint16 maxValue = GetMaxSkillValueForLevel();
+ if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE)
+ skillValue = maxValue;
+ else if (getClass() == CLASS_DEATH_KNIGHT)
+ skillValue = std::min(std::max<uint16>({ 1, uint16((getLevel() - 1) * 5) }), maxValue);
+ else if (skillId == SKILL_FIST_WEAPONS)
+ skillValue = std::max<uint16>(1, GetSkillValue(SKILL_UNARMED));
+ else if (skillId == SKILL_LOCKPICKING)
+ skillValue = std::max<uint16>(1, GetSkillValue(SKILL_LOCKPICKING));
+
+ SetSkill(skillId, 0, skillValue, maxValue);
+ break;
+ }
+ case SKILL_RANGE_MONO:
+ SetSkill(skillId, 0, 1, 1);
+ break;
+ case SKILL_RANGE_RANK:
+ {
+ if (!rank)
+ break;
+
+ SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcInfo->SkillTierID);
+ uint16 maxValue = tier->Value[std::max<int32>(rank - 1, 0)];
+ uint16 skillValue = 1;
+ if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE)
+ skillValue = maxValue;
+ else if (getClass() == CLASS_DEATH_KNIGHT)
+ skillValue = std::min(std::max<uint16>({ uint16(1), uint16((getLevel() - 1) * 5) }), maxValue);
+
+ SetSkill(skillId, rank, skillValue, maxValue);
+ break;
+ }
+ default:
+ break;
}
}
+
void Player::LearnQuestRewardedSpells(Quest const* quest)
{
int32 spell_id = quest->GetRewSpellCast();
@@ -23396,31 +23526,41 @@ void Player::LearnQuestRewardedSpells()
void Player::LearnSkillRewardedSpells(uint32 skillId, uint32 skillValue)
{
- uint32 raceMask = getRaceMask();
+ uint32 raceMask = getRaceMask();
uint32 classMask = getClassMask();
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
{
- SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j);
- if (!pAbility || pAbility->SkillLine != skillId || pAbility->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
+ SkillLineAbilityEntry const* ability = sSkillLineAbilityStore.LookupEntry(j);
+ if (!ability || ability->SkillLine != skillId)
+ continue;
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(ability->SpellID);
+ if (!spellInfo)
+ continue;
+
+ if (ability->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE && ability->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
continue;
+
// Check race if set
- if (pAbility->RaceMask && !(pAbility->RaceMask & raceMask))
+ if (ability->RaceMask && !(ability->RaceMask & raceMask))
continue;
+
// Check class if set
- if (pAbility->ClassMask && !(pAbility->ClassMask & classMask))
+ if (ability->ClassMask && !(ability->ClassMask & classMask))
continue;
- if (sSpellMgr->GetSpellInfo(pAbility->SpellID))
- {
- // need unlearn spell
- if (skillValue < pAbility->MinSkillLineRank)
- RemoveSpell(pAbility->SpellID);
- // need learn
- else if (!IsInWorld())
- AddSpell(pAbility->SpellID, true, true, true, false);
- else
- LearnSpell(pAbility->SpellID, true);
- }
+ // check level, skip class spells if not high enough
+ if (getLevel() < spellInfo->SpellLevel)
+ continue;
+
+ // need unlearn spell
+ if (skillValue < ability->MinSkillLineRank && ability->AquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
+ RemoveSpell(ability->SpellID);
+ // need learn
+ else if (!IsInWorld())
+ AddSpell(ability->SpellID, true, true, true, false, false, true);
+ else
+ LearnSpell(ability->SpellID, true, true);
}
}
@@ -25135,8 +25275,8 @@ void Player::_LoadSkills(PreparedQueryResult result)
uint16 value = fields[1].GetUInt16();
uint16 max = fields[2].GetUInt16();
- SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skill);
- if (!skillLine)
+ SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skill, getRace(), getClass());
+ if (!rcEntry)
{
TC_LOG_ERROR("entities.player", "Character: %s (%s Race: %u Class: %u) has skill %u not allowed for his race/class combination",
GetName().c_str(), GetGUID().ToString().c_str(), uint32(getRace()), uint32(getClass()), skill);
@@ -25146,7 +25286,7 @@ void Player::_LoadSkills(PreparedQueryResult result)
}
// set fixed skill ranges
- switch (GetSkillRangeType(skillLine, false))
+ switch (GetSkillRangeType(rcEntry))
{
case SKILL_RANGE_LANGUAGE: // 300..300
value = max = 300;
@@ -25180,15 +25320,19 @@ void Player::_LoadSkills(PreparedQueryResult result)
SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_ID_OFFSET + field, offset, skill);
uint16 step = 0;
- if (skillLine->CategoryID == SKILL_CATEGORY_SECONDARY)
- step = max / 75;
-
- if (skillLine->CategoryID == SKILL_CATEGORY_PROFESSION)
+ SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(rcEntry->SkillID);
+ if (skillLine)
{
- step = max / 75;
+ if (skillLine->CategoryID == SKILL_CATEGORY_SECONDARY)
+ step = max / 75;
+
+ if (skillLine->CategoryID == SKILL_CATEGORY_PROFESSION)
+ {
+ step = max / 75;
- if (professionCount < 2)
- SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + professionCount++, skill);
+ if (professionCount < 2)
+ SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + professionCount++, skill);
+ }
}
SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_STEP_OFFSET + field, offset, step);
@@ -25407,37 +25551,36 @@ void Player::CompletedAchievement(AchievementEntry const* entry)
bool Player::LearnTalent(uint32 talentId)
{
- uint8 group = GetActiveTalentGroup();
-
- // check if talent specialization is learnt
- if (!GetTalentSpec(group))
- return false;
-
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
if (!talentInfo)
return false;
+
+ uint32 maxTalentTier = GetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS);
// prevent learn talent for different class (cheating)
- if (getClass() != talentInfo->ClassID)
+ if (talentInfo->ClassID != getClass())
return false;
- // Check player level
- // TODO: fix level requirements for deathknights
- uint8 levelReq = std::min(15*talentInfo->TierID + 15, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
- if (getLevel() < levelReq)
+ // check if we have enough talent points
+ if (talentInfo->TierID > maxTalentTier)
return false;
- // Check if such tier talent hasn't been picked already
- TalentGroupInfo* talentGroupInfo = GetTalentGroupInfo(group);
- if (talentGroupInfo->Talents[talentInfo->TierID])
- return false;
+ // Check if player doesnt have any spell in selected collumn
+ for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++)
+ {
+ if (TalentEntry const* talent = sTalentStore.LookupEntry(i))
+ {
+ if (talentInfo->TierID == talent->TierID && HasSpell(talent->SpellID))
+ return false;
+ }
+ }
// spell not set in talent.dbc
uint32 spellid = talentInfo->SpellID;
if (spellid == 0)
{
- TC_LOG_ERROR("entities.player", "Talent.dbc have for talent: %u spell id = 0", talentId);
+ TC_LOG_ERROR("entities.player", "Talent.dbc has no spellInfo for talent: %u (spell id = 0)", talentId);
return false;
}
@@ -25445,54 +25588,39 @@ bool Player::LearnTalent(uint32 talentId)
if (HasSpell(spellid))
return false;
- // Check talent spec
- if (talentInfo->SpecID != GetTalentSpec(group))
+ if (!AddTalent(talentId, GetActiveTalentGroup(), true))
return false;
- // learn! (other talent ranks will unlearned at learning)
LearnSpell(spellid, false);
- AddTalent(talentId, group);
- TC_LOG_INFO("misc", "TalentID: %u Spell: %u Group: %u\n", talentId, spellid, group);
+ TC_LOG_INFO("misc", "TalentID: %u Spell: %u Group: %u\n", talentId, spellid, GetActiveTalentGroup());
return true;
}
void Player::LearnTalentSpecialization(uint32 talentSpec)
{
- SetTalentSpec(GetActiveTalentGroup(), talentSpec);
-
- // Replace default spells by specialization spells
- auto specSpells = sSpecializationSpellsBySpecStore.find(talentSpec);
- if (specSpells != sSpecializationSpellsBySpecStore.end())
- {
- for (auto it = specSpells->second.begin(); it != specSpells->second.end(); ++it)
- {
- SpecializationSpellsEntry const* specSpell = *it;
+ if (GetActiveTalentSpec())
+ return;
- // Unlearn spell if it is replaced by new specialization
- if (specSpell->OverridesSpellID)
- RemoveSpell(specSpell->OverridesSpellID, true);
+ SetTalentSpec(GetActiveTalentGroup(), talentSpec);
- // Learn new spell
- if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(specSpell->SpellID))
- if (spellInfo->BaseLevel <= getLevel())
- LearnSpell(specSpell->SpellID, false);
- }
- }
+ SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, talentSpec);
+ SendTalentsInfoData();
- if (CanUseMastery())
+ std::list<uint32> learnList = GetSpellsForLevels(0, getRaceMask(), GetActiveTalentSpec(), 0, getLevel());
+ for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++)
{
- ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(talentSpec);
- for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i)
- if (SpellInfo const* masterySpell = sSpellMgr->GetSpellInfo(chrSpec->MasterySpellID[i]))
- if (masterySpell->IsPassive() && IsNeedCastPassiveSpellAtLearn(masterySpell))
- CastSpell(this, masterySpell->Id, true);
+ if (!HasSpell(*iter))
+ LearnSpell(*iter, true);
}
+ SaveToDB();
+
SendTalentsInfoData();
}
+
void Player::AddKnownCurrency(uint32 itemId)
{
if (CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId))
@@ -25577,23 +25705,39 @@ void Player::SendTalentsInfoData()
for (uint8 i = 0; i < groupsCount; ++i)
{
- TalentGroupInfo* groupInfo = GetTalentGroupInfo(i);
WorldPackets::Talent::TalentGroupInfo groupInfoPkt;
- groupInfoPkt.SpecID = groupInfo->SpecID;
+ groupInfoPkt.SpecID = GetTalentSpec(i);
+
+ groupInfoPkt.TalentIDs.reserve(GetTalentMap(i)->size());
- groupInfoPkt.TalentIDs.reserve(MAX_TALENT_TIERS);
- for (uint32 x = 0; x < MAX_TALENT_TIERS; ++x)
+ for (PlayerTalentMap::const_iterator itr = GetTalentMap(i)->begin(); itr != GetTalentMap(i)->end(); ++itr)
{
- // Do not send empty talents
- if (!groupInfo->Talents[x])
- break;
+ TalentEntry const* talentInfo = sTalentStore.LookupEntry(itr->first);
+ if (!talentInfo)
+ {
+ TC_LOG_ERROR("entities.player", "Player %s has unknown talent id: %u", GetName().c_str(), itr->first);
+ continue;
+ }
- groupInfoPkt.TalentIDs.push_back(groupInfo->Talents[x]);
+ if (talentInfo->ClassID != getClass())
+ continue;
+
+ SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
+ if (!spellEntry)
+ {
+ TC_LOG_ERROR("entities.player", "Player %s has unknown talent spell: %u", GetName().c_str(), talentInfo->SpellID);
+ continue;
+ }
+
+ if (!HasTalent(itr->first, i))
+ continue;
+
+ groupInfoPkt.TalentIDs.push_back(uint16(itr->first));
}
for (uint32 x = 0; x < MAX_GLYPH_SLOT_INDEX; ++x)
- groupInfoPkt.GlyphIDs[x] = groupInfo->Glyphs[x];
+ groupInfoPkt.GlyphIDs[x] = uint16(GetGlyph(i, x));
packet.Info.TalentGroups.push_back(groupInfoPkt);
}
@@ -25905,7 +26049,7 @@ void Player::_LoadTalents(PreparedQueryResult result)
if (result)
{
do
- AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt8());
+ AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt8(), false);
while (result->NextRow());
}
}
@@ -25918,17 +26062,12 @@ void Player::_SaveTalents(SQLTransaction& trans)
for (uint8 group = 0; group < MAX_TALENT_GROUPS; ++group)
{
- TalentGroupInfo* talentGroupInfo = GetTalentGroupInfo(group);
-
- for (uint32 tier = 0; tier < MAX_TALENT_TIERS; ++tier)
+ for (PlayerTalentMap::iterator itr = GetTalentMap(group)->begin(); itr != GetTalentMap(group)->end(); ++itr)
{
- if (!talentGroupInfo->Talents[tier])
- continue;
-
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_TALENT);
stmt->setUInt64(0, GetGUID().GetCounter());
- stmt->setUInt32(1, talentGroupInfo->Talents[tier]);
- stmt->setUInt8(2, group);
+ stmt->setUInt32(1, itr->first);
+ stmt->setUInt8(2, itr->second->spec);
trans->Append(stmt);
}
}
@@ -26027,30 +26166,27 @@ void Player::ActivateTalentGroup(uint8 group)
if (getClass() != talentInfo->ClassID)
continue;
+ SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
+ if (!spellEntry)
+ continue;
+
RemoveSpell(talentInfo->SpellID, true);
+
+ // search for spells that the talent teaches and unlearn them
+ for (SpellEffectInfo const* effect : spellEntry->GetEffectsForDifficulty(DIFFICULTY_NONE))
+ if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
+ RemoveSpell(effect->TriggerSpell, true);
}
// Unlearn specialization specific spells
- auto specSpells = sSpecializationSpellsBySpecStore.find(GetActiveTalentSpec());
- if (specSpells != sSpecializationSpellsBySpecStore.end())
+ std::list<uint32> learnList = GetSpellsForLevels(0, getRaceMask(), GetActiveTalentSpec(), 0, getLevel());
+ for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++)
{
- for (auto it = specSpells->second.begin(); it != specSpells->second.end(); ++it)
- {
- SpecializationSpellsEntry const* specSpell = *it;
- if (HasSpell(specSpell->SpellID)) {
- RemoveSpell(specSpell->SpellID, true);
- LearnSpell(specSpell->OverridesSpellID, false);
- }
- }
+ if (HasSpell(*iter))
+ RemoveSpell(*iter, true);
}
- // Unlearn mastery spells
- ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec());
- for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i)
- if (chrSpec->MasterySpellID[i])
- RemoveSpell(chrSpec->MasterySpellID[i], true);
-
- // set glyphs
+ // remove glyphs
for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
// remove secondary glyph
if (uint32 oldglyph = GetGlyph(GetActiveTalentGroup(), slot))
@@ -26060,6 +26196,15 @@ void Player::ActivateTalentGroup(uint8 group)
// Activate new group
SetActiveTalentGroup(group);
+ uint32 spentTalents = 0;
+
+ learnList = GetSpellsForLevels(getClass(), getRaceMask(), GetActiveTalentSpec(), 0, getLevel());
+ for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++)
+ {
+ if (!HasSpell(*iter))
+ LearnSpell(*iter, true);
+ }
+
for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
@@ -26071,31 +26216,12 @@ void Player::ActivateTalentGroup(uint8 group)
if (getClass() != talentInfo->ClassID)
continue;
- LearnSpell(talentInfo->SpellID, false);
- }
+ ++spentTalents;
- // Replace default spells with specialization specific spells
- auto newSpecSpells = sSpecializationSpellsBySpecStore.find(GetActiveTalentSpec());
- if (newSpecSpells != sSpecializationSpellsBySpecStore.end())
- {
- for (auto it = newSpecSpells->second.begin(); it != newSpecSpells->second.end(); ++it)
- {
- SpecializationSpellsEntry const* specSpell = *it;
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(specSpell->SpellID);
- if (getLevel() >= spellInfo->BaseLevel) {
- if (specSpell->OverridesSpellID)
- RemoveSpell(specSpell->OverridesSpellID, true);
- LearnSpell(specSpell->SpellID, false);
- }
- }
+ if (HasTalent(talentInfo->SpellID, group))
+ LearnSpell(talentInfo->SpellID, false);
}
- if (CanUseMastery())
- if (ChrSpecializationEntry const* newChrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec()))
- for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i)
- if (newChrSpec->MasterySpellID[i])
- LearnSpell(newChrSpec->MasterySpellID[i], false);
-
// set glyphs
for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
{
@@ -26109,6 +26235,7 @@ void Player::ActivateTalentGroup(uint8 group)
SetGlyph(slot, glyph);
}
+ SetUsedTalentCount(spentTalents);
InitTalentForLevel();
{
@@ -26128,7 +26255,9 @@ void Player::ActivateTalentGroup(uint8 group)
SetPower(pw, 0);
if (!sChrSpecializationStore.LookupEntry(GetActiveTalentSpec()))
+ {
ResetTalents(true);
+ }
}
void Player::ResetTimeSync()
@@ -27168,3 +27297,9 @@ void Player::SendSupercededSpell(uint32 oldSpell, uint32 newSpell)
data << uint32(newSpell) << uint32(oldSpell);
GetSession()->SendPacket(&data);
}
+
+uint32 Player::CalculateTalentsPoints() const
+{
+ // 1 talent point for every 15 levels
+ return getLevel() >= 100 ? 7 : uint32(floor(getLevel() / 15.f));
+} \ No newline at end of file
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 2d919159819..6b9e925e28d 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -116,6 +116,12 @@ struct PlayerSpell
bool disabled : 1; // first rank has been learned in result talent learn but currently talent unlearned, save max learned ranks
};
+struct PlayerTalent
+{
+ PlayerSpellState state : 8;
+ uint8 spec : 8;
+};
+
extern uint32 const MasterySpells[MAX_CLASSES];
enum TalentSpecialization // talent tabs
@@ -184,6 +190,7 @@ struct PlayerCurrency
uint32 weekCount;
};
+typedef std::unordered_map<uint32, PlayerTalent*> PlayerTalentMap;
typedef std::unordered_map<uint32, PlayerSpell*> PlayerSpellMap;
typedef std::list<SpellModifier*> SpellModList;
typedef std::unordered_map<uint32, PlayerCurrency> PlayerCurrenciesMap;
@@ -412,9 +419,9 @@ struct PlayerInfo
uint16 displayId_f;
PlayerCreateInfoItems item;
PlayerCreateInfoSpells customSpells;
- PlayerCreateInfoSpells spells;
PlayerCreateInfoSpells castSpells;
PlayerCreateInfoActions action;
+ PlayerCreateInfoSkills skills;
PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
};
@@ -1223,56 +1230,41 @@ private:
bool _isPvP;
};
-struct TalentGroupInfo
+struct PlayerTalentInfo
{
- uint32 Talents[MAX_TALENT_TIERS];
- uint32 Glyphs[MAX_GLYPH_SLOT_INDEX];
- uint32 SpecID;
-
- bool HasTalent(uint32 talentId)
- {
- for (uint32 i = 0; i < MAX_TALENT_TIERS; ++i)
- if (Talents[i] == talentId)
- return true;
- return false;
- }
-
- uint32 TalentCount()
+ PlayerTalentInfo() : UsedTalentCount(0), ResetTalentsCost(0), ResetTalentsTime(0), ActiveGroup(0), GroupsCount(1)
{
- for (uint32 i = 0; i < MAX_TALENT_TIERS; ++i)
- if (!Talents[i])
- return i;
- return MAX_TALENT_TIERS;
+ for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i)
+ {
+ GroupInfo[i].Talents = new PlayerTalentMap();
+ memset(GroupInfo[i].Glyphs, 0, MAX_GLYPH_SLOT_INDEX * sizeof(uint32));
+ GroupInfo[i].TalentTree = 0;
+ }
}
- void Reset()
- {
- for (uint32 i = 0; i < MAX_TALENT_TIERS; ++i)
- Talents[i] = 0;
- }
-};
-
-struct PlayerTalentInfo
-{
- PlayerTalentInfo() : ResetTalentsCost(0), ResetTalentsTime(0), ActiveGroup(0), GroupsCount(1)
+ ~PlayerTalentInfo()
{
for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i)
{
- memset(GroupInfo[i].Talents, 0, sizeof(uint32)*MAX_TALENT_TIERS);
- memset(GroupInfo[i].Glyphs, 0, MAX_GLYPH_SLOT_INDEX * sizeof(uint32));
- GroupInfo[i].SpecID = 0;
+ for (PlayerTalentMap::const_iterator itr = GroupInfo[i].Talents->begin(); itr != GroupInfo[i].Talents->end(); ++itr)
+ delete itr->second;
+ delete GroupInfo[i].Talents;
}
}
- TalentGroupInfo GroupInfo[MAX_TALENT_GROUPS];
+ struct TalentGroupInfo
+ {
+ PlayerTalentMap* Talents;
+ uint32 Glyphs[MAX_GLYPH_SLOT_INDEX];
+ uint32 TalentTree;
+ } GroupInfo[MAX_TALENT_GROUPS];
+
+ uint32 UsedTalentCount;
uint32 ResetTalentsCost;
time_t ResetTalentsTime;
uint8 ActiveGroup;
uint8 GroupsCount;
-
- uint32 UsedTalentCount;
-
private:
PlayerTalentInfo(PlayerTalentInfo const&);
};
@@ -1824,7 +1816,8 @@ class Player : public Unit, public GridObject<Player>
void RemoveSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true);
void ResetSpells(bool myClassOnly = false);
void LearnCustomSpells();
- void LearnDefaultSpells();
+ void LearnDefaultSkills();
+ void LearnDefaultSkill(uint32 skillId, uint16 rank);
void LearnQuestRewardedSpells();
void LearnQuestRewardedSpells(Quest const* quest);
void LearnSpellHighestRank(uint32 spellid);
@@ -1837,26 +1830,33 @@ class Player : public Unit, public GridObject<Player>
// Talents
uint32 GetUsedTalentCount() const { return _talentMgr->UsedTalentCount; }
void SetUsedTalentCount(uint32 talents) { _talentMgr->UsedTalentCount = talents; }
-
uint32 GetTalentResetCost() const { return _talentMgr->ResetTalentsCost; }
void SetTalentResetCost(uint32 cost) { _talentMgr->ResetTalentsCost = cost; }
uint32 GetTalentResetTime() const { return _talentMgr->ResetTalentsTime; }
void SetTalentResetTime(time_t time_) { _talentMgr->ResetTalentsTime = time_; }
- uint8 GetActiveTalentGroup() const { return _talentMgr->ActiveGroup; }
- void SetActiveTalentGroup(uint8 group){ _talentMgr->ActiveGroup = group; }
+
uint8 GetTalentGroupsCount() const { return _talentMgr->GroupsCount; }
void SetTalentGroupsCount(uint8 count) { _talentMgr->GroupsCount = count; }
- uint32 GetTalentSpec(uint8 group) const { return _talentMgr->GroupInfo[group].SpecID; }
- void SetTalentSpec(uint8 group, uint32 talentSpec) const { _talentMgr->GroupInfo[group].SpecID = talentSpec; }
- uint32 GetActiveTalentSpec() const { return _talentMgr->GroupInfo[_talentMgr->ActiveGroup].SpecID; }
+ uint8 GetActiveTalentGroup() const { return _talentMgr->ActiveGroup; }
+ void SetActiveTalentGroup(uint8 group){ _talentMgr->ActiveGroup = group; }
+
+ uint32 GetTalentSpec(uint8 group) const { return _talentMgr->GroupInfo[group].TalentTree; }
+ void SetTalentSpec(uint8 group, uint32 talentSpec) const { _talentMgr->GroupInfo[group].TalentTree = talentSpec; }
+ uint32 GetActiveTalentSpec() const { return _talentMgr->GroupInfo[_talentMgr->ActiveGroup].TalentTree; }
+
+
+ bool ResetTalents(bool noCost = false, bool resetTalents = true, bool resetSpecialization = true);
+ bool RemoveTalent(uint32 talentId);
- bool ResetTalents(bool no_cost = false);
uint32 GetNextResetTalentsCost() const;
void InitTalentForLevel();
void SendTalentsInfoData();
bool LearnTalent(uint32 talentId);
- bool AddTalent(uint32 talentId, uint8 spec);
+ bool AddTalent(uint32 talentId, uint8 spec, bool learning);
bool HasTalent(uint32 talentId, uint8 spec);
+ uint32 CalculateTalentsPoints() const;
+
+
void LearnTalentSpecialization(uint32 talentSpec);
// Dual Spec
@@ -1870,7 +1870,8 @@ class Player : public Unit, public GridObject<Player>
void SetGlyph(uint8 slot, uint32 glyph);
uint32 GetGlyph(uint8 group, uint8 slot) const { return _talentMgr->GroupInfo[group].Glyphs[slot]; }
- TalentGroupInfo* GetTalentGroupInfo(uint8 group) { return &_talentMgr->GroupInfo[group]; }
+ PlayerTalentMap const* GetTalentMap(uint8 spec) const { return _talentMgr->GroupInfo[spec].Talents; }
+ PlayerTalentMap* GetTalentMap(uint8 spec) { return _talentMgr->GroupInfo[spec].Talents; }
ActionButtonList const& GetActionButtons() const { return m_actionButtons; }
uint32 GetFreePrimaryProfessionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS); }
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 5fbc6e7632c..e4f12614b33 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -3033,7 +3033,7 @@ Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint3
bp = *(baseAmount + effect->EffectIndex);
else
bp = effect->BasePoints;
- TC_LOG_ERROR("spells", "_TryStackingOrRefreshingExistingAura spell %u effMask %u currIdx %u", newAura->Id, effMask, effect->EffectIndex);
+
int32* oldBP = const_cast<int32*>(&(foundAura->GetEffect(effect->EffectIndex)->m_baseAmount)); // todo 6.x review GetBaseAmount and GetCastItemGUID in this case
*oldBP = bp;
}
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 4858800b3d2..ae39c405e26 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -3279,16 +3279,16 @@ void ObjectMgr::LoadPlayerInfo()
}
}
- // Load playercreate spells
- TC_LOG_INFO("server.loading", "Loading Player Create Spell Data...");
+ // Load playercreate skills
+ TC_LOG_INFO("server.loading", "Loading Player Create Skill Data...");
{
uint32 oldMSTime = getMSTime();
- QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell");
+ QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, rank FROM playercreateinfo_skills");
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell` is empty.");
+ TC_LOG_ERROR("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty.");
}
else
{
@@ -3299,17 +3299,31 @@ void ObjectMgr::LoadPlayerInfo()
Field* fields = result->Fetch();
uint32 raceMask = fields[0].GetUInt32();
uint32 classMask = fields[1].GetUInt32();
- uint32 spellId = fields[2].GetUInt32();
+ PlayerCreateInfoSkill skill;
+ skill.SkillId = fields[2].GetUInt16();
+ skill.Rank = fields[3].GetUInt16();
+
+ if (skill.Rank >= MAX_SKILL_STEP)
+ {
+ TC_LOG_ERROR("sql.sql", "Skill rank value %hu set for skill %hu raceMask %u classMask %u is too high, max allowed value is %d", skill.Rank, skill.SkillId, raceMask, classMask, MAX_SKILL_STEP);
+ continue;
+ }
if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
{
- TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_spell` table, ignoring.", raceMask);
+ TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_skills` table, ignoring.", raceMask);
continue;
}
if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
{
- TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_spell` table, ignoring.", classMask);
+ TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_skills` table, ignoring.", classMask);
+ continue;
+ }
+
+ if (!sSkillLineStore.LookupEntry(skill.SkillId))
+ {
+ TC_LOG_ERROR("sql.sql", "Wrong skill id %u in `playercreateinfo_skills` table, ignoring.", skill.SkillId);
continue;
}
@@ -3321,23 +3335,21 @@ void ObjectMgr::LoadPlayerInfo()
{
if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
{
+ if (!GetSkillRaceClassInfo(skill.SkillId, raceIndex, classIndex))
+ continue;
+
if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
{
- info->spells.push_back(spellId);
+ info->skills.push_back(skill);
++count;
}
- // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them.
- // Either split the masks per class, or per race, which kind of kills the point yet.
- // else if (raceMask != 0 && classMask != 0)
- // TC_LOG_ERROR("sql.sql", "Racemask/classmask (%u/%u) combination was found containing an invalid race/class combination (%u/%u) in `%s` (Spell %u), ignoring.", raceMask, classMask, raceIndex, classIndex, tableName.c_str(), spellId);
}
}
}
}
- }
- while (result->NextRow());
+ } while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u player create skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -7846,34 +7858,27 @@ int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 rac
return 0;
}
-SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial)
+SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry)
{
- switch (pSkill->CategoryID)
+ SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillID);
+ if (!skill)
+ return SKILL_RANGE_NONE;
+
+ if (sSkillTiersStore.LookupEntry(rcEntry->SkillTierID))
+ return SKILL_RANGE_RANK;
+
+ if (rcEntry->SkillID == SKILL_RUNEFORGING)
+ return SKILL_RANGE_MONO;
+
+ switch (skill->CategoryID)
{
+ case SKILL_CATEGORY_ARMOR:
+ return SKILL_RANGE_MONO;
case SKILL_CATEGORY_LANGUAGES:
return SKILL_RANGE_LANGUAGE;
- case SKILL_CATEGORY_WEAPON:
- return SKILL_RANGE_LEVEL;
- case SKILL_CATEGORY_ARMOR:
- case SKILL_CATEGORY_CLASS:
- if (pSkill->ID != SKILL_LOCKPICKING)
- return SKILL_RANGE_MONO;
- else
- return SKILL_RANGE_LEVEL;
- case SKILL_CATEGORY_SECONDARY:
- case SKILL_CATEGORY_PROFESSION:
- // not set skills for professions and racial abilities
- if (IsProfessionSkill(pSkill->ID))
- return SKILL_RANGE_RANK;
- else if (racial)
- return SKILL_RANGE_NONE;
- else
- return SKILL_RANGE_MONO;
- default:
- case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc
- case SKILL_CATEGORY_GENERIC: //only GENERIC(DND)
- return SKILL_RANGE_NONE;
}
+
+ return SKILL_RANGE_LEVEL;
}
void ObjectMgr::LoadGameTele()
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index bf0d889057b..e7191735ca1 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -668,7 +668,7 @@ enum SkillRangeType
SKILL_RANGE_NONE // 0..0 always
};
-SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial);
+SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry);
#define MAX_PLAYER_NAME 12 // max allowed by client name length
#define MAX_INTERNAL_PLAYER_NAME 15 // max server internal player name length (> MAX_PLAYER_NAME for support declined names)
diff --git a/src/server/game/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp
index f0c1b28117b..278169c2d63 100644
--- a/src/server/game/Handlers/SkillHandler.cpp
+++ b/src/server/game/Handlers/SkillHandler.cpp
@@ -28,59 +28,22 @@
#include "WorldSession.h"
#include "TalentPackets.h"
-void WorldSession::HandleLearnTalentOpcode(WorldPacket& recvData)
+void WorldSession::HandleLearnTalentOpcode(WorldPackets::Talent::LearnTalent& packet)
{
- /* TODO: 6.x update packet struct (note: LearnTalent no longer has rank argument)
- uint32 talentId, requestedRank;
- recvData >> talentId >> requestedRank;
-
- if (_player->LearnTalent(talentId, requestedRank))
- _player->SendTalentsInfoData(false);*/
+ bool anythingLearned = false;
+ for (uint32 talentId : packet.Talents)
+ {
+ if (_player->LearnTalent(talentId))
+ anythingLearned = true;
+ }
+
+ if (anythingLearned)
+ _player->SendTalentsInfoData();
}
void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket)
{
- /* TODO: 6.x update packet struct
TC_LOG_DEBUG("network", "CMSG_LEARN_PREVIEW_TALENTS");
-
- int32 tabPage;
- uint32 talentsCount;
- recvPacket >> tabPage; // talent tree
-
- // prevent cheating (selecting new tree with points already in another)
- if (tabPage >= 0) // -1 if player already has specialization
- {
- if (TalentTabEntry const* talentTabEntry = sTalentTabStore.LookupEntry(_player->GetPrimaryTalentTree(_player->GetActiveSpec())))
- {
- if (talentTabEntry->tabpage != uint32(tabPage))
- {
- recvPacket.rfinish();
- return;
- }
- }
- }
-
- recvPacket >> talentsCount;
-
- uint32 talentId, talentRank;
-
- // Client has max 21 talents for tree for 3 trees, rounded up : 70
- uint32 const MaxTalentsCount = 70;
-
- for (uint32 i = 0; i < talentsCount && i < MaxTalentsCount; ++i)
- {
- recvPacket >> talentId >> talentRank;
-
- if (!_player->LearnTalent(talentId, talentRank))
- {
- recvPacket.rfinish();
- break;
- }
- }
-
- _player->SendTalentsInfoData(false);
-
- recvPacket.rfinish();*/
}
void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket& recvData)
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index 3ca660dccd1..a6bd43f72c6 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -371,6 +371,29 @@ void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::SpellCastRequest&
// not have spell in spellbook
return;
}
+
+ if (Player* plr = caster->ToPlayer())
+ {
+ uint32 specId = plr->GetActiveTalentSpec();
+ if (specId)
+ {
+ if (sSpecializationOverrideSpellMap.find(specId) != sSpecializationOverrideSpellMap.end())
+ {
+ if (sSpecializationOverrideSpellMap[specId].find(castRequest.SpellID) != sSpecializationOverrideSpellMap[specId].end())
+ {
+ SpellInfo const* newSpellInfo = sSpellMgr->GetSpellInfo(sSpecializationOverrideSpellMap[specId][castRequest.SpellID]);
+ if (newSpellInfo)
+ {
+ if (newSpellInfo->SpellLevel <= caster->getLevel())
+ {
+ spellInfo = newSpellInfo;
+ castRequest.SpellID = newSpellInfo->Id;
+ }
+ }
+ }
+ }
+ }
+ }
Unit::AuraEffectList swaps = mover->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS);
Unit::AuraEffectList const& swaps2 = mover->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_2);
diff --git a/src/server/game/Server/Packets/SpellPackets.cpp b/src/server/game/Server/Packets/SpellPackets.cpp
index 7c536363335..1e181d37ff9 100644
--- a/src/server/game/Server/Packets/SpellPackets.cpp
+++ b/src/server/game/Server/Packets/SpellPackets.cpp
@@ -182,8 +182,6 @@ void WorldPackets::Spells::SpellCastRequest::Read()
_worldPacket >> SpellID;
_worldPacket >> Misc;
- TC_LOG_ERROR("spells", "SpellID %u Misc %u", SpellID, Misc);
-
_worldPacket.ResetBitPos();
TargetFlags = _worldPacket.ReadBits(21);
@@ -469,3 +467,12 @@ WorldPacket const* WorldPackets::Spells::SetSpellModifier::Write()
return &_worldPacket;
}
+
+WorldPacket const* WorldPackets::Spells::SendRemovedSpell::Write()
+{
+ _worldPacket << uint32(Spells.size());
+ for (uint32 spellId : Spells)
+ _worldPacket << uint32(spellId);
+
+ return &_worldPacket;
+} \ No newline at end of file
diff --git a/src/server/game/Server/Packets/SpellPackets.h b/src/server/game/Server/Packets/SpellPackets.h
index e9ae7ada77b..6971b730742 100644
--- a/src/server/game/Server/Packets/SpellPackets.h
+++ b/src/server/game/Server/Packets/SpellPackets.h
@@ -223,6 +223,16 @@ namespace WorldPackets
std::vector<SpellModifier> Modifiers;
};
+
+ class SendRemovedSpell final : public ServerPacket
+ {
+ public:
+ SendRemovedSpell() : ServerPacket(SMSG_REMOVED_SPELL, 4) { }
+
+ WorldPacket const* Write() override;
+
+ std::vector<uint32> Spells;
+ };
}
}
diff --git a/src/server/game/Server/Packets/TalentPackets.cpp b/src/server/game/Server/Packets/TalentPackets.cpp
index 4855f663662..d340b476cf9 100644
--- a/src/server/game/Server/Packets/TalentPackets.cpp
+++ b/src/server/game/Server/Packets/TalentPackets.cpp
@@ -41,3 +41,17 @@ void WorldPackets::Talent::SetSpecialization::Read()
{
_worldPacket >> SpecGroupIndex;
}
+
+
+void WorldPackets::Talent::LearnTalent::Read()
+{
+ uint32 count;
+ _worldPacket >> count;
+
+ for (uint32 i = 0; i < count; ++i)
+ {
+ uint16 talent;
+ _worldPacket >> talent;
+ Talents.push_back(talent);
+ }
+} \ No newline at end of file
diff --git a/src/server/game/Server/Packets/TalentPackets.h b/src/server/game/Server/Packets/TalentPackets.h
index 21753e22c8d..2bc8b3934a8 100644
--- a/src/server/game/Server/Packets/TalentPackets.h
+++ b/src/server/game/Server/Packets/TalentPackets.h
@@ -57,6 +57,19 @@ namespace WorldPackets
uint32 SpecGroupIndex = 0;
};
+
+ class LearnTalent final : public ClientPacket
+ {
+ public:
+ LearnTalent(WorldPacket&& packet) : ClientPacket(std::move(packet))
+ {
+ ASSERT(packet.GetOpcode() == CMSG_LEARN_TALENT);
+ }
+
+ void Read() override;
+ std::vector<uint16> Talents;
+
+ };
}
}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 79671b85892..671f0deb919 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -369,7 +369,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_KEEP_ALIVE, STATUS_NEVER, PROCESS_THREADUNSAFE, &WorldSession::Handle_EarlyProccess );
DEFINE_OPCODE_HANDLER_OLD(CMSG_LEARN_PREVIEW_TALENTS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnPreviewTalents );
DEFINE_OPCODE_HANDLER_OLD(CMSG_LEARN_PREVIEW_TALENTS_PET, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnPreviewTalentsPet );
- DEFINE_OPCODE_HANDLER_OLD(CMSG_LEARN_TALENT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnTalentOpcode );
+ DEFINE_HANDLER(CMSG_LEARN_TALENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Talent::LearnTalent, &WorldSession::HandleLearnTalentOpcode);
DEFINE_HANDLER(CMSG_LEAVE_CHANNEL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Channel::LeaveChannel, &WorldSession::HandleLeaveChannel);
DEFINE_OPCODE_HANDLER_OLD(CMSG_LFG_GET_STATUS, STATUS_UNHANDLED, PROCESS_THREADSAFE, &WorldSession::HandleLfgGetStatus );
DEFINE_OPCODE_HANDLER_OLD(CMSG_LFG_JOIN, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgJoinOpcode );
@@ -1247,7 +1247,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_REFER_A_FRIEND_FAILURE, STATUS_UNHANDLED);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_REFORGE_RESULT, STATUS_UNHANDLED);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_REFRESH_SPELL_HISTORY, STATUS_UNHANDLED);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_REMOVED_SPELL, STATUS_UNHANDLED);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_REMOVED_SPELL, STATUS_NEVER);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_REPORT_PVP_AFK_RESULT, STATUS_UNHANDLED);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_REQUEST_CEMETERY_LIST_RESPONSE, STATUS_UNHANDLED);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_REQUEST_PVP_REWARDS_RESPONSE, STATUS_UNHANDLED);
diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h
index f6bdf0892d7..ee95bd840ab 100644
--- a/src/server/game/Server/Protocol/Opcodes.h
+++ b/src/server/game/Server/Protocol/Opcodes.h
@@ -314,7 +314,7 @@ enum OpcodeClient : uint32
CMSG_KEEP_ALIVE = 0xBADD,
CMSG_LEARN_PREVIEW_TALENTS = 0xBADD,
CMSG_LEARN_PREVIEW_TALENTS_PET = 0xBADD,
- CMSG_LEARN_TALENT = 0xBADD,
+ CMSG_LEARN_TALENT = 0x0BB6,
CMSG_LEAVE_CHANNEL = 0x19F2,
CMSG_LFG_GET_STATUS = 0xBADD,
CMSG_LFG_JOIN = 0xBADD,
@@ -1269,7 +1269,7 @@ enum OpcodeServer : uint32
SMSG_REFER_A_FRIEND_FAILURE = 0xBADD,
SMSG_REFORGE_RESULT = 0xBADD,
SMSG_REFRESH_SPELL_HISTORY = 0x0A2A,
- SMSG_REMOVED_SPELL = 0xBADD,
+ SMSG_REMOVED_SPELL = 0x0B3B,
SMSG_REPORT_PVP_AFK_RESULT = 0xBADD,
SMSG_REQUEST_CEMETERY_LIST_RESPONSE = 0x059E,
SMSG_REQUEST_PVP_REWARDS_RESPONSE = 0xBADD,
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 26db96b232d..910a8ad4aa6 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -147,6 +147,7 @@ namespace WorldPackets
namespace Talent
{
class SetSpecialization;
+ class LearnTalent;
}
namespace Trade
@@ -876,7 +877,7 @@ class WorldSession
void HandleCancelGrowthAuraOpcode(WorldPacket& recvPacket);
void HandleCancelAutoRepeatSpellOpcode(WorldPacket& recvPacket);
- void HandleLearnTalentOpcode(WorldPacket& recvPacket);
+ void HandleLearnTalentOpcode(WorldPackets::Talent::LearnTalent& packet);
void HandleLearnPreviewTalents(WorldPacket& recvPacket);
void HandleTalentWipeConfirmOpcode(WorldPacket& recvPacket);
void HandleUnlearnSkillOpcode(WorldPacket& recvPacket);
diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp
index 9d2d4426efe..e571f30b74e 100644
--- a/src/server/scripts/Commands/cs_learn.cpp
+++ b/src/server/scripts/Commands/cs_learn.cpp
@@ -204,7 +204,7 @@ public:
// learn highest rank of talent and learn all non-talent spell ranks (recursive by tree)
player->LearnSpellHighestRank(talentInfo->SpellID);
- player->AddTalent(talentInfo->SpellID, player->GetActiveTalentGroup());
+ player->AddTalent(talentInfo->SpellID, player->GetActiveTalentGroup(), true);
}
handler->SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS);
@@ -306,7 +306,7 @@ public:
if (!handler->extractPlayerTarget((char*)args, &target))
return false;
- target->LearnDefaultSpells();
+ target->LearnDefaultSkills();
target->LearnCustomSpells();
target->LearnQuestRewardedSpells();