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:
Taras
2026-06-13 15:30:34 +02:00
committed by GitHub
parent 12f1834d41
commit 81457d95f2
3 changed files with 36 additions and 1 deletions

View File

@@ -33,6 +33,8 @@
#include "Tokenize.h"
#include "World.h"
#include <algorithm>
bool IsPrimaryProfessionSkill(uint32 skill)
{
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
@@ -687,6 +689,24 @@ SpellLearnSkillNode const* SpellMgr::GetSpellLearnSkill(uint32 spell_id) const
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
{
SpellTargetPositionMap::const_iterator itr = mSpellTargetPositions.find(std::make_pair(spell_id, effIndex));

View File

@@ -687,6 +687,7 @@ public:
// Spell learning
[[nodiscard]] SpellLearnSkillNode const* GetSpellLearnSkill(uint32 spell_id) const;
[[nodiscard]] std::vector<uint32> GetSkillRankSpells(uint32 skillId) const;
// Spell target coordinates
[[nodiscard]] SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const;

View File

@@ -309,6 +309,8 @@ public:
static bool HandleLearnAllCraftsCommand(ChatHandler* handler)
{
Player* target = handler->GetSession()->GetPlayer();
for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
{
SkillLineEntry const* skillInfo = sSkillLineStore.LookupEntry(i);
@@ -318,7 +320,10 @@ public:
if ((skillInfo->categoryId == SKILL_CATEGORY_PROFESSION || skillInfo->categoryId == SKILL_CATEGORY_SECONDARY) &&
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)
{
// 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();
for (SkillLineAbilityEntry const* skillLine : GetSkillLineAbilitiesBySkillLine(skillId))