Core/Players: no longer rely on artificially created spell ranks and manually manage talent spell ranks

This commit is contained in:
Ovahlord
2023-11-21 02:04:32 +01:00
parent 6b0a3afe68
commit ca3d9ca0fc
5 changed files with 51 additions and 118 deletions

View File

@@ -2711,34 +2711,18 @@ void DeleteSpellFromAllPlayers(uint32 spellId)
CharacterDatabase.Execute(stmt);
}
bool Player::AddTalent(TalentEntry const* talent, uint16 rank, uint8 talentGroupId, bool learning)
bool Player::AddTalent(TalentEntry const* talent, uint8 rank, uint8 talentGroupId, bool learning)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellRank[rank], DIFFICULTY_NONE);
if (!spellInfo)
{
// do character spell book cleanup (all characters)
if (!IsInWorld() && !learning) // spell load case
{
TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) does not exist. Deleting for all characters in `character_spell` and `character_talent`.", talent->SpellRank[rank]);
DeleteSpellFromAllPlayers(talent->SpellRank[rank]);
}
else
TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) does not exist", talent->SpellRank[rank]);
TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) does not exist", talent->SpellRank[rank]);
return false;
}
}
if (!SpellMgr::IsSpellValid(spellInfo, this, false))
{
// do character spell book cleanup (all characters)
if (!IsInWorld() && !learning) // spell load case
{
TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) is invalid. Deleting for all characters in `character_spell` and `character_talent`.", talent->SpellRank[rank]);
DeleteSpellFromAllPlayers(talent->SpellRank[rank]);
}
else
TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) is invalid", talent->SpellRank[rank]);
TC_LOG_ERROR("spells", "Player::AddTalent: Spell (ID: {}) is invalid", talent->SpellRank[rank]);
return false;
}
@@ -2746,19 +2730,30 @@ bool Player::AddTalent(TalentEntry const* talent, uint16 rank, uint8 talentGroup
auto itr = talentMap.find(talent->ID);
if (itr != talentMap.end())
{
// Remove the previously learned talent
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellRank[itr->second.Rank], DIFFICULTY_NONE))
{
RemoveSpell(spellInfo->Id, true);
// search for spells that the talent teaches and unlearn them
for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL) && spellEffectInfo.TriggerSpell > 0)
RemoveSpell(spellEffectInfo.TriggerSpell, true);
}
itr->second.State = PLAYERSPELL_UNCHANGED;
itr->second.Rank = static_cast<uint8>(rank);
}
else
talentMap[talent->ID] = { PLAYERSPELL_UNCHANGED, static_cast<uint8>(rank) };
talentMap[talent->ID] = { PLAYERSPELL_UNCHANGED, rank };
// Inactive talent groups will only be initialized
if (GetActiveTalentGroup() != talentGroupId)
return true;
LearnSpell(spellInfo->Id, false);
if (talent->OverridesSpellID)
AddOverrideSpell(talent->OverridesSpellID, talent->SpellID);
if (GetActiveTalentGroup() == talentGroupId)
{
LearnSpell(spellInfo->Id, true);
if (talent->OverridesSpellID)
AddOverrideSpell(talent->OverridesSpellID, talent->SpellID);
}
if (learning)
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2::ChangeTalent);
@@ -2774,19 +2769,15 @@ void Player::RemoveTalent(TalentEntry const* talent)
return;
uint32 spellId = talent->SpellRank[itr->second.Rank];
if (!spellId)
return;
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE))
{
RemoveSpell(spellId, true);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE);
if (!spellInfo)
return;
RemoveSpell(spellId, true);
// search for spells that the talent teaches and unlearn them
for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL) && spellEffectInfo.TriggerSpell > 0)
RemoveSpell(spellEffectInfo.TriggerSpell, true);
// search for spells that the talent teaches and unlearn them
for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL) && spellEffectInfo.TriggerSpell > 0)
RemoveSpell(spellEffectInfo.TriggerSpell, true);
}
if (talent->OverridesSpellID)
RemoveOverrideSpell(talent->OverridesSpellID, talent->SpellID);
@@ -26117,7 +26108,7 @@ bool Player::ModifierTreeSatisfied(uint32 modifierTreeId) const
static constexpr uint8 NEEDED_TALENT_POINT_PER_TIER = 5;
bool Player::LearnTalent(uint32 talentId, uint16 requestedRank)
bool Player::LearnTalent(uint32 talentId, uint8 requestedRank)
{
// No talent points left to spend, skip learn request
if (!m_activePlayerData->CharacterPoints)
@@ -26196,16 +26187,14 @@ bool Player::LearnTalent(uint32 talentId, uint16 requestedRank)
return false;
}
// already known
if (HasSpell(spellId))
if (!AddTalent(talentInfo, requestedRank, GetActiveTalentGroup(), true))
return false;
AddTalent(talentInfo, requestedRank, GetActiveTalentGroup(), true);
TC_LOG_DEBUG("misc", "Player::LearnTalent: TalentID: {} Spell: {} Group: {}\n", talentId, spellId, uint32(GetActiveTalentGroup()));
// update free talent points
SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::CharacterPoints), static_cast<int32>(CalculateTalentsPoints() - GetSpentTalentPointsCount()));
SendTalentsInfoData();
return true;
}
@@ -27029,16 +27018,23 @@ void Player::ActivateTalentGroup(uint8 talentGroup)
if (!talentEntry)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talentEntry->SpellRank[pair.second.Rank], DIFFICULTY_NONE);
if (!spellInfo)
continue;
for (auto it = talentEntry->SpellRank.rbegin(); it != talentEntry->SpellRank.rend(); ++it)
{
uint32 spellId = *it;
if (!spellId)
continue;
RemoveSpell(spellInfo->Id, true);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE);
if (!spellInfo)
continue;
// search for spells that the talent teaches and unlearn them
for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL) && spellEffectInfo.TriggerSpell > 0)
RemoveSpell(spellEffectInfo.TriggerSpell, true);
RemoveSpell(spellInfo->Id, true);
// search for spells that the talent teaches and unlearn them
for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL) && spellEffectInfo.TriggerSpell > 0)
RemoveSpell(spellEffectInfo.TriggerSpell, true);
}
if (talentEntry->OverridesSpellID)
RemoveOverrideSpell(talentEntry->OverridesSpellID, talentEntry->SpellID);

View File

@@ -1826,8 +1826,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void InitTalentForLevel();
void SendTalentsInfoData();
uint32 CalculateTalentsPoints() const;
bool LearnTalent(uint32 talentId, uint16 requestedRank);
bool AddTalent(TalentEntry const* talent, uint16 rank, uint8 talentGroupId, bool learning);
bool LearnTalent(uint32 talentId, uint8 requestedRank);
bool AddTalent(TalentEntry const* talent, uint8 rank, uint8 talentGroupId, bool learning);
void RemoveTalent(TalentEntry const* talent);
void EnablePvpRules(bool dueToCombat = false);

View File

@@ -32,8 +32,7 @@ void WorldSession::HandleLearnTalentsOpcode(WorldPackets::Talent::LearnTalents&
void WorldSession::HandleLearnTalentOpcode(WorldPackets::Talent::LearnTalent& packet)
{
if (_player->LearnTalent(packet.TalentID, packet.RequestedRank))
_player->SendTalentsInfoData();
_player->LearnTalent(packet.TalentID, packet.RequestedRank);
}
void WorldSession::HandleLearnPvpTalentsOpcode(WorldPackets::Talent::LearnPvpTalents& /*packet*/)

View File

@@ -809,69 +809,8 @@ void SpellMgr::UnloadSpellInfoChains()
mSpellChains.clear();
}
void SpellMgr::LoadSpellTalentRanks()
{
// cleanup core data before reload - remove reference to ChainNode from SpellInfo
UnloadSpellInfoChains();
for (TalentEntry const* talentInfo : sTalentStore)
{
SpellInfo const* lastSpell = nullptr;
for (uint8 rank = talentInfo->SpellRank.size() - 1; rank > 0; --rank)
{
if (talentInfo->SpellRank[rank])
{
lastSpell = GetSpellInfo(talentInfo->SpellRank[rank], DIFFICULTY_NONE);
break;
}
}
if (!lastSpell)
continue;
SpellInfo const* firstSpell = GetSpellInfo(talentInfo->SpellRank[0], DIFFICULTY_NONE);
if (!firstSpell)
{
TC_LOG_ERROR("spells", "SpellMgr::LoadSpellTalentRanks: First Rank Spell {} for TalentEntry {} does not exist.", talentInfo->SpellRank[0], talentInfo->ID);
continue;
}
SpellInfo const* prevSpell = nullptr;
for (uint8 rank = 0; rank < talentInfo->SpellRank.size(); ++rank)
{
uint32 spellId = talentInfo->SpellRank[rank];
if (!spellId)
break;
SpellInfo const* currentSpell = GetSpellInfo(spellId, DIFFICULTY_NONE);
if (!currentSpell)
{
TC_LOG_ERROR("spells", "SpellMgr::LoadSpellTalentRanks: Spell {} (Rank: {}) for TalentEntry {} does not exist.", spellId, rank + 1, talentInfo->ID);
break;
}
SpellChainNode node;
node.first = firstSpell;
node.last = lastSpell;
node.rank = rank + 1;
node.prev = prevSpell;
node.next = node.rank < MAX_TALENT_RANK ? GetSpellInfo(talentInfo->SpellRank[node.rank], DIFFICULTY_NONE) : nullptr;
mSpellChains[spellId] = node;
for (SpellInfo const& difficultyInfo : _GetSpellInfo(spellId))
const_cast<SpellInfo&>(difficultyInfo).ChainEntry = &mSpellChains[spellId];
prevSpell = currentSpell;
}
}
}
void SpellMgr::LoadSpellRanks()
{
// cleanup data and load spell ranks for talents from dbc
LoadSpellTalentRanks();
uint32 oldMSTime = getMSTime();
std::map<uint32 /*spell*/, uint32 /*next*/> chains;

View File

@@ -768,7 +768,6 @@ class TC_GAME_API SpellMgr
// Loading data at server startup
void UnloadSpellInfoChains();
void LoadSpellTalentRanks();
void LoadSpellRanks();
void LoadSpellRequired();
void LoadSpellLearnSkills();