aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/DataStores/DBCStores.cpp158
-rw-r--r--src/server/game/DataStores/DBCStores.h11
-rw-r--r--src/server/game/DataStores/DBCStructure.h13
-rw-r--r--src/server/game/DataStores/DBCfmt.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp219
-rw-r--r--src/server/game/Entities/Player/Player.h15
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp90
-rw-r--r--src/server/game/Globals/ObjectMgr.h2
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp2
-rw-r--r--src/server/game/Spells/SpellInfo.cpp6
-rw-r--r--src/server/game/Spells/SpellMgr.cpp39
-rw-r--r--src/server/game/Spells/SpellMgr.h2
-rw-r--r--src/server/game/World/World.cpp2
-rw-r--r--src/server/scripts/Commands/cs_learn.cpp2
14 files changed, 355 insertions, 207 deletions
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index a4ba710f5db..2f5f3f9159d 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -21,6 +21,7 @@
#include "Containers.h"
#include "Log.h"
#include "SharedDefines.h"
+#include "SpellInfo.h"
#include "SpellMgr.h"
#include "TransportMgr.h"
#include "DBCfmt.h"
@@ -192,8 +193,13 @@ DBCStorage <SpecializationSpellsEntry> sSpecializationSpellsStore(Specialization
DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore(SpellItemEnchantmentfmt);
DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt);
DBCStorage <SpellEntry> sSpellStore(SpellEntryfmt);
+DBCStorage <SpellEffectScalingEntry> sSpellEffectScalingStore(SpellEffectScalingfmt);
+
SpellCategoryStore sSpellsByCategoryStore;
PetFamilySpellsStore sPetFamilySpellsStore;
+SpellsPerClassStore sSpellsPerClassStore;
+ClassBySkillIdStore sClassBySkillIdStore;
+SpellEffectScallingByEffectId sSpellEffectScallingByEffectId;
DBCStorage <SpellScalingEntry> sSpellScalingStore(SpellScalingEntryfmt);
@@ -217,6 +223,8 @@ DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore(StableSlotPricesfmt);
DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt);
DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt);
TalentBySpellIDMap sTalentBySpellIDMap;
+SpecializationSpellsMap sSpecializationSpellsMap;
+SpecializationOverrideSpellsMap sSpecializationOverrideSpellMap;
DBCStorage <TotemCategoryEntry> sTotemCategoryStore(TotemCategoryEntryfmt);
DBCStorage <TransportAnimationEntry> sTransportAnimationStore(TransportAnimationfmt);
@@ -544,6 +552,7 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCastTimesStore, dbcPath, "SpellCastTimes.dbc");//15595
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellDurationStore, dbcPath, "SpellDuration.dbc");//19116
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellFocusObjectStore, dbcPath, "SpellFocusObject.dbc");//19116
+ LoadDBC(availableDbcLocales, bad_dbc_files, sSpellEffectScalingStore, dbcPath, "SpellEffectScaling.dbc");//19116
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellItemEnchantmentStore, dbcPath, "SpellItemEnchantment.dbc");//19116
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellItemEnchantmentConditionStore, dbcPath, "SpellItemEnchantmentCondition.dbc");//19116
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellRadiusStore, dbcPath, "SpellRadius.dbc");//19116
@@ -553,41 +562,67 @@ void LoadDBCStores(const std::string& dataPath)
//LoadDBC(availableDbcLocales, bad_dbc_files, sStableSlotPricesStore, dbcPath, "StableSlotPrices.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sSummonPropertiesStore, dbcPath, "SummonProperties.dbc");//15595
- // Must be done when sSkillLineAbilityStore, sSpellStore, sSpellLevelsStore and sCreatureFamilyStore are all loaded
- /* TODO: Requires spells attributes from SpellMisc.db2 is loaded after dbc
+ for (uint32 j = 0; j < sSpellEffectScalingStore.GetNumRows(); j++)
+ {
+ SpellEffectScalingEntry const* spellEffectScaling = sSpellEffectScalingStore.LookupEntry(j);
+ if (!spellEffectScaling)
+ continue;
+
+ sSpellEffectScallingByEffectId.insert(std::make_pair(spellEffectScaling->SpellEffectID, j));
+ }
+ std::map<std::string, uint32> classIdByName;
+ for (uint32 j = 0; j < sChrClassesStore.GetNumRows(); j++)
+ {
+ ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(j);
+ if (!classEntry)
+ continue;
+
+ classIdByName.insert(std::make_pair(std::string(classEntry->Name_lang), j));
+ }
+
+ for (uint32 j = 0; j < sSkillLineStore.GetNumRows(); j++)
+ {
+ SkillLineEntry const* skillEntry = sSkillLineStore.LookupEntry(j);
+ if (!skillEntry)
+ continue;
+
+ if (skillEntry->CategoryID!= SKILL_CATEGORY_CLASS)
+ continue;
+
+ std::map<std::string, uint32> ::const_iterator iter = classIdByName.find(std::string(skillEntry->DisplayName_lang));
+ if (iter == classIdByName.end())
+ continue;
+
+ sClassBySkillIdStore.insert(std::make_pair(j, iter->second));
+ }
+
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
{
- SkillLineAbilityEntry const* skillLine = sSkillLineAbilityStore.LookupEntry(j);
- if (!skillLine)
+ SkillLineAbilityEntry const* skillAbility = sSkillLineAbilityStore.LookupEntry(j);
+ if (!skillAbility)
continue;
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->SpellID);
+ if (skillAbility->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
+ continue;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillAbility->SpellID);
if (!spellInfo)
continue;
- SpellLevelsEntry const* levels = sSpellLevelsStore.LookupEntry(spellInfo->SpellLevelsID);
- if (spellInfo->SpellLevelsID && (!levels || levels->spellLevel))
+ SpellLevelsEntry const* spellLevels = sSpellLevelsStore.LookupEntry(spellInfo->LevelsID);
+ if (!spellLevels || !spellLevels->SpellLevel)
continue;
- if (spellInfo && spellInfo->Attributes & SPELL_ATTR0_PASSIVE)
- {
- for (uint32 i = 1; i < sCreatureFamilyStore.GetNumRows(); ++i)
- {
- CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(i);
- if (!cFamily)
- continue;
+ uint32 classId = GetClassBySkillId(skillAbility->SkillLine);
- if (skillLine->SkillLine != cFamily->SkillLine[0] && skillLine->SkillLine != cFamily->SkillLine[1])
- continue;
+ if (!classId)
+ continue;
- if (skillLine->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
- continue;
+ if (sSpellsPerClassStore.find(classId) == sSpellsPerClassStore.end())
+ sSpellsPerClassStore.insert(make_pair(classId, std::list<SkillLineAbilityEntry const*>()));
- sPetFamilySpellsStore[i].insert(spellInfo->ID);
- }
- }
+ sSpellsPerClassStore[classId].push_back(skillAbility);
}
- */
LoadDBC(availableDbcLocales, bad_dbc_files, sTalentStore, dbcPath, "Talent.dbc");//15595
@@ -1183,3 +1218,82 @@ std::set<uint32> const& GetPhasesForGroup(uint32 group)
{
return sPhasesByGroup[group];
}
+
+uint32 GetClassBySkillId(uint32 skillId)
+{
+ ClassBySkillIdStore::const_iterator iter = sClassBySkillIdStore.find(skillId);
+ if (iter != sClassBySkillIdStore.end())
+ return iter->second;
+ return 0;
+}
+
+uint32 GetSkillIdByClass(uint32 classId)
+{
+ for (ClassBySkillIdStore::const_iterator iter = sClassBySkillIdStore.begin(); iter != sClassBySkillIdStore.end(); iter++)
+ if (iter->second == classId)
+ return iter->first;
+ return 0;
+}
+
+std::list<uint32> GetSpellsForLevels(uint32 classId, uint32 raceMask, uint32 specializationId, uint32 minLevel, uint32 maxLevel)
+{
+ std::list<uint32> spellList;
+
+ if (classId != 0)
+ {
+ SpellsPerClassStore::const_iterator classIter = sSpellsPerClassStore.find(classId);
+ if (classIter != sSpellsPerClassStore.end())
+ {
+ const std::list<SkillLineAbilityEntry const*>& learnSpellList = classIter->second;
+ for (std::list<SkillLineAbilityEntry const*>::const_iterator iter = learnSpellList.begin(); iter != learnSpellList.end(); iter++)
+ {
+ SkillLineAbilityEntry const* skillLine = *iter;
+ if (!skillLine)
+ continue;
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(skillLine->SpellID);
+ if (!spellInfo)
+ continue;
+
+ if (skillLine->RaceMask && !(skillLine->RaceMask & raceMask))
+ continue;
+
+ if (spellInfo->SpellLevel <= minLevel || spellInfo->SpellLevel > maxLevel)
+ continue;
+
+ spellList.push_back(spellInfo->Id);
+ }
+ }
+ }
+
+ if (!specializationId)
+ return spellList;
+
+ SpecializationSpellsMap::const_iterator specIter = sSpecializationSpellsMap.find(specializationId);
+ if (specIter != sSpecializationSpellsMap.end())
+ {
+ const std::vector<uint32>& learnSpellList = specIter->second;
+ for (int i = 0; i < learnSpellList.size(); i++)
+ {
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(learnSpellList[i]);
+ if (!spellInfo)
+ {
+ TC_LOG_ERROR("spells", "GetSpellsForLevels: spell %u not found in spellstore", learnSpellList[i]);
+ continue;
+ }
+ if (spellInfo->SpellLevel <= minLevel || spellInfo->SpellLevel > maxLevel)
+ continue;
+
+ spellList.push_back(spellInfo->Id);
+ }
+ }
+ return spellList;
+}
+
+uint32 GetTalentSpellCost(uint32 spellId)
+{
+ TalentBySpellIDMap::const_iterator itr = sTalentBySpellIDMap.find(spellId);
+ if (itr == sTalentBySpellIDMap.end())
+ return 0;
+ return 1;
+} \ No newline at end of file
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index e9a9b3c692f..a631a471eb4 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -26,11 +26,14 @@
#include <list>
+typedef std::map<uint32, uint32> SpecializationOverrideSpellsList;
+typedef std::map<uint32, SpecializationOverrideSpellsList> SpecializationOverrideSpellsMap;
+
typedef std::list<uint32> SimpleFactionsList;
SimpleFactionsList const* GetFactionTeamList(uint32 faction);
char const* GetPetName(uint32 petfamily, uint32 dbclang);
-
+uint32 GetTalentSpellCost(uint32 spellId);
TalentEntry const* GetTalentBySpellID(uint32 spellID);
int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found
@@ -66,6 +69,9 @@ bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredT
void Zone2MapCoordinates(float &x, float &y, uint32 zone);
void Map2ZoneCoordinates(float &x, float &y, uint32 zone);
+uint32 GetClassBySkillId(uint32 skillId);
+uint32 GetSkillIdByClass(uint32 classId);
+std::list<uint32> GetSpellsForLevels(uint32 classId, uint32 raceMask, uint32 specializationId, uint32 minLevel, uint32 maxLevel);
typedef std::map<uint32/*pair32(map, diff)*/, MapDifficulty> MapDifficultyMap;
MapDifficulty const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty);
@@ -94,6 +100,9 @@ typedef std::unordered_map<uint32, SpecializationSpellsBySpecEntry> Specializati
typedef ChrSpecializationEntry const* ChrSpecializationByIndexArray[MAX_CLASSES][MAX_SPECIALIZATIONS];
typedef std::unordered_map<uint32, TalentEntry const*> TalentBySpellIDMap;
+typedef std::map<uint32, std::vector<uint32> > SpecializationSpellsMap;
+extern SpecializationSpellsMap sSpecializationSpellsMap;
+extern SpecializationOverrideSpellsMap sSpecializationOverrideSpellMap;
extern DBCStorage <AchievementEntry> sAchievementStore;
extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore;
extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions
diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h
index 77182b1bdf9..4a58428abb1 100644
--- a/src/server/game/DataStores/DBCStructure.h
+++ b/src/server/game/DataStores/DBCStructure.h
@@ -1803,6 +1803,16 @@ struct SpellEffectEntry
#define MAX_SPELL_EFFECTS 32
#define MAX_EFFECT_MASK 0xFFFFFFFF
+// SpellEffectScaling.dbc
+struct SpellEffectScalingEntry
+{
+ uint32 ID; // 0
+ float Coefficient; // 1
+ float Variance; // 2
+ float ResourceCoefficient; // 3
+ uint32 SpellEffectID; // 4
+};
+
// SpellAuraOptions.dbc
struct SpellAuraOptionsEntry
{
@@ -1865,6 +1875,9 @@ typedef std::set<uint32> SpellCategorySet;
typedef std::map<uint32, SpellCategorySet > SpellCategoryStore;
typedef std::set<uint32> PetFamilySpellsSet;
typedef std::map<uint32, PetFamilySpellsSet > PetFamilySpellsStore;
+typedef std::unordered_map<uint32, std::list<SkillLineAbilityEntry const*> > SpellsPerClassStore;
+typedef std::unordered_map<uint32, uint32> ClassBySkillIdStore;
+typedef std::unordered_map<uint32, uint32> SpellEffectScallingByEffectId;
struct SpellCastTimesEntry
{
diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h
index 9f53cb09ad8..bd0283aee7f 100644
--- a/src/server/game/DataStores/DBCfmt.h
+++ b/src/server/game/DataStores/DBCfmt.h
@@ -135,6 +135,7 @@ const std::string CustomSpellEffectEntryIndex = "Id";
char const SpellEntryfmt[] = "nsxxxiiiiiiiiiiiiiiiiiii";
const std::string CustomSpellEntryfmt = "ppppppppppppppapaaaaaaaaapaaaaaapapppaapppaaapa";
const std::string CustomSpellEntryIndex = "Id";
+char const SpellEffectScalingfmt[] = "nfffi";
char const SpellFocusObjectfmt[] = "nx";
char const SpellItemEnchantmentfmt[] = "niiiiiiiiiixiiiiiiiiiiifff";
char const SpellItemEnchantmentConditionfmt[] = "nbbbbbiiiiibbbbbbbbbbiiiiibbbbb";
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index c9a782cc78b..be86bf2b13d 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -1121,7 +1121,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac
}
// original spells
- LearnDefaultSkills();
+ LearnDefaultSpells();
LearnCustomSpells();
// original action bar
@@ -3528,9 +3528,11 @@ 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 && spellInfo->HasEffect(DIFFICULTY_NONE, SPELL_EFFECT_LEARN_SPELL))
+ if (!loading && talentCost > 0 && 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);
@@ -3541,12 +3543,15 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
if (IsNeedCastPassiveSpellAtLearn(spellInfo))
CastSpell(this, spellId, true);
}
- else if (spellInfo->HasEffect(DIFFICULTY_NONE, SPELL_EFFECT_SKILL_STEP))
+ else if (spellInfo->HasEffect(SPELL_EFFECT_SKILL_STEP))
{
CastSpell(this, spellId, true);
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())
{
@@ -3554,38 +3559,58 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
SetFreePrimaryProfessions(freeProfs-1);
}
+ // add dependent skills
+ uint16 maxskill = GetMaxSkillValueForLevel();
+
+ SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId);
+
SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
- // add dependent skills if this spell is not learned from adding skill already
- if (!fromSkill)
+ if (spellLearnSkill)
{
- if (SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId))
- {
- uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
- uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
+ 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 ? GetMaxSkillValueForLevel() : spellLearnSkill->maxvalue;
+ uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : 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
+ 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)
{
- // not ranked skills
- for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
- {
- SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
- if (!pSkill)
- continue;
+ SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
+ if (!pSkill)
+ continue;
+
+ if (HasSkill(pSkill->ID))
+ 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);
+ 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)))
+ {
+ switch (GetSkillRangeType(pSkill, _spell_idx->second->RaceMask!= 0))
+ {
+ 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;
+ }
}
}
}
@@ -5868,14 +5893,14 @@ void Player::UpdateSkillsForLevel()
continue;
uint32 pskill = itr->first;
- SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pskill, getRace(), getClass());
- if (!rcEntry)
+ SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(pskill);
+ if (!pSkill)
continue;
- if (GetSkillRangeType(rcEntry) != SKILL_RANGE_LEVEL)
+ if (GetSkillRangeType(pSkill, false) != SKILL_RANGE_LEVEL)
continue;
- if (IsWeaponSkill(rcEntry->SkillID))
+ if (IsWeaponSkill(pSkill->ID))
continue;
uint16 field = itr->second.pos / 2;
@@ -17424,7 +17449,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
// after spell and quest load
InitTalentForLevel();
- LearnDefaultSkills();
+ LearnDefaultSpells();
LearnCustomSpells();
// must be before inventory (some items required reputation check)
@@ -23220,7 +23245,7 @@ void Player::ResetSpells(bool myClassOnly)
for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter)
RemoveSpell(iter->first, false, false); // only iter->first can be accessed, object by iter->second can be deleted already
- LearnDefaultSkills();
+ LearnDefaultSpells();
LearnCustomSpells();
LearnQuestRewardedSpells();
}
@@ -23243,69 +23268,18 @@ void Player::LearnCustomSpells()
}
}
-void Player::LearnDefaultSkills()
+void Player::LearnDefaultSpells()
{
- // learn default race/class skills
+ // learn default race/class spells
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
- for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
+ for (PlayerCreateInfoSpells::const_iterator itr = info->spells.begin(); itr != info->spells.end(); ++itr)
{
- uint32 skillId = itr->SkillId;
- if (HasSkill(skillId))
- continue;
-
- LearnDefaultSkill(skillId, itr->Rank);
- }
-}
-
-void Player::LearnDefaultSkill(uint32 skillId, uint16 rank)
-{
- SkillRaceClassInfoEntry const* rcInfo = GetSkillRaceClassInfo(skillId, getRace(), getClass());
- if (!rcInfo)
- return;
-
- TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial skill, id = %u", uint32(getClass()), uint32(getRace()), skillId);
- switch (GetSkillRangeType(rcInfo))
- {
- case SKILL_RANGE_LANGUAGE:
- SetSkill(skillId, 0, 300, 300);
- break;
- case SKILL_RANGE_LEVEL:
- {
- uint16 skillValue = 1;
- uint16 maxValue = GetMaxSkillValueForLevel();
- if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE)
- skillValue = maxValue;
- else if (getClass() == CLASS_DEATH_KNIGHT)
- skillValue = std::min(std::max<uint16>({ 1, uint16((getLevel() - 1) * 5) }), maxValue);
- else if (skillId == SKILL_FIST_WEAPONS)
- skillValue = std::max<uint16>(1, GetSkillValue(SKILL_UNARMED));
- else if (skillId == SKILL_LOCKPICKING)
- skillValue = std::max<uint16>(1, GetSkillValue(SKILL_LOCKPICKING));
-
- SetSkill(skillId, 0, skillValue, maxValue);
- break;
- }
- case SKILL_RANGE_MONO:
- SetSkill(skillId, 0, 1, 1);
- break;
- case SKILL_RANGE_RANK:
- {
- if (!rank)
- break;
-
- SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcInfo->SkillTierID);
- uint16 maxValue = tier->Value[std::max<int32>(rank - 1, 0)];
- uint16 skillValue = 1;
- if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE)
- skillValue = maxValue;
- else if (getClass() == CLASS_DEATH_KNIGHT)
- skillValue = std::min(std::max<uint16>({ uint16(1), uint16((getLevel() - 1) * 5) }), maxValue);
-
- SetSkill(skillId, rank, skillValue, maxValue);
- break;
- }
- default:
- break;
+ uint32 tspell = *itr;
+ TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell);
+ if (!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add
+ AddSpell(tspell, true, true, true, false);
+ else // but send in normal spell in game learn case
+ LearnSpell(tspell, true);
}
}
@@ -23423,36 +23397,31 @@ void Player::LearnQuestRewardedSpells()
void Player::LearnSkillRewardedSpells(uint32 skillId, uint32 skillValue)
{
- uint32 raceMask = getRaceMask();
+ uint32 raceMask = getRaceMask();
uint32 classMask = getClassMask();
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
{
- SkillLineAbilityEntry const* ability = sSkillLineAbilityStore.LookupEntry(j);
- if (!ability || ability->SkillLine != skillId)
- continue;
-
- if (!sSpellMgr->GetSpellInfo(ability->SpellID))
+ SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j);
+ if (!pAbility || pAbility->SkillLine != skillId || pAbility->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
continue;
-
- if (ability->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE && ability->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
- continue;
-
// Check race if set
- if (ability->RaceMask && !(ability->RaceMask & raceMask))
+ if (pAbility->RaceMask && !(pAbility->RaceMask & raceMask))
continue;
-
// Check class if set
- if (ability->ClassMask && !(ability->ClassMask & classMask))
+ if (pAbility->ClassMask && !(pAbility->ClassMask & classMask))
continue;
- // need unlearn spell
- if (skillValue < ability->MinSkillLineRank && ability->AquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
- RemoveSpell(ability->SpellID);
- // need learn
- else if (!IsInWorld())
- AddSpell(ability->SpellID, true, true, true, false, false, true);
- else
- LearnSpell(ability->SpellID, true, true);
+ if (sSpellMgr->GetSpellInfo(pAbility->SpellID))
+ {
+ // need unlearn spell
+ if (skillValue < pAbility->MinSkillLineRank)
+ RemoveSpell(pAbility->SpellID);
+ // need learn
+ else if (!IsInWorld())
+ AddSpell(pAbility->SpellID, true, true, true, false);
+ else
+ LearnSpell(pAbility->SpellID, true);
+ }
}
}
@@ -25167,8 +25136,8 @@ void Player::_LoadSkills(PreparedQueryResult result)
uint16 value = fields[1].GetUInt16();
uint16 max = fields[2].GetUInt16();
- SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skill, getRace(), getClass());
- if (!rcEntry)
+ SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skill);
+ if (!skillLine)
{
TC_LOG_ERROR("entities.player", "Character: %s (%s Race: %u Class: %u) has skill %u not allowed for his race/class combination",
GetName().c_str(), GetGUID().ToString().c_str(), uint32(getRace()), uint32(getClass()), skill);
@@ -25178,7 +25147,7 @@ void Player::_LoadSkills(PreparedQueryResult result)
}
// set fixed skill ranges
- switch (GetSkillRangeType(rcEntry))
+ switch (GetSkillRangeType(skillLine, false))
{
case SKILL_RANGE_LANGUAGE: // 300..300
value = max = 300;
@@ -25212,19 +25181,15 @@ void Player::_LoadSkills(PreparedQueryResult result)
SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_ID_OFFSET + field, offset, skill);
uint16 step = 0;
- SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(rcEntry->SkillID);
- if (skillLine)
- {
- if (skillLine->CategoryID == SKILL_CATEGORY_SECONDARY)
- step = max / 75;
+ if (skillLine->CategoryID == SKILL_CATEGORY_SECONDARY)
+ step = max / 75;
- if (skillLine->CategoryID == SKILL_CATEGORY_PROFESSION)
- {
- step = max / 75;
+ if (skillLine->CategoryID == SKILL_CATEGORY_PROFESSION)
+ {
+ step = max / 75;
- if (professionCount < 2)
- SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + professionCount++, skill);
- }
+ if (professionCount < 2)
+ SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + professionCount++, skill);
}
SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_STEP_OFFSET + field, offset, step);
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 1dcd75a156e..2d919159819 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -329,7 +329,7 @@ enum ReputationSource
};
#define ACTION_BUTTON_ACTION(X) (uint64(X) & 0x00000000FFFFFFFF)
-#define ACTION_BUTTON_TYPE(X) ((uint64(X) & 0xFFFFFFFF00000000) >> 32)
+#define ACTION_BUTTON_TYPE(X) ((uint64(X) & 0xFFFFFFFF00000000) >> 56)
#define MAX_ACTION_BUTTON_ACTION_VALUE (0xFFFFFFFF)
struct ActionButton
@@ -344,7 +344,7 @@ struct ActionButton
uint32 GetAction() const { return ACTION_BUTTON_ACTION(packedData); }
void SetActionAndType(uint32 action, ActionButtonType type)
{
- uint64 newData = uint64(action) | (uint64(type) << 32);
+ uint64 newData = uint64(action) | (uint64(type) << 56);
if (newData != packedData || uState == ACTIONBUTTON_DELETED)
{
packedData = newData;
@@ -412,9 +412,9 @@ struct PlayerInfo
uint16 displayId_f;
PlayerCreateInfoItems item;
PlayerCreateInfoSpells customSpells;
+ PlayerCreateInfoSpells spells;
PlayerCreateInfoSpells castSpells;
PlayerCreateInfoActions action;
- PlayerCreateInfoSkills skills;
PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
};
@@ -1270,6 +1270,9 @@ struct PlayerTalentInfo
uint8 ActiveGroup;
uint8 GroupsCount;
+
+ uint32 UsedTalentCount;
+
private:
PlayerTalentInfo(PlayerTalentInfo const&);
};
@@ -1821,8 +1824,7 @@ class Player : public Unit, public GridObject<Player>
void RemoveSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true);
void ResetSpells(bool myClassOnly = false);
void LearnCustomSpells();
- void LearnDefaultSkills();
- void LearnDefaultSkill(uint32 skillId, uint16 rank);
+ void LearnDefaultSpells();
void LearnQuestRewardedSpells();
void LearnQuestRewardedSpells(Quest const* quest);
void LearnSpellHighestRank(uint32 spellid);
@@ -1833,6 +1835,9 @@ class Player : public Unit, public GridObject<Player>
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; }
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index a01251af433..040c9600852 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -3270,17 +3270,16 @@ void ObjectMgr::LoadPlayerInfo()
}
}
-
- // Load playercreate skills
- TC_LOG_INFO("server.loading", "Loading Player Create Skill Data...");
+ // Load playercreate spells
+ TC_LOG_INFO("server.loading", "Loading Player Create Spell Data...");
{
uint32 oldMSTime = getMSTime();
- QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, rank FROM playercreateinfo_skills");
+ QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell");
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty.");
+ TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell` is empty.");
}
else
{
@@ -3291,31 +3290,17 @@ void ObjectMgr::LoadPlayerInfo()
Field* fields = result->Fetch();
uint32 raceMask = fields[0].GetUInt32();
uint32 classMask = fields[1].GetUInt32();
- PlayerCreateInfoSkill skill;
- skill.SkillId = fields[2].GetUInt16();
- skill.Rank = fields[3].GetUInt16();
-
- if (skill.Rank >= MAX_SKILL_STEP)
- {
- TC_LOG_ERROR("sql.sql", "Skill rank value %hu set for skill %hu raceMask %u classMask %u is too high, max allowed value is %d", skill.Rank, skill.SkillId, raceMask, classMask, MAX_SKILL_STEP);
- continue;
- }
+ uint32 spellId = fields[2].GetUInt32();
if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
{
- TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_skills` table, ignoring.", raceMask);
+ TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_spell` table, ignoring.", raceMask);
continue;
}
if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
{
- TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_skills` table, ignoring.", classMask);
- continue;
- }
-
- if (!sSkillLineStore.LookupEntry(skill.SkillId))
- {
- TC_LOG_ERROR("sql.sql", "Wrong skill id %u in `playercreateinfo_skills` table, ignoring.", skill.SkillId);
+ TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_spell` table, ignoring.", classMask);
continue;
}
@@ -3327,26 +3312,28 @@ void ObjectMgr::LoadPlayerInfo()
{
if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
{
- if (!GetSkillRaceClassInfo(skill.SkillId, raceIndex, classIndex))
- continue;
-
if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
{
- info->skills.push_back(skill);
+ info->spells.push_back(spellId);
++count;
}
+ // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them.
+ // Either split the masks per class, or per race, which kind of kills the point yet.
+ // else if (raceMask != 0 && classMask != 0)
+ // TC_LOG_ERROR("sql.sql", "Racemask/classmask (%u/%u) combination was found containing an invalid race/class combination (%u/%u) in `%s` (Spell %u), ignoring.", raceMask, classMask, raceIndex, classIndex, tableName.c_str(), spellId);
}
}
}
}
- } while (result->NextRow());
+ }
+ while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u player create skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
}
- // Load playercreate spells
- TC_LOG_INFO("server.loading", "Loading Player Create Spell Data...");
+ // Load playercreate custom spells
+ TC_LOG_INFO("server.loading", "Loading Player Create Custom Spell Data...");
{
uint32 oldMSTime = getMSTime();
@@ -3354,7 +3341,7 @@ void ObjectMgr::LoadPlayerInfo()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty.");
+ TC_LOG_ERROR("server.loading", ">> Loaded 0 player create custom spells. DB table `playercreateinfo_spell_custom` is empty.");
}
else
{
@@ -7850,27 +7837,34 @@ int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 rac
return 0;
}
-SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry)
+SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial)
{
- SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillID);
- if (!skill)
- return SKILL_RANGE_NONE;
-
- if (sSkillTiersStore.LookupEntry(rcEntry->SkillTierID))
- return SKILL_RANGE_RANK;
-
- if (rcEntry->SkillID == SKILL_RUNEFORGING)
- return SKILL_RANGE_MONO;
-
- switch (skill->CategoryID)
+ switch (pSkill->CategoryID)
{
- case SKILL_CATEGORY_ARMOR:
- return SKILL_RANGE_MONO;
case SKILL_CATEGORY_LANGUAGES:
return SKILL_RANGE_LANGUAGE;
+ case SKILL_CATEGORY_WEAPON:
+ return SKILL_RANGE_LEVEL;
+ case SKILL_CATEGORY_ARMOR:
+ case SKILL_CATEGORY_CLASS:
+ if (pSkill->ID != SKILL_LOCKPICKING)
+ return SKILL_RANGE_MONO;
+ else
+ return SKILL_RANGE_LEVEL;
+ case SKILL_CATEGORY_SECONDARY:
+ case SKILL_CATEGORY_PROFESSION:
+ // not set skills for professions and racial abilities
+ if (IsProfessionSkill(pSkill->ID))
+ return SKILL_RANGE_RANK;
+ else if (racial)
+ return SKILL_RANGE_NONE;
+ else
+ return SKILL_RANGE_MONO;
+ default:
+ case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc
+ case SKILL_CATEGORY_GENERIC: //only GENERIC(DND)
+ return SKILL_RANGE_NONE;
}
-
- return SKILL_RANGE_LEVEL;
}
void ObjectMgr::LoadGameTele()
@@ -9367,4 +9361,4 @@ std::string ObjectMgr::GetRealmName(uint32 realm) const
{
RealmNameContainer::const_iterator iter = _realmNameStore.find(realm);
return iter != _realmNameStore.end() ? iter->second : "";
-}
+} \ No newline at end of file
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index e7191735ca1..bf0d889057b 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -668,7 +668,7 @@ enum SkillRangeType
SKILL_RANGE_NONE // 0..0 always
};
-SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry);
+SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial);
#define MAX_PLAYER_NAME 12 // max allowed by client name length
#define MAX_INTERNAL_PLAYER_NAME 15 // max server internal player name length (> MAX_PLAYER_NAME for support declined names)
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index 1acddcc5523..015ce3224a3 100644
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -274,8 +274,6 @@ Aura* Aura::TryRefreshStackOrCreate(SpellInfo const* spellproto, uint32 tryEffMa
if (!effMask)
return NULL;
- TC_LOG_ERROR("spells", "TryRefreshStackOrCreate spell %u tryEffMask %u effMask %u", spellproto->Id, tryEffMask, effMask);
-
if (Aura* foundAura = owner->ToUnit()->_TryStackingOrRefreshingExistingAura(spellproto, effMask, caster, baseAmount, castItem, casterGUID))
{
// we've here aura, which script triggered removal after modding stack amount
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 85ad4a67d3f..8eefeae232a 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -1253,6 +1253,9 @@ bool SpellInfo::NeedsToBeTriggeredByCaster(SpellInfo const* triggeringSpell, uin
SpellEffectInfoVector effects = GetEffectsForDifficulty(difficulty);
for (SpellEffectInfo const* effect : effects)
{
+ if (!effect)
+ continue;
+
if (effect->TargetA.GetTarget() != TARGET_UNIT_CASTER && effect->TargetA.GetTarget() != TARGET_DEST_CASTER
&& effect->TargetB.GetTarget() != TARGET_UNIT_CASTER && effect->TargetB.GetTarget() != TARGET_DEST_CASTER)
{
@@ -1297,6 +1300,9 @@ bool SpellInfo::IsStackableWithRanks() const
SpellEffectInfoVector effects = GetEffectsForDifficulty(DIFFICULTY_NONE);
for (SpellEffectInfo const* effect : effects)
{
+ if (!effect)
+ continue;
+
switch (SpellFamilyName)
{
case SPELLFAMILY_PALADIN:
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 3211ff389e8..22c68271aba 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3739,3 +3739,42 @@ void SpellMgr::LoadSpellInfoCorrections()
TC_LOG_INFO("server.loading", ">> Loaded SpellInfo corrections in %u ms", GetMSTimeDiffToNow(oldMSTime));
}
+
+void SpellMgr::LoadPetFamilySpellsStore()
+{
+ for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
+ {
+ SkillLineAbilityEntry const* skillLine = sSkillLineAbilityStore.LookupEntry(j);
+ if (!skillLine)
+ continue;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->SpellID);
+ if (!spellInfo)
+ continue;
+
+ SpellLevelsEntry const* levels = sSpellLevelsStore.LookupEntry(spellInfo->LevelsID);
+ if (spellInfo->LevelsID && (!levels || levels->SpellLevel))
+ continue;
+
+ if (SpellMiscEntry const* spellMisc = sSpellMiscStore.LookupEntry(spellInfo->MiscID))
+ {
+ if (spellMisc->Attributes & SPELL_ATTR0_PASSIVE)
+ {
+ for (uint32 i = 1; i < sCreatureFamilyStore.GetNumRows(); ++i)
+ {
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(i);
+ if (!cFamily)
+ continue;
+
+ if (skillLine->SkillLine != cFamily->SkillLine[0] && skillLine->SkillLine != cFamily->SkillLine[1])
+ continue;
+
+ if (skillLine->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
+ continue;
+
+ sPetFamilySpellsStore[i].insert(spellInfo->ID);
+ }
+ }
+ }
+ }
+}
diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h
index 5be0cfda84c..0e591a27e81 100644
--- a/src/server/game/Spells/SpellMgr.h
+++ b/src/server/game/Spells/SpellMgr.h
@@ -699,6 +699,8 @@ class SpellMgr
}
uint32 GetSpellInfoStoreSize() const { return mSpellInfoMap.size(); }
+ void LoadPetFamilySpellsStore();
+
private:
SpellInfo* _GetSpellInfo(uint32 spellId) { return spellId < GetSpellInfoStoreSize() ? mSpellInfoMap[spellId] : NULL; }
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index a9d12fd11f8..c24bd53bd57 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1413,6 +1413,8 @@ void World::SetInitialWorldSettings()
LoadDBCStores(m_dataPath);
LoadDB2Stores(m_dataPath);
+ sSpellMgr->LoadPetFamilySpellsStore();
+
TC_LOG_INFO("server.loading", "Loading SpellInfo store...");
sSpellMgr->LoadSpellInfoStore();
diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp
index 2cf97403595..9d2d4426efe 100644
--- a/src/server/scripts/Commands/cs_learn.cpp
+++ b/src/server/scripts/Commands/cs_learn.cpp
@@ -306,7 +306,7 @@ public:
if (!handler->extractPlayerTarget((char*)args, &target))
return false;
- target->LearnDefaultSkills();
+ target->LearnDefaultSpells();
target->LearnCustomSpells();
target->LearnQuestRewardedSpells();