diff --git a/data/sql/updates/pending_db_world/rev_1621752432246342200.sql b/data/sql/updates/pending_db_world/rev_1621752432246342200.sql new file mode 100644 index 000000000..f42133b48 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1621752432246342200.sql @@ -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'); diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index f50bf9aec..2789eac77 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -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; diff --git a/src/server/game/Chat/ChatLink.cpp b/src/server/game/Chat/ChatLink.cpp index 167dbd1ae..8c6ea96ed 100644 --- a/src/server/game/Chat/ChatLink.cpp +++ b/src/server/game/Chat/ChatLink.cpp @@ -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; } diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 645041c2b..a624a926e 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -123,6 +123,9 @@ DBCStorage sScalingStatValuesStore(ScalingStatValuesfmt DBCStorage sSkillLineStore(SkillLinefmt); DBCStorage sSkillLineAbilityStore(SkillLineAbilityfmt); +DBCStorage sSkillRaceClassInfoStore(SkillRaceClassInfofmt); +SkillRaceClassInfoMap SkillRaceClassInfoBySkill; +DBCStorage sSkillTiersStore(SkillTiersfmt); DBCStorage 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; +} diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index b489cdfe1..67d574ce4 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -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 SkillRaceClassInfoMap; +typedef std::pair SkillRaceClassInfoBounds; +SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_); + extern DBCStorage sAchievementStore; extern DBCStorage sAchievementCriteriaStore; extern DBCStorage sAchievementCategoryStore; @@ -129,6 +133,7 @@ extern DBCStorage sScalingStatDistributionStore; extern DBCStorage sScalingStatValuesStore; extern DBCStorage sSkillLineStore; extern DBCStorage sSkillLineAbilityStore; +extern DBCStorage sSkillTiersStore; extern DBCStorage sSoundEntriesStore; extern DBCStorage sSpellCastTimesStore; extern DBCStorage sSpellCategoryStore; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a84db68cf..6ae77c6b8 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -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({ 1, uint16((getLevel() - 1) * 5) }), maxValue); + } + else if (skillId == SKILL_FIST_WEAPONS) + { + skillValue = std::max(1, GetSkillValue(SKILL_UNARMED)); + } + else if (skillId == SKILL_LOCKPICKING) + { + skillValue = std::max(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(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(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 diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 916bb8089..ca777c749 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -282,6 +282,14 @@ struct PlayerCreateInfoAction typedef std::list PlayerCreateInfoActions; +struct PlayerCreateInfoSkill +{ + uint16 SkillId; + uint16 Rank; +}; + +typedef std::list 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); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 0b323f08a..33d3b088d 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -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()) diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index ceae6dec8..c297d2a8f 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -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() diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index c852640b2..95a576171 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -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) diff --git a/src/server/game/Skills/SkillDiscovery.cpp b/src/server/game/Skills/SkillDiscovery.cpp index 8103fa7f4..f995af2ed 100644 --- a/src/server/game/Skills/SkillDiscovery.cpp +++ b/src/server/game/Skills/SkillDiscovery.cpp @@ -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) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 251cb7330..dd68f297f 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -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; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 69b092d52..d67692682 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -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; diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp index 65c4323c0..93b229d52 100644 --- a/src/server/scripts/Commands/cs_learn.cpp +++ b/src/server/scripts/Commands/cs_learn.cpp @@ -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); } } diff --git a/src/server/shared/DataStores/DBCEnums.h b/src/server/shared/DataStores/DBCEnums.h index 62b829e8d..675095b22 100644 --- a/src/server/shared/DataStores/DBCEnums.h +++ b/src/server/shared/DataStores/DBCEnums.h @@ -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 diff --git a/src/server/shared/DataStores/DBCStructure.h b/src/server/shared/DataStores/DBCStructure.h index e64742774..62a047225 100644 --- a/src/server/shared/DataStores/DBCStructure.h +++ b/src/server/shared/DataStores/DBCStructure.h @@ -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 diff --git a/src/server/shared/DataStores/DBCfmt.h b/src/server/shared/DataStores/DBCfmt.h index f24f54f4a..71f685f96 100644 --- a/src/server/shared/DataStores/DBCfmt.h +++ b/src/server/shared/DataStores/DBCfmt.h @@ -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";