diff options
author | Rat <gmstreetrat@gmail.com> | 2014-12-04 19:23:35 +0100 |
---|---|---|
committer | Rat <gmstreetrat@gmail.com> | 2014-12-04 19:23:35 +0100 |
commit | 08f486bfcf77d007aeb324b5c941433545234b0e (patch) | |
tree | d5f4ae067e15e8800c097d0da40f31339e147e8d /src | |
parent | df2514f0444a1b39f07ed63208f5db3ea4eb5c6d (diff) |
Core/Spells: some updates to player spell and talent learning
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/DataStores/DBCStores.cpp | 15 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 551 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 93 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 81 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 2 | ||||
-rw-r--r-- | src/server/game/Handlers/SkillHandler.cpp | 57 | ||||
-rw-r--r-- | src/server/game/Handlers/SpellHandler.cpp | 23 | ||||
-rw-r--r-- | src/server/game/Server/Packets/SpellPackets.cpp | 11 | ||||
-rw-r--r-- | src/server/game/Server/Packets/SpellPackets.h | 10 | ||||
-rw-r--r-- | src/server/game/Server/Packets/TalentPackets.cpp | 14 | ||||
-rw-r--r-- | src/server/game/Server/Packets/TalentPackets.h | 13 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.h | 4 | ||||
-rw-r--r-- | src/server/game/Server/WorldSession.h | 3 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_learn.cpp | 4 |
17 files changed, 532 insertions, 357 deletions
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index cc85b56615b..f23dd49102b 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -518,7 +518,10 @@ void LoadDBCStores(const std::string& dataPath) SpecializationSpellsEntry const* specSpells = sSpecializationSpellsStore.LookupEntry(i); if (!specSpells) continue; - sSpecializationSpellsBySpecStore[specSpells->SpecID].insert(specSpells); + sSpecializationSpellsBySpecStore[specSpells->SpecID].push_back(specSpells); + + if (specSpells->OverridesSpellID) + sSpecializationOverrideSpellMap[specSpells->SpecID][specSpells->OverridesSpellID] = specSpells->SpellID; } LoadDBC(availableDbcLocales, bad_dbc_files, sSpellStore, dbcPath, "Spell.dbc"/*, &CustomSpellEntryfmt, &CustomSpellEntryIndex*/); LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCategoriesStore, dbcPath, "SpellCategories.dbc");//15595 @@ -1256,16 +1259,16 @@ std::list<uint32> GetSpellsForLevels(uint32 classId, uint32 raceMask, uint32 spe if (!specializationId) return spellList; - SpecializationSpellsMap::const_iterator specIter = sSpecializationSpellsMap.find(specializationId); - if (specIter != sSpecializationSpellsMap.end()) + SpecializationSpellsBySpecStore::const_iterator specIter = sSpecializationSpellsBySpecStore.find(specializationId); + if (specIter != sSpecializationSpellsBySpecStore.end()) { - const std::vector<uint32>& learnSpellList = specIter->second; + SpecializationSpellsBySpecEntry learnSpellList = specIter->second; for (int i = 0; i < learnSpellList.size(); i++) { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(learnSpellList[i]); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(learnSpellList[i]->SpellID); if (!spellInfo) { - TC_LOG_ERROR("spells", "GetSpellsForLevels: spell %u not found in spellstore", learnSpellList[i]); + TC_LOG_ERROR("spells", "GetSpellsForLevels: spell %u not found in spellstore", learnSpellList[i]->SpellID); continue; } if (spellInfo->SpellLevel <= minLevel || spellInfo->SpellLevel > maxLevel) diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index a6ecadfaf45..633376d85aa 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -93,7 +93,7 @@ typedef std::unordered_multimap<uint32, SkillRaceClassInfoEntry const*> SkillRac typedef std::pair<SkillRaceClassInfoMap::iterator, SkillRaceClassInfoMap::iterator> SkillRaceClassInfoBounds; SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_); -typedef std::set<SpecializationSpellsEntry const*> SpecializationSpellsBySpecEntry; +typedef std::vector<SpecializationSpellsEntry const*> SpecializationSpellsBySpecEntry; typedef std::unordered_map<uint32, SpecializationSpellsBySpecEntry> SpecializationSpellsBySpecStore; typedef ChrSpecializationEntry const* ChrSpecializationByIndexArray[MAX_CLASSES][MAX_SPECIALIZATIONS]; typedef std::unordered_map<uint32, TalentEntry const*> TalentBySpellIDMap; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index d18f8542f16..740c96753ab 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1122,7 +1122,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac } // original spells - LearnDefaultSpells(); + LearnDefaultSkills(); LearnCustomSpells(); // original action bar @@ -2985,11 +2985,39 @@ void Player::GiveLevel(uint8 level) SetByteFlag(PLAYER_FIELD_BYTES, 1, 0x01); } + std::list<uint32> learnList = GetSpellsForLevels(getClass(), getRaceMask(), GetTalentSpec(GetActiveTalentGroup()), oldLevel, level); + for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++) + { + if (!HasSpell(*iter)) + LearnSpell(*iter, true); + } + sScriptMgr->OnPlayerLevelChanged(this, oldLevel); } void Player::InitTalentForLevel() { + uint8 level = getLevel(); + // talents base at level diff (talents = level - 9 but some can be used already) + if (level < 15) + { + // Remove all talent points + if (GetUsedTalentCount() > 0) // Free any used talents + { + ResetTalents(true); + } + } + else + { + if (level < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL) || GetTalentGroupsCount() == 0) + { + SetTalentGroupsCount(1); + SetActiveTalentGroup(0); + } + } + + SetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS, CalculateTalentsPoints()); + if (!GetSession()->PlayerLoading()) SendTalentsInfoData(); // update at client } @@ -3267,7 +3295,7 @@ void DeleteSpellFromAllPlayers(uint32 spellId) } } -bool Player::AddTalent(uint32 talentId, uint8 spec) +bool Player::AddTalent(uint32 talentId, uint8 spec, bool learning) { TalentEntry const* talentEntry = sTalentStore.LookupEntry(talentId); @@ -3309,15 +3337,28 @@ bool Player::AddTalent(uint32 talentId, uint8 spec) return false; } - TalentGroupInfo* talentGroupInfo = GetTalentGroupInfo(spec); + PlayerTalentMap::iterator itr = GetTalentMap(spec)->find(talentId); + if (itr == GetTalentMap(spec)->end()) + { + //if (GetTalentBySpellID(talentEntry->SpellID)) + { + PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED; + PlayerTalent* newtalent = new PlayerTalent(); - // Check if player already has this talent - if (talentGroupInfo->HasTalent(talentId)) - return false; + newtalent->state = state; + newtalent->spec = spec; - talentGroupInfo->Talents[talentEntry->TierID] = talentId; + (*GetTalentMap(spec))[talentId] = newtalent; - return true; + return true; + } + //else + // TC_LOG_ERROR("spells", "Player::addTalent: Talent %u not found in talent store.", talentId); + } + else + itr->second->state = PLAYERSPELL_UNCHANGED; + + return false; } bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading /*= false*/, bool fromSkill /*= false*/) @@ -3421,9 +3462,9 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent SendSupercededSpell(spellId, next_active_spell_id); else { - WorldPacket data(SMSG_REMOVED_SPELL, 4); - data << uint32(spellId); - GetSession()->SendPacket(&data); + WorldPackets::Spells::SendRemovedSpell removedSpells; + removedSpells.Spells.push_back(spellId); + GetSession()->SendPacket(removedSpells.Write()); } } @@ -3594,11 +3635,15 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent if (HasSkill(pSkill->ID)) continue; + SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pSkill->ID, getRace(), getClass()); + if (!rcEntry) + continue; + if (_spell_idx->second->AquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN || // lockpicking/runeforging special case, not have SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN ((pSkill->ID == SKILL_LOCKPICKING || pSkill->ID == SKILL_RUNEFORGING) && (_spell_idx->second->TrivialSkillLineRankHigh == 0 || _spell_idx->second->TrivialSkillLineRankHigh == 1))) { - switch (GetSkillRangeType(pSkill, _spell_idx->second->RaceMask!= 0)) + switch (GetSkillRangeType(rcEntry)) { case SKILL_RANGE_LANGUAGE: SetSkill(pSkill->ID, GetSkillStep(pSkill->ID), 300, 300); @@ -3690,6 +3735,7 @@ bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const bool Player::IsCurrentSpecMasterySpell(SpellInfo const* spellInfo) const { + if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec())) return spellInfo->Id == chrSpec->MasterySpellID[0] || spellInfo->Id == chrSpec->MasterySpellID[1]; @@ -3893,9 +3939,9 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank) // remove from spell book if not replaced by lesser rank if (!prev_activate) { - WorldPacket data(SMSG_REMOVED_SPELL, 4); - data << uint32(spell_id); - GetSession()->SendPacket(&data); + WorldPackets::Spells::SendRemovedSpell removedSpells; + removedSpells.Spells.push_back(spell_id); + GetSession()->SendPacket(removedSpells.Write()); } } @@ -4080,17 +4126,20 @@ uint32 Player::GetNextResetTalentsCost() const } } -bool Player::ResetTalents(bool no_cost) +bool Player::ResetTalents(bool noCost, bool resetTalents, bool resetSpecialization) { - sScriptMgr->OnPlayerTalentsReset(this, no_cost); + if (!resetTalents && !resetSpecialization) + return false; + sScriptMgr->OnPlayerTalentsReset(this, noCost); + // not need after this call if (HasAtLoginFlag(AT_LOGIN_RESET_TALENTS)) RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS, true); uint32 cost = 0; - if (!no_cost && !sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST)) + if (!noCost && !sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST)) { cost = GetNextResetTalentsCost(); @@ -4103,66 +4152,54 @@ bool Player::ResetTalents(bool no_cost) RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true); - uint8 group = GetActiveTalentGroup(); - uint32 specID = GetActiveTalentSpec(); - - for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) + if (resetTalents) { - TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); + for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) + { + TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); - if (!talentInfo) - continue; + if (!talentInfo) + continue; - // unlearn only talents for character class - // some spell learned by one class as normal spells or know at creation but another class learn it as talent, - // to prevent unexpected lost normal learned spell skip another class talents - if (getClass() != talentInfo->ClassID) - continue; + // unlearn only talents for character class + // some spell learned by one class as normal spells or know at creation but another class learn it as talent, + // to prevent unexpected lost normal learned spell skip another class talents + if (talentInfo->ClassID != getClass()) + continue; - const SpellInfo* _spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID); + SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID); + if (!spellEntry) + continue; - if (!_spellEntry) - continue; + RemoveSpell(spellEntry->Id, false); - RemoveSpell(talentInfo->SpellID, true); + // search for spells that the talent teaches and unlearn them, 6.x remove? + for (SpellEffectInfo const* effect : spellEntry->GetEffectsForDifficulty(DIFFICULTY_NONE)) + if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL) + RemoveSpell(effect->TriggerSpell, true); - // search for spells that the talent teaches and unlearn them - for (SpellEffectInfo const* effect : _spellEntry->GetEffectsForDifficulty(DIFFICULTY_NONE)) - if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL) - RemoveSpell(effect->TriggerSpell, true); + GetTalentMap(GetActiveTalentGroup())->erase(talentId); + } } - // Remove all specialization specific spells and give default ones which were overriden - auto specSpells = sSpecializationSpellsBySpecStore.find(specID); - if (specSpells != sSpecializationSpellsBySpecStore.end()) + if (resetSpecialization) { - for (auto it = specSpells->second.begin(); it != specSpells->second.end(); ++it) + std::list<uint32> learnList = GetSpellsForLevels(0, getRaceMask(), GetTalentSpec(GetActiveTalentGroup()), 0, getLevel()); + for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++) { - SpecializationSpellsEntry const* specSpell = *it; - if (HasSpell(specSpell->SpellID)) { - RemoveSpell(specSpell->SpellID, true); - if (specSpell->OverridesSpellID) - LearnSpell(specSpell->OverridesSpellID, false); - } + if (HasSpell(*iter)) + RemoveSpell(*iter, true); } + SetTalentSpec(GetActiveTalentGroup(), 0); + SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, 0); } - // Unlearn masteries - if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(specID)) - for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i) - if (uint32 mastery = chrSpec->MasterySpellID[i]) - RemoveAurasDueToSpell(mastery); - - // Reset talents store - GetTalentGroupInfo(group)->Reset(); - SetTalentSpec(group, 0); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); _SaveTalents(trans); _SaveSpells(trans); CharacterDatabase.CommitTransaction(trans); - if (!no_cost) + if (!noCost) { ModifyMoney(-(int64)cost); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost); @@ -4183,6 +4220,35 @@ bool Player::ResetTalents(bool no_cost) return true; } +bool Player::RemoveTalent(uint32 talentId) +{ + TalentEntry const* talent = sTalentStore.LookupEntry(talentId); + if (!talent) + return false; + + uint32 spellId = talent->SpellID; + + SpellInfo const* unlearnSpellProto = sSpellMgr->GetSpellInfo(spellId); + + RemoveSpell(spellId, false); + + // 6.x remove? + for (SpellEffectInfo const* effect : unlearnSpellProto->GetEffectsForDifficulty(DIFFICULTY_NONE)) + if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL) + RemoveSpell(effect->TriggerSpell, false); + + GetTalentMap(GetActiveTalentSpec())->erase(talentId); + + // Needs to be executed orthewise the talents will be screwedsx + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + _SaveTalents(trans); + _SaveSpells(trans); + CharacterDatabase.CommitTransaction(trans); + + SendTalentsInfoData(); + return true; +} + Mail* Player::GetMail(uint32 id) { for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr) @@ -4249,7 +4315,8 @@ bool Player::HasSpell(uint32 spell) const bool Player::HasTalent(uint32 talentId, uint8 group) { - return GetTalentGroupInfo(group)->HasTalent(talentId); + PlayerTalentMap::const_iterator itr = GetTalentMap(group)->find(talentId); + return (itr != GetTalentMap(group)->end()); } bool Player::HasActiveSpell(uint32 spell) const @@ -5892,14 +5959,14 @@ 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; - if (IsWeaponSkill(pSkill->ID)) + if (IsWeaponSkill(rcEntry->SkillID)) continue; uint16 field = itr->second.pos / 2; @@ -17415,11 +17482,16 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) break; uint32 talentSpec = atoul(talentSpecs[i]); - if (sChrSpecializationStore.LookupEntry(talentSpec)) - SetTalentSpec(i, talentSpec); - else - SetAtLoginFlag(AT_LOGIN_RESET_TALENTS); + if (talentSpec) + { + if (sChrSpecializationStore.LookupEntry(talentSpec)) + SetTalentSpec(i, talentSpec); + else + SetAtLoginFlag(AT_LOGIN_RESET_TALENTS); + } } + + SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetActiveTalentSpec()); _LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS)); _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS)); @@ -17442,7 +17514,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) // after spell and quest load InitTalentForLevel(); - LearnDefaultSpells(); + LearnDefaultSkills(); LearnCustomSpells(); // must be before inventory (some items required reputation check) @@ -18486,6 +18558,13 @@ void Player::_LoadSpells(PreparedQueryResult result) AddSpell((*result)[0].GetUInt32(), (*result)[1].GetBool(), false, false, (*result)[2].GetBool(), true); while (result->NextRow()); } + + std::list<uint32> learnList = GetSpellsForLevels(getClass(), getRaceMask(), GetActiveTalentSpec(), 0, getLevel()); + for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++) + { + if (!HasSpell(*iter)) + LearnSpell(*iter, true); + } } void Player::_LoadGroup(PreparedQueryResult result) @@ -23243,8 +23322,8 @@ void Player::ResetSpells(bool myClassOnly) else for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter) RemoveSpell(iter->first, false, false); // only iter->first can be accessed, object by iter->second can be deleted already - - LearnDefaultSpells(); + + LearnDefaultSkills(); LearnCustomSpells(); LearnQuestRewardedSpells(); } @@ -23267,21 +23346,72 @@ void Player::LearnCustomSpells() } } -void Player::LearnDefaultSpells() +void Player::LearnDefaultSkills() { - // learn default race/class spells + // learn default race/class skills PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass()); - for (PlayerCreateInfoSpells::const_iterator itr = info->spells.begin(); itr != info->spells.end(); ++itr) + for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr) { - uint32 tspell = *itr; - TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell); - if (!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add - AddSpell(tspell, true, true, true, false); - else // but send in normal spell in game learn case - LearnSpell(tspell, true); + 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; + + switch (GetSkillRangeType(rcInfo)) + { + case SKILL_RANGE_LANGUAGE: + SetSkill(skillId, 0, 300, 300); + break; + case SKILL_RANGE_LEVEL: + { + uint16 skillValue = 1; + uint16 maxValue = GetMaxSkillValueForLevel(); + if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE) + skillValue = maxValue; + else if (getClass() == CLASS_DEATH_KNIGHT) + skillValue = std::min(std::max<uint16>({ 1, uint16((getLevel() - 1) * 5) }), maxValue); + else if (skillId == SKILL_FIST_WEAPONS) + skillValue = std::max<uint16>(1, GetSkillValue(SKILL_UNARMED)); + else if (skillId == SKILL_LOCKPICKING) + skillValue = std::max<uint16>(1, GetSkillValue(SKILL_LOCKPICKING)); + + SetSkill(skillId, 0, skillValue, maxValue); + break; + } + case SKILL_RANGE_MONO: + SetSkill(skillId, 0, 1, 1); + break; + case SKILL_RANGE_RANK: + { + if (!rank) + break; + + SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcInfo->SkillTierID); + uint16 maxValue = tier->Value[std::max<int32>(rank - 1, 0)]; + uint16 skillValue = 1; + if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE) + skillValue = maxValue; + else if (getClass() == CLASS_DEATH_KNIGHT) + skillValue = std::min(std::max<uint16>({ uint16(1), uint16((getLevel() - 1) * 5) }), maxValue); + + SetSkill(skillId, rank, skillValue, maxValue); + break; + } + default: + break; } } + void Player::LearnQuestRewardedSpells(Quest const* quest) { int32 spell_id = quest->GetRewSpellCast(); @@ -23396,31 +23526,41 @@ void Player::LearnQuestRewardedSpells() void Player::LearnSkillRewardedSpells(uint32 skillId, uint32 skillValue) { - uint32 raceMask = getRaceMask(); + uint32 raceMask = getRaceMask(); uint32 classMask = getClassMask(); for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) { - SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j); - if (!pAbility || pAbility->SkillLine != skillId || pAbility->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE) + SkillLineAbilityEntry const* ability = sSkillLineAbilityStore.LookupEntry(j); + if (!ability || ability->SkillLine != skillId) + continue; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(ability->SpellID); + if (!spellInfo) + continue; + + if (ability->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE && ability->AquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN) continue; + // Check race if set - if (pAbility->RaceMask && !(pAbility->RaceMask & raceMask)) + if (ability->RaceMask && !(ability->RaceMask & raceMask)) continue; + // Check class if set - if (pAbility->ClassMask && !(pAbility->ClassMask & classMask)) + if (ability->ClassMask && !(ability->ClassMask & classMask)) continue; - if (sSpellMgr->GetSpellInfo(pAbility->SpellID)) - { - // need unlearn spell - if (skillValue < pAbility->MinSkillLineRank) - RemoveSpell(pAbility->SpellID); - // need learn - else if (!IsInWorld()) - AddSpell(pAbility->SpellID, true, true, true, false); - else - LearnSpell(pAbility->SpellID, true); - } + // check level, skip class spells if not high enough + if (getLevel() < spellInfo->SpellLevel) + continue; + + // need unlearn spell + if (skillValue < ability->MinSkillLineRank && ability->AquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE) + RemoveSpell(ability->SpellID); + // need learn + else if (!IsInWorld()) + AddSpell(ability->SpellID, true, true, true, false, false, true); + else + LearnSpell(ability->SpellID, true, true); } } @@ -25135,8 +25275,8 @@ void Player::_LoadSkills(PreparedQueryResult result) uint16 value = fields[1].GetUInt16(); uint16 max = fields[2].GetUInt16(); - SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skill); - if (!skillLine) + SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skill, getRace(), getClass()); + if (!rcEntry) { TC_LOG_ERROR("entities.player", "Character: %s (%s Race: %u Class: %u) has skill %u not allowed for his race/class combination", GetName().c_str(), GetGUID().ToString().c_str(), uint32(getRace()), uint32(getClass()), skill); @@ -25146,7 +25286,7 @@ void Player::_LoadSkills(PreparedQueryResult result) } // set fixed skill ranges - switch (GetSkillRangeType(skillLine, false)) + switch (GetSkillRangeType(rcEntry)) { case SKILL_RANGE_LANGUAGE: // 300..300 value = max = 300; @@ -25180,15 +25320,19 @@ void Player::_LoadSkills(PreparedQueryResult result) SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_ID_OFFSET + field, offset, skill); uint16 step = 0; - if (skillLine->CategoryID == SKILL_CATEGORY_SECONDARY) - step = max / 75; - - if (skillLine->CategoryID == SKILL_CATEGORY_PROFESSION) + SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(rcEntry->SkillID); + if (skillLine) { - step = max / 75; + if (skillLine->CategoryID == SKILL_CATEGORY_SECONDARY) + step = max / 75; + + if (skillLine->CategoryID == SKILL_CATEGORY_PROFESSION) + { + step = max / 75; - if (professionCount < 2) - SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + professionCount++, skill); + if (professionCount < 2) + SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + professionCount++, skill); + } } SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_STEP_OFFSET + field, offset, step); @@ -25407,37 +25551,36 @@ void Player::CompletedAchievement(AchievementEntry const* entry) bool Player::LearnTalent(uint32 talentId) { - uint8 group = GetActiveTalentGroup(); - - // check if talent specialization is learnt - if (!GetTalentSpec(group)) - return false; - TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); if (!talentInfo) return false; + + uint32 maxTalentTier = GetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS); // prevent learn talent for different class (cheating) - if (getClass() != talentInfo->ClassID) + if (talentInfo->ClassID != getClass()) return false; - // Check player level - // TODO: fix level requirements for deathknights - uint8 levelReq = std::min(15*talentInfo->TierID + 15, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)); - if (getLevel() < levelReq) + // check if we have enough talent points + if (talentInfo->TierID > maxTalentTier) return false; - // Check if such tier talent hasn't been picked already - TalentGroupInfo* talentGroupInfo = GetTalentGroupInfo(group); - if (talentGroupInfo->Talents[talentInfo->TierID]) - return false; + // Check if player doesnt have any spell in selected collumn + for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++) + { + if (TalentEntry const* talent = sTalentStore.LookupEntry(i)) + { + if (talentInfo->TierID == talent->TierID && HasSpell(talent->SpellID)) + return false; + } + } // spell not set in talent.dbc uint32 spellid = talentInfo->SpellID; if (spellid == 0) { - TC_LOG_ERROR("entities.player", "Talent.dbc have for talent: %u spell id = 0", talentId); + TC_LOG_ERROR("entities.player", "Talent.dbc has no spellInfo for talent: %u (spell id = 0)", talentId); return false; } @@ -25445,54 +25588,39 @@ bool Player::LearnTalent(uint32 talentId) if (HasSpell(spellid)) return false; - // Check talent spec - if (talentInfo->SpecID != GetTalentSpec(group)) + if (!AddTalent(talentId, GetActiveTalentGroup(), true)) return false; - // learn! (other talent ranks will unlearned at learning) LearnSpell(spellid, false); - AddTalent(talentId, group); - TC_LOG_INFO("misc", "TalentID: %u Spell: %u Group: %u\n", talentId, spellid, group); + TC_LOG_INFO("misc", "TalentID: %u Spell: %u Group: %u\n", talentId, spellid, GetActiveTalentGroup()); return true; } void Player::LearnTalentSpecialization(uint32 talentSpec) { - SetTalentSpec(GetActiveTalentGroup(), talentSpec); - - // Replace default spells by specialization spells - auto specSpells = sSpecializationSpellsBySpecStore.find(talentSpec); - if (specSpells != sSpecializationSpellsBySpecStore.end()) - { - for (auto it = specSpells->second.begin(); it != specSpells->second.end(); ++it) - { - SpecializationSpellsEntry const* specSpell = *it; + if (GetActiveTalentSpec()) + return; - // Unlearn spell if it is replaced by new specialization - if (specSpell->OverridesSpellID) - RemoveSpell(specSpell->OverridesSpellID, true); + SetTalentSpec(GetActiveTalentGroup(), talentSpec); - // Learn new spell - if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(specSpell->SpellID)) - if (spellInfo->BaseLevel <= getLevel()) - LearnSpell(specSpell->SpellID, false); - } - } + SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, talentSpec); + SendTalentsInfoData(); - if (CanUseMastery()) + std::list<uint32> learnList = GetSpellsForLevels(0, getRaceMask(), GetActiveTalentSpec(), 0, getLevel()); + for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++) { - ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(talentSpec); - for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i) - if (SpellInfo const* masterySpell = sSpellMgr->GetSpellInfo(chrSpec->MasterySpellID[i])) - if (masterySpell->IsPassive() && IsNeedCastPassiveSpellAtLearn(masterySpell)) - CastSpell(this, masterySpell->Id, true); + if (!HasSpell(*iter)) + LearnSpell(*iter, true); } + SaveToDB(); + SendTalentsInfoData(); } + void Player::AddKnownCurrency(uint32 itemId) { if (CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId)) @@ -25577,23 +25705,39 @@ void Player::SendTalentsInfoData() for (uint8 i = 0; i < groupsCount; ++i) { - TalentGroupInfo* groupInfo = GetTalentGroupInfo(i); WorldPackets::Talent::TalentGroupInfo groupInfoPkt; - groupInfoPkt.SpecID = groupInfo->SpecID; + groupInfoPkt.SpecID = GetTalentSpec(i); + + groupInfoPkt.TalentIDs.reserve(GetTalentMap(i)->size()); - groupInfoPkt.TalentIDs.reserve(MAX_TALENT_TIERS); - for (uint32 x = 0; x < MAX_TALENT_TIERS; ++x) + for (PlayerTalentMap::const_iterator itr = GetTalentMap(i)->begin(); itr != GetTalentMap(i)->end(); ++itr) { - // Do not send empty talents - if (!groupInfo->Talents[x]) - break; + TalentEntry const* talentInfo = sTalentStore.LookupEntry(itr->first); + if (!talentInfo) + { + TC_LOG_ERROR("entities.player", "Player %s has unknown talent id: %u", GetName().c_str(), itr->first); + continue; + } - groupInfoPkt.TalentIDs.push_back(groupInfo->Talents[x]); + if (talentInfo->ClassID != getClass()) + continue; + + SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID); + if (!spellEntry) + { + TC_LOG_ERROR("entities.player", "Player %s has unknown talent spell: %u", GetName().c_str(), talentInfo->SpellID); + continue; + } + + if (!HasTalent(itr->first, i)) + continue; + + groupInfoPkt.TalentIDs.push_back(uint16(itr->first)); } for (uint32 x = 0; x < MAX_GLYPH_SLOT_INDEX; ++x) - groupInfoPkt.GlyphIDs[x] = groupInfo->Glyphs[x]; + groupInfoPkt.GlyphIDs[x] = uint16(GetGlyph(i, x)); packet.Info.TalentGroups.push_back(groupInfoPkt); } @@ -25905,7 +26049,7 @@ void Player::_LoadTalents(PreparedQueryResult result) if (result) { do - AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt8()); + AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt8(), false); while (result->NextRow()); } } @@ -25918,17 +26062,12 @@ void Player::_SaveTalents(SQLTransaction& trans) for (uint8 group = 0; group < MAX_TALENT_GROUPS; ++group) { - TalentGroupInfo* talentGroupInfo = GetTalentGroupInfo(group); - - for (uint32 tier = 0; tier < MAX_TALENT_TIERS; ++tier) + for (PlayerTalentMap::iterator itr = GetTalentMap(group)->begin(); itr != GetTalentMap(group)->end(); ++itr) { - if (!talentGroupInfo->Talents[tier]) - continue; - stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_TALENT); stmt->setUInt64(0, GetGUID().GetCounter()); - stmt->setUInt32(1, talentGroupInfo->Talents[tier]); - stmt->setUInt8(2, group); + stmt->setUInt32(1, itr->first); + stmt->setUInt8(2, itr->second->spec); trans->Append(stmt); } } @@ -26027,30 +26166,27 @@ void Player::ActivateTalentGroup(uint8 group) if (getClass() != talentInfo->ClassID) continue; + SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID); + if (!spellEntry) + continue; + RemoveSpell(talentInfo->SpellID, true); + + // search for spells that the talent teaches and unlearn them + for (SpellEffectInfo const* effect : spellEntry->GetEffectsForDifficulty(DIFFICULTY_NONE)) + if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL) + RemoveSpell(effect->TriggerSpell, true); } // Unlearn specialization specific spells - auto specSpells = sSpecializationSpellsBySpecStore.find(GetActiveTalentSpec()); - if (specSpells != sSpecializationSpellsBySpecStore.end()) + std::list<uint32> learnList = GetSpellsForLevels(0, getRaceMask(), GetActiveTalentSpec(), 0, getLevel()); + for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++) { - for (auto it = specSpells->second.begin(); it != specSpells->second.end(); ++it) - { - SpecializationSpellsEntry const* specSpell = *it; - if (HasSpell(specSpell->SpellID)) { - RemoveSpell(specSpell->SpellID, true); - LearnSpell(specSpell->OverridesSpellID, false); - } - } + if (HasSpell(*iter)) + RemoveSpell(*iter, true); } - // Unlearn mastery spells - ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec()); - for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i) - if (chrSpec->MasterySpellID[i]) - RemoveSpell(chrSpec->MasterySpellID[i], true); - - // set glyphs + // remove glyphs for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) // remove secondary glyph if (uint32 oldglyph = GetGlyph(GetActiveTalentGroup(), slot)) @@ -26060,6 +26196,15 @@ void Player::ActivateTalentGroup(uint8 group) // Activate new group SetActiveTalentGroup(group); + uint32 spentTalents = 0; + + learnList = GetSpellsForLevels(getClass(), getRaceMask(), GetActiveTalentSpec(), 0, getLevel()); + for (std::list<uint32>::const_iterator iter = learnList.begin(); iter != learnList.end(); iter++) + { + if (!HasSpell(*iter)) + LearnSpell(*iter, true); + } + for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) { TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); @@ -26071,31 +26216,12 @@ void Player::ActivateTalentGroup(uint8 group) if (getClass() != talentInfo->ClassID) continue; - LearnSpell(talentInfo->SpellID, false); - } + ++spentTalents; - // Replace default spells with specialization specific spells - auto newSpecSpells = sSpecializationSpellsBySpecStore.find(GetActiveTalentSpec()); - if (newSpecSpells != sSpecializationSpellsBySpecStore.end()) - { - for (auto it = newSpecSpells->second.begin(); it != newSpecSpells->second.end(); ++it) - { - SpecializationSpellsEntry const* specSpell = *it; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(specSpell->SpellID); - if (getLevel() >= spellInfo->BaseLevel) { - if (specSpell->OverridesSpellID) - RemoveSpell(specSpell->OverridesSpellID, true); - LearnSpell(specSpell->SpellID, false); - } - } + if (HasTalent(talentInfo->SpellID, group)) + LearnSpell(talentInfo->SpellID, false); } - if (CanUseMastery()) - if (ChrSpecializationEntry const* newChrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec())) - for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i) - if (newChrSpec->MasterySpellID[i]) - LearnSpell(newChrSpec->MasterySpellID[i], false); - // set glyphs for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) { @@ -26109,6 +26235,7 @@ void Player::ActivateTalentGroup(uint8 group) SetGlyph(slot, glyph); } + SetUsedTalentCount(spentTalents); InitTalentForLevel(); { @@ -26128,7 +26255,9 @@ void Player::ActivateTalentGroup(uint8 group) SetPower(pw, 0); if (!sChrSpecializationStore.LookupEntry(GetActiveTalentSpec())) + { ResetTalents(true); + } } void Player::ResetTimeSync() @@ -27168,3 +27297,9 @@ void Player::SendSupercededSpell(uint32 oldSpell, uint32 newSpell) data << uint32(newSpell) << uint32(oldSpell); GetSession()->SendPacket(&data); } + +uint32 Player::CalculateTalentsPoints() const +{ + // 1 talent point for every 15 levels + return getLevel() >= 100 ? 7 : uint32(floor(getLevel() / 15.f)); +}
\ No newline at end of file diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 2d919159819..6b9e925e28d 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -116,6 +116,12 @@ struct PlayerSpell bool disabled : 1; // first rank has been learned in result talent learn but currently talent unlearned, save max learned ranks }; +struct PlayerTalent +{ + PlayerSpellState state : 8; + uint8 spec : 8; +}; + extern uint32 const MasterySpells[MAX_CLASSES]; enum TalentSpecialization // talent tabs @@ -184,6 +190,7 @@ struct PlayerCurrency uint32 weekCount; }; +typedef std::unordered_map<uint32, PlayerTalent*> PlayerTalentMap; typedef std::unordered_map<uint32, PlayerSpell*> PlayerSpellMap; typedef std::list<SpellModifier*> SpellModList; typedef std::unordered_map<uint32, PlayerCurrency> PlayerCurrenciesMap; @@ -412,9 +419,9 @@ struct PlayerInfo uint16 displayId_f; PlayerCreateInfoItems item; PlayerCreateInfoSpells customSpells; - PlayerCreateInfoSpells spells; PlayerCreateInfoSpells castSpells; PlayerCreateInfoActions action; + PlayerCreateInfoSkills skills; PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1 }; @@ -1223,56 +1230,41 @@ private: bool _isPvP; }; -struct TalentGroupInfo +struct PlayerTalentInfo { - uint32 Talents[MAX_TALENT_TIERS]; - uint32 Glyphs[MAX_GLYPH_SLOT_INDEX]; - uint32 SpecID; - - bool HasTalent(uint32 talentId) - { - for (uint32 i = 0; i < MAX_TALENT_TIERS; ++i) - if (Talents[i] == talentId) - return true; - return false; - } - - uint32 TalentCount() + PlayerTalentInfo() : UsedTalentCount(0), ResetTalentsCost(0), ResetTalentsTime(0), ActiveGroup(0), GroupsCount(1) { - for (uint32 i = 0; i < MAX_TALENT_TIERS; ++i) - if (!Talents[i]) - return i; - return MAX_TALENT_TIERS; + for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) + { + GroupInfo[i].Talents = new PlayerTalentMap(); + memset(GroupInfo[i].Glyphs, 0, MAX_GLYPH_SLOT_INDEX * sizeof(uint32)); + GroupInfo[i].TalentTree = 0; + } } - void Reset() - { - for (uint32 i = 0; i < MAX_TALENT_TIERS; ++i) - Talents[i] = 0; - } -}; - -struct PlayerTalentInfo -{ - PlayerTalentInfo() : ResetTalentsCost(0), ResetTalentsTime(0), ActiveGroup(0), GroupsCount(1) + ~PlayerTalentInfo() { for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) { - memset(GroupInfo[i].Talents, 0, sizeof(uint32)*MAX_TALENT_TIERS); - memset(GroupInfo[i].Glyphs, 0, MAX_GLYPH_SLOT_INDEX * sizeof(uint32)); - GroupInfo[i].SpecID = 0; + for (PlayerTalentMap::const_iterator itr = GroupInfo[i].Talents->begin(); itr != GroupInfo[i].Talents->end(); ++itr) + delete itr->second; + delete GroupInfo[i].Talents; } } - TalentGroupInfo GroupInfo[MAX_TALENT_GROUPS]; + struct TalentGroupInfo + { + PlayerTalentMap* Talents; + uint32 Glyphs[MAX_GLYPH_SLOT_INDEX]; + uint32 TalentTree; + } GroupInfo[MAX_TALENT_GROUPS]; + + uint32 UsedTalentCount; uint32 ResetTalentsCost; time_t ResetTalentsTime; uint8 ActiveGroup; uint8 GroupsCount; - - uint32 UsedTalentCount; - private: PlayerTalentInfo(PlayerTalentInfo const&); }; @@ -1824,7 +1816,8 @@ class Player : public Unit, public GridObject<Player> void RemoveSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true); void ResetSpells(bool myClassOnly = false); void LearnCustomSpells(); - void LearnDefaultSpells(); + void LearnDefaultSkills(); + void LearnDefaultSkill(uint32 skillId, uint16 rank); void LearnQuestRewardedSpells(); void LearnQuestRewardedSpells(Quest const* quest); void LearnSpellHighestRank(uint32 spellid); @@ -1837,26 +1830,33 @@ class Player : public Unit, public GridObject<Player> // Talents uint32 GetUsedTalentCount() const { return _talentMgr->UsedTalentCount; } void SetUsedTalentCount(uint32 talents) { _talentMgr->UsedTalentCount = talents; } - uint32 GetTalentResetCost() const { return _talentMgr->ResetTalentsCost; } void SetTalentResetCost(uint32 cost) { _talentMgr->ResetTalentsCost = cost; } uint32 GetTalentResetTime() const { return _talentMgr->ResetTalentsTime; } void SetTalentResetTime(time_t time_) { _talentMgr->ResetTalentsTime = time_; } - uint8 GetActiveTalentGroup() const { return _talentMgr->ActiveGroup; } - void SetActiveTalentGroup(uint8 group){ _talentMgr->ActiveGroup = group; } + uint8 GetTalentGroupsCount() const { return _talentMgr->GroupsCount; } void SetTalentGroupsCount(uint8 count) { _talentMgr->GroupsCount = count; } - uint32 GetTalentSpec(uint8 group) const { return _talentMgr->GroupInfo[group].SpecID; } - void SetTalentSpec(uint8 group, uint32 talentSpec) const { _talentMgr->GroupInfo[group].SpecID = talentSpec; } - uint32 GetActiveTalentSpec() const { return _talentMgr->GroupInfo[_talentMgr->ActiveGroup].SpecID; } + uint8 GetActiveTalentGroup() const { return _talentMgr->ActiveGroup; } + void SetActiveTalentGroup(uint8 group){ _talentMgr->ActiveGroup = group; } + + uint32 GetTalentSpec(uint8 group) const { return _talentMgr->GroupInfo[group].TalentTree; } + void SetTalentSpec(uint8 group, uint32 talentSpec) const { _talentMgr->GroupInfo[group].TalentTree = talentSpec; } + uint32 GetActiveTalentSpec() const { return _talentMgr->GroupInfo[_talentMgr->ActiveGroup].TalentTree; } + + + bool ResetTalents(bool noCost = false, bool resetTalents = true, bool resetSpecialization = true); + bool RemoveTalent(uint32 talentId); - bool ResetTalents(bool no_cost = false); uint32 GetNextResetTalentsCost() const; void InitTalentForLevel(); void SendTalentsInfoData(); bool LearnTalent(uint32 talentId); - bool AddTalent(uint32 talentId, uint8 spec); + bool AddTalent(uint32 talentId, uint8 spec, bool learning); bool HasTalent(uint32 talentId, uint8 spec); + uint32 CalculateTalentsPoints() const; + + void LearnTalentSpecialization(uint32 talentSpec); // Dual Spec @@ -1870,7 +1870,8 @@ class Player : public Unit, public GridObject<Player> void SetGlyph(uint8 slot, uint32 glyph); uint32 GetGlyph(uint8 group, uint8 slot) const { return _talentMgr->GroupInfo[group].Glyphs[slot]; } - TalentGroupInfo* GetTalentGroupInfo(uint8 group) { return &_talentMgr->GroupInfo[group]; } + PlayerTalentMap const* GetTalentMap(uint8 spec) const { return _talentMgr->GroupInfo[spec].Talents; } + PlayerTalentMap* GetTalentMap(uint8 spec) { return _talentMgr->GroupInfo[spec].Talents; } ActionButtonList const& GetActionButtons() const { return m_actionButtons; } uint32 GetFreePrimaryProfessionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 5fbc6e7632c..e4f12614b33 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -3033,7 +3033,7 @@ Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint3 bp = *(baseAmount + effect->EffectIndex); else bp = effect->BasePoints; - TC_LOG_ERROR("spells", "_TryStackingOrRefreshingExistingAura spell %u effMask %u currIdx %u", newAura->Id, effMask, effect->EffectIndex); + int32* oldBP = const_cast<int32*>(&(foundAura->GetEffect(effect->EffectIndex)->m_baseAmount)); // todo 6.x review GetBaseAmount and GetCastItemGUID in this case *oldBP = bp; } diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 4858800b3d2..ae39c405e26 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -3279,16 +3279,16 @@ void ObjectMgr::LoadPlayerInfo() } } - // Load playercreate spells - TC_LOG_INFO("server.loading", "Loading Player Create Spell Data..."); + // Load playercreate skills + TC_LOG_INFO("server.loading", "Loading Player Create Skill Data..."); { uint32 oldMSTime = getMSTime(); - QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell"); + QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, rank FROM playercreateinfo_skills"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell` is empty."); + TC_LOG_ERROR("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty."); } else { @@ -3299,17 +3299,31 @@ void ObjectMgr::LoadPlayerInfo() Field* fields = result->Fetch(); uint32 raceMask = fields[0].GetUInt32(); uint32 classMask = fields[1].GetUInt32(); - uint32 spellId = fields[2].GetUInt32(); + PlayerCreateInfoSkill skill; + skill.SkillId = fields[2].GetUInt16(); + skill.Rank = fields[3].GetUInt16(); + + if (skill.Rank >= MAX_SKILL_STEP) + { + TC_LOG_ERROR("sql.sql", "Skill rank value %hu set for skill %hu raceMask %u classMask %u is too high, max allowed value is %d", skill.Rank, skill.SkillId, raceMask, classMask, MAX_SKILL_STEP); + continue; + } if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE)) { - TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_spell` table, ignoring.", raceMask); + TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_skills` table, ignoring.", raceMask); continue; } if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE)) { - TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_spell` table, ignoring.", classMask); + TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_skills` table, ignoring.", classMask); + continue; + } + + if (!sSkillLineStore.LookupEntry(skill.SkillId)) + { + TC_LOG_ERROR("sql.sql", "Wrong skill id %u in `playercreateinfo_skills` table, ignoring.", skill.SkillId); continue; } @@ -3321,23 +3335,21 @@ void ObjectMgr::LoadPlayerInfo() { if (classMask == 0 || ((1 << (classIndex - 1)) & classMask)) { + if (!GetSkillRaceClassInfo(skill.SkillId, raceIndex, classIndex)) + continue; + if (PlayerInfo* info = _playerInfo[raceIndex][classIndex]) { - info->spells.push_back(spellId); + info->skills.push_back(skill); ++count; } - // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them. - // Either split the masks per class, or per race, which kind of kills the point yet. - // else if (raceMask != 0 && classMask != 0) - // TC_LOG_ERROR("sql.sql", "Racemask/classmask (%u/%u) combination was found containing an invalid race/class combination (%u/%u) in `%s` (Spell %u), ignoring.", raceMask, classMask, raceIndex, classIndex, tableName.c_str(), spellId); } } } } - } - while (result->NextRow()); + } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u player create skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -7846,34 +7858,27 @@ 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: - return SKILL_RANGE_LEVEL; - case SKILL_CATEGORY_ARMOR: - case SKILL_CATEGORY_CLASS: - if (pSkill->ID != SKILL_LOCKPICKING) - return SKILL_RANGE_MONO; - else - return SKILL_RANGE_LEVEL; - case SKILL_CATEGORY_SECONDARY: - case SKILL_CATEGORY_PROFESSION: - // not set skills for professions and racial abilities - if (IsProfessionSkill(pSkill->ID)) - return SKILL_RANGE_RANK; - else if (racial) - return SKILL_RANGE_NONE; - else - return SKILL_RANGE_MONO; - default: - case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc - case SKILL_CATEGORY_GENERIC: //only GENERIC(DND) - return SKILL_RANGE_NONE; } + + return SKILL_RANGE_LEVEL; } void ObjectMgr::LoadGameTele() diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index bf0d889057b..e7191735ca1 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -668,7 +668,7 @@ enum SkillRangeType SKILL_RANGE_NONE // 0..0 always }; -SkillRangeType GetSkillRangeType(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/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp index f0c1b28117b..278169c2d63 100644 --- a/src/server/game/Handlers/SkillHandler.cpp +++ b/src/server/game/Handlers/SkillHandler.cpp @@ -28,59 +28,22 @@ #include "WorldSession.h" #include "TalentPackets.h" -void WorldSession::HandleLearnTalentOpcode(WorldPacket& recvData) +void WorldSession::HandleLearnTalentOpcode(WorldPackets::Talent::LearnTalent& packet) { - /* TODO: 6.x update packet struct (note: LearnTalent no longer has rank argument) - uint32 talentId, requestedRank; - recvData >> talentId >> requestedRank; - - if (_player->LearnTalent(talentId, requestedRank)) - _player->SendTalentsInfoData(false);*/ + bool anythingLearned = false; + for (uint32 talentId : packet.Talents) + { + if (_player->LearnTalent(talentId)) + anythingLearned = true; + } + + if (anythingLearned) + _player->SendTalentsInfoData(); } void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket) { - /* TODO: 6.x update packet struct TC_LOG_DEBUG("network", "CMSG_LEARN_PREVIEW_TALENTS"); - - int32 tabPage; - uint32 talentsCount; - recvPacket >> tabPage; // talent tree - - // prevent cheating (selecting new tree with points already in another) - if (tabPage >= 0) // -1 if player already has specialization - { - if (TalentTabEntry const* talentTabEntry = sTalentTabStore.LookupEntry(_player->GetPrimaryTalentTree(_player->GetActiveSpec()))) - { - if (talentTabEntry->tabpage != uint32(tabPage)) - { - recvPacket.rfinish(); - return; - } - } - } - - recvPacket >> talentsCount; - - uint32 talentId, talentRank; - - // Client has max 21 talents for tree for 3 trees, rounded up : 70 - uint32 const MaxTalentsCount = 70; - - for (uint32 i = 0; i < talentsCount && i < MaxTalentsCount; ++i) - { - recvPacket >> talentId >> talentRank; - - if (!_player->LearnTalent(talentId, talentRank)) - { - recvPacket.rfinish(); - break; - } - } - - _player->SendTalentsInfoData(false); - - recvPacket.rfinish();*/ } void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket& recvData) diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 3ca660dccd1..a6bd43f72c6 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -371,6 +371,29 @@ void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::SpellCastRequest& // not have spell in spellbook return; } + + if (Player* plr = caster->ToPlayer()) + { + uint32 specId = plr->GetActiveTalentSpec(); + if (specId) + { + if (sSpecializationOverrideSpellMap.find(specId) != sSpecializationOverrideSpellMap.end()) + { + if (sSpecializationOverrideSpellMap[specId].find(castRequest.SpellID) != sSpecializationOverrideSpellMap[specId].end()) + { + SpellInfo const* newSpellInfo = sSpellMgr->GetSpellInfo(sSpecializationOverrideSpellMap[specId][castRequest.SpellID]); + if (newSpellInfo) + { + if (newSpellInfo->SpellLevel <= caster->getLevel()) + { + spellInfo = newSpellInfo; + castRequest.SpellID = newSpellInfo->Id; + } + } + } + } + } + } Unit::AuraEffectList swaps = mover->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS); Unit::AuraEffectList const& swaps2 = mover->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_2); diff --git a/src/server/game/Server/Packets/SpellPackets.cpp b/src/server/game/Server/Packets/SpellPackets.cpp index 7c536363335..1e181d37ff9 100644 --- a/src/server/game/Server/Packets/SpellPackets.cpp +++ b/src/server/game/Server/Packets/SpellPackets.cpp @@ -182,8 +182,6 @@ void WorldPackets::Spells::SpellCastRequest::Read() _worldPacket >> SpellID; _worldPacket >> Misc; - TC_LOG_ERROR("spells", "SpellID %u Misc %u", SpellID, Misc); - _worldPacket.ResetBitPos(); TargetFlags = _worldPacket.ReadBits(21); @@ -469,3 +467,12 @@ WorldPacket const* WorldPackets::Spells::SetSpellModifier::Write() return &_worldPacket; } + +WorldPacket const* WorldPackets::Spells::SendRemovedSpell::Write() +{ + _worldPacket << uint32(Spells.size()); + for (uint32 spellId : Spells) + _worldPacket << uint32(spellId); + + return &_worldPacket; +}
\ No newline at end of file diff --git a/src/server/game/Server/Packets/SpellPackets.h b/src/server/game/Server/Packets/SpellPackets.h index e9ae7ada77b..6971b730742 100644 --- a/src/server/game/Server/Packets/SpellPackets.h +++ b/src/server/game/Server/Packets/SpellPackets.h @@ -223,6 +223,16 @@ namespace WorldPackets std::vector<SpellModifier> Modifiers; }; + + class SendRemovedSpell final : public ServerPacket + { + public: + SendRemovedSpell() : ServerPacket(SMSG_REMOVED_SPELL, 4) { } + + WorldPacket const* Write() override; + + std::vector<uint32> Spells; + }; } } diff --git a/src/server/game/Server/Packets/TalentPackets.cpp b/src/server/game/Server/Packets/TalentPackets.cpp index 4855f663662..d340b476cf9 100644 --- a/src/server/game/Server/Packets/TalentPackets.cpp +++ b/src/server/game/Server/Packets/TalentPackets.cpp @@ -41,3 +41,17 @@ void WorldPackets::Talent::SetSpecialization::Read() { _worldPacket >> SpecGroupIndex; } + + +void WorldPackets::Talent::LearnTalent::Read() +{ + uint32 count; + _worldPacket >> count; + + for (uint32 i = 0; i < count; ++i) + { + uint16 talent; + _worldPacket >> talent; + Talents.push_back(talent); + } +}
\ No newline at end of file diff --git a/src/server/game/Server/Packets/TalentPackets.h b/src/server/game/Server/Packets/TalentPackets.h index 21753e22c8d..2bc8b3934a8 100644 --- a/src/server/game/Server/Packets/TalentPackets.h +++ b/src/server/game/Server/Packets/TalentPackets.h @@ -57,6 +57,19 @@ namespace WorldPackets uint32 SpecGroupIndex = 0; }; + + class LearnTalent final : public ClientPacket + { + public: + LearnTalent(WorldPacket&& packet) : ClientPacket(std::move(packet)) + { + ASSERT(packet.GetOpcode() == CMSG_LEARN_TALENT); + } + + void Read() override; + std::vector<uint16> Talents; + + }; } } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 79671b85892..671f0deb919 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -369,7 +369,7 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER_OLD(CMSG_KEEP_ALIVE, STATUS_NEVER, PROCESS_THREADUNSAFE, &WorldSession::Handle_EarlyProccess ); DEFINE_OPCODE_HANDLER_OLD(CMSG_LEARN_PREVIEW_TALENTS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnPreviewTalents ); DEFINE_OPCODE_HANDLER_OLD(CMSG_LEARN_PREVIEW_TALENTS_PET, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnPreviewTalentsPet ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_LEARN_TALENT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnTalentOpcode ); + DEFINE_HANDLER(CMSG_LEARN_TALENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Talent::LearnTalent, &WorldSession::HandleLearnTalentOpcode); DEFINE_HANDLER(CMSG_LEAVE_CHANNEL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Channel::LeaveChannel, &WorldSession::HandleLeaveChannel); DEFINE_OPCODE_HANDLER_OLD(CMSG_LFG_GET_STATUS, STATUS_UNHANDLED, PROCESS_THREADSAFE, &WorldSession::HandleLfgGetStatus ); DEFINE_OPCODE_HANDLER_OLD(CMSG_LFG_JOIN, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgJoinOpcode ); @@ -1247,7 +1247,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_REFER_A_FRIEND_FAILURE, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_REFORGE_RESULT, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_REFRESH_SPELL_HISTORY, STATUS_UNHANDLED); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_REMOVED_SPELL, STATUS_UNHANDLED); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_REMOVED_SPELL, STATUS_NEVER); DEFINE_SERVER_OPCODE_HANDLER(SMSG_REPORT_PVP_AFK_RESULT, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_REQUEST_CEMETERY_LIST_RESPONSE, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_REQUEST_PVP_REWARDS_RESPONSE, STATUS_UNHANDLED); diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index f6bdf0892d7..ee95bd840ab 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -314,7 +314,7 @@ enum OpcodeClient : uint32 CMSG_KEEP_ALIVE = 0xBADD, CMSG_LEARN_PREVIEW_TALENTS = 0xBADD, CMSG_LEARN_PREVIEW_TALENTS_PET = 0xBADD, - CMSG_LEARN_TALENT = 0xBADD, + CMSG_LEARN_TALENT = 0x0BB6, CMSG_LEAVE_CHANNEL = 0x19F2, CMSG_LFG_GET_STATUS = 0xBADD, CMSG_LFG_JOIN = 0xBADD, @@ -1269,7 +1269,7 @@ enum OpcodeServer : uint32 SMSG_REFER_A_FRIEND_FAILURE = 0xBADD, SMSG_REFORGE_RESULT = 0xBADD, SMSG_REFRESH_SPELL_HISTORY = 0x0A2A, - SMSG_REMOVED_SPELL = 0xBADD, + SMSG_REMOVED_SPELL = 0x0B3B, SMSG_REPORT_PVP_AFK_RESULT = 0xBADD, SMSG_REQUEST_CEMETERY_LIST_RESPONSE = 0x059E, SMSG_REQUEST_PVP_REWARDS_RESPONSE = 0xBADD, diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 26db96b232d..910a8ad4aa6 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -147,6 +147,7 @@ namespace WorldPackets namespace Talent { class SetSpecialization; + class LearnTalent; } namespace Trade @@ -876,7 +877,7 @@ class WorldSession void HandleCancelGrowthAuraOpcode(WorldPacket& recvPacket); void HandleCancelAutoRepeatSpellOpcode(WorldPacket& recvPacket); - void HandleLearnTalentOpcode(WorldPacket& recvPacket); + void HandleLearnTalentOpcode(WorldPackets::Talent::LearnTalent& packet); void HandleLearnPreviewTalents(WorldPacket& recvPacket); void HandleTalentWipeConfirmOpcode(WorldPacket& recvPacket); void HandleUnlearnSkillOpcode(WorldPacket& recvPacket); diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp index 9d2d4426efe..e571f30b74e 100644 --- a/src/server/scripts/Commands/cs_learn.cpp +++ b/src/server/scripts/Commands/cs_learn.cpp @@ -204,7 +204,7 @@ public: // learn highest rank of talent and learn all non-talent spell ranks (recursive by tree) player->LearnSpellHighestRank(talentInfo->SpellID); - player->AddTalent(talentInfo->SpellID, player->GetActiveTalentGroup()); + player->AddTalent(talentInfo->SpellID, player->GetActiveTalentGroup(), true); } handler->SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS); @@ -306,7 +306,7 @@ public: if (!handler->extractPlayerTarget((char*)args, &target)) return false; - target->LearnDefaultSpells(); + target->LearnDefaultSkills(); target->LearnCustomSpells(); target->LearnQuestRewardedSpells(); |