Skip to content

Commit

Permalink
Custom: Better version of .learn all_myspells command
Browse files Browse the repository at this point in the history
  • Loading branch information
MantisLord committed May 1, 2024
1 parent a9b63a4 commit 7d367d3
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 45 deletions.
45 changes: 7 additions & 38 deletions src/game/Chat/Level3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1498,47 +1498,16 @@ bool ChatHandler::HandleLearnAllMyClassCommand(char* /*args*/)
return true;
}

bool ChatHandler::HandleLearnAllMySpellsCommand(char* /*args*/)
bool ChatHandler::HandleLearnAllMySpellsCommand(char* args)
{
Player* player = m_session->GetPlayer();
ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(player->getClass());
if (!clsEntry)
return true;
uint32 family = clsEntry->spellfamily;

for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i)
{
SkillLineAbilityEntry const* entry = sSkillLineAbilityStore.LookupEntry(i);
if (!entry)
continue;

SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(entry->spellId);
if (!spellInfo)
continue;

// skip server-side/triggered spells
if (spellInfo->spellLevel == 0)
continue;

// skip wrong class/race skills
if (!player->IsSpellFitByClassAndRace(spellInfo->Id))
continue;

// skip other spell families
if (spellInfo->SpellFamilyName != family)
continue;

// skip spells with first rank learned as talent (and all talents then also)
uint32 first_rank = sSpellMgr.GetFirstSpellInChain(spellInfo->Id);
if (GetTalentSpellCost(first_rank) > 0)
continue;

// skip broken spells
if (!SpellMgr::IsSpellValid(spellInfo, player, false))
continue;

player->learnSpell(spellInfo->Id, false);
}
uint32 param;
ExtractOptUInt32(&args, param, 0);
if (param != 0)
player->learnClassLevelSpells(true);
else
player->learnClassLevelSpells(false);

SendSysMessage(LANG_COMMAND_LEARN_CLASS_SPELLS);
return true;
Expand Down
1 change: 1 addition & 0 deletions src/game/Entities/Creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ struct TrainerSpell

// helpers
bool IsCastable() const { return learnedSpell != spell; }
bool operator<(const TrainerSpell& rhs) const { return (reqLevel < rhs.reqLevel) || ((reqLevel == rhs.reqLevel) && (spell < rhs.spell)); }
};

typedef std::unordered_map < uint32 /*spellid*/, TrainerSpell > TrainerSpellMap;
Expand Down
167 changes: 161 additions & 6 deletions src/game/Entities/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16083,7 +16083,7 @@ void Player::_LoadQuestStatus(std::unique_ptr<QueryResult> queryResult)
if (questStatusData.m_rewarded)
{
// learn rewarded spell if unknown
learnQuestRewardedSpells(pQuest);
learnQuestRewardedOrSrcSpells(pQuest);

// set rewarded title if any
if (pQuest->GetCharTitleId())
Expand Down Expand Up @@ -19698,11 +19698,166 @@ void Player::learnDefaultSpells()
}
}

void Player::learnQuestRewardedSpells(Quest const* quest)
void Player::learnClassLevelSpells(bool includeHighLevelQuestRewards)
{
uint32 spell_id = quest->GetRewSpellCast();
ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(getClass());
if (!clsEntry)
return;
uint32 family = clsEntry->spellfamily;

// special cases which aren't sourced from trainers and normally require quests to obtain - added here for convenience
ObjectMgr::QuestMap const& qTemplates = sObjectMgr.GetQuestTemplates();
for (const auto& qTemplate : qTemplates)
{
Quest const* quest = qTemplate.second;
if (!quest)
continue;

// only class quests player could do
if (quest->GetRequiredClasses() == 0 || !SatisfyQuestClass(quest, false) || !SatisfyQuestRace(quest, false) || !SatisfyQuestLevel(quest, false))
continue;

// custom filter for scripting purposes
if (!includeHighLevelQuestRewards && quest->GetMinLevel() >= 60)
continue;

learnQuestRewardedOrSrcSpells(quest);

if (quest->GetSrcSpell())
learnQuestRewardedOrSrcSpells(quest, true);
}
// spells normally learned from items instead of trainers or quests, there's probably a better way to do this
if (GetLevel() == 70)
{
switch (getClass())
{
case CLASS_PRIEST:
{
if (!HasSpell(39374)) // Prayer of Shadow Protection (rank 2)
learnSpell(39374, false);
break;
}
case CLASS_MAGE:
{
if (!HasSpell(27127)) // Arcane Brilliance (rank 2)
learnSpell(27127, false);
if (!HasSpell(27090)) // Conjure Water (rank 9)
learnSpell(27090, false);
if (!HasSpell(33717)) // Conjure Food (rank 8)
learnSpell(33717, false);
if (!HasSpell(43987)) // Ritual of Refreshment
learnSpell(43987, false);
break;
}
case CLASS_DRUID:
{
if (!HasSpell(26991)) // Gift of the Wild (rank 3)
learnSpell(26991, false);
break;
}
}
}

std::set<TrainerSpell> const& trainerSpells = sObjectMgr.GetTrainerSpells();
for (const auto& tSpell : trainerSpells)
{
SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(tSpell.spell);
if (!spellInfo)
continue;

uint32 reqLevel = 0;
if (!IsSpellFitByClassAndRace(tSpell.spell, &reqLevel))
continue;

reqLevel = tSpell.isProvidedReqLevel ? tSpell.reqLevel : std::max(reqLevel, tSpell.reqLevel);

TrainerSpellState state = GetTrainerSpellState(&tSpell, reqLevel);
if (state == TRAINER_SPELL_RED)
continue;

if (tSpell.conditionId && !sObjectMgr.IsConditionSatisfied(tSpell.conditionId, this, GetMap(), this, CONDITION_FROM_TRAINER))
continue;

// special case for Judgement/Seal of Righteousness
if (spellInfo->Id == 10321 && getClass() == CLASS_PALADIN)
{
CastSpell(this, spellInfo->Id, TRIGGERED_OLD_TRIGGERED);
continue;
}

// skip other spell families (minus a few exceptions)
if (spellInfo->SpellFamilyName != family)
{
SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBoundsBySpellId(tSpell.spell);
if (bounds.first == bounds.second)
continue;

SkillLineAbilityEntry const* skillInfo = bounds.first->second;
if (!skillInfo)
continue;

switch (skillInfo->skillId)
{
case SKILL_SUBTLETY:
case SKILL_POISONS:
case SKILL_BEAST_MASTERY:
case SKILL_SURVIVAL:
case SKILL_DEFENSE:
case SKILL_DUAL_WIELD:
case SKILL_FERAL_COMBAT:
case SKILL_PROTECTION:
case SKILL_BEAST_TRAINING:
case SKILL_PLATE_MAIL:
case SKILL_DEMONOLOGY:
case SKILL_ENHANCEMENT:
case SKILL_MAIL:
case SKILL_HOLY2:
case SKILL_LOCKPICKING:
case SKILL_SWORDS:
case SKILL_AXES:
case SKILL_BOWS:
case SKILL_GUNS:
case SKILL_MACES:
case SKILL_2H_SWORDS:
case SKILL_STAVES:
case SKILL_2H_MACES:
case SKILL_2H_AXES:
case SKILL_DAGGERS:
case SKILL_THROWN:
case SKILL_CROSSBOWS:
case SKILL_SPEARS:
case SKILL_POLEARMS:
case SKILL_WANDS:
case SKILL_FIST_WEAPONS:
break;
default:
continue;
}
}

// skip spells with first rank learned as talent (and all talents then also)
uint32 first_rank = sSpellMgr.GetFirstSpellInChain(tSpell.spell);
if (GetTalentSpellCost(first_rank) > 0)
continue;

// skip broken spells
if (!SpellMgr::IsSpellValid(spellInfo, this, false))
continue;

learnSpell(tSpell.spell, false);
}
}

void Player::learnQuestRewardedOrSrcSpells(Quest const* quest, bool srcSpell)
{
uint32 spell_id = 0;

if (srcSpell)
spell_id = quest->GetSrcSpell();
else
spell_id = quest->GetRewSpellCast();

// skip quests without rewarded spell
// skip quests without rewarded or source spell
if (!spell_id)
return;

Expand Down Expand Up @@ -19730,7 +19885,7 @@ void Player::learnQuestRewardedSpells(Quest const* quest)
if (sSpellMgr.GetSpellRank(learned_0) > 1)
return;

CastSpell(this, spell_id, TRIGGERED_OLD_TRIGGERED);
CastSpell(this, spell_id, TRIGGERED_OLD_TRIGGERED | TRIGGERED_INSTANT_CAST);
}

void Player::learnQuestRewardedSpells()
Expand All @@ -19746,7 +19901,7 @@ void Player::learnQuestRewardedSpells()
if (!quest)
continue;

learnQuestRewardedSpells(quest);
learnQuestRewardedOrSrcSpells(quest);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/game/Entities/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -1555,8 +1555,9 @@ class Player : public Unit
void resetSpells();
void learnDefaultSpells();
void learnQuestRewardedSpells();
void learnQuestRewardedSpells(Quest const* quest);
void learnQuestRewardedOrSrcSpells(Quest const* quest, bool srcSpell = false);
void learnSpellHighRank(uint32 spellid);
void learnClassLevelSpells(bool includeHighLevelQuestRewards = false);

uint32 GetFreeTalentPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS1); }
void SetFreeTalentPoints(uint32 points) { SetUInt32Value(PLAYER_CHARACTER_POINTS1, points); }
Expand Down
3 changes: 3 additions & 0 deletions src/game/Globals/ObjectMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9304,6 +9304,9 @@ void ObjectMgr::LoadTrainers(char const* tableName, bool isTemplates)
if (SpellMgr::IsProfessionSpell(spell))
data.trainerType = 2;

if (mTrainerSpellSet.find(trainerSpell) == mTrainerSpellSet.end())
mTrainerSpellSet.insert(trainerSpell);

++count;
}
while (queryResult->NextRow());
Expand Down
3 changes: 3 additions & 0 deletions src/game/Globals/ObjectMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ class ObjectMgr
std::unordered_map<uint32, std::pair<bool, std::vector<uint32>>> const& GetCreatureSpawnEntry() const { return m_creatureSpawnEntryMap; }

std::vector<uint32> LoadGameobjectInfo();
std::set<TrainerSpell> const& GetTrainerSpells() const { return mTrainerSpellSet; }

void PackGroupIds();
Group* GetGroupById(uint32 id) const;
Expand Down Expand Up @@ -1301,6 +1302,8 @@ class ObjectMgr
std::unordered_map<uint32, std::pair<bool, std::vector<uint32>>> m_gameobjectSpawnEntryMap;
std::unordered_map<uint32, GameObjectTemplateAddon> m_gameobjectAddonTemplates;

std::set<TrainerSpell> mTrainerSpellSet;

PointOfInterestMap mPointsOfInterest;

PetCreateSpellMap mPetCreateSpell;
Expand Down

0 comments on commit 7d367d3

Please sign in to comment.