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 2a3546ca36

* Cherry-Pick d28b66bca8

* Cherry-Pick 193408f335

- 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

View File

@@ -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');

View File

@@ -1478,7 +1478,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellIter->first);
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)
++spellCount;
@@ -1540,7 +1540,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellIter->first);
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)
++spellCount;

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);
return false;
}
SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId);
SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillInfo->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;
}

View File

@@ -123,6 +123,9 @@ DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore(ScalingStatValuesfmt
DBCStorage <SkillLineEntry> sSkillLineStore(SkillLinefmt);
DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore(SkillLineAbilityfmt);
DBCStorage <SkillRaceClassInfoEntry> sSkillRaceClassInfoStore(SkillRaceClassInfofmt);
SkillRaceClassInfoMap SkillRaceClassInfoBySkill;
DBCStorage <SkillTiersEntry> sSkillTiersStore(SkillTiersfmt);
DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt);
@@ -324,6 +327,7 @@ void LoadDBCStores(const std::string& dataPath)
LOAD_DBC(sScalingStatValuesStore, "ScalingStatValues.dbc", "scalingstatvalues_dbc");
LOAD_DBC(sSkillLineStore, "SkillLine.dbc", "skillline_dbc");
LOAD_DBC(sSkillLineAbilityStore, "SkillLineAbility.dbc", "skilllineability_dbc");
LOAD_DBC(sSkillRaceClassInfoStore, "SkillRaceClassInfo.dbc", "skillraceclassinfo_dbc");
LOAD_DBC(sSoundEntriesStore, "SoundEntries.dbc", "soundentries_dbc");
LOAD_DBC(sSpellStore, "Spell.dbc", "spell_dbc");
LOAD_DBC(sSpellCastTimesStore, "SpellCastTimes.dbc", "spellcasttimes_dbc");
@@ -392,21 +396,35 @@ void LoadDBCStores(const std::string& dataPath)
if (i->Category)
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)
{
SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId);
SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->Spell);
if (spellInfo && spellInfo->Attributes & SPELL_ATTR0_PASSIVE)
{
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;
}
if (spellInfo->SpellLevel)
{
continue;
}
if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL)
if (skillLine->AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
{
continue;
}
sPetFamilySpellsStore[cFamily->ID].insert(spellInfo->Id);
}
@@ -1081,3 +1099,24 @@ uint32 GetDefaultMapLight(uint32 mapId)
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;
}

View File

@@ -55,6 +55,10 @@ CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, ui
LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty);
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 <AchievementCriteriaEntry> sAchievementCriteriaStore;
extern DBCStorage <AchievementCategoryEntry> sAchievementCategoryStore;
@@ -129,6 +133,7 @@ extern DBCStorage <ScalingStatDistributionEntry> sScalingStatDistributionStore;
extern DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore;
extern DBCStorage <SkillLineEntry> sSkillLineStore;
extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore;
extern DBCStorage <SkillTiersEntry> sSkillTiersStore;
extern DBCStorage <SoundEntriesEntry> sSoundEntriesStore;
extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore;
extern DBCStorage <SpellCategoryEntry> sSpellCategoryStore;

View File

@@ -20,6 +20,7 @@
#include "ChannelMgr.h"
#include "CharacterDatabaseCleaner.h"
#include "Chat.h"
#include "Config.h"
#include "Common.h"
#include "ConditionMgr.h"
#include "CreatureAI.h"
@@ -1186,7 +1187,8 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
}
// original spells
learnDefaultSpells();
LearnDefaultSkills();
LearnCustomSpells();
// original action bar
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);
}
// 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();
SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId);
SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
@@ -4102,29 +4098,28 @@ bool Player::_addSpell(uint32 spellId, uint8 addSpecMask, bool temporary, bool l
}
else
{
// pussywizard: checked, ok
// 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->skillId);
if (!pSkill || HasSkill(pSkill->id))
continue;
if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || // pussywizard: learning autolearned spell from skill ensures having the skill
((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
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
if (!pSkill)
{
switch (GetSkillRangeType(pSkill, _spell_idx->second->racemask != 0))
continue;
}
if (_spell_idx->second->AcquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(pSkill->id))
{
LearnDefaultSkill(pSkill->id, 0);
}
if (pSkill->id == SKILL_MOUNTS && !Has310Flyer(false))
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
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;
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310)
{
SetHas310Flyer(true);
}
}
}
}
@@ -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)
{
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE, _spell_idx->second->skillId);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS, _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->SkillLine);
}
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())
return;
@@ -4210,15 +4205,15 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
// pussywizard: remove non-talent higher ranks (recursive)
// 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))
removeSpell(nextSpell, removeSpecMask, onlyTemporary);
// 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))
{
SpellsRequiringSpellMapBounds spellsRequiringSpell = sSpellMgr->GetSpellsRequiringSpellBounds(spellId);
SpellsRequiringSpellMapBounds spellsRequiringSpell = sSpellMgr->GetSpellsRequiringSpellBounds(firstRankSpellId);
for (auto spellsItr = spellsRequiringSpell.first; spellsItr != spellsRequiringSpell.second; ++spellsItr)
{
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
itr = m_spells.find(spellId);
itr = m_spells.find(spell_id);
if (itr == m_spells.end())
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: however ill leave this here just in case
// 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)
{
// pussywizard: remove pet auras
if (PetAura const* petSpell = sSpellMgr->GetPetAura(spellId, i))
if (PetAura const* petSpell = sSpellMgr->GetPetAura(spell_id, i))
RemovePetAura(petSpell);
// pussywizard: remove all triggered auras
@@ -4275,13 +4270,13 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
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)
Has310Flyer(true, spellId);
Has310Flyer(true, spell_id);
// pussywizard: remove dependent skill
SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId);
SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spell_id);
if (spellLearnSkill)
{
uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spellId);
uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spell_id);
if (!prev_spell) // pussywizard: first rank, remove skill
SetSkill(spellLearnSkill->skill, 0, 0, 0);
@@ -4313,24 +4308,28 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
}
else
{
// pussywizard: checked, ok
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spell_id);
// most likely will never be used, haven't heard of cases where players unlearn a mount
if (Has310Flyer(false) && spellInfo)
{
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
if (!pSkill)
continue;
// pussywizard: don't understand why whole skill is removed when just single spell from it is removed
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))
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{
// not reset skills for professions and racial abilities
if ((pSkill->categoryId == SKILL_CATEGORY_SECONDARY || pSkill->categoryId == SKILL_CATEGORY_PROFESSION) && (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask != 0))
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
if (!pSkill)
continue;
// pussywizard: this is needed for weapon/armor/language skills to remove them when loosing spell
SetSkill(pSkill->id, GetSkillStep(pSkill->id), 0, 0);
if (_spell_idx->second->SkillLine == SKILL_MOUNTS)
{
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)
{
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)
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);
SendLearnPacket(spellId, false);
sScriptMgr->OnPlayerForgotSpell(this, spell_id);
SendLearnPacket(spell_id, false);
}
}
@@ -4363,7 +4362,7 @@ bool Player::Has310Flyer(bool checkAllSpells, uint32 excludeSpellId)
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(itr->first);
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)
spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
@@ -6304,9 +6303,6 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)
if (!skill_id)
return false;
if (skill_id == SKILL_FIST_WEAPONS)
skill_id = SKILL_UNARMED;
SkillStatusMap::iterator itr = mSkillStatus.find(skill_id);
if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
return false;
@@ -6328,6 +6324,7 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)
SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(new_value, max));
if (itr->second.uState != SKILL_NEW)
itr->second.uState = SKILL_CHANGED;
UpdateSkillEnchantments(skill_id, value, new_value);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skill_id);
return true;
@@ -6355,25 +6352,25 @@ bool Player::UpdateCraftSkill(uint32 spellid)
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
SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spellid);
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);
}
uint32 craft_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_CRAFTING);
return UpdateSkillPro(_spell_idx->second->skillId, SkillGainChance(SkillValue,
_spell_idx->second->max_value,
(_spell_idx->second->max_value + _spell_idx->second->min_value) / 2,
_spell_idx->second->min_value),
craft_skill_gain);
return UpdateSkillPro(_spell_idx->second->SkillLine, SkillGainChance(SkillValue,
_spell_idx->second->TrivialSkillLineRankHigh,
(_spell_idx->second->TrivialSkillLineRankHigh + _spell_idx->second->TrivialSkillLineRankLow) / 2,
_spell_idx->second->TrivialSkillLineRankLow),
craft_skill_gain);
}
}
return false;
@@ -6524,7 +6521,7 @@ void Player::UpdateWeaponSkill(Unit* victim, WeaponAttackType attType)
UpdateSkill(SKILL_UNARMED, 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)
{
@@ -6610,11 +6607,11 @@ void Player::UpdateSkillsForLevel()
continue;
uint32 pskill = itr->first;
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(pskill);
if (!pSkill)
SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pskill, getRace(), getClass());
if (!rcEntry)
continue;
if (GetSkillRangeType(pSkill, false) != SKILL_RANGE_LEVEL)
if (GetSkillRangeType(rcEntry) != SKILL_RANGE_LEVEL)
continue;
uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);
@@ -6626,7 +6623,7 @@ void Player::UpdateSkillsForLevel()
if (max != 1)
{
/// maximize skill always
if (alwaysMaxSkill)
if (alwaysMaxSkill || (rcEntry->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE))
{
SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(maxSkill, maxSkill));
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
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
if (SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j))
if (pAbility->skillId == id)
removeSpell(sSpellMgr->GetFirstSpellInChain(pAbility->spellId), SPEC_MASK_ALL, false);
if (pAbility->SkillLine == id)
removeSpell(sSpellMgr->GetFirstSpellInChain(pAbility->Spell), SPEC_MASK_ALL, false);
}
}
else if (newVal) //add
@@ -18395,7 +18392,9 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
m_specsCount = fields[64].GetUInt8();
m_activeSpec = fields[65].GetUInt8();
learnDefaultSpells(); // pussywizard: move before loading spells and talents
LearnDefaultSkills();
LearnCustomSpells();
_LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS));
_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)
removeSpell(iter->first, SPEC_MASK_ALL, false);
learnDefaultSpells();
LearnDefaultSkills();
LearnCustomSpells();
learnQuestRewardedSpells();
}
void Player::learnDefaultSpells()
void Player::LearnCustomSpells()
{
// xinef: learn default race/class spells
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(true), getClass());
for (PlayerCreateInfoSpells::const_iterator itr = info->spell.begin(); itr != info->spell.end(); ++itr)
_addSpell(*itr, SPEC_MASK_ALL, true);
if (!sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS))
{
return;
}
// 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)
@@ -23932,40 +24024,47 @@ void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value)
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
{
SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j);
if (!pAbility || pAbility->skillId != skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
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))
if (!pAbility || pAbility->SkillLine != skill_id)
{
// need unlearn spell
if (skill_value < pAbility->req_skill_value)
removeSpell(pAbility->spellId, SPEC_MASK_ALL, true);
// need learn
else
{
// Xinef: there is next spell and we have enough skill value to obtain it - skip current 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;
}
continue;
}
addSpell(pAbility->spellId, SPEC_MASK_ALL, true, true, true);
}
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
if (skill_value < pAbility->MinSkillLineRank && pAbility->AcquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
{
removeSpell(pAbility->Spell, GetActiveSpec(), true);
}
// need learn
else if (!IsInWorld())
{
addSpell(pAbility->Spell, true, true, true, false);
}
else
{
learnSpell(pAbility->Spell);
}
}
}
@@ -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)
{
// 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;
// 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;
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);
}
uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
uint32 Player::GetBaseWeaponSkillValue(WeaponAttackType attType) const
{
Item* item = GetWeaponForAttack(attType, true);
@@ -24666,8 +24765,8 @@ uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
if (attType != BASE_ATTACK && !item)
return 0;
// weapon skill or (unarmed for base attack and for fist weapons)
uint32 skill = (item && item->GetSkill() != SKILL_FIST_WEAPONS) ? item->GetSkill() : uint32(SKILL_UNARMED);
// weapon skill or (unarmed for base attack)
uint32 skill = item ? item->GetSkill() : uint32(SKILL_UNARMED);
return GetBaseSkillValue(skill);
}
@@ -25830,15 +25929,15 @@ void Player::_LoadSkills(PreparedQueryResult result)
uint16 value = fields[1].GetUInt16();
uint16 max = fields[2].GetUInt16();
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
if (!pSkill)
SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skill, getRace(), getClass());
if (!rcEntry)
{
LOG_ERROR("entities.player", "Character %s has skill %u that does not exist.", GetGUID().ToString().c_str(), skill);
continue;
}
// set fixed skill ranges
switch (GetSkillRangeType(pSkill, false))
switch (GetSkillRangeType(rcEntry))
{
case SKILL_RANGE_LANGUAGE: // 300..300
value = max = 300;
@@ -25846,9 +25945,12 @@ void Player::_LoadSkills(PreparedQueryResult result)
case SKILL_RANGE_MONO: // 1..1, grey monolite bar
value = max = 1;
break;
case SKILL_RANGE_LEVEL:
max = GetMaxSkillValueForLevel();
default:
break;
}
if (value == 0)
{
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;
}
// enable unlearn button for primary professions only
if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 1));
else
SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 0));
uint16 skillStep = 0;
if (SkillTiersEntry const* skillTier = sSkillTiersStore.LookupEntry(rcEntry->SkillTierID))
{
for (uint32 i = 0; i < MAX_SKILL_STEP; ++i)
{
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_BONUS_INDEX(count), 0);
@@ -25892,34 +26003,6 @@ void Player::_LoadSkills(PreparedQueryResult result)
SetUInt32Value(PLAYER_SKILL_VALUE_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

View File

@@ -282,6 +282,14 @@ struct PlayerCreateInfoAction
typedef std::list<PlayerCreateInfoAction> PlayerCreateInfoActions;
struct PlayerCreateInfoSkill
{
uint16 SkillId;
uint16 Rank;
};
typedef std::list<PlayerCreateInfoSkill> PlayerCreateInfoSkills;
struct PlayerInfo
{
// existence checked by displayId != 0
@@ -296,8 +304,9 @@ struct PlayerInfo
uint16 displayId_m{0};
uint16 displayId_f{0};
PlayerCreateInfoItems item;
PlayerCreateInfoSpells spell;
PlayerCreateInfoSpells customSpells;
PlayerCreateInfoActions action;
PlayerCreateInfoSkills skills;
PlayerLevelInfo* levelInfo{nullptr}; //[level-1] 0..MaxPlayerLevel-1
};
@@ -1693,7 +1702,9 @@ public:
void learnSpell(uint32 spellId);
void removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporary);
void resetSpells();
void learnDefaultSpells();
void LearnCustomSpells();
void LearnDefaultSkills();
void LearnDefaultSkill(uint32 skillId, uint16 rank);
void learnQuestRewardedSpells();
void learnQuestRewardedSpells(Quest const* quest);
void learnSpellHighRank(uint32 spellid);

View File

@@ -3220,12 +3220,10 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target)
if (IsInFeralForm())
return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact
// weapon skill or (unarmed for base attack and fist weapons)
uint32 skill;
if (item && item->GetSkill() != SKILL_FIST_WEAPONS)
// weapon skill or (unarmed for base attack)
uint32 skill = SKILL_UNARMED;
if (item)
skill = item->GetSkill();
else
skill = SKILL_UNARMED;
// in PvP use full skill instead current skill value
value = (target && target->IsControlledByPlayer())

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
LOG_INFO("server.loading", "Loading Player Create Spell Data...");
{
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 %s", tableName.c_str());
QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");
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
{
@@ -3510,13 +3583,13 @@ void ObjectMgr::LoadPlayerInfo()
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;
}
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;
}
@@ -3530,7 +3603,7 @@ void ObjectMgr::LoadPlayerInfo()
{
if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
{
info->spell.push_back(spellId);
info->customSpells.push_back(spellId);
++count;
}
}
@@ -3539,7 +3612,7 @@ void ObjectMgr::LoadPlayerInfo()
}
} 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", " ");
}
}
@@ -7943,37 +8016,33 @@ int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 rac
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)
{
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;
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_LEVEL;
}
void ObjectMgr::LoadGameTele()

View File

@@ -645,7 +645,7 @@ enum SkillRangeType
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_INTERNAL_PLAYER_NAME 15 // max server internal player name length (> MAX_PLAYER_NAME for support declined names)

View File

@@ -111,7 +111,7 @@ void LoadSkillDiscoveryTable()
}
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
{
@@ -153,7 +153,7 @@ uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player)
return 0;
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;
for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)

View File

@@ -970,10 +970,10 @@ bool SpellInfo::IsAbilityLearnedWithProfession() const
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{
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;
if (pAbility->req_skill_value > 0)
if (pAbility->MinSkillLineRank > 0)
return true;
}
@@ -985,7 +985,7 @@ bool SpellInfo::IsAbilityOfSkillType(uint32 skillType) const
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(Id);
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 false;

View File

@@ -40,7 +40,7 @@ bool IsPartOfSkillLine(uint32 skillId, uint32 spellId)
{
SkillLineAbilityMapBounds skillBounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
for (SkillLineAbilityMap::const_iterator itr = skillBounds.first; itr != skillBounds.second; ++itr)
if (itr->second->skillId == skillId)
if (itr->second->SkillLine == skillId)
return true;
return false;
@@ -2031,7 +2031,7 @@ void SpellMgr::LoadSkillLineAbilityMap()
if (!SkillInfo)
continue;
mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId, SkillInfo));
mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->Spell, SkillInfo));
++count;
}
@@ -2270,13 +2270,13 @@ void SpellMgr::LoadPetLevelupSpellMap()
// (!creatureFamily->skillLine[1] || skillLine->skillId != creatureFamily->skillLine[1]))
// continue;
if (skillLine->skillId != creatureFamily->skillLine[j])
if (skillLine->SkillLine != creatureFamily->skillLine[j])
continue;
if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL)
if (skillLine->AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
continue;
SpellInfo const* spell = GetSpellInfo(skillLine->spellId);
SpellInfo const* spell = GetSpellInfo(skillLine->Spell);
if (!spell) // not exist or triggered or talent
continue;

View File

@@ -118,7 +118,7 @@ public:
if (!entry)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry->spellId);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry->Spell);
if (!spellInfo)
continue;
@@ -290,7 +290,8 @@ public:
if (!handler->extractPlayerTarget((char*)args, &target))
return false;
target->learnDefaultSpells();
target->LearnDefaultSkills();
target->LearnCustomSpells();
target->learnQuestRewardedSpells();
handler->PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST, handler->GetNameLink(target).c_str());
@@ -404,26 +405,26 @@ public:
continue;
// wrong skill
if (skillLine->skillId != skillId)
if (skillLine->SkillLine != skillId)
continue;
// not high rank
if (skillLine->forward_spellid)
if (skillLine->SupercededBySpell)
continue;
// skip racial skills
if (skillLine->racemask != 0)
if (skillLine->RaceMask != 0)
continue;
// skip wrong class skills
if (skillLine->classmask && (skillLine->classmask & classmask) == 0)
if (skillLine->ClassMask && (skillLine->ClassMask & classmask) == 0)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(skillLine->spellId);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(skillLine->Spell);
if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo))
continue;
player->learnSpell(skillLine->spellId);
player->learnSpell(skillLine->Spell);
}
}

View File

@@ -310,8 +310,8 @@ enum MapFlags
enum AbilytyLearnType
{
ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1,
ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2
SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE = 1, // Spell state will update depending on skill value
SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN = 2 // Spell will be learned/removed together with entire skill
};
enum ItemEnchantmentType
@@ -333,6 +333,16 @@ enum ItemLimitCategoryMode
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
{
SPELL_CATEGORY_FLAG_COOLDOWN_SCALES_WITH_WEAPON_SPEED = 0x01, // unused

View File

@@ -1467,22 +1467,19 @@ struct ScalingStatValuesEntry
// uint32 displayOrder; // 19 m_sortIndex
//};
//struct SkillRaceClassInfoEntry{
// uint32 id; // 0 m_ID
// uint32 skillId; // 1 m_skillID
// uint32 raceMask; // 2 m_raceMask
// uint32 classMask; // 3 m_classMask
// uint32 flags; // 4 m_flags
// uint32 reqLevel; // 5 m_minLevel
// uint32 skillTierId; // 6 m_skillTierID
// uint32 skillCostID; // 7 m_skillCostIndex
//};
struct SkillRaceClassInfoEntry
{
//uint32 ID; // 0
uint32 SkillID; // 1
uint32 RaceMask; // 2
uint32 ClassMask; // 3
uint32 Flags; // 4
//uint32 MinLevel; // 5
uint32 SkillTierID; // 6
//uint32 SkillCostIndex; // 7
};
//struct SkillTiersEntry{
// uint32 id; // 0 m_ID
// uint32 skillValue[16]; // 1-17 m_cost
// uint32 maxSkillValue[16]; // 18-32 m_valueMax
//};
#define MAX_SKILL_STEP 16
struct SkillLineEntry
{
@@ -1501,19 +1498,26 @@ struct SkillLineEntry
struct SkillLineAbilityEntry
{
uint32 id; // 0 m_ID
uint32 skillId; // 1 m_skillLine
uint32 spellId; // 2 m_spell
uint32 racemask; // 3 m_raceMask
uint32 classmask; // 4 m_classMask
//uint32 racemaskNot; // 5 m_excludeRace
//uint32 classmaskNot; // 6 m_excludeClass
uint32 req_skill_value; // 7 m_minSkillLineRank
uint32 forward_spellid; // 8 m_supercededBySpell
uint32 learnOnGetSkill; // 9 m_acquireMethod
uint32 max_value; // 10 m_trivialSkillLineRankHigh
uint32 min_value; // 11 m_trivialSkillLineRankLow
//uint32 characterPoints[2]; // 12-13 m_characterPoints[2]
uint32 ID; // 0
uint32 SkillLine; // 1
uint32 Spell; // 2
uint32 RaceMask; // 3
uint32 ClassMask; // 4
//uint32 ExcludeRace; // 5
//uint32 ExcludeClass; // 6
uint32 MinSkillLineRank; // 7
uint32 SupercededBySpell; // 8
uint32 AcquireMethod; // 9
uint32 TrivialSkillLineRankHigh; // 10
uint32 TrivialSkillLineRankLow; // 11
//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

View File

@@ -82,6 +82,8 @@ char constexpr ScalingStatDistributionfmt[] = "niiiiiiiiiiiiiiiiiiiii";
char constexpr ScalingStatValuesfmt[] = "iniiiiiiiiiiiiiiiiiiiiii";
char constexpr SkillLinefmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi";
char constexpr SkillLineAbilityfmt[] = "niiiixxiiiiixx";
char constexpr SkillRaceClassInfofmt[] = "diiiixix";
char constexpr SkillTiersfmt[] = "nxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiii";
char constexpr SoundEntriesfmt[] = "nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
char constexpr SpellCastTimefmt[] = "nixx";
char constexpr SpellCategoryfmt[] = "ni";