fix(Core/Commands): Learn profession rank spells in .learn all recipes (#25114)
Co-authored-by: Ludwig <sudlud@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,8 @@
|
|||||||
#include "Tokenize.h"
|
#include "Tokenize.h"
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
bool IsPrimaryProfessionSkill(uint32 skill)
|
bool IsPrimaryProfessionSkill(uint32 skill)
|
||||||
{
|
{
|
||||||
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
|
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
|
||||||
@@ -687,6 +689,24 @@ SpellLearnSkillNode const* SpellMgr::GetSpellLearnSkill(uint32 spell_id) const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint32> SpellMgr::GetSkillRankSpells(uint32 skillId) const
|
||||||
|
{
|
||||||
|
// Returns every spell that grants this skill via SPELL_EFFECT_SKILL,
|
||||||
|
// i.e. the profession rank/proficiency spells (Apprentice -> Grand Master),
|
||||||
|
// ordered by step. Not strictly limited to the six ranks: any skill-granting
|
||||||
|
// spell for the line is included.
|
||||||
|
std::vector<uint32> result;
|
||||||
|
for (auto const& [spellId, node] : mSpellLearnSkills)
|
||||||
|
if (node.skill == skillId)
|
||||||
|
result.push_back(spellId);
|
||||||
|
|
||||||
|
std::ranges::sort(result, [this](uint32 a, uint32 b)
|
||||||
|
{
|
||||||
|
return mSpellLearnSkills.at(a).step < mSpellLearnSkills.at(b).step;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
SpellTargetPosition const* SpellMgr::GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const
|
SpellTargetPosition const* SpellMgr::GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const
|
||||||
{
|
{
|
||||||
SpellTargetPositionMap::const_iterator itr = mSpellTargetPositions.find(std::make_pair(spell_id, effIndex));
|
SpellTargetPositionMap::const_iterator itr = mSpellTargetPositions.find(std::make_pair(spell_id, effIndex));
|
||||||
|
|||||||
@@ -687,6 +687,7 @@ public:
|
|||||||
|
|
||||||
// Spell learning
|
// Spell learning
|
||||||
[[nodiscard]] SpellLearnSkillNode const* GetSpellLearnSkill(uint32 spell_id) const;
|
[[nodiscard]] SpellLearnSkillNode const* GetSpellLearnSkill(uint32 spell_id) const;
|
||||||
|
[[nodiscard]] std::vector<uint32> GetSkillRankSpells(uint32 skillId) const;
|
||||||
|
|
||||||
// Spell target coordinates
|
// Spell target coordinates
|
||||||
[[nodiscard]] SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const;
|
[[nodiscard]] SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const;
|
||||||
|
|||||||
@@ -309,6 +309,8 @@ public:
|
|||||||
|
|
||||||
static bool HandleLearnAllCraftsCommand(ChatHandler* handler)
|
static bool HandleLearnAllCraftsCommand(ChatHandler* handler)
|
||||||
{
|
{
|
||||||
|
Player* target = handler->GetSession()->GetPlayer();
|
||||||
|
|
||||||
for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
|
for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
|
||||||
{
|
{
|
||||||
SkillLineEntry const* skillInfo = sSkillLineStore.LookupEntry(i);
|
SkillLineEntry const* skillInfo = sSkillLineStore.LookupEntry(i);
|
||||||
@@ -318,7 +320,10 @@ public:
|
|||||||
if ((skillInfo->categoryId == SKILL_CATEGORY_PROFESSION || skillInfo->categoryId == SKILL_CATEGORY_SECONDARY) &&
|
if ((skillInfo->categoryId == SKILL_CATEGORY_PROFESSION || skillInfo->categoryId == SKILL_CATEGORY_SECONDARY) &&
|
||||||
skillInfo->canLink) // only prof. with recipes have
|
skillInfo->canLink) // only prof. with recipes have
|
||||||
{
|
{
|
||||||
HandleLearnSkillRecipesHelper(handler->GetSession()->GetPlayer(), skillInfo->id);
|
HandleLearnSkillRecipesHelper(target, skillInfo->id);
|
||||||
|
|
||||||
|
uint16 const maxLevel = target->GetPureMaxSkillValue(skillInfo->id);
|
||||||
|
target->SetSkill(skillInfo->id, target->GetSkillStep(skillInfo->id), maxLevel, maxLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,6 +393,15 @@ public:
|
|||||||
|
|
||||||
static void HandleLearnSkillRecipesHelper(Player* player, uint32 skillId)
|
static void HandleLearnSkillRecipesHelper(Player* player, uint32 skillId)
|
||||||
{
|
{
|
||||||
|
// Rank spells (Apprentice -> Grand Master) must be learned so that the
|
||||||
|
// skill-cleanup loop in Player::SetSkill (which calls removeSpell on the
|
||||||
|
// first spell in each chain) can walk forward and strip every rank on
|
||||||
|
// profession unlearn. Without the first rank in the spellbook that loop
|
||||||
|
// bails out and the leftover rank spells re-grant the skill after relog
|
||||||
|
// (issue #2330).
|
||||||
|
for (uint32 rankSpell : sSpellMgr->GetSkillRankSpells(skillId))
|
||||||
|
player->learnSpell(rankSpell);
|
||||||
|
|
||||||
uint32 classmask = player->getClassMask();
|
uint32 classmask = player->getClassMask();
|
||||||
|
|
||||||
for (SkillLineAbilityEntry const* skillLine : GetSkillLineAbilitiesBySkillLine(skillId))
|
for (SkillLineAbilityEntry const* skillLine : GetSkillLineAbilitiesBySkillLine(skillId))
|
||||||
|
|||||||
Reference in New Issue
Block a user