aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2015-01-30 23:58:16 +0100
committerShauren <shauren.trinity@gmail.com>2015-01-30 23:58:16 +0100
commit3f28fd304d833eadefff8d343ab45caf8d0575c2 (patch)
treed62b98f75a4607bd1e7cdc2944dce7f730981f6d /src/server/game/Entities
parentbeb1a06ea56526de838cc8e0de15317660a9ddcf (diff)
Core/Spells: Reimplemented automatic spell learning
* Fixed learning/unlearning talents
Diffstat (limited to 'src/server/game/Entities')
-rw-r--r--src/server/game/Entities/Player/Player.cpp574
-rw-r--r--src/server/game/Entities/Player/Player.h47
-rw-r--r--src/server/game/Entities/Unit/StatSystem.cpp2
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp15
-rw-r--r--src/server/game/Entities/Unit/Unit.h1
5 files changed, 353 insertions, 286 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 0c3d928f055..d373e46fc41 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -2998,13 +2998,14 @@ void Player::InitTalentForLevel()
{
uint8 level = getLevel();
// talents base at level diff (talents = level - 9 but some can be used already)
+ if (level < MIN_SPECIALIZATION_LEVEL)
+ ResetTalentSpecialization();
+
+ uint32 talentTiers = CalculateTalentsTiers();
if (level < 15)
{
// Remove all talent points
- if (GetUsedTalentCount() > 0) // Free any used talents
- {
- ResetTalents(true);
- }
+ ResetTalents(true);
}
else
{
@@ -3013,9 +3014,15 @@ void Player::InitTalentForLevel()
SetTalentGroupsCount(1);
SetActiveTalentGroup(0);
}
+
+ if (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_MORE_TALENTS_THAN_ALLOWED))
+ for (uint32 t = talentTiers; t < MAX_TALENT_TIERS; ++t)
+ for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c)
+ for (TalentEntry const* talent : sTalentByPos[getClass()][t][c])
+ RemoveTalent(talent);
}
- SetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS, CalculateTalentsPoints());
+ SetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS, talentTiers);
if (!GetSession()->PlayerLoading())
SendTalentsInfoData(); // update at client
@@ -3294,29 +3301,20 @@ void DeleteSpellFromAllPlayers(uint32 spellId)
}
}
-bool Player::AddTalent(uint32 talentId, uint8 spec, bool learning)
+bool Player::AddTalent(TalentEntry const* talent, uint8 spec, bool learning)
{
- TalentEntry const* talentEntry = sTalentStore.LookupEntry(talentId);
-
- // Check if talent exists in Talent.dbc
- if (!talentEntry)
- {
- TC_LOG_ERROR("spells", "Player::addTalent: Talent %u not found", talentId);
- return false;
- }
-
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talentEntry->SpellID);
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellID);
if (!spellInfo)
{
// do character spell book cleanup (all characters)
- if (!IsInWorld()) // spell load case
+ if (!IsInWorld() && !learning) // spell load case
{
- TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", talentEntry->SpellID);
+ TC_LOG_ERROR("spells", "Player::AddTalent: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", talent->SpellID);
- DeleteSpellFromAllPlayers(talentEntry->SpellID);
+ DeleteSpellFromAllPlayers(talent->SpellID);
}
else
- TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request.", talentEntry->SpellID);
+ TC_LOG_ERROR("spells", "Player::AddTalent: Non-existed in SpellStore spell #%u request.", talent->SpellID);
return false;
}
@@ -3324,40 +3322,58 @@ bool Player::AddTalent(uint32 talentId, uint8 spec, bool learning)
if (!SpellMgr::IsSpellValid(spellInfo, this, false))
{
// do character spell book cleanup (all characters)
- if (!IsInWorld()) // spell load case
+ if (!IsInWorld() && !learning) // spell load case
{
- TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", talentEntry->SpellID);
+ TC_LOG_ERROR("spells", "Player::AddTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", talent->SpellID);
- DeleteSpellFromAllPlayers(talentEntry->SpellID);
+ DeleteSpellFromAllPlayers(talent->SpellID);
}
else
- TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed.", talentEntry->SpellID);
+ TC_LOG_ERROR("spells", "Player::AddTalent: Broken spell #%u learning not allowed.", talent->SpellID);
return false;
}
- 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();
+ if (talent->OverridesSpellID)
+ AddOverrideSpell(talent->OverridesSpellID, talent->SpellID);
- newtalent->state = state;
- newtalent->spec = spec;
+ PlayerTalentMap::iterator itr = GetTalentMap(spec)->find(talent->ID);
+ if (itr != GetTalentMap(spec)->end())
+ itr->second->state = PLAYERSPELL_UNCHANGED;
+ else
+ {
+ PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
+ PlayerTalent* newtalent = new PlayerTalent();
- (*GetTalentMap(spec))[talentId] = newtalent;
+ newtalent->state = state;
+ newtalent->spec = spec;
- return true;
- }
- //else
- // TC_LOG_ERROR("spells", "Player::addTalent: Talent %u not found in talent store.", talentId);
+ (*GetTalentMap(spec))[talent->ID] = newtalent;
}
- else
- itr->second->state = PLAYERSPELL_UNCHANGED;
- return false;
+ return true;
+}
+
+void Player::RemoveTalent(TalentEntry const* talent)
+{
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellID);
+ if (!spellInfo)
+ return;
+
+ RemoveSpell(talent->SpellID, true);
+
+ // search for spells that the talent teaches and unlearn them
+ for (SpellEffectInfo const* effect : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE))
+ if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
+ RemoveSpell(effect->TriggerSpell, true);
+
+ 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->state = PLAYERSPELL_REMOVED;
}
bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading /*= false*/, bool fromSkill /*= false*/)
@@ -3453,7 +3469,7 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
if (active)
{
if (spellInfo->IsPassive() && IsNeedCastPassiveSpellAtLearn(spellInfo))
- CastSpell (this, spellId, true);
+ CastSpell(this, spellId, true);
}
else if (IsInWorld())
{
@@ -3569,11 +3585,9 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
return false;
}
- uint32 talentCost = GetTalentSpellCost(spellId);
-
// cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
// note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
- if (!loading && talentCost > 0 && spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL))
+ if (!loading && spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT) && spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL))
{
// ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
CastSpell(this, spellId, true);
@@ -3590,72 +3604,45 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
return false;
}
- // update used talent points count
- SetUsedTalentCount(GetUsedTalentCount() + talentCost);
-
// update free primary prof.points (if any, can be none in case GM .learn prof. learning)
if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
{
if (spellInfo->IsPrimaryProfessionFirstRank())
- SetFreePrimaryProfessions(freeProfs-1);
+ SetFreePrimaryProfessions(freeProfs - 1);
}
- // add dependent skills
- uint16 maxskill = GetMaxSkillValueForLevel();
-
- SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId);
-
SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
- if (spellLearnSkill)
+ // add dependent skills if this spell is not learned from adding skill already
+ if (!fromSkill)
{
- uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
- uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
+ if (SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId))
+ {
+ uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
+ uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
- if (skill_value < spellLearnSkill->value)
- skill_value = spellLearnSkill->value;
+ if (skill_value < spellLearnSkill->value)
+ skill_value = spellLearnSkill->value;
- uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue;
+ uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : spellLearnSkill->maxvalue;
- if (skill_max_value < new_skill_max_value)
- skill_max_value = new_skill_max_value;
+ if (skill_max_value < new_skill_max_value)
+ skill_max_value = new_skill_max_value;
- SetSkill(spellLearnSkill->skill, spellLearnSkill->step, skill_value, skill_max_value);
- }
- else
- {
- // not ranked skills
- for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
+ SetSkill(spellLearnSkill->skill, spellLearnSkill->step, skill_value, skill_max_value);
+ }
+ else
{
- SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
- if (!pSkill)
- continue;
-
- 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)))
+ // not ranked skills
+ for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
{
- switch (GetSkillRangeType(rcEntry))
- {
- case SKILL_RANGE_LANGUAGE:
- SetSkill(pSkill->ID, GetSkillStep(pSkill->ID), 300, 300);
- break;
- case SKILL_RANGE_LEVEL:
- SetSkill(pSkill->ID, GetSkillStep(pSkill->ID), 1, GetMaxSkillValueForLevel());
- break;
- case SKILL_RANGE_MONO:
- SetSkill(pSkill->ID, GetSkillStep(pSkill->ID), 1, 1);
- break;
- default:
- break;
- }
+ SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
+ if (!pSkill)
+ continue;
+
+ ///@todo: confirm if rogues start with lockpicking skill at level 1 but only receive the spell to use it at level 16
+ if ((_spell_idx->second->AquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(pSkill->ID)) || (pSkill->ID == SKILL_LOCKPICKING && _spell_idx->second->TrivialSkillLineRankHigh == 0))
+ LearnDefaultSkill(pSkill->ID, 0);
}
}
}
@@ -3734,8 +3721,7 @@ bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const
bool Player::IsCurrentSpecMasterySpell(SpellInfo const* spellInfo) const
{
-
- if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec()))
+ if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup())))
return spellInfo->Id == chrSpec->MasterySpellID[0] || spellInfo->Id == chrSpec->MasterySpellID[1];
return false;
@@ -3790,7 +3776,8 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
// unlearn non talent higher ranks (recursive)
if (uint32 nextSpell = sSpellMgr->GetNextSpellInChain(spell_id))
{
- if (HasSpell(nextSpell) && !GetTalentBySpellID(nextSpell))
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(nextSpell);
+ if (HasSpell(nextSpell) && !spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT))
RemoveSpell(nextSpell, disabled, false);
}
//unlearn spells dependent from recently removed spells
@@ -3916,6 +3903,8 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
}
}
+ m_overrideSpells.erase(spell_id);
+
if (spell_id == 46917 && m_canTitanGrip)
SetCanTitanGrip(false);
@@ -4118,11 +4107,8 @@ uint32 Player::GetNextResetTalentsCost() const
}
}
-bool Player::ResetTalents(bool noCost, bool resetTalents, bool resetSpecialization)
+bool Player::ResetTalents(bool noCost)
{
- if (!resetTalents && !resetSpecialization)
- return false;
-
sScriptMgr->OnPlayerTalentsReset(this, noCost);
// not need after this call
@@ -4144,41 +4130,30 @@ bool Player::ResetTalents(bool noCost, bool resetTalents, bool resetSpecializati
RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
- if (resetTalents)
+ for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
{
- 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;
-
- SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
- if (!spellEntry)
- continue;
+ TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
+ if (!talentInfo)
+ continue;
- RemoveSpell(spellEntry->Id, false);
+ // 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;
- // 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);
+ // skip non-existant talent ranks
+ if (talentInfo->SpellID == 0)
+ continue;
- GetTalentMap(GetActiveTalentGroup())->erase(talentId);
- }
+ RemoveTalent(talentInfo);
}
- if (resetSpecialization)
- {
- SetTalentSpec(GetActiveTalentGroup(), 0);
- SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, 0);
- }
+ // Remove spec specific spells
+ RemoveSpecializationSpells();
+
+ SetSpecId(GetActiveTalentGroup(), 0);
+ SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, 0);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
_SaveTalents(trans);
@@ -4206,35 +4181,6 @@ bool Player::ResetTalents(bool noCost, bool resetTalents, bool resetSpecializati
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)
@@ -4299,10 +4245,10 @@ bool Player::HasSpell(uint32 spell) const
!itr->second->disabled);
}
-bool Player::HasTalent(uint32 talentId, uint8 group)
+bool Player::HasTalent(uint32 talentId, uint8 group) const
{
PlayerTalentMap::const_iterator itr = GetTalentMap(group)->find(talentId);
- return (itr != GetTalentMap(group)->end());
+ return (itr != GetTalentMap(group)->end() && itr->second->state != PLAYERSPELL_REMOVED);
}
bool Player::HasActiveSpell(uint32 spell) const
@@ -5951,26 +5897,28 @@ void Player::UpdateSkillsForLevel()
if (!rcEntry)
continue;
- if (GetSkillRangeType(rcEntry) != SKILL_RANGE_LEVEL)
- continue;
-
- if (IsWeaponSkill(rcEntry->SkillID))
- continue;
-
uint16 field = itr->second.pos / 2;
uint8 offset = itr->second.pos & 1; // itr->second.pos % 2
- //uint16 val = GetUInt16Value(PLAYER_SKILL_LINEID + SKILL_RANK_OFFSET + field, offset);
- uint16 max = GetUInt16Value(PLAYER_SKILL_LINEID + SKILL_MAX_RANK_OFFSET + field, offset);
-
- /// update only level dependent max skill values
- if (max != 1)
+ if (GetSkillRangeType(rcEntry) == SKILL_RANGE_LEVEL)
{
- SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_RANK_OFFSET + field, offset, maxSkill);
- SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_MAX_RANK_OFFSET + field, offset, maxSkill);
- if (itr->second.uState != SKILL_NEW)
- itr->second.uState = SKILL_CHANGED;
+ if (!IsWeaponSkill(rcEntry->SkillID))
+ {
+ uint16 max = GetUInt16Value(PLAYER_SKILL_LINEID + SKILL_MAX_RANK_OFFSET + field, offset);
+
+ /// update only level dependent max skill values
+ if (max != 1)
+ {
+ SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_RANK_OFFSET + field, offset, maxSkill);
+ SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_MAX_RANK_OFFSET + field, offset, maxSkill);
+ if (itr->second.uState != SKILL_NEW)
+ itr->second.uState = SKILL_CHANGED;
+ }
+ }
}
+
+ // Update level dependent skillline spells
+ LearnSkillRewardedSpells(rcEntry->SkillID, GetUInt16Value(PLAYER_SKILL_LINEID + SKILL_RANK_OFFSET + field, offset));
}
}
@@ -8415,7 +8363,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8
Spell* spell = new Spell(this, spellInfo, (count > 0) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
spell->m_CastItem = item;
spell->m_cast_count = cast_count; // set count of casts
- spell->m_glyphIndex = glyphIndex; // glyph index
+ spell->m_misc.Data = glyphIndex; // glyph index
spell->prepare(&targets);
++count;
@@ -8443,7 +8391,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8
Spell* spell = new Spell(this, spellInfo, (count > 0) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
spell->m_CastItem = item;
spell->m_cast_count = cast_count; // set count of casts
- spell->m_glyphIndex = glyphIndex; // glyph index
+ spell->m_misc.Data = glyphIndex; // glyph index
spell->prepare(&targets);
++count;
@@ -17193,17 +17141,19 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
if (talentSpec)
{
if (sChrSpecializationStore.LookupEntry(talentSpec))
- SetTalentSpec(i, talentSpec);
+ SetSpecId(i, talentSpec);
else
SetAtLoginFlag(AT_LOGIN_RESET_TALENTS);
}
}
- SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetActiveTalentSpec());
+ SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetSpecId(GetActiveTalentGroup()));
_LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS));
_LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS));
+ LearnSpecializationSpells();
+
_LoadGlyphs(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GLYPHS));
_LoadAuras(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURAS), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURA_EFFECTS), time_diff);
_LoadGlyphAuras();
@@ -18912,7 +18862,7 @@ void Player::SaveToDB(bool create /*=false*/)
ss.str("");
for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i)
- ss << GetTalentSpec(i) << " ";
+ ss << GetSpecId(i) << " ";
stmt->setString(index++, ss.str());
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
stmt->setUInt8(index++, m_stableSlots);
@@ -19049,7 +18999,7 @@ void Player::SaveToDB(bool create /*=false*/)
ss.str("");
for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i)
- ss << GetTalentSpec(i) << " ";
+ ss << GetSpecId(i) << " ";
stmt->setString(index++, ss.str());
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
stmt->setUInt8(index++, m_stableSlots);
@@ -25404,43 +25354,60 @@ void Player::CompletedAchievement(AchievementEntry const* entry)
bool Player::LearnTalent(uint32 talentId)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
-
if (!talentInfo)
return false;
- uint32 maxTalentTier = GetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS);
+ if (talentInfo->SpecID && talentInfo->SpecID != GetSpecId(GetActiveTalentGroup()))
+ return false;
// prevent learn talent for different class (cheating)
if (talentInfo->ClassID != getClass())
return false;
// check if we have enough talent points
- if (talentInfo->TierID > maxTalentTier)
+ if (talentInfo->TierID >= GetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS))
return false;
- // Check if player doesnt have any spell in selected collumn
- for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++)
+ // 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 : sTalentByPos[getClass()][talentInfo->TierID][talentInfo->ColumnIndex])
{
- if (TalentEntry const* talent = sTalentStore.LookupEntry(i))
+ if (!talent->SpecID)
+ bestSlotMatch = talent;
+ else if (talent->SpecID == GetSpecId(GetActiveTalentGroup()))
{
- if (talentInfo->TierID == talent->TierID && HasSpell(talent->SpellID))
- return false;
+ bestSlotMatch = talent;
+ break;
}
}
+ if (talentInfo != bestSlotMatch)
+ return false;
+
+ // Check if player doesnt have any talent in current tier
+ for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c)
+ for (TalentEntry const* talent : sTalentByPos[getClass()][talentInfo->TierID][c])
+ if (HasTalent(talent->ID, GetActiveTalentGroup()))
+ return false;
+
// spell not set in talent.dbc
uint32 spellid = talentInfo->SpellID;
- if (spellid == 0)
+ if (!spellid)
{
TC_LOG_ERROR("entities.player", "Talent.dbc has no spellInfo for talent: %u (spell id = 0)", talentId);
return false;
}
// already known
- if (HasSpell(spellid))
+ if (HasTalent(talentId, GetActiveTalentGroup()) || HasSpell(spellid))
return false;
- if (!AddTalent(talentId, GetActiveTalentGroup(), true))
+ if (!AddTalent(talentInfo, GetActiveTalentGroup(), true))
return false;
LearnSpell(spellid, false);
@@ -25452,49 +25419,42 @@ bool Player::LearnTalent(uint32 talentId)
void Player::LearnTalentSpecialization(uint32 talentSpec)
{
- if (GetActiveTalentSpec())
+ if (GetSpecId(GetActiveTalentGroup()))
return;
- SetTalentSpec(GetActiveTalentGroup(), talentSpec);
-
+ SetSpecId(GetActiveTalentGroup(), talentSpec);
SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, talentSpec);
- PlayerTalentMap* talents = GetTalentMap(GetActiveTalentGroup());
-
- for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
- {
- TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
-
- if (!talentInfo || talentInfo->ClassID != getClass() || talentInfo->SpecID != talentSpec)
- continue;
-
- for (PlayerTalentMap::iterator itr = talents->begin(); itr != talents->end();)
- {
- TalentEntry const* talent = sTalentStore.LookupEntry(itr->first);
- if (!talent || talent->TierID != talentInfo->TierID)
- {
- ++itr;
- continue;
- }
- RemoveSpell(talent->SpellID, false);
- itr = talents->erase(itr);
-
- TC_LOG_DEBUG("spells", "Player %s unlearning talent id: %u tier: %u due to specialization change", GetName().c_str(), talent->ID, talent->TierID);
- }
- }
+ // Reset only talents that have different spells for each spec
+ uint32 class_ = getClass();
+ for (uint32 t = 0; t < MAX_TALENT_TIERS; ++t)
+ for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c)
+ if (sTalentByPos[class_][t][c].size() > 1)
+ for (TalentEntry const* talent : sTalentByPos[class_][t][c])
+ RemoveTalent(talent);
+ LearnSpecializationSpells();
SendTalentsInfoData();
+}
- SaveToDB();
+void Player::ResetTalentSpecialization()
+{
+ if (!GetSpecId(GetActiveTalentGroup()))
+ return;
- SendTalentsInfoData();
-}
+ SetSpecId(GetActiveTalentGroup(), 0);
+ SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, 0);
+ // Reset only talents that have different spells for each spec
+ uint32 class_ = getClass();
+ for (uint32 t = 0; t < MAX_TALENT_TIERS; ++t)
+ for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c)
+ if (sTalentByPos[class_][t][c].size() > 1)
+ for (TalentEntry const* talent : sTalentByPos[class_][t][c])
+ RemoveTalent(talent);
-void Player::AddKnownCurrency(uint32 itemId)
-{
- if (CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId))
- SetFlag64(0, (1LL << (ctEntry->ID-1)));
+ RemoveSpecializationSpells();
+ SendTalentsInfoData();
}
void Player::UpdateFallInformationIfNeed(MovementInfo const& minfo, uint16 opcode)
@@ -25577,12 +25537,14 @@ void Player::SendTalentsInfoData()
{
WorldPackets::Talent::TalentGroupInfo groupInfoPkt;
- groupInfoPkt.SpecID = GetTalentSpec(i);
-
+ groupInfoPkt.SpecID = GetSpecId(i);
groupInfoPkt.TalentIDs.reserve(GetTalentMap(i)->size());
for (PlayerTalentMap::const_iterator itr = GetTalentMap(i)->begin(); itr != GetTalentMap(i)->end(); ++itr)
{
+ if (itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+
TalentEntry const* talentInfo = sTalentStore.LookupEntry(itr->first);
if (!talentInfo)
{
@@ -25600,9 +25562,6 @@ void Player::SendTalentsInfoData()
continue;
}
- if (!HasTalent(itr->first, i))
- continue;
-
groupInfoPkt.TalentIDs.push_back(uint16(itr->first));
}
@@ -25919,7 +25878,8 @@ void Player::_LoadTalents(PreparedQueryResult result)
if (result)
{
do
- AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt8(), false);
+ if (TalentEntry const* talent = sTalentStore.LookupEntry((*result)[0].GetUInt32()))
+ AddTalent(talent, (*result)[1].GetUInt8(), false);
while (result->NextRow());
}
}
@@ -25930,15 +25890,25 @@ void Player::_SaveTalents(SQLTransaction& trans)
stmt->setUInt64(0, GetGUID().GetCounter());
trans->Append(stmt);
+ PlayerTalentMap* talents;
for (uint8 group = 0; group < MAX_TALENT_GROUPS; ++group)
{
- for (PlayerTalentMap::iterator itr = GetTalentMap(group)->begin(); itr != GetTalentMap(group)->end(); ++itr)
+ talents = GetTalentMap(group);
+ for (PlayerTalentMap::iterator itr = talents->begin(); itr != talents->end();)
{
+ if (itr->second->state == PLAYERSPELL_REMOVED)
+ {
+ delete itr->second;
+ 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, itr->second->spec);
trans->Append(stmt);
+ ++itr;
}
}
}
@@ -25989,12 +25959,12 @@ void Player::UpdateTalentGroupCount(uint8 count)
SendTalentsInfoData();
}
-void Player::ActivateTalentGroup(uint8 group)
+void Player::ActivateTalentGroup(uint8 spec)
{
- if (GetActiveTalentGroup() == group)
+ if (GetActiveTalentGroup() == spec)
return;
- if (group > GetTalentGroupsCount())
+ if (spec > GetTalentGroupsCount())
return;
if (IsNonMeleeSpellCast(false))
@@ -26026,57 +25996,76 @@ void Player::ActivateTalentGroup(uint8 group)
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 (getClass() != talentInfo->ClassID)
+ if (talentInfo->ClassID != getClass())
+ continue;
+
+ if (talentInfo->SpellID == 0)
continue;
- SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
- if (!spellEntry)
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
+ if (!spellInfo)
continue;
RemoveSpell(talentInfo->SpellID, true);
// search for spells that the talent teaches and unlearn them
- for (SpellEffectInfo const* effect : spellEntry->GetEffectsForDifficulty(DIFFICULTY_NONE))
+ for (SpellEffectInfo const* effect : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE))
if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
RemoveSpell(effect->TriggerSpell, true);
+
+ if (talentInfo->OverridesSpellID)
+ RemoveOverrideSpell(talentInfo->OverridesSpellID, talentInfo->SpellID);
}
- // remove glyphs
+ // Remove spec specific spells
+ RemoveSpecializationSpells();
+
+ // set glyphs
for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
// remove secondary glyph
if (uint32 oldglyph = GetGlyph(GetActiveTalentGroup(), slot))
if (GlyphPropertiesEntry const* old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph))
RemoveAurasDueToSpell(old_gp->SpellID);
- // Activate new group
- SetActiveTalentGroup(group);
-
- uint32 spentTalents = 0;
+ SetActiveTalentGroup(spec);
+ SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetSpecId(spec));
for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
-
if (!talentInfo)
continue;
// learn only talents for character class
- if (getClass() != talentInfo->ClassID)
+ if (talentInfo->ClassID != getClass())
continue;
- ++spentTalents;
+ if (!talentInfo->SpellID)
+ continue;
- if (HasTalent(talentInfo->SpellID, group))
- LearnSpell(talentInfo->SpellID, false);
+ // if the talent can be found in the newly activated PlayerTalentMap
+ if (HasTalent(talentInfo->ID, GetActiveTalentGroup()))
+ {
+ LearnSpell(talentInfo->SpellID, false); // add the talent to the PlayerSpellMap
+ if (talentInfo->OverridesSpellID)
+ AddOverrideSpell(talentInfo->OverridesSpellID, talentInfo->SpellID);
+ }
}
+ LearnSpecializationSpells();
+
+ if (CanUseMastery())
+ if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup())))
+ for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i)
+ if (uint32 mastery = specialization->MasterySpellID[i])
+ LearnSpell(mastery, false);
+
// set glyphs
for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
{
@@ -26090,7 +26079,6 @@ void Player::ActivateTalentGroup(uint8 group)
SetGlyph(slot, glyph);
}
- SetUsedTalentCount(spentTalents);
InitTalentForLevel();
{
@@ -26109,10 +26097,8 @@ void Player::ActivateTalentGroup(uint8 group)
SetPower(pw, 0);
- if (!sChrSpecializationStore.LookupEntry(GetActiveTalentSpec()))
- {
+ if (!sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup())))
ResetTalents(true);
- }
}
void Player::ResetTimeSync()
@@ -27153,10 +27139,17 @@ void Player::SendSupercededSpell(uint32 oldSpell, uint32 newSpell)
GetSession()->SendPacket(&data);
}
-uint32 Player::CalculateTalentsPoints() const
+uint32 Player::CalculateTalentsTiers() const
{
- // 1 talent point for every 15 levels
- return getLevel() >= 100 ? 7 : uint32(floor(getLevel() / 15.f));
+ static uint32 const DefaultTalentRowLevels[MAX_TALENT_TIERS] = { 15, 30, 45, 60, 75, 90, 100 };
+ static uint32 const DKTalentRowLevels[MAX_TALENT_TIERS] = { 57, 58, 59, 60, 75, 90, 100 };
+
+ uint32 const* rowLevels = (getClass() != CLASS_DEATH_KNIGHT) ? DefaultTalentRowLevels : DKTalentRowLevels;
+ for (uint32 i = MAX_TALENT_TIERS; i; --i)
+ if (getLevel() >= rowLevels[i - 1])
+ return i;
+
+ return 0;
}
Difficulty Player::GetDifficultyID(MapEntry const* mapEntry) const
@@ -27219,3 +27212,64 @@ Difficulty Player::CheckLoadedLegacyRaidDifficultyID(Difficulty difficulty)
return difficulty;
}
+
+SpellInfo const* Player::GetCastSpellInfo(SpellInfo const* spellInfo) const
+{
+ auto range = m_overrideSpells.equal_range(spellInfo->Id);
+ for (auto itr = range.first; itr != range.second; ++itr)
+ if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo(itr->second))
+ return Unit::GetCastSpellInfo(newInfo);
+
+ return Unit::GetCastSpellInfo(spellInfo);
+}
+
+void Player::RemoveOverrideSpell(uint32 overridenSpellId, uint32 newSpellId)
+{
+ auto range = m_overrideSpells.equal_range(overridenSpellId);
+ for (auto itr = range.first; itr != range.second; ++itr)
+ {
+ if (itr->second == newSpellId)
+ {
+ m_overrideSpells.erase(itr);
+ break;
+ }
+ }
+}
+
+void Player::LearnSpecializationSpells()
+{
+ if (std::vector<SpecializationSpellsEntry const*> const* specSpells = GetSpecializationSpells(GetSpecId(GetActiveTalentGroup())))
+ {
+ for (size_t j = 0; j < specSpells->size(); ++j)
+ {
+ SpecializationSpellsEntry const* specSpell = specSpells->at(j);
+ LearnSpell(specSpell->SpellID, false);
+ if (specSpell->OverridesSpellID)
+ AddOverrideSpell(specSpell->OverridesSpellID, specSpell->SpellID);
+ }
+ }
+}
+
+void Player::RemoveSpecializationSpells()
+{
+ for (uint32 i = 0; i < MAX_SPECIALIZATIONS; ++i)
+ {
+ if (ChrSpecializationEntry const* specialization = sChrSpecializationByIndexStore[getClass()][i])
+ {
+ if (std::vector<SpecializationSpellsEntry const*> const* specSpells = GetSpecializationSpells(specialization->ID))
+ {
+ for (size_t j = 0; j < specSpells->size(); ++j)
+ {
+ SpecializationSpellsEntry const* specSpell = specSpells->at(j);
+ RemoveSpell(specSpell->SpellID, true);
+ if (specSpell->OverridesSpellID)
+ RemoveOverrideSpell(specSpell->OverridesSpellID, specSpell->SpellID);
+ }
+ }
+
+ for (uint32 j = 0; j < MAX_MASTERY_SPELLS; ++j)
+ if (uint32 mastery = specialization->MasterySpellID[j])
+ RemoveAurasDueToSpell(mastery);
+ }
+ }
+}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index e72a22fefbb..a22cd007ee0 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1247,13 +1247,15 @@ private:
struct PlayerTalentInfo
{
- PlayerTalentInfo() : UsedTalentCount(0), ResetTalentsCost(0), ResetTalentsTime(0), ActiveGroup(0), GroupsCount(1)
+ PlayerTalentInfo() :
+ ResetTalentsCost(0), ResetTalentsTime(0),
+ ActiveGroup(0), GroupsCount(1)
{
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;
+ GroupInfo[i].SpecId = 0;
}
}
@@ -1271,10 +1273,9 @@ struct PlayerTalentInfo
{
PlayerTalentMap* Talents;
uint32 Glyphs[MAX_GLYPH_SLOT_INDEX];
- uint32 TalentTree;
+ uint32 SpecId;
} GroupInfo[MAX_TALENT_GROUPS];
- uint32 UsedTalentCount;
uint32 ResetTalentsCost;
time_t ResetTalentsTime;
uint8 ActiveGroup;
@@ -1284,6 +1285,7 @@ private:
PlayerTalentInfo(PlayerTalentInfo const&);
};
+
class Player : public Unit, public GridObject<Player>
{
friend class WorldSession;
@@ -1820,6 +1822,7 @@ class Player : public Unit, public GridObject<Player>
void SendRemoveControlBar();
bool HasSpell(uint32 spell) const override;
bool HasActiveSpell(uint32 spell) const; // show in spellbook
+ SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo) const override;
TrainerSpellState GetTrainerSpellState(TrainerSpell const* trainer_spell) const;
bool IsSpellFitByClassAndRace(uint32 spell_id) const;
bool IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const;
@@ -1839,41 +1842,37 @@ class Player : public Unit, public GridObject<Player>
void LearnSpellHighestRank(uint32 spellid);
void AddTemporarySpell(uint32 spellId);
void RemoveTemporarySpell(uint32 spellId);
+ void AddOverrideSpell(uint32 overridenSpellId, uint32 newSpellId) { m_overrideSpells.emplace(overridenSpellId, newSpellId); }
+ void RemoveOverrideSpell(uint32 overridenSpellId, uint32 newSpellId);
+ void LearnSpecializationSpells();
+ void RemoveSpecializationSpells();
void SetReputation(uint32 factionentry, uint32 value);
uint32 GetReputation(uint32 factionentry) const;
std::string GetGuildName();
// 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 GetTalentGroupsCount() const { return _talentMgr->GroupsCount; }
- void SetTalentGroupsCount(uint8 count) { _talentMgr->GroupsCount = count; }
+ uint32 GetSpecId(uint8 group) const { return _talentMgr->GroupInfo[group].SpecId; }
+ void SetSpecId(uint8 group, uint32 tree) { _talentMgr->GroupInfo[group].SpecId = tree; }
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].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 noCost = false);
uint32 GetNextResetTalentsCost() const;
void InitTalentForLevel();
void SendTalentsInfoData();
bool LearnTalent(uint32 talentId);
- bool AddTalent(uint32 talentId, uint8 spec, bool learning);
- bool HasTalent(uint32 talentId, uint8 spec);
- uint32 CalculateTalentsPoints() const;
-
-
+ bool AddTalent(TalentEntry const* talent, uint8 spec, bool learning);
+ bool HasTalent(uint32 spell_id, uint8 spec) const;
+ void RemoveTalent(TalentEntry const* talent);
+ uint32 CalculateTalentsTiers() const;
void LearnTalentSpecialization(uint32 talentSpec);
+ void ResetTalentSpecialization();
// Dual Spec
void UpdateTalentGroupCount(uint8 count);
@@ -2801,6 +2800,7 @@ class Player : public Unit, public GridObject<Player>
PlayerMails m_mail;
PlayerSpellMap m_spells;
+ std::unordered_multimap<uint32 /*overridenSpellId*/, uint32 /*newSpellId*/> m_overrideSpells;
uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use
GlobalCooldownMgr m_GlobalCooldownMgr;
@@ -2919,9 +2919,6 @@ class Player : public Unit, public GridObject<Player>
void RefundItem(Item* item);
void SendItemRefundResult(Item* item, ItemExtendedCostEntry const* iece, uint8 error);
- // know currencies are not removed at any point (0 displayed)
- void AddKnownCurrency(uint32 itemId);
-
void AdjustQuestReqItemCount(Quest const* quest);
bool IsCanDelayTeleport() const { return m_bCanDelayTeleport; }
diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp
index cbb62fcad73..237f963be2b 100644
--- a/src/server/game/Entities/Unit/StatSystem.cpp
+++ b/src/server/game/Entities/Unit/StatSystem.cpp
@@ -526,7 +526,7 @@ void Player::UpdateMastery()
value += GetRatingBonusValue(CR_MASTERY);
SetFloatValue(PLAYER_MASTERY, value);
- ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec());
+ ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup()));
if (!chrSpec)
return;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 1a67ac2609b..59822acb7ad 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -16569,3 +16569,18 @@ void Unit::Whisper(uint32 textId, Player* target, bool isBossWhisper /*= false*/
packet.Initalize(isBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER, LANG_UNIVERSAL, this, target, DB2Manager::GetBroadcastTextValue(bct, locale, getGender()), 0, "", locale);
target->SendDirectMessage(packet.Write());
}
+
+SpellInfo const* Unit::GetCastSpellInfo(SpellInfo const* spellInfo) const
+{
+ Unit::AuraEffectList swaps = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS);
+ Unit::AuraEffectList const& swaps2 = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_2);
+ if (!swaps2.empty())
+ swaps.insert(swaps.end(), swaps2.begin(), swaps2.end());
+
+ for (AuraEffect const* auraEffect : swaps)
+ if (auraEffect->IsAffectingSpell(spellInfo))
+ if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo(auraEffect->GetAmount()))
+ return newInfo;
+
+ return spellInfo;
+}
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index a42d9de5a63..0311b67befd 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -1936,6 +1936,7 @@ class Unit : public WorldObject
Spell* GetCurrentSpell(uint32 spellType) const { return m_currentSpells[spellType]; }
Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
int32 GetCurrentSpellCastTime(uint32 spell_id) const;
+ virtual SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo) const;
ObjectGuid m_SummonSlot[MAX_SUMMON_SLOT];
ObjectGuid m_ObjectSlot[MAX_GAMEOBJECT_SLOT];