fix(Core/Player): Use SkillLineAbility.dbc to determine player initia… (#6015)

* fix(Core/Player): Use SkillLineAbility.dbc to determine player initial spells - skill assignment done in a new table `playercreateinfo_skills`

* Cherry-pick https://github.com/TrinityCore/TrinityCore/commit/2a3546ca36468b81d673bdb27315006144af3165

* Cherry-Pick https://github.com/TrinityCore/TrinityCore/commit/d28b66bca88de621fd00495e4edc7e4d9d01e7eb

* Cherry-Pick https://github.com/TrinityCore/TrinityCore/commit/193408f3351b8ce1fb17dc87e11fe2e18b46ec41

- Closes https://github.com/azerothcore/azerothcore-wotlk/issues/1659
- Closes https://github.com/azerothcore/azerothcore-wotlk/issues/6036
- Closes https://github.com/chromiecraft/chromiecraft/issues/693

Co-Authored-By: Shauren shauren.trinity@gmail.com
Co-Authored-By: Rothend 67004168+Rothend@users.noreply.github.com
Co-Authored-By: claudiodfc claudio.daniel.f.c@gmail.com
This commit is contained in:
Kitzunu
2021-06-21 21:23:18 +02:00
committed by GitHub
parent eeab4f5de6
commit 1be561e03b
17 changed files with 579 additions and 251 deletions
@@ -0,0 +1,106 @@
INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1621752432246342200');
DROP TABLE IF EXISTS `skillraceclassinfo_dbc`;
CREATE TABLE `skillraceclassinfo_dbc` (
`ID` INT NOT NULL DEFAULT 0,
`SkillID` INT NOT NULL DEFAULT 0,
`RaceMask` INT NOT NULL DEFAULT 0,
`ClassMask` INT NOT NULL DEFAULT 0,
`Flags` INT NOT NULL DEFAULT 0,
`MinLevel` INT NOT NULL DEFAULT 0,
`SkillTierID` INT NOT NULL DEFAULT 0,
`SkillCostIndex` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=UTF8MB4;
DROP TABLE IF EXISTS `playercreateinfo_spell`;
DROP TABLE IF EXISTS `playercreateinfo_skills`;
CREATE TABLE `playercreateinfo_skills` (
`raceMask` INT UNSIGNED NOT NULL,
`classMask` INT UNSIGNED NOT NULL,
`skill` SMALLINT UNSIGNED NOT NULL,
`rank` SMALLINT UNSIGNED NOT NULL DEFAULT 0,
`comment` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`raceMask`,`classMask`,`skill`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
DELETE FROM `playercreateinfo_skills`;
INSERT INTO `playercreateinfo_skills` (`raceMask`, `classMask`, `skill`, `rank`, `comment`) VALUES
(0, 0, 95, 0, 'Defense'),
(0, 0, 162, 0, 'Unarmed'),
(0, 0, 183, 0, 'GENERIC (DND)'),
(0, 0, 415, 0, 'Cloth'),
(0, 0, 777, 0, 'Mounts'),
(0, 0, 778, 0, 'Companion Pets'),
(0, 1, 26, 0, 'Warrior - Arms'),
(0, 1, 256, 0, 'Warrior - Fury'),
(0, 1, 257, 0, 'Warrior - Protection'),
(0, 2, 184, 0, 'Paladin - Retribution'),
(0, 2, 267, 0, 'Paladin - Protection'),
(0, 2, 594, 0, 'Paladin - Holy'),
(0, 4, 50, 0, 'Hunter - Beast Mastery'),
(0, 4, 51, 0, 'Hunter - Survival'),
(0, 4, 163, 0, 'Hunter - Marksmanship'),
(0, 8, 38, 0, 'Rogue - Combat'),
(0, 8, 39, 0, 'Rogue - Subtlety'),
(0, 8, 176, 0, 'Thrown'),
(0, 8, 253, 0, 'Rogue - Assassination'),
(0, 16, 56, 0, 'Priest - Holy'),
(0, 16, 78, 0, 'Priest - Shadow'),
(0, 16, 613, 0, 'Priest - Discipline'),
(0, 32, 129, 4, 'Death Knight - First Aid'),
(0, 32, 229, 0, 'Polearms'),
(0, 32, 293, 0, 'Plate'),
(0, 32, 762, 0, 'Death Knight - Riding'),
(0, 32, 770, 0, 'Death Knight - Blood'),
(0, 32, 771, 0, 'Death Knight - Frost'),
(0, 32, 772, 0, 'Death Knight - Unholy'),
(0, 35, 55, 0, 'Two-Handed Swords'),
(0, 35, 413, 0, 'Mail'),
(0, 37, 44, 0, 'Axes'),
(0, 37, 172, 0, 'Two-Handed Axes'),
(0, 39, 43, 0, 'Swords'),
(0, 40, 118, 0, 'Dual Wield'),
(0, 64, 373, 0, 'Shaman - Enhancement'),
(0, 64, 374, 0, 'Shaman - Restoration'),
(0, 64, 375, 0, 'Shaman - Elemental'),
(0, 67, 433, 0, 'Shield'),
(0, 128, 6, 0, 'Mage - Frost'),
(0, 128, 8, 0, 'Mage - Fire'),
(0, 128, 237, 0, 'Mage - Arcane'),
(0, 256, 354, 0, 'Warlock - Demonology'),
(0, 256, 355, 0, 'Warlock - Affliction'),
(0, 256, 593, 0, 'Warlock - Destruction'),
(0, 400, 228, 0, 'Wands'),
(0, 1024, 134, 0, 'Druid - Feral'),
(0, 1024, 573, 0, 'Druid - Restoration'),
(0, 1024, 574, 0, 'Druid - Balance'),
(0, 1107, 54, 0, 'Maces'),
(0, 1135, 414, 0, 'Leather'),
(0, 1488, 136, 0, 'Staves'),
(1, 0, 754, 0, 'Human - Racial'),
(2, 0, 125, 0, 'Orc - Racial'),
(4, 0, 101, 0, 'Dwarf - Racial'),
(4, 0, 111, 0, 'Language: Dwarven'),
(8, 0, 113, 0, 'Language: Darnassian'),
(8, 0, 126, 0, 'Night Elf - Racial'),
(16, 0, 220, 0, 'Undead - Racial'),
(16, 0, 673, 0, 'Language: Forsaken'),
(32, 0, 115, 0, 'Language: Taurahe'),
(32, 0, 124, 0, 'Tauren - Racial'),
(36, 4, 46, 0, 'Guns'),
(64, 0, 313, 0, 'Language: Gnomish'),
(64, 0, 753, 0, 'Gnome - Racial'),
(128, 0, 315, 0, 'Language: Troll'),
(128, 0, 733, 0, 'Troll - Racial'),
(512, 0, 137, 0, 'Language: Thalassian'),
(512, 0, 756, 0, 'Blood Elf - Racial'),
(650, 4, 45, 0, 'Bows'),
(690, 0, 109, 0, 'Language: Orcish'),
(735, 1293, 173, 0, 'Daggers'),
(1024, 0, 759, 0, 'Language: Draenei'),
(1024, 0, 760, 0, 'Draenei - Racial'),
(1024, 4, 226, 0, 'Crossbows'),
(1061, 3, 160, 0, 'Two-Handed Maces'),
(1101, 0, 98, 0, 'Language: Common');
@@ -1478,7 +1478,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellIter->first); SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellIter->first);
for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
{ {
if (skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine) if (skillIter->second->SkillLine == achievementCriteria->learn_skillline_spell.skillLine)
{ {
// xinef: do not add couter twice if by any chance skill is listed twice in dbc (eg. skill 777 and spell 22717) // xinef: do not add couter twice if by any chance skill is listed twice in dbc (eg. skill 777 and spell 22717)
++spellCount; ++spellCount;
@@ -1540,7 +1540,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellIter->first); SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellIter->first);
for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
{ {
if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine) if (skillIter->second->SkillLine == achievementCriteria->learn_skill_line.skillLine)
{ {
// xinef: do not add couter twice if by any chance skill is listed twice in dbc (eg. skill 777 and spell 22717) // xinef: do not add couter twice if by any chance skill is listed twice in dbc (eg. skill 777 and spell 22717)
++spellCount; ++spellCount;
+2 -2
View File
@@ -291,10 +291,10 @@ bool SpellChatLink::ValidateName(char* buffer, const char* context)
LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): skill line ability not found for spell %u", context, _spell->Id); LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): skill line ability not found for spell %u", context, _spell->Id);
return false; return false;
} }
SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId); SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillInfo->SkillLine);
if (!skillLine) if (!skillLine)
{ {
LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): skill line not found for skill %u", context, skillInfo->skillId); LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): skill line not found for skill %u", context, skillInfo->SkillLine);
return false; return false;
} }
+42 -3
View File
@@ -123,6 +123,9 @@ DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore(ScalingStatValuesfmt
DBCStorage <SkillLineEntry> sSkillLineStore(SkillLinefmt); DBCStorage <SkillLineEntry> sSkillLineStore(SkillLinefmt);
DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore(SkillLineAbilityfmt); DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore(SkillLineAbilityfmt);
DBCStorage <SkillRaceClassInfoEntry> sSkillRaceClassInfoStore(SkillRaceClassInfofmt);
SkillRaceClassInfoMap SkillRaceClassInfoBySkill;
DBCStorage <SkillTiersEntry> sSkillTiersStore(SkillTiersfmt);
DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt); DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt);
@@ -324,6 +327,7 @@ void LoadDBCStores(const std::string& dataPath)
LOAD_DBC(sScalingStatValuesStore, "ScalingStatValues.dbc", "scalingstatvalues_dbc"); LOAD_DBC(sScalingStatValuesStore, "ScalingStatValues.dbc", "scalingstatvalues_dbc");
LOAD_DBC(sSkillLineStore, "SkillLine.dbc", "skillline_dbc"); LOAD_DBC(sSkillLineStore, "SkillLine.dbc", "skillline_dbc");
LOAD_DBC(sSkillLineAbilityStore, "SkillLineAbility.dbc", "skilllineability_dbc"); LOAD_DBC(sSkillLineAbilityStore, "SkillLineAbility.dbc", "skilllineability_dbc");
LOAD_DBC(sSkillRaceClassInfoStore, "SkillRaceClassInfo.dbc", "skillraceclassinfo_dbc");
LOAD_DBC(sSoundEntriesStore, "SoundEntries.dbc", "soundentries_dbc"); LOAD_DBC(sSoundEntriesStore, "SoundEntries.dbc", "soundentries_dbc");
LOAD_DBC(sSpellStore, "Spell.dbc", "spell_dbc"); LOAD_DBC(sSpellStore, "Spell.dbc", "spell_dbc");
LOAD_DBC(sSpellCastTimesStore, "SpellCastTimes.dbc", "spellcasttimes_dbc"); LOAD_DBC(sSpellCastTimesStore, "SpellCastTimes.dbc", "spellcasttimes_dbc");
@@ -392,21 +396,35 @@ void LoadDBCStores(const std::string& dataPath)
if (i->Category) if (i->Category)
sSpellsByCategoryStore[i->Category].insert(i->Id); sSpellsByCategoryStore[i->Category].insert(i->Id);
for (SkillRaceClassInfoEntry const* entry : sSkillRaceClassInfoStore)
{
if (sSkillLineStore.LookupEntry(entry->SkillID))
{
SkillRaceClassInfoBySkill.emplace(entry->SkillID, entry);
}
}
for (SkillLineAbilityEntry const* skillLine : sSkillLineAbilityStore) for (SkillLineAbilityEntry const* skillLine : sSkillLineAbilityStore)
{ {
SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId); SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->Spell);
if (spellInfo && spellInfo->Attributes & SPELL_ATTR0_PASSIVE) if (spellInfo && spellInfo->Attributes & SPELL_ATTR0_PASSIVE)
{ {
for (CreatureFamilyEntry const* cFamily : sCreatureFamilyStore) for (CreatureFamilyEntry const* cFamily : sCreatureFamilyStore)
{ {
if (skillLine->skillId != cFamily->skillLine[0] && skillLine->skillId != cFamily->skillLine[1]) if (skillLine->SkillLine != cFamily->skillLine[0] && skillLine->SkillLine != cFamily->skillLine[1])
{
continue; continue;
}
if (spellInfo->SpellLevel) if (spellInfo->SpellLevel)
{
continue; continue;
}
if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) if (skillLine->AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
{
continue; continue;
}
sPetFamilySpellsStore[cFamily->ID].insert(spellInfo->Id); sPetFamilySpellsStore[cFamily->ID].insert(spellInfo->Id);
} }
@@ -1081,3 +1099,24 @@ uint32 GetDefaultMapLight(uint32 mapId)
return 0; return 0;
} }
SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_)
{
SkillRaceClassInfoBounds bounds = SkillRaceClassInfoBySkill.equal_range(skill);
for (SkillRaceClassInfoMap::iterator itr = bounds.first; itr != bounds.second; ++itr)
{
if (itr->second->RaceMask && !(itr->second->RaceMask & (1 << (race - 1))))
{
continue;
}
if (itr->second->ClassMask && !(itr->second->ClassMask & (1 << (class_ - 1))))
{
continue;
}
return itr->second;
}
return nullptr;
}
+5
View File
@@ -55,6 +55,10 @@ CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, ui
LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty); LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty);
uint32 GetDefaultMapLight(uint32 mapId); uint32 GetDefaultMapLight(uint32 mapId);
typedef std::unordered_multimap<uint32, SkillRaceClassInfoEntry const*> SkillRaceClassInfoMap;
typedef std::pair<SkillRaceClassInfoMap::iterator, SkillRaceClassInfoMap::iterator> SkillRaceClassInfoBounds;
SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_);
extern DBCStorage <AchievementEntry> sAchievementStore; extern DBCStorage <AchievementEntry> sAchievementStore;
extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore; extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore;
extern DBCStorage <AchievementCategoryEntry> sAchievementCategoryStore; extern DBCStorage <AchievementCategoryEntry> sAchievementCategoryStore;
@@ -129,6 +133,7 @@ extern DBCStorage <ScalingStatDistributionEntry> sScalingStatDistributionStore;
extern DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore; extern DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore;
extern DBCStorage <SkillLineEntry> sSkillLineStore; extern DBCStorage <SkillLineEntry> sSkillLineStore;
extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore; extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore;
extern DBCStorage <SkillTiersEntry> sSkillTiersStore;
extern DBCStorage <SoundEntriesEntry> sSoundEntriesStore; extern DBCStorage <SoundEntriesEntry> sSoundEntriesStore;
extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore; extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore;
extern DBCStorage <SpellCategoryEntry> sSpellCategoryStore; extern DBCStorage <SpellCategoryEntry> sSpellCategoryStore;
+230 -147
View File
@@ -20,6 +20,7 @@
#include "ChannelMgr.h" #include "ChannelMgr.h"
#include "CharacterDatabaseCleaner.h" #include "CharacterDatabaseCleaner.h"
#include "Chat.h" #include "Chat.h"
#include "Config.h"
#include "Common.h" #include "Common.h"
#include "ConditionMgr.h" #include "ConditionMgr.h"
#include "CreatureAI.h" #include "CreatureAI.h"
@@ -1186,7 +1187,8 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
} }
// original spells // original spells
learnDefaultSpells(); LearnDefaultSkills();
LearnCustomSpells();
// original action bar // original action bar
for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr) for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr)
@@ -4077,12 +4079,6 @@ bool Player::_addSpell(uint32 spellId, uint8 addSpecMask, bool temporary, bool l
SetFreePrimaryProfessions(freeProfs - 1); SetFreePrimaryProfessions(freeProfs - 1);
} }
// pussywizard: update 310 flyer speed
if (!Has310Flyer(false))
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310)
SetHas310Flyer(true);
uint16 maxskill = GetMaxSkillValueForLevel(); uint16 maxskill = GetMaxSkillValueForLevel();
SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId); SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId);
SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId); SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
@@ -4102,29 +4098,28 @@ bool Player::_addSpell(uint32 spellId, uint8 addSpecMask, bool temporary, bool l
} }
else else
{ {
// pussywizard: checked, ok // not ranked skills
for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx) for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
{ {
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId); SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
if (!pSkill || HasSkill(pSkill->id)) if (!pSkill)
{
continue; continue;
}
if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || // pussywizard: learning autolearned spell from skill ensures having the skill if (_spell_idx->second->AcquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(pSkill->id))
((pSkill->id == SKILL_LOCKPICKING || pSkill->id == SKILL_RUNEFORGING) && _spell_idx->second->max_value == 0)) // pussywizard: learning any spell from lockpicking or runeforging ensures having the skill
{ {
switch (GetSkillRangeType(pSkill, _spell_idx->second->racemask != 0)) LearnDefaultSkill(pSkill->id, 0);
}
if (pSkill->id == SKILL_MOUNTS && !Has310Flyer(false))
{ {
case SKILL_RANGE_LANGUAGE: for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
SetSkill(pSkill->id, GetSkillStep(pSkill->id), 300, 300); {
break; if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310)
case SKILL_RANGE_LEVEL: {
SetSkill(pSkill->id, GetSkillStep(pSkill->id), 1, GetMaxSkillValueForLevel()); SetHas310Flyer(true);
break; }
case SKILL_RANGE_MONO:
SetSkill(pSkill->id, GetSkillStep(pSkill->id), 1, 1);
break;
default:
break;
} }
} }
} }
@@ -4135,8 +4130,8 @@ bool Player::_addSpell(uint32 spellId, uint8 addSpecMask, bool temporary, bool l
{ {
for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx) for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
{ {
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE, _spell_idx->second->skillId); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE, _spell_idx->second->SkillLine);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS, _spell_idx->second->skillId); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS, _spell_idx->second->SkillLine);
} }
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL, spellId); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL, spellId);
} }
@@ -4194,9 +4189,9 @@ void Player::learnSpell(uint32 spellId)
} }
} }
void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporary) void Player::removeSpell(uint32 spell_id, uint8 removeSpecMask, bool onlyTemporary)
{ {
PlayerSpellMap::iterator itr = m_spells.find(spellId); PlayerSpellMap::iterator itr = m_spells.find(spell_id);
if (itr == m_spells.end()) if (itr == m_spells.end())
return; return;
@@ -4210,15 +4205,15 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
// pussywizard: remove non-talent higher ranks (recursive) // pussywizard: remove non-talent higher ranks (recursive)
// pussywizard: do this at the beginning, not in the middle of removing! // pussywizard: do this at the beginning, not in the middle of removing!
if (uint32 nextSpell = sSpellMgr->GetNextSpellInChain(spellId)) if (uint32 nextSpell = sSpellMgr->GetNextSpellInChain(spell_id))
if (!GetTalentSpellPos(nextSpell)) if (!GetTalentSpellPos(nextSpell))
removeSpell(nextSpell, removeSpecMask, onlyTemporary); removeSpell(nextSpell, removeSpecMask, onlyTemporary);
// xinef: if current spell has talentcost, remove spells requiring this spell // xinef: if current spell has talentcost, remove spells requiring this spell
uint32 firstRankSpellId = sSpellMgr->GetFirstSpellInChain(spellId); uint32 firstRankSpellId = sSpellMgr->GetFirstSpellInChain(spell_id);
if (GetTalentSpellCost(firstRankSpellId)) if (GetTalentSpellCost(firstRankSpellId))
{ {
SpellsRequiringSpellMapBounds spellsRequiringSpell = sSpellMgr->GetSpellsRequiringSpellBounds(spellId); SpellsRequiringSpellMapBounds spellsRequiringSpell = sSpellMgr->GetSpellsRequiringSpellBounds(firstRankSpellId);
for (auto spellsItr = spellsRequiringSpell.first; spellsItr != spellsRequiringSpell.second; ++spellsItr) for (auto spellsItr = spellsRequiringSpell.first; spellsItr != spellsRequiringSpell.second; ++spellsItr)
{ {
removeSpell(spellsItr->second, removeSpecMask, onlyTemporary); removeSpell(spellsItr->second, removeSpecMask, onlyTemporary);
@@ -4226,7 +4221,7 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
} }
// pussywizard: re-search, it can be corrupted in prev loop // pussywizard: re-search, it can be corrupted in prev loop
itr = m_spells.find(spellId); itr = m_spells.find(spell_id);
if (itr == m_spells.end()) if (itr == m_spells.end())
return; return;
@@ -4249,13 +4244,13 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
// xinef: this is used for talents and they are not removed in removeSpell function... // xinef: this is used for talents and they are not removed in removeSpell function...
// xinef: however ill leave this here just in case // xinef: however ill leave this here just in case
// pussywizard: remove owned aura obtained from currently removed spell // pussywizard: remove owned aura obtained from currently removed spell
RemoveOwnedAura(spellId); RemoveOwnedAura(spell_id);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{ {
// pussywizard: remove pet auras // pussywizard: remove pet auras
if (PetAura const* petSpell = sSpellMgr->GetPetAura(spellId, i)) if (PetAura const* petSpell = sSpellMgr->GetPetAura(spell_id, i))
RemovePetAura(petSpell); RemovePetAura(petSpell);
// pussywizard: remove all triggered auras // pussywizard: remove all triggered auras
@@ -4275,13 +4270,13 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
if (Has310Flyer(false)) if (Has310Flyer(false))
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310) if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310)
Has310Flyer(true, spellId); Has310Flyer(true, spell_id);
// pussywizard: remove dependent skill // pussywizard: remove dependent skill
SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId); SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spell_id);
if (spellLearnSkill) if (spellLearnSkill)
{ {
uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spellId); uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spell_id);
if (!prev_spell) // pussywizard: first rank, remove skill if (!prev_spell) // pussywizard: first rank, remove skill
SetSkill(spellLearnSkill->skill, 0, 0, 0); SetSkill(spellLearnSkill->skill, 0, 0, 0);
@@ -4313,24 +4308,28 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
} }
else else
{ {
// pussywizard: checked, ok SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spell_id);
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId); // most likely will never be used, haven't heard of cases where players unlearn a mount
if (Has310Flyer(false) && spellInfo)
{
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{ {
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId); SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
if (!pSkill) if (!pSkill)
continue; continue;
// pussywizard: don't understand why whole skill is removed when just single spell from it is removed if (_spell_idx->second->SkillLine == SKILL_MOUNTS)
if ((_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL && pSkill->categoryId != SKILL_CATEGORY_CLASS) || // pussywizard: don't unlearn class skills
((pSkill->id == SKILL_LOCKPICKING || pSkill->id == SKILL_RUNEFORGING) && _spell_idx->second->max_value == 0))
{ {
// not reset skills for professions and racial abilities for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if ((pSkill->categoryId == SKILL_CATEGORY_SECONDARY || pSkill->categoryId == SKILL_CATEGORY_PROFESSION) && (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask != 0)) {
continue; if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED &&
spellInfo->Effects[i].CalcValue() == 310)
// pussywizard: this is needed for weapon/armor/language skills to remove them when loosing spell {
SetSkill(pSkill->id, GetSkillStep(pSkill->id), 0, 0); Has310Flyer(true, spell_id); // with true as first argument its also used to set/remove the flag
break;
}
}
}
} }
} }
} }
@@ -4338,8 +4337,8 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
// pussywizard: remove from spell book (can't be replaced by previous rank, because such spells can't be unlearnt) // pussywizard: remove from spell book (can't be replaced by previous rank, because such spells can't be unlearnt)
if (!onlyTemporary || ((!spellInfo->HasAttribute(SpellAttr0(SPELL_ATTR0_PASSIVE | SPELL_ATTR0_DO_NOT_DISPLAY)) || !spellInfo->HasAnyAura()) && !spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL))) if (!onlyTemporary || ((!spellInfo->HasAttribute(SpellAttr0(SPELL_ATTR0_PASSIVE | SPELL_ATTR0_DO_NOT_DISPLAY)) || !spellInfo->HasAnyAura()) && !spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL)))
{ {
sScriptMgr->OnPlayerForgotSpell(this, spellId); sScriptMgr->OnPlayerForgotSpell(this, spell_id);
SendLearnPacket(spellId, false); SendLearnPacket(spell_id, false);
} }
} }
@@ -4363,7 +4362,7 @@ bool Player::Has310Flyer(bool checkAllSpells, uint32 excludeSpellId)
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(itr->first); SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(itr->first);
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{ {
if (_spell_idx->second->skillId != SKILL_MOUNTS) if (_spell_idx->second->SkillLine != SKILL_MOUNTS)
break; // We can break because mount spells belong only to one skillline (at least 310 flyers do) break; // We can break because mount spells belong only to one skillline (at least 310 flyers do)
spellInfo = sSpellMgr->AssertSpellInfo(itr->first); spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
@@ -6304,9 +6303,6 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)
if (!skill_id) if (!skill_id)
return false; return false;
if (skill_id == SKILL_FIST_WEAPONS)
skill_id = SKILL_UNARMED;
SkillStatusMap::iterator itr = mSkillStatus.find(skill_id); SkillStatusMap::iterator itr = mSkillStatus.find(skill_id);
if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
return false; return false;
@@ -6328,6 +6324,7 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)
SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(new_value, max)); SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(new_value, max));
if (itr->second.uState != SKILL_NEW) if (itr->second.uState != SKILL_NEW)
itr->second.uState = SKILL_CHANGED; itr->second.uState = SKILL_CHANGED;
UpdateSkillEnchantments(skill_id, value, new_value); UpdateSkillEnchantments(skill_id, value, new_value);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skill_id); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skill_id);
return true; return true;
@@ -6355,24 +6352,24 @@ bool Player::UpdateCraftSkill(uint32 spellid)
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{ {
if (_spell_idx->second->skillId) if (_spell_idx->second->SkillLine)
{ {
uint32 SkillValue = GetPureSkillValue(_spell_idx->second->skillId); uint32 SkillValue = GetPureSkillValue(_spell_idx->second->SkillLine);
// Alchemy Discoveries here // Alchemy Discoveries here
SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spellid); SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spellid);
if (spellEntry && spellEntry->Mechanic == MECHANIC_DISCOVERY) if (spellEntry && spellEntry->Mechanic == MECHANIC_DISCOVERY)
{ {
if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this)) if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->SkillLine, spellid, this))
learnSpell(discoveredSpell); learnSpell(discoveredSpell);
} }
uint32 craft_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_CRAFTING); uint32 craft_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_CRAFTING);
return UpdateSkillPro(_spell_idx->second->skillId, SkillGainChance(SkillValue, return UpdateSkillPro(_spell_idx->second->SkillLine, SkillGainChance(SkillValue,
_spell_idx->second->max_value, _spell_idx->second->TrivialSkillLineRankHigh,
(_spell_idx->second->max_value + _spell_idx->second->min_value) / 2, (_spell_idx->second->TrivialSkillLineRankHigh + _spell_idx->second->TrivialSkillLineRankLow) / 2,
_spell_idx->second->min_value), _spell_idx->second->TrivialSkillLineRankLow),
craft_skill_gain); craft_skill_gain);
} }
} }
@@ -6524,7 +6521,7 @@ void Player::UpdateWeaponSkill(Unit* victim, WeaponAttackType attType)
UpdateSkill(SKILL_UNARMED, weapon_skill_gain); UpdateSkill(SKILL_UNARMED, weapon_skill_gain);
UpdateSkill(SKILL_FIST_WEAPONS, weapon_skill_gain); UpdateSkill(SKILL_FIST_WEAPONS, weapon_skill_gain);
} }
else if (tmpitem) else if (tmpitem && tmpitem->GetTemplate()->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE)
{ {
switch (tmpitem->GetTemplate()->SubClass) switch (tmpitem->GetTemplate()->SubClass)
{ {
@@ -6610,11 +6607,11 @@ void Player::UpdateSkillsForLevel()
continue; continue;
uint32 pskill = itr->first; uint32 pskill = itr->first;
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(pskill); SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pskill, getRace(), getClass());
if (!pSkill) if (!rcEntry)
continue; continue;
if (GetSkillRangeType(pSkill, false) != SKILL_RANGE_LEVEL) if (GetSkillRangeType(rcEntry) != SKILL_RANGE_LEVEL)
continue; continue;
uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos); uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);
@@ -6626,7 +6623,7 @@ void Player::UpdateSkillsForLevel()
if (max != 1) if (max != 1)
{ {
/// maximize skill always /// maximize skill always
if (alwaysMaxSkill) if (alwaysMaxSkill || (rcEntry->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE))
{ {
SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(maxSkill, maxSkill)); SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(maxSkill, maxSkill));
if (itr->second.uState != SKILL_NEW) if (itr->second.uState != SKILL_NEW)
@@ -6717,8 +6714,8 @@ void Player::SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal)
// remove all spells that related to this skill // remove all spells that related to this skill
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
if (SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j)) if (SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j))
if (pAbility->skillId == id) if (pAbility->SkillLine == id)
removeSpell(sSpellMgr->GetFirstSpellInChain(pAbility->spellId), SPEC_MASK_ALL, false); removeSpell(sSpellMgr->GetFirstSpellInChain(pAbility->Spell), SPEC_MASK_ALL, false);
} }
} }
else if (newVal) //add else if (newVal) //add
@@ -18395,7 +18392,9 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
m_specsCount = fields[64].GetUInt8(); m_specsCount = fields[64].GetUInt8();
m_activeSpec = fields[65].GetUInt8(); m_activeSpec = fields[65].GetUInt8();
learnDefaultSpells(); // pussywizard: move before loading spells and talents LearnDefaultSkills();
LearnCustomSpells();
_LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS)); _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS));
_LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS)); _LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS));
@@ -23868,16 +23867,109 @@ void Player::resetSpells()
for (PlayerSpellMap::const_iterator iter = spellMap.begin(); iter != spellMap.end(); ++iter) for (PlayerSpellMap::const_iterator iter = spellMap.begin(); iter != spellMap.end(); ++iter)
removeSpell(iter->first, SPEC_MASK_ALL, false); removeSpell(iter->first, SPEC_MASK_ALL, false);
learnDefaultSpells(); LearnDefaultSkills();
LearnCustomSpells();
learnQuestRewardedSpells(); learnQuestRewardedSpells();
} }
void Player::learnDefaultSpells() void Player::LearnCustomSpells()
{ {
// xinef: learn default race/class spells if (!sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS))
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(true), getClass()); {
for (PlayerCreateInfoSpells::const_iterator itr = info->spell.begin(); itr != info->spell.end(); ++itr) return;
_addSpell(*itr, SPEC_MASK_ALL, true); }
// learn default race/class spells
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
for (PlayerCreateInfoSpells::const_iterator itr = info->customSpells.begin(); itr != info->customSpells.end(); ++itr)
{
uint32 tspell = *itr;
LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell);
}
}
void Player::LearnDefaultSkills()
{
// learn default race/class skills
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.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;
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 (sWorld->getBoolConfig(CONFIG_ALWAYS_MAXSKILL) && !IsProfessionOrRidingSkill(skillId))
{
skillValue = maxValue;
}
else 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;
}
} }
void Player::learnQuestRewardedSpells(Quest const* quest) void Player::learnQuestRewardedSpells(Quest const* quest)
@@ -23932,40 +24024,47 @@ void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value)
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
{ {
SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j); SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j);
if (!pAbility || pAbility->skillId != skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL) if (!pAbility || pAbility->SkillLine != skill_id)
continue;
// Check race if set
if (pAbility->racemask && !(pAbility->racemask & raceMask))
continue;
// Check class if set
if (pAbility->classmask && !(pAbility->classmask & classMask))
continue;
if (sSpellMgr->GetSpellInfo(pAbility->spellId))
{ {
continue;
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(pAbility->Spell);
if (!spellInfo)
{
continue;
}
if (pAbility->AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE && pAbility->AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
{
continue;
}
// Check race if set
if (pAbility->RaceMask && !(pAbility->RaceMask & raceMask))
{
continue;
}
// Check class if set
if (pAbility->ClassMask && !(pAbility->ClassMask & classMask))
{
continue;
}
// need unlearn spell // need unlearn spell
if (skill_value < pAbility->req_skill_value) if (skill_value < pAbility->MinSkillLineRank && pAbility->AcquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
removeSpell(pAbility->spellId, SPEC_MASK_ALL, true); {
removeSpell(pAbility->Spell, GetActiveSpec(), true);
}
// need learn // need learn
else if (!IsInWorld())
{
addSpell(pAbility->Spell, true, true, true, false);
}
else else
{ {
// Xinef: there is next spell and we have enough skill value to obtain it - skip current spell learnSpell(pAbility->Spell);
if (pAbility->req_skill_value > 1 && pAbility->forward_spellid)
{
bool hasForwardSpell = false;
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(pAbility->forward_spellid);
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
if (skill_value >= _spell_idx->second->req_skill_value)
{
hasForwardSpell = true;
break;
}
if (hasForwardSpell)
continue;
}
addSpell(pAbility->spellId, SPEC_MASK_ALL, true, true, true);
}
} }
} }
} }
@@ -24189,11 +24288,11 @@ bool Player::IsSpellFitByClassAndRace(uint32 spell_id) const
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{ {
// skip wrong race skills // skip wrong race skills
if (_spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0) if (_spell_idx->second->RaceMask && (_spell_idx->second->RaceMask & racemask) == 0)
continue; continue;
// skip wrong class skills // skip wrong class skills
if (_spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0) if (_spell_idx->second->ClassMask && (_spell_idx->second->ClassMask & classmask) == 0)
continue; continue;
return true; return true;
@@ -24658,7 +24757,7 @@ bool Player::IsAtRecruitAFriendDistance(WorldObject const* pOther) const
return pOther->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE); return pOther->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE);
} }
uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const uint32 Player::GetBaseWeaponSkillValue(WeaponAttackType attType) const
{ {
Item* item = GetWeaponForAttack(attType, true); Item* item = GetWeaponForAttack(attType, true);
@@ -24666,8 +24765,8 @@ uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
if (attType != BASE_ATTACK && !item) if (attType != BASE_ATTACK && !item)
return 0; return 0;
// weapon skill or (unarmed for base attack and for fist weapons) // weapon skill or (unarmed for base attack)
uint32 skill = (item && item->GetSkill() != SKILL_FIST_WEAPONS) ? item->GetSkill() : uint32(SKILL_UNARMED); uint32 skill = item ? item->GetSkill() : uint32(SKILL_UNARMED);
return GetBaseSkillValue(skill); return GetBaseSkillValue(skill);
} }
@@ -25830,15 +25929,15 @@ void Player::_LoadSkills(PreparedQueryResult result)
uint16 value = fields[1].GetUInt16(); uint16 value = fields[1].GetUInt16();
uint16 max = fields[2].GetUInt16(); uint16 max = fields[2].GetUInt16();
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill); SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skill, getRace(), getClass());
if (!pSkill) if (!rcEntry)
{ {
LOG_ERROR("entities.player", "Character %s has skill %u that does not exist.", GetGUID().ToString().c_str(), skill); LOG_ERROR("entities.player", "Character %s has skill %u that does not exist.", GetGUID().ToString().c_str(), skill);
continue; continue;
} }
// set fixed skill ranges // set fixed skill ranges
switch (GetSkillRangeType(pSkill, false)) switch (GetSkillRangeType(rcEntry))
{ {
case SKILL_RANGE_LANGUAGE: // 300..300 case SKILL_RANGE_LANGUAGE: // 300..300
value = max = 300; value = max = 300;
@@ -25846,9 +25945,12 @@ void Player::_LoadSkills(PreparedQueryResult result)
case SKILL_RANGE_MONO: // 1..1, grey monolite bar case SKILL_RANGE_MONO: // 1..1, grey monolite bar
value = max = 1; value = max = 1;
break; break;
case SKILL_RANGE_LEVEL:
max = GetMaxSkillValueForLevel();
default: default:
break; break;
} }
if (value == 0) if (value == 0)
{ {
LOG_ERROR("entities.player", "Character %s has skill %u with value 0. Will be deleted.", GetGUID().ToString().c_str(), skill); LOG_ERROR("entities.player", "Character %s has skill %u with value 0. Will be deleted.", GetGUID().ToString().c_str(), skill);
@@ -25863,11 +25965,20 @@ void Player::_LoadSkills(PreparedQueryResult result)
continue; continue;
} }
// enable unlearn button for primary professions only uint16 skillStep = 0;
if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION) if (SkillTiersEntry const* skillTier = sSkillTiersStore.LookupEntry(rcEntry->SkillTierID))
SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 1)); {
else for (uint32 i = 0; i < MAX_SKILL_STEP; ++i)
SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 0)); {
if (skillTier->Value[skillStep] == max)
{
skillStep = i + 1;
break;
}
}
}
SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, skillStep));
SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), MAKE_SKILL_VALUE(value, max)); SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), MAKE_SKILL_VALUE(value, max));
SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0); SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0);
@@ -25892,34 +26003,6 @@ void Player::_LoadSkills(PreparedQueryResult result)
SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), 0); SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), 0);
SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0); SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0);
} }
// special settings
if (getClass() == CLASS_DEATH_KNIGHT)
{
uint8 base_level = std::min(getLevel(), uint8(sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)));
if (base_level < 1)
base_level = 1;
uint16 base_skill = (base_level - 1) * 5; // 270 at starting level 55
if (base_skill < 1)
base_skill = 1; // skill mast be known and then > 0 in any case
if (GetPureSkillValue(SKILL_FIRST_AID) < base_skill)
SetSkill(SKILL_FIRST_AID, 4 /*artisan*/, base_skill, 300);
if (GetPureSkillValue(SKILL_AXES) < base_skill)
SetSkill(SKILL_AXES, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_DEFENSE) < base_skill)
SetSkill(SKILL_DEFENSE, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_POLEARMS) < base_skill)
SetSkill(SKILL_POLEARMS, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_SWORDS) < base_skill)
SetSkill(SKILL_SWORDS, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_2H_AXES) < base_skill)
SetSkill(SKILL_2H_AXES, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_2H_SWORDS) < base_skill)
SetSkill(SKILL_2H_SWORDS, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_UNARMED) < base_skill)
SetSkill(SKILL_UNARMED, 0, base_skill, base_skill);
}
} }
uint32 Player::GetPhaseMaskForSpawn() const uint32 Player::GetPhaseMaskForSpawn() const
+13 -2
View File
@@ -282,6 +282,14 @@ struct PlayerCreateInfoAction
typedef std::list<PlayerCreateInfoAction> PlayerCreateInfoActions; typedef std::list<PlayerCreateInfoAction> PlayerCreateInfoActions;
struct PlayerCreateInfoSkill
{
uint16 SkillId;
uint16 Rank;
};
typedef std::list<PlayerCreateInfoSkill> PlayerCreateInfoSkills;
struct PlayerInfo struct PlayerInfo
{ {
// existence checked by displayId != 0 // existence checked by displayId != 0
@@ -296,8 +304,9 @@ struct PlayerInfo
uint16 displayId_m{0}; uint16 displayId_m{0};
uint16 displayId_f{0}; uint16 displayId_f{0};
PlayerCreateInfoItems item; PlayerCreateInfoItems item;
PlayerCreateInfoSpells spell; PlayerCreateInfoSpells customSpells;
PlayerCreateInfoActions action; PlayerCreateInfoActions action;
PlayerCreateInfoSkills skills;
PlayerLevelInfo* levelInfo{nullptr}; //[level-1] 0..MaxPlayerLevel-1 PlayerLevelInfo* levelInfo{nullptr}; //[level-1] 0..MaxPlayerLevel-1
}; };
@@ -1693,7 +1702,9 @@ public:
void learnSpell(uint32 spellId); void learnSpell(uint32 spellId);
void removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporary); void removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporary);
void resetSpells(); void resetSpells();
void learnDefaultSpells(); void LearnCustomSpells();
void LearnDefaultSkills();
void LearnDefaultSkill(uint32 skillId, uint16 rank);
void learnQuestRewardedSpells(); void learnQuestRewardedSpells();
void learnQuestRewardedSpells(Quest const* quest); void learnQuestRewardedSpells(Quest const* quest);
void learnSpellHighRank(uint32 spellid); void learnSpellHighRank(uint32 spellid);
+3 -5
View File
@@ -3220,12 +3220,10 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target)
if (IsInFeralForm()) if (IsInFeralForm())
return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact
// weapon skill or (unarmed for base attack and fist weapons) // weapon skill or (unarmed for base attack)
uint32 skill; uint32 skill = SKILL_UNARMED;
if (item && item->GetSkill() != SKILL_FIST_WEAPONS) if (item)
skill = item->GetSkill(); skill = item->GetSkill();
else
skill = SKILL_UNARMED;
// in PvP use full skill instead current skill value // in PvP use full skill instead current skill value
value = (target && target->IsControlledByPlayer()) value = (target && target->IsControlledByPlayer())
+103 -34
View File
@@ -3485,17 +3485,90 @@ void ObjectMgr::LoadPlayerInfo()
} }
} }
// Load playercreate skills
LOG_INFO("server.loading", "Loading Player Create Skill Data...");
{
uint32 oldMSTime = getMSTime();
QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, `rank` FROM playercreateinfo_skills");
if (!result)
{
LOG_ERROR("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty.");
}
else
{
uint32 count = 0;
do
{
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)
{
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;
}
if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
{
LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_skills` table, ignoring.", raceMask);
continue;
}
if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
{
LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_skills` table, ignoring.", classMask);
continue;
}
if (!sSkillLineStore.LookupEntry(skill.SkillId))
{
LOG_ERROR("sql.sql", "Wrong skill id %u in `playercreateinfo_skills` table, ignoring.", skill.SkillId);
continue;
}
for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
{
if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
{
for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
{
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);
++count;
}
}
}
}
}
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded %u player create skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
}
// Load playercreate spells // Load playercreate spells
LOG_INFO("server.loading", "Loading Player Create Spell Data..."); LOG_INFO("server.loading", "Loading Player Create Spell Data...");
{ {
uint32 oldMSTime = getMSTime(); uint32 oldMSTime = getMSTime();
std::string tableName = sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS) ? "playercreateinfo_spell_custom" : "playercreateinfo_spell"; QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");
QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM %s", tableName.c_str());
if (!result) if (!result)
{ {
LOG_ERROR("sql.sql", ">> Loaded 0 player create spells. DB table `%s` is empty.", sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS) ? "playercreateinfo_spell_custom" : "playercreateinfo_spell"); LOG_INFO("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty.");
} }
else else
{ {
@@ -3510,13 +3583,13 @@ void ObjectMgr::LoadPlayerInfo()
if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE)) if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
{ {
LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_spell` table, ignoring.", raceMask); LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_spell_custom` table, ignoring.", raceMask);
continue; continue;
} }
if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE)) if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
{ {
LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_spell` table, ignoring.", classMask); LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_spell_custom` table, ignoring.", classMask);
continue; continue;
} }
@@ -3530,7 +3603,7 @@ void ObjectMgr::LoadPlayerInfo()
{ {
if (PlayerInfo* info = _playerInfo[raceIndex][classIndex]) if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
{ {
info->spell.push_back(spellId); info->customSpells.push_back(spellId);
++count; ++count;
} }
} }
@@ -3539,7 +3612,7 @@ void ObjectMgr::LoadPlayerInfo()
} }
} while (result->NextRow()); } while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded %u player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", ">> Loaded %u custom player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " "); LOG_INFO("server.loading", " ");
} }
} }
@@ -7943,37 +8016,33 @@ int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 rac
return 0; return 0;
} }
SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial) SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry)
{ {
switch (pSkill->categoryId) SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillID);
if (!skill)
{ {
case SKILL_CATEGORY_LANGUAGES:
return SKILL_RANGE_LANGUAGE;
case SKILL_CATEGORY_WEAPON:
if (pSkill->id != SKILL_FIST_WEAPONS)
return SKILL_RANGE_LEVEL;
else
return SKILL_RANGE_MONO;
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_NONE;
} }
if (sSkillTiersStore.LookupEntry(rcEntry->SkillTierID))
{
return SKILL_RANGE_RANK;
}
if (rcEntry->SkillID == SKILL_RUNEFORGING)
{
return SKILL_RANGE_MONO;
}
switch (skill->categoryId)
{
case SKILL_CATEGORY_ARMOR:
return SKILL_RANGE_MONO;
case SKILL_CATEGORY_LANGUAGES:
return SKILL_RANGE_LANGUAGE;
}
return SKILL_RANGE_LEVEL;
} }
void ObjectMgr::LoadGameTele() void ObjectMgr::LoadGameTele()
+1 -1
View File
@@ -645,7 +645,7 @@ enum SkillRangeType
SKILL_RANGE_NONE, // 0..0 always SKILL_RANGE_NONE, // 0..0 always
}; };
SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial); SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry);
#define MAX_PLAYER_NAME 12 // max allowed by client name length #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) #define MAX_INTERNAL_PLAYER_NAME 15 // max server internal player name length (> MAX_PLAYER_NAME for support declined names)
+2 -2
View File
@@ -111,7 +111,7 @@ void LoadSkillDiscoveryTable()
} }
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back(SkillDiscoveryEntry(spellId, reqSkillValue, chance)); SkillDiscoveryStore[-int32(_spell_idx->second->SkillLine)].push_back(SkillDiscoveryEntry(spellId, reqSkillValue, chance));
} }
else else
{ {
@@ -153,7 +153,7 @@ uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player)
return 0; return 0;
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId); SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
uint32 skillvalue = bounds.first != bounds.second ? player->GetSkillValue(bounds.first->second->skillId) : uint32(0); uint32 skillvalue = bounds.first != bounds.second ? player->GetSkillValue(bounds.first->second->SkillLine) : uint32(0);
float full_chance = 0; float full_chance = 0;
for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
+3 -3
View File
@@ -970,10 +970,10 @@ bool SpellInfo::IsAbilityLearnedWithProfession() const
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{ {
SkillLineAbilityEntry const* pAbility = _spell_idx->second; SkillLineAbilityEntry const* pAbility = _spell_idx->second;
if (!pAbility || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL) if (!pAbility || pAbility->AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
continue; continue;
if (pAbility->req_skill_value > 0) if (pAbility->MinSkillLineRank > 0)
return true; return true;
} }
@@ -985,7 +985,7 @@ bool SpellInfo::IsAbilityOfSkillType(uint32 skillType) const
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(Id); SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(Id);
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
if (_spell_idx->second->skillId == uint32(skillType)) if (_spell_idx->second->SkillLine == uint32(skillType))
return true; return true;
return false; return false;
+5 -5
View File
@@ -40,7 +40,7 @@ bool IsPartOfSkillLine(uint32 skillId, uint32 spellId)
{ {
SkillLineAbilityMapBounds skillBounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId); SkillLineAbilityMapBounds skillBounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
for (SkillLineAbilityMap::const_iterator itr = skillBounds.first; itr != skillBounds.second; ++itr) for (SkillLineAbilityMap::const_iterator itr = skillBounds.first; itr != skillBounds.second; ++itr)
if (itr->second->skillId == skillId) if (itr->second->SkillLine == skillId)
return true; return true;
return false; return false;
@@ -2031,7 +2031,7 @@ void SpellMgr::LoadSkillLineAbilityMap()
if (!SkillInfo) if (!SkillInfo)
continue; continue;
mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId, SkillInfo)); mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->Spell, SkillInfo));
++count; ++count;
} }
@@ -2270,13 +2270,13 @@ void SpellMgr::LoadPetLevelupSpellMap()
// (!creatureFamily->skillLine[1] || skillLine->skillId != creatureFamily->skillLine[1])) // (!creatureFamily->skillLine[1] || skillLine->skillId != creatureFamily->skillLine[1]))
// continue; // continue;
if (skillLine->skillId != creatureFamily->skillLine[j]) if (skillLine->SkillLine != creatureFamily->skillLine[j])
continue; continue;
if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) if (skillLine->AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
continue; continue;
SpellInfo const* spell = GetSpellInfo(skillLine->spellId); SpellInfo const* spell = GetSpellInfo(skillLine->Spell);
if (!spell) // not exist or triggered or talent if (!spell) // not exist or triggered or talent
continue; continue;
+9 -8
View File
@@ -118,7 +118,7 @@ public:
if (!entry) if (!entry)
continue; continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry->spellId); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry->Spell);
if (!spellInfo) if (!spellInfo)
continue; continue;
@@ -290,7 +290,8 @@ public:
if (!handler->extractPlayerTarget((char*)args, &target)) if (!handler->extractPlayerTarget((char*)args, &target))
return false; return false;
target->learnDefaultSpells(); target->LearnDefaultSkills();
target->LearnCustomSpells();
target->learnQuestRewardedSpells(); target->learnQuestRewardedSpells();
handler->PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST, handler->GetNameLink(target).c_str()); handler->PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST, handler->GetNameLink(target).c_str());
@@ -404,26 +405,26 @@ public:
continue; continue;
// wrong skill // wrong skill
if (skillLine->skillId != skillId) if (skillLine->SkillLine != skillId)
continue; continue;
// not high rank // not high rank
if (skillLine->forward_spellid) if (skillLine->SupercededBySpell)
continue; continue;
// skip racial skills // skip racial skills
if (skillLine->racemask != 0) if (skillLine->RaceMask != 0)
continue; continue;
// skip wrong class skills // skip wrong class skills
if (skillLine->classmask && (skillLine->classmask & classmask) == 0) if (skillLine->ClassMask && (skillLine->ClassMask & classmask) == 0)
continue; continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(skillLine->spellId); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(skillLine->Spell);
if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo)) if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo))
continue; continue;
player->learnSpell(skillLine->spellId); player->learnSpell(skillLine->Spell);
} }
} }
+12 -2
View File
@@ -310,8 +310,8 @@ enum MapFlags
enum AbilytyLearnType enum AbilytyLearnType
{ {
ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1, SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE = 1, // Spell state will update depending on skill value
ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2 SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN = 2 // Spell will be learned/removed together with entire skill
}; };
enum ItemEnchantmentType enum ItemEnchantmentType
@@ -333,6 +333,16 @@ enum ItemLimitCategoryMode
ITEM_LIMIT_CATEGORY_MODE_EQUIP = 1, // limit applied to amount equipped items (including used gems) ITEM_LIMIT_CATEGORY_MODE_EQUIP = 1, // limit applied to amount equipped items (including used gems)
}; };
enum SkillRaceClassInfoFlags
{
SKILL_FLAG_NO_SKILLUP_MESSAGE = 0x2,
SKILL_FLAG_ALWAYS_MAX_VALUE = 0x10,
SKILL_FLAG_UNLEARNABLE = 0x20, // Skill can be unlearned
SKILL_FLAG_INCLUDE_IN_SORT = 0x80, // Spells belonging to a skill with this flag will additionally compare skill ids when sorting spellbook in client
SKILL_FLAG_NOT_TRAINABLE = 0x100,
SKILL_FLAG_MONO_VALUE = 0x400 // Skill always has value 1 - clientside display flag, real value can be different
};
enum SpellCategoryFlags enum SpellCategoryFlags
{ {
SPELL_CATEGORY_FLAG_COOLDOWN_SCALES_WITH_WEAPON_SPEED = 0x01, // unused SPELL_CATEGORY_FLAG_COOLDOWN_SCALES_WITH_WEAPON_SPEED = 0x01, // unused
+32 -28
View File
@@ -1467,22 +1467,19 @@ struct ScalingStatValuesEntry
// uint32 displayOrder; // 19 m_sortIndex // uint32 displayOrder; // 19 m_sortIndex
//}; //};
//struct SkillRaceClassInfoEntry{ struct SkillRaceClassInfoEntry
// uint32 id; // 0 m_ID {
// uint32 skillId; // 1 m_skillID //uint32 ID; // 0
// uint32 raceMask; // 2 m_raceMask uint32 SkillID; // 1
// uint32 classMask; // 3 m_classMask uint32 RaceMask; // 2
// uint32 flags; // 4 m_flags uint32 ClassMask; // 3
// uint32 reqLevel; // 5 m_minLevel uint32 Flags; // 4
// uint32 skillTierId; // 6 m_skillTierID //uint32 MinLevel; // 5
// uint32 skillCostID; // 7 m_skillCostIndex uint32 SkillTierID; // 6
//}; //uint32 SkillCostIndex; // 7
};
//struct SkillTiersEntry{ #define MAX_SKILL_STEP 16
// uint32 id; // 0 m_ID
// uint32 skillValue[16]; // 1-17 m_cost
// uint32 maxSkillValue[16]; // 18-32 m_valueMax
//};
struct SkillLineEntry struct SkillLineEntry
{ {
@@ -1501,19 +1498,26 @@ struct SkillLineEntry
struct SkillLineAbilityEntry struct SkillLineAbilityEntry
{ {
uint32 id; // 0 m_ID uint32 ID; // 0
uint32 skillId; // 1 m_skillLine uint32 SkillLine; // 1
uint32 spellId; // 2 m_spell uint32 Spell; // 2
uint32 racemask; // 3 m_raceMask uint32 RaceMask; // 3
uint32 classmask; // 4 m_classMask uint32 ClassMask; // 4
//uint32 racemaskNot; // 5 m_excludeRace //uint32 ExcludeRace; // 5
//uint32 classmaskNot; // 6 m_excludeClass //uint32 ExcludeClass; // 6
uint32 req_skill_value; // 7 m_minSkillLineRank uint32 MinSkillLineRank; // 7
uint32 forward_spellid; // 8 m_supercededBySpell uint32 SupercededBySpell; // 8
uint32 learnOnGetSkill; // 9 m_acquireMethod uint32 AcquireMethod; // 9
uint32 max_value; // 10 m_trivialSkillLineRankHigh uint32 TrivialSkillLineRankHigh; // 10
uint32 min_value; // 11 m_trivialSkillLineRankLow uint32 TrivialSkillLineRankLow; // 11
//uint32 characterPoints[2]; // 12-13 m_characterPoints[2] //uint32 CharacterPoints[2]; // 12-13
};
struct SkillTiersEntry
{
uint32 ID; // 0
//uint32 Cost[MAX_SKILL_STEP]; // 1-16
uint32 Value[MAX_SKILL_STEP]; // 17-32
}; };
struct SoundEntriesEntry struct SoundEntriesEntry
+2
View File
@@ -82,6 +82,8 @@ char constexpr ScalingStatDistributionfmt[] = "niiiiiiiiiiiiiiiiiiiii";
char constexpr ScalingStatValuesfmt[] = "iniiiiiiiiiiiiiiiiiiiiii"; char constexpr ScalingStatValuesfmt[] = "iniiiiiiiiiiiiiiiiiiiiii";
char constexpr SkillLinefmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi"; char constexpr SkillLinefmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi";
char constexpr SkillLineAbilityfmt[] = "niiiixxiiiiixx"; char constexpr SkillLineAbilityfmt[] = "niiiixxiiiiixx";
char constexpr SkillRaceClassInfofmt[] = "diiiixix";
char constexpr SkillTiersfmt[] = "nxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiii";
char constexpr SoundEntriesfmt[] = "nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; char constexpr SoundEntriesfmt[] = "nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
char constexpr SpellCastTimefmt[] = "nixx"; char constexpr SpellCastTimefmt[] = "nixx";
char constexpr SpellCategoryfmt[] = "ni"; char constexpr SpellCategoryfmt[] = "ni";