diff options
author | Shauren <shauren.trinity@gmail.com> | 2016-06-09 21:33:18 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2016-06-09 21:33:18 +0200 |
commit | f14c66b5e4c157fa3ba4d18532a274f651d6c741 (patch) | |
tree | b907507c91c43b74d7e84289447a567451bd0e77 /src | |
parent | 6226f04cafbcecb5b936cdb320b5bc05db3e4369 (diff) |
Core/Players: Updated talent specializations
Diffstat (limited to 'src')
21 files changed, 201 insertions, 321 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 240337afac8..7d8023fa272 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -77,9 +77,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER, "SELECT guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, customDisplay1, customDisplay2, customDisplay3, bankSlots, restState, playerFlags, " "position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, " - "resettalents_time, talentTree, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeonDifficulty, " + "resettalents_time, primarySpecialization, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeonDifficulty, " "totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, " - "health, power1, power2, power3, power4, power5, power6, instance_id, talentGroupsCount, activeTalentGroup, lootSpecId, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels, raidDifficulty, legacyRaidDifficulty " + "health, power1, power2, power3, power4, power5, power6, instance_id, activeTalentGroup, lootSpecId, exploredZones, knownTitles, actionBars, grantableLevels, raidDifficulty, legacyRaidDifficulty " "FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH); @@ -391,17 +391,17 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_CHARACTER, "INSERT INTO characters (guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, customDisplay1, customDisplay2, customDisplay3, bankSlots, restState, playerFlags, " "map, instance_id, dungeonDifficulty, raidDifficulty, legacyRaidDifficulty, position_x, position_y, position_z, orientation, trans_x, trans_y, trans_z, trans_o, transguid, " "taximask, cinematic, " - "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, talentTree, " + "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, primarySpecialization, " "extra_flags, stable_slots, at_login, zone, " "death_expire_time, taxi_path, totalKills, " "todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, " - "power4, power5, power6, latency, talentGroupsCount, activeTalentGroup, lootSpecId, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels) VALUES " - "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); + "power4, power5, power6, latency, activeTalentGroup, lootSpecId, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels) VALUES " + "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,skin=?,face=?,hairStyle=?,hairColor=?,facialStyle=?,customDisplay1=?,customDisplay2=?,customDisplay3=?,bankSlots=?,restState=?,playerFlags=?," "map=?,instance_id=?,dungeonDifficulty=?,raidDifficulty=?,legacyRaidDifficulty=?,position_x=?,position_y=?,position_z=?,orientation=?,trans_x=?,trans_y=?,trans_z=?,trans_o=?,transguid=?,taximask=?,cinematic=?,totaltime=?,leveltime=?,rest_bonus=?," - "logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,talentTree=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?," + "logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,primarySpecialization=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?," "totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?," - "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,power6=?,latency=?,talentGroupsCount=?,activeTalentGroup=?,lootSpecId=?,exploredZones=?," + "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,power6=?,latency=?,activeTalentGroup=?,lootSpecId=?,exploredZones=?," "equipmentCache=?,knownTitles=?,actionBars=?,grantableLevels=?,online=? WHERE guid=?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG, "UPDATE characters SET at_login = at_login | ? WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp index 006cbd07116..dffc49949a3 100644 --- a/src/server/game/AI/PlayerAI/PlayerAI.cpp +++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp @@ -41,13 +41,13 @@ bool PlayerAI::IsPlayerHealer(Player const* who) default: return false; case CLASS_PALADIN: - return who->GetSpecId(who->GetActiveTalentGroup()) == TALENT_SPEC_PALADIN_HOLY; + return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_PALADIN_HOLY; case CLASS_PRIEST: - return who->GetSpecId(who->GetActiveTalentGroup()) == TALENT_SPEC_PRIEST_DISCIPLINE || who->GetSpecId(who->GetActiveTalentGroup()) == TALENT_SPEC_PRIEST_HOLY; + return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_PRIEST_DISCIPLINE || who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_PRIEST_HOLY; case CLASS_SHAMAN: - return who->GetSpecId(who->GetActiveTalentGroup()) == TALENT_SPEC_SHAMAN_RESTORATION; + return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_SHAMAN_RESTORATION; case CLASS_DRUID: - return who->GetSpecId(who->GetActiveTalentGroup()) == TALENT_SPEC_DRUID_RESTORATION; + return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_DRUID_RESTORATION; } } @@ -74,11 +74,11 @@ bool PlayerAI::IsPlayerRangedAttacker(Player const* who) return false; } case CLASS_PRIEST: - return who->GetSpecId(who->GetActiveTalentGroup()) == TALENT_SPEC_PRIEST_SHADOW; + return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_PRIEST_SHADOW; case CLASS_SHAMAN: - return who->GetSpecId(who->GetActiveTalentGroup()) == TALENT_SPEC_SHAMAN_ELEMENTAL; + return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_SHAMAN_ELEMENTAL; case CLASS_DRUID: - return who->GetSpecId(who->GetActiveTalentGroup()) == TALENT_SPEC_DRUID_BALANCE; + return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_DRUID_BALANCE; } } diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 8a5cc02bcf6..825a56820f2 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -1089,7 +1089,7 @@ void Battleground::AddPlayer(Player* player) BattlegroundPlayer bp; bp.OfflineRemoveTime = 0; bp.Team = team; - bp.ActiveSpec = player->GetSpecId(player->GetActiveTalentGroup()); + bp.ActiveSpec = player->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID); // Add to list/maps m_Players[player->GetGUID()] = bp; diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 79d05ecb2bb..a052d4d44d4 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -2397,7 +2397,7 @@ bool ConditionMgr::IsPlayerMeetingCondition(Player* player, PlayerConditionEntry if (condition->ChrSpecializationIndex >= 0 || condition->ChrSpecializationRole >= 0) { - if (ChrSpecializationEntry const* spec = sChrSpecializationStore.LookupEntry(player->GetSpecId(player->GetActiveTalentGroup()))) + if (ChrSpecializationEntry const* spec = sChrSpecializationStore.LookupEntry(player->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID))) { if (condition->ChrSpecializationIndex >= 0 && spec->OrderIndex != uint32(condition->ChrSpecializationIndex)) return false; diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 2ef4a85c679..ff6fb779650 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -711,8 +711,8 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) for (TalentEntry const* talentInfo : sTalentStore) { ASSERT(talentInfo->ClassID < MAX_CLASSES); - ASSERT(talentInfo->TierID < MAX_TALENT_TIERS, "MAX_TALENT_TIERS must be at least %u", MAX_TALENT_TIERS); - ASSERT(talentInfo->ColumnIndex < MAX_TALENT_COLUMNS, "MAX_TALENT_COLUMNS must be at least %u", MAX_TALENT_COLUMNS); + ASSERT(talentInfo->TierID < MAX_TALENT_TIERS, "MAX_TALENT_TIERS must be at least %u", talentInfo->TierID); + ASSERT(talentInfo->ColumnIndex < MAX_TALENT_COLUMNS, "MAX_TALENT_COLUMNS must be at least %u", talentInfo->ColumnIndex); _talentsByPosition[talentInfo->ClassID][talentInfo->TierID][talentInfo->ColumnIndex].push_back(talentInfo); } diff --git a/src/server/game/Entities/Item/ItemTemplate.cpp b/src/server/game/Entities/Item/ItemTemplate.cpp index 67f683849a9..e33bea37e54 100644 --- a/src/server/game/Entities/Item/ItemTemplate.cpp +++ b/src/server/game/Entities/Item/ItemTemplate.cpp @@ -218,7 +218,7 @@ bool ItemTemplate::CanWinForPlayer(Player const* player) const if (specs.empty()) return true; - uint32 spec = player->GetSpecId(player->GetActiveTalentGroup()); + uint32 spec = player->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID); if (!spec) spec = player->GetDefaultSpecId(); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 2f626fc39ea..fd96b65b2cf 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2510,12 +2510,6 @@ void Player::InitTalentForLevel() } else { - if (level < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL) || GetTalentGroupsCount() == 0) - { - SetTalentGroupsCount(1); - SetActiveTalentGroup(0); - } - if (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_MORE_TALENTS_THAN_ALLOWED)) for (uint32 t = talentTiers; t < MAX_TALENT_TIERS; ++t) for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c) @@ -3227,7 +3221,7 @@ bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const bool Player::IsCurrentSpecMasterySpell(SpellInfo const* spellInfo) const { - if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup()))) + if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID))) return spellInfo->Id == chrSpec->MasterySpellID[0] || spellInfo->Id == chrSpec->MasterySpellID[1]; return false; @@ -13303,8 +13297,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool break; } case GOSSIP_OPTION_LEARNDUALSPEC: - if (!(GetTalentGroupsCount() == 1 && creature->isCanTrainingAndResetTalentsOf(this) && !(getLevel() < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)))) - canTalk = false; + canTalk = false; break; case GOSSIP_OPTION_UNLEARNTALENTS: if (!creature->isCanTrainingAndResetTalentsOf(this)) @@ -13508,16 +13501,6 @@ void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 men GetSession()->SendTrainerList(guid); break; case GOSSIP_OPTION_LEARNDUALSPEC: - if (GetTalentGroupsCount() == 1 && getLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)) - { - // Cast spells that teach dual spec - // Both are also ImplicitTarget self and must be cast by player - CastSpell(this, 63680, true, nullptr, nullptr, GetGUID()); - CastSpell(this, 63624, true, nullptr, nullptr, GetGUID()); - - // Should show another Gossip text with "Congratulations..." - PlayerTalkClass->SendCloseGossip(); - } break; case GOSSIP_OPTION_UNLEARNTALENTS: PlayerTalkClass->SendCloseGossip(); @@ -16285,12 +16268,12 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) //"SELECT guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, customDisplay1, customDisplay2, customDisplay3, bankSlots, restState, playerFlags, " // 20 21 22 23 24 25 26 27 28 29 30 31 32 //"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, " - // 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 - //"resettalents_time, talentTree, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeonDifficulty, " + // 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 + //"resettalents_time, primarySpecialization, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeonDifficulty, " // 48 49 50 51 52 53 //"totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, " - // 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 - //"health, power1, power2, power3, power4, power5, power6, instance_id, talentGroupsCount, activeTalentGroup, lootSpecId, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels, raidDifficulty, legacyRaidDifficulty " + // 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 + //"health, power1, power2, power3, power4, power5, power6, instance_id, activeTalentGroup, lootSpecId, exploredZones, knownTitles, actionBars, grantableLevels, raidDifficulty, legacyRaidDifficulty " // //"FROM characters WHERE guid = ?", CONNECTION_ASYNC); PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM); @@ -16360,8 +16343,8 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8()); SetUInt32Value(PLAYER_XP, fields[7].GetUInt32()); - _LoadIntoDataField(fields[65].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); - _LoadIntoDataField(fields[67].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE * 2); + _LoadIntoDataField(fields[64].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); + _LoadIntoDataField(fields[65].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE * 2); SetObjectScale(1.0f); SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); @@ -16403,7 +16386,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) } // set which actionbars the client has active - DO NOT REMOVE EVER AGAIN (can be changed though, if it does change fieldwise) - SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES, fields[68].GetUInt8()); + SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES, fields[66].GetUInt8()); InitDisplayIds(); @@ -16439,8 +16422,8 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) uint32 instanceId = fields[61].GetUInt32(); SetDungeonDifficultyID(CheckLoadedDungeonDifficultyID(Difficulty(fields[47].GetUInt8()))); - SetRaidDifficultyID(CheckLoadedRaidDifficultyID(Difficulty(fields[70].GetUInt8()))); - SetLegacyRaidDifficultyID(CheckLoadedLegacyRaidDifficultyID(Difficulty(fields[71].GetUInt8()))); + SetRaidDifficultyID(CheckLoadedRaidDifficultyID(Difficulty(fields[68].GetUInt8()))); + SetLegacyRaidDifficultyID(CheckLoadedLegacyRaidDifficultyID(Difficulty(fields[69].GetUInt8()))); std::string taxi_nodes = fields[46].GetString(); @@ -16751,6 +16734,10 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) SetTalentResetCost(fields[32].GetUInt32()); SetTalentResetTime(time_t(fields[33].GetUInt32())); + SetPrimarySpecialization(fields[34].GetUInt32()); + ChrSpecializationEntry const* primarySpec = sChrSpecializationStore.LookupEntry(GetPrimarySpecialization()); + if (!primarySpec || primarySpec->ClassID != getClass()) + SetPrimarySpecialization(0); m_taxi.LoadTaxiMask(fields[25].GetString()); // must be before InitTaxiNodesForLevel @@ -16833,42 +16820,29 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) _LoadSkills(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SKILLS)); UpdateSkillsForLevel(); //update skills after load, to make sure they are correctly update at player load - SetTalentGroupsCount(fields[62].GetUInt8()); - SetActiveTalentGroup(fields[63].GetUInt8()); + SetActiveTalentGroup(fields[62].GetUInt8()); - uint32 lootSpecId = fields[64].GetUInt32(); + uint32 lootSpecId = fields[63].GetUInt32(); if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(lootSpecId)) - { if (chrSpec->ClassID == getClass()) SetLootSpecId(lootSpecId); - } // sanity check - if (GetTalentGroupsCount() > MAX_TALENT_GROUPS || GetActiveTalentGroup() > MAX_TALENT_GROUP || GetTalentGroupsCount() < MIN_TALENT_GROUPS) + if (GetActiveTalentGroup() >= MAX_SPECIALIZATIONS) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player %s (%s) has invalid SpecCount = %u and/or invalid ActiveSpec = %u.", - GetName().c_str(), GetGUID().ToString().c_str(), GetTalentGroupsCount(), GetActiveTalentGroup()); + TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player %s (%s) has invalid invalid ActiveSpec = %u.", + GetName().c_str(), GetGUID().ToString().c_str(), GetActiveTalentGroup()); SetActiveTalentGroup(0); } - // Only load selected specializations, learning mastery spells requires this - Tokenizer talentSpecs(fields[34].GetString(), ' ', MAX_TALENT_GROUPS); - for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) + if (ChrSpecializationEntry const* spec = sDB2Manager.GetChrSpecializationByIndex(getClass(), GetActiveTalentGroup())) { - if (i >= talentSpecs.size()) - break; - - uint32 talentSpec = atoul(talentSpecs[i]); - if (talentSpec) - { - if (sChrSpecializationStore.LookupEntry(talentSpec)) - SetSpecId(i, talentSpec); - else - SetAtLoginFlag(AT_LOGIN_RESET_TALENTS); - } + SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, spec->ID); + if (!GetPrimarySpecialization()) + SetPrimarySpecialization(spec->ID); } - - SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetSpecId(GetActiveTalentGroup())); + else + ResetTalentSpecialization(); _LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS)); _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS)); @@ -17012,7 +16986,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) } // RaF stuff. - m_grantableLevels = fields[69].GetUInt8(); + m_grantableLevels = fields[67].GetUInt8(); if (GetSession()->IsARecruiter() || (GetSession()->GetRecruiterId() != 0)) SetFlag(OBJECT_DYNAMIC_FLAGS, UNIT_DYNFLAG_REFER_A_FRIEND); @@ -18598,11 +18572,7 @@ void Player::SaveToDB(bool create /*=false*/) //save, but in tavern/city stmt->setUInt32(index++, GetTalentResetCost()); stmt->setUInt32(index++, GetTalentResetTime()); - - ss.str(""); - for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) - ss << GetSpecId(i) << " "; - stmt->setString(index++, ss.str()); + stmt->setUInt32(index++, GetPrimarySpecialization()); stmt->setUInt16(index++, (uint16)m_ExtraFlags); stmt->setUInt8(index++, m_stableSlots); stmt->setUInt16(index++, (uint16)m_atLoginFlags); @@ -18637,7 +18607,6 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt32(index++, GetSession()->GetLatency()); - stmt->setUInt8(index++, GetTalentGroupsCount()); stmt->setUInt8(index++, GetActiveTalentGroup()); stmt->setUInt32(index++, GetLootSpecId()); @@ -18744,11 +18713,7 @@ void Player::SaveToDB(bool create /*=false*/) //save, but in tavern/city stmt->setUInt32(index++, GetTalentResetCost()); stmt->setUInt32(index++, GetTalentResetTime()); - - ss.str(""); - for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) - ss << GetSpecId(i) << " "; - stmt->setString(index++, ss.str()); + stmt->setUInt32(index++, GetPrimarySpecialization()); stmt->setUInt16(index++, (uint16)m_ExtraFlags); stmt->setUInt8(index++, m_stableSlots); stmt->setUInt16(index++, (uint16)m_atLoginFlags); @@ -18783,7 +18748,6 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt32(index++, GetSession()->GetLatency()); - stmt->setUInt8(index++, GetTalentGroupsCount()); stmt->setUInt8(index++, GetActiveTalentGroup()); stmt->setUInt32(index++, GetLootSpecId()); @@ -24614,22 +24578,33 @@ bool Player::ModifierTreeSatisfied(uint32 modifierTreeId) const return m_achievementMgr->ModifierTreeSatisfied(modifierTreeId); } -bool Player::LearnTalent(uint32 talentId) +TalentLearnResult Player::LearnTalent(uint32 talentId, int32* spellOnCooldown) { + if (IsInCombat()) + return TALENT_FAILED_AFFECTING_COMBAT; + + if (isDead() || GetMap()->IsBattlegroundOrArena()) + return TALENT_FAILED_CANT_DO_THAT_RIGHT_NOW; + + if (!GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID)) + return TALENT_FAILED_NO_PRIMARY_TREE_SELECTED; + TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); if (!talentInfo) - return false; + return TALENT_FAILED_UNKNOWN; - if (talentInfo->SpecID && talentInfo->SpecID != GetSpecId(GetActiveTalentGroup())) - return false; + if (talentInfo->SpecID && talentInfo->SpecID != GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID)) + return TALENT_FAILED_UNKNOWN; // prevent learn talent for different class (cheating) if (talentInfo->ClassID != getClass()) - return false; + return TALENT_FAILED_UNKNOWN; // check if we have enough talent points if (talentInfo->TierID >= GetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS)) - return false; + return TALENT_FAILED_UNKNOWN; + + // TODO: prevent changing talents that are on cooldown // Check if there is a different talent for us to learn in selected slot // Example situation: @@ -24642,7 +24617,7 @@ bool Player::LearnTalent(uint32 talentId) { if (!talent->SpecID) bestSlotMatch = talent; - else if (talent->SpecID == GetSpecId(GetActiveTalentGroup())) + else if (talent->SpecID == GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID)) { bestSlotMatch = talent; break; @@ -24650,63 +24625,53 @@ bool Player::LearnTalent(uint32 talentId) } if (talentInfo != bestSlotMatch) - return false; + return TALENT_FAILED_UNKNOWN; // Check if player doesn't have any talent in current tier for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c) + { for (TalentEntry const* talent : sDB2Manager.GetTalentsByPosition(getClass(), talentInfo->TierID, c)) - if (HasTalent(talent->ID, GetActiveTalentGroup())) - return false; + { + if (HasTalent(talent->ID, GetActiveTalentGroup()) && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC)) + return TALENT_FAILED_REST_AREA; + + if (GetSpellHistory()->HasCooldown(talent->SpellID)) + { + *spellOnCooldown = talent->SpellID; + return TALENT_FAILED_CANT_REMOVE_TALENT; + } + + RemoveTalent(talent); + } + } // spell not set in talent.dbc uint32 spellid = talentInfo->SpellID; if (!spellid) { TC_LOG_ERROR("entities.player", "Player::LearnTalent: Talent.dbc has no spellInfo for talent: %u (spell id = 0)", talentId); - return false; + return TALENT_FAILED_UNKNOWN; } // already known if (HasTalent(talentId, GetActiveTalentGroup()) || HasSpell(spellid)) - return false; + return TALENT_FAILED_UNKNOWN; if (!AddTalent(talentInfo, GetActiveTalentGroup(), true)) - return false; + return TALENT_FAILED_UNKNOWN; LearnSpell(spellid, false); TC_LOG_DEBUG("misc", "Player::LearnTalent: TalentID: %u Spell: %u Group: %u\n", talentId, spellid, GetActiveTalentGroup()); - return true; -} - -void Player::LearnTalentSpecialization(uint32 talentSpec) -{ - if (GetSpecId(GetActiveTalentGroup())) - return; - - SetSpecId(GetActiveTalentGroup(), talentSpec); - SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, talentSpec); - - // Reset only talents that have different spells for each spec - uint32 class_ = getClass(); - for (uint32 t = 0; t < MAX_TALENT_TIERS; ++t) - for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c) - if (sDB2Manager.GetTalentsByPosition(class_, t, c).size() > 1) - for (TalentEntry const* talent : sDB2Manager.GetTalentsByPosition(class_, t, c)) - RemoveTalent(talent); - - LearnSpecializationSpells(); - SendTalentsInfoData(); - UpdateItemSetAuras(false); + return TALENT_LEARN_OK; } void Player::ResetTalentSpecialization() { - if (!GetSpecId(GetActiveTalentGroup())) + if (!GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID)) return; - SetSpecId(GetActiveTalentGroup(), 0); SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, 0); // Reset only talents that have different spells for each spec @@ -24791,16 +24756,17 @@ bool Player::CanSeeSpellClickOn(Creature const* c) const void Player::SendTalentsInfoData() { WorldPackets::Talent::UpdateTalentData packet; - + packet.Info.PrimarySpecialization = GetPrimarySpecialization(); packet.Info.ActiveGroup = GetActiveTalentGroup(); - uint8 groupsCount = GetTalentGroupsCount(); - - for (uint8 i = 0; i < groupsCount; ++i) + for (uint8 i = 0; i < MAX_SPECIALIZATIONS; ++i) { - WorldPackets::Talent::TalentGroupInfo groupInfoPkt; + ChrSpecializationEntry const* spec = sDB2Manager.GetChrSpecializationByIndex(getClass(), i); + if (!spec) + continue; - groupInfoPkt.SpecID = GetSpecId(i); + WorldPackets::Talent::TalentGroupInfo groupInfoPkt; + groupInfoPkt.SpecID = spec->ID; groupInfoPkt.TalentIDs.reserve(GetTalentMap(i)->size()); for (PlayerTalentMap::const_iterator itr = GetTalentMap(i)->begin(); itr != GetTalentMap(i)->end(); ++itr) @@ -25048,58 +25014,9 @@ void Player::_SaveTalents(SQLTransaction& trans) } } -void Player::UpdateTalentGroupCount(uint8 count) +void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec) { - uint32 curCount = GetTalentGroupsCount(); - if (curCount == count) - return; - - if (GetActiveTalentGroup() >= count) - ActivateTalentGroup(0); - - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt; - - // Copy spec data - if (count > curCount) - { - _SaveActions(trans); // make sure the button list is cleaned up - for (ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end(); ++itr) - { - stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACTION); - stmt->setUInt64(0, GetGUID().GetCounter()); - stmt->setUInt8(1, 1); - stmt->setUInt8(2, itr->first); - stmt->setUInt32(3, itr->second.GetAction()); - stmt->setUInt8(4, uint8(itr->second.GetType())); - trans->Append(stmt); - } - } - // Delete spec data for removed spec. - else if (count < curCount) - { - _SaveActions(trans); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC); - stmt->setUInt8(0, GetActiveTalentGroup()); - stmt->setUInt64(1, GetGUID().GetCounter()); - trans->Append(stmt); - - } - - CharacterDatabase.CommitTransaction(trans); - - SetTalentGroupsCount(count); - - SendTalentsInfoData(); -} - -void Player::ActivateTalentGroup(uint8 spec) -{ - if (GetActiveTalentGroup() == spec) - return; - - if (spec > GetTalentGroupsCount()) + if (GetActiveTalentGroup() == spec->OrderIndex) return; if (IsNonMeleeSpellCast(false)) @@ -25160,8 +25077,10 @@ void Player::ActivateTalentGroup(uint8 spec) // Remove spec specific spells RemoveSpecializationSpells(); - SetActiveTalentGroup(spec); - SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetSpecId(spec)); + SetActiveTalentGroup(spec->OrderIndex); + SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, spec->ID); + if (!GetPrimarySpecialization()) + SetPrimarySpecialization(spec->ID); for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) { @@ -25188,10 +25107,9 @@ void Player::ActivateTalentGroup(uint8 spec) LearnSpecializationSpells(); if (CanUseMastery()) - if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup()))) - for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i) - if (uint32 mastery = specialization->MasterySpellID[i]) - LearnSpell(mastery, false); + for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i) + if (uint32 mastery = spec->MasterySpellID[i]) + LearnSpell(mastery, false); InitTalentForLevel(); @@ -25211,9 +25129,6 @@ void Player::ActivateTalentGroup(uint8 spec) SetPower(pw, 0); UpdateItemSetAuras(false); - - if (!sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup()))) - ResetTalents(true); } void Player::ResetTimeSync() @@ -25850,7 +25765,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy bool Player::CanUseMastery() const { - if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup()))) + if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID))) return HasSpell(chrSpec->MasterySpellID[0]) || HasSpell(chrSpec->MasterySpellID[1]); return false; @@ -26068,7 +25983,7 @@ void Player::RemoveOverrideSpell(uint32 overridenSpellId, uint32 newSpellId) void Player::LearnSpecializationSpells() { - if (std::vector<SpecializationSpellsEntry const*> const* specSpells = sDB2Manager.GetSpecializationSpells(GetSpecId(GetActiveTalentGroup()))) + if (std::vector<SpecializationSpellsEntry const*> const* specSpells = sDB2Manager.GetSpecializationSpells(GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID))) { for (size_t j = 0; j < specSpells->size(); ++j) { diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 5bee6c6c117..e46712d0580 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1136,42 +1136,38 @@ struct ResurrectionData uint32 Aura; }; +enum TalentLearnResult +{ + TALENT_LEARN_OK = 0, + TALENT_FAILED_UNKNOWN = 1, + TALENT_FAILED_NOT_ENOUGH_TALENTS_IN_PRIMARY_TREE = 2, + TALENT_FAILED_NO_PRIMARY_TREE_SELECTED = 3, + TALENT_FAILED_CANT_DO_THAT_RIGHT_NOW = 4, + TALENT_FAILED_AFFECTING_COMBAT = 5, + TALENT_FAILED_CANT_REMOVE_TALENT = 6, + TALENT_FAILED_CANT_DO_THAT_CHALLENGE_MODE_ACTIVE = 7, + TALENT_FAILED_REST_AREA = 8 +}; + static uint32 const DefaultTalentRowLevels[MAX_TALENT_TIERS] = { 15, 30, 45, 60, 75, 90, 100 }; static uint32 const DKTalentRowLevels[MAX_TALENT_TIERS] = { 57, 58, 59, 60, 75, 90, 100 }; static uint32 const DHTalentRowLevels[MAX_TALENT_TIERS] = { 99, 100, 102, 104, 106, 108, 110 }; struct TC_GAME_API PlayerTalentInfo { - PlayerTalentInfo() : - ResetTalentsCost(0), ResetTalentsTime(0), - ActiveGroup(0), GroupsCount(1) - { - for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) - { - GroupInfo[i].Talents = new PlayerTalentMap(); - GroupInfo[i].SpecId = 0; - } - } - - ~PlayerTalentInfo() + PlayerTalentInfo() : ResetTalentsCost(0), ResetTalentsTime(0), PrimarySpecialization(0), ActiveGroup(0) { - for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) - delete GroupInfo[i].Talents; } - struct TalentGroupInfo - { - PlayerTalentMap* Talents; - uint32 SpecId; - } GroupInfo[MAX_TALENT_GROUPS]; - + PlayerTalentMap Talents[MAX_SPECIALIZATIONS]; uint32 ResetTalentsCost; time_t ResetTalentsTime; + uint32 PrimarySpecialization; uint8 ActiveGroup; - uint8 GroupsCount; private: - PlayerTalentInfo(PlayerTalentInfo const&); + PlayerTalentInfo(PlayerTalentInfo const&) = delete; + PlayerTalentInfo& operator=(PlayerTalentInfo const&) = delete; }; class TC_GAME_API Player : public Unit, public GridObject<Player> @@ -1753,32 +1749,28 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void SetTalentResetCost(uint32 cost) { _talentMgr->ResetTalentsCost = cost; } time_t GetTalentResetTime() const { return _talentMgr->ResetTalentsTime; } void SetTalentResetTime(time_t time_) { _talentMgr->ResetTalentsTime = time_; } - uint32 GetSpecId(uint8 group) const { return _talentMgr->GroupInfo[group].SpecId; } - void SetSpecId(uint8 group, uint32 tree) { _talentMgr->GroupInfo[group].SpecId = tree; } + uint32 GetPrimarySpecialization() const { return _talentMgr->PrimarySpecialization; } + void SetPrimarySpecialization(uint32 spec) { _talentMgr->PrimarySpecialization = spec; } 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 GetDefaultSpecId() const; bool ResetTalents(bool noCost = false); uint32 GetNextResetTalentsCost() const; void InitTalentForLevel(); void SendTalentsInfoData(); - bool LearnTalent(uint32 talentId); + TalentLearnResult LearnTalent(uint32 talentId, int32* spellOnCooldown); bool AddTalent(TalentEntry const* talent, uint8 spec, bool learning); bool HasTalent(uint32 spell_id, uint8 spec) const; void RemoveTalent(TalentEntry const* talent); uint32 CalculateTalentsTiers() const; - void LearnTalentSpecialization(uint32 talentSpec); void ResetTalentSpecialization(); // Dual Spec - void UpdateTalentGroupCount(uint8 count); - void ActivateTalentGroup(uint8 group); + void ActivateTalentGroup(ChrSpecializationEntry const* spec); - PlayerTalentMap const* GetTalentMap(uint8 spec) const { return _talentMgr->GroupInfo[spec].Talents; } - PlayerTalentMap* GetTalentMap(uint8 spec) { return _talentMgr->GroupInfo[spec].Talents; } + PlayerTalentMap const* GetTalentMap(uint8 spec) const { return &_talentMgr->Talents[spec]; } + PlayerTalentMap* GetTalentMap(uint8 spec) { return &_talentMgr->Talents[spec]; } ActionButtonList const& GetActionButtons() const { return m_actionButtons; } uint32 GetFreePrimaryProfessionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS); } diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 447169a28d0..c3b8ef8e07e 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -534,7 +534,7 @@ void Player::UpdateMastery() value += GetRatingBonusValue(CR_MASTERY); SetFloatValue(PLAYER_MASTERY, value); - ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup())); + ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID)); if (!chrSpec) return; diff --git a/src/server/game/Handlers/InspectHandler.cpp b/src/server/game/Handlers/InspectHandler.cpp index 3ac39c2d500..02746109348 100644 --- a/src/server/game/Handlers/InspectHandler.cpp +++ b/src/server/game/Handlers/InspectHandler.cpp @@ -69,7 +69,7 @@ void WorldSession::HandleInspectOpcode(WorldPackets::Inspect::Inspect& inspect) } inspectResult.InspecteeGUID = inspect.Target; - inspectResult.SpecializationID = player->GetSpecId(player->GetActiveTalentGroup()); + inspectResult.SpecializationID = player->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID); SendPacket(inspectResult.Write()); } diff --git a/src/server/game/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp index 3596eee68f6..e9fafac5d1d 100644 --- a/src/server/game/Handlers/SkillHandler.cpp +++ b/src/server/game/Handlers/SkillHandler.cpp @@ -28,10 +28,23 @@ void WorldSession::HandleLearnTalentsOpcode(WorldPackets::Talent::LearnTalents& packet) { + WorldPackets::Talent::LearnTalentsFailed learnTalentsFailed; bool anythingLearned = false; for (uint32 talentId : packet.Talents) - if (_player->LearnTalent(talentId)) + { + if (TalentLearnResult result = _player->LearnTalent(talentId, &learnTalentsFailed.SpellID)) + { + if (!learnTalentsFailed.Reason) + learnTalentsFailed.Reason = result; + + learnTalentsFailed.Talents.push_back(talentId); + } + else anythingLearned = true; + } + + if (learnTalentsFailed.Reason) + SendPacket(learnTalentsFailed.Write()); if (anythingLearned) _player->SendTalentsInfoData(); @@ -77,35 +90,3 @@ void WorldSession::HandleUnlearnSkillOpcode(WorldPackets::Spells::UnlearnSkill& GetPlayer()->SetSkill(packet.SkillLine, 0, 0, 0); } - -void WorldSession::HandleSetSpecializationOpcode(WorldPackets::Talent::SetSpecialization& packet) -{ - Player* player = GetPlayer(); - - if (packet.SpecGroupIndex >= MAX_SPECIALIZATIONS) - { - TC_LOG_DEBUG("network", "WORLD: HandleSetSpecializationOpcode - specialization index %u out of range", packet.SpecGroupIndex); - return; - } - - ChrSpecializationEntry const* chrSpec = sDB2Manager.GetChrSpecializationByIndex(player->getClass(), packet.SpecGroupIndex); - if (!chrSpec) - { - TC_LOG_DEBUG("network", "WORLD: HandleSetSpecializationOpcode - specialization index %u not found", packet.SpecGroupIndex); - return; - } - - if (chrSpec->ClassID != player->getClass()) - { - TC_LOG_DEBUG("network", "WORLD: HandleSetSpecializationOpcode - specialization %u does not belong to class %u", chrSpec->ID, player->getClass()); - return; - } - - if (player->getLevel() < MIN_SPECIALIZATION_LEVEL) - { - TC_LOG_DEBUG("network", "WORLD: HandleSetSpecializationOpcode - player level too low for specializations"); - return; - } - - player->LearnTalentSpecialization(chrSpec->ID); -} diff --git a/src/server/game/Server/Packets/TalentPackets.cpp b/src/server/game/Server/Packets/TalentPackets.cpp index bc7b6aec20c..1069b8a89f7 100644 --- a/src/server/game/Server/Packets/TalentPackets.cpp +++ b/src/server/game/Server/Packets/TalentPackets.cpp @@ -19,41 +19,31 @@ WorldPacket const* WorldPackets::Talent::UpdateTalentData::Write() { - _worldPacket << Info.ActiveGroup; + _worldPacket << uint8(Info.ActiveGroup); + _worldPacket << uint32(Info.PrimarySpecialization); _worldPacket << uint32(Info.TalentGroups.size()); for (auto& talentGroupInfo : Info.TalentGroups) { - _worldPacket << talentGroupInfo.SpecID; + _worldPacket << uint32(talentGroupInfo.SpecID); _worldPacket << uint32(talentGroupInfo.TalentIDs.size()); - - //for (uint32 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) - // _worldPacket << talentGroupInfo.GlyphIDs[i]; + _worldPacket << uint32(talentGroupInfo.PvPTalentIDs.size()); for (uint16 talentID : talentGroupInfo.TalentIDs) - _worldPacket << talentID; + _worldPacket << uint16(talentID); + + for (uint16 talentID : talentGroupInfo.PvPTalentIDs) + _worldPacket << uint16(talentID); } return &_worldPacket; } -void WorldPackets::Talent::SetSpecialization::Read() -{ - _worldPacket >> SpecGroupIndex; -} - - void WorldPackets::Talent::LearnTalents::Read() { - uint32 count; - _worldPacket >> count; - - for (uint32 i = 0; i < count; ++i) - { - uint16 talent; - _worldPacket >> talent; - Talents.push_back(talent); - } + Talents.resize(_worldPacket.ReadBits(6)); + for (uint32 i = 0; i < Talents.size(); ++i) + _worldPacket >> Talents[i]; } WorldPacket const* WorldPackets::Talent::RespecWipeConfirm::Write() @@ -69,3 +59,14 @@ void WorldPackets::Talent::ConfirmRespecWipe::Read() _worldPacket >> RespecMaster; _worldPacket >> RespecType; } + +WorldPacket const* WorldPackets::Talent::LearnTalentsFailed::Write() +{ + _worldPacket.WriteBits(Reason, 4); + _worldPacket << int32(SpellID); + _worldPacket << uint32(Talents.size()); + if (!Talents.empty()) + _worldPacket.append(Talents.data(), Talents.size()); + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/TalentPackets.h b/src/server/game/Server/Packets/TalentPackets.h index 9e09605b362..57893bb94a4 100644 --- a/src/server/game/Server/Packets/TalentPackets.h +++ b/src/server/game/Server/Packets/TalentPackets.h @@ -19,6 +19,7 @@ #define TalentPackets_h__ #include "Packet.h" +#include "PacketUtilities.h" #include "Player.h" namespace WorldPackets @@ -27,14 +28,15 @@ namespace WorldPackets { struct TalentGroupInfo { - uint32 SpecID; + uint32 SpecID = 0; std::vector<uint16> TalentIDs; - //uint16 GlyphIDs[MAX_GLYPH_SLOT_INDEX]; + std::vector<uint16> PvPTalentIDs; }; struct TalentInfoUpdate { - uint8 ActiveGroup; + uint8 ActiveGroup = 0; + uint32 PrimarySpecialization = 0; std::vector<TalentGroupInfo> TalentGroups; }; @@ -48,26 +50,13 @@ namespace WorldPackets TalentInfoUpdate Info; }; - class SetSpecialization final : public ClientPacket - { - public: - SetSpecialization(WorldPacket&& packet) : ClientPacket(CMSG_SET_SPECIALIZATION, std::move(packet)) { } - - void Read() override; - - uint32 SpecGroupIndex = 0; - }; - class LearnTalents final : public ClientPacket { public: - LearnTalents(WorldPacket&& packet) : ClientPacket(std::move(packet)) - { - ASSERT(packet.GetOpcode() == CMSG_LEARN_TALENTS); - } + LearnTalents(WorldPacket&& packet) : ClientPacket(CMSG_LEARN_TALENTS, std::move(packet)) { } void Read() override; - std::vector<uint16> Talents; + Array<uint16, MAX_TALENT_TIERS> Talents; }; class RespecWipeConfirm final : public ServerPacket @@ -93,6 +82,17 @@ namespace WorldPackets uint8 RespecType = 0; }; + class LearnTalentsFailed final : public ServerPacket + { + public: + LearnTalentsFailed() : ServerPacket(SMSG_LEARN_TALENTS_FAILED, 1 + 4 + 4 + 2 * MAX_TALENT_TIERS) { } + + WorldPacket const* Write() override; + + uint32 Reason = 0; + int32 SpellID = 0; + std::vector<uint16> Talents; + }; } } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index ca6f86e8a18..8cd8dd7cd84 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -428,7 +428,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_JOIN_RATED_BATTLEGROUND, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_KEEP_ALIVE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPacket, &WorldSession::Handle_EarlyProccess); DEFINE_HANDLER(CMSG_KEYBOUND_OVERRIDE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_HANDLER(CMSG_LEARN_TALENTS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Talent::LearnTalents, &WorldSession::HandleLearnTalentsOpcode); + DEFINE_HANDLER(CMSG_LEARN_TALENTS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Talent::LearnTalents, &WorldSession::HandleLearnTalentsOpcode); DEFINE_HANDLER(CMSG_LEAVE_GROUP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::LeaveGroup, &WorldSession::HandleLeaveGroupOpcode); DEFINE_HANDLER(CMSG_LEAVE_PET_BATTLE_QUEUE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_LFG_LIST_APPLY_TO_GROUP, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); @@ -701,7 +701,6 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_SET_SELECTION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::SetSelection, &WorldSession::HandleSetSelectionOpcode); DEFINE_HANDLER(CMSG_SET_SHEATHED, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Combat::SetSheathed, &WorldSession::HandleSetSheathedOpcode); DEFINE_HANDLER(CMSG_SET_SORT_BAGS_RIGHT_TO_LEFT, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_HANDLER(CMSG_SET_SPECIALIZATION, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Talent::SetSpecialization, &WorldSession::HandleSetSpecializationOpcode); DEFINE_HANDLER(CMSG_SET_TAXI_BENCHMARK_MODE, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Misc::SetTaxiBenchmarkMode, &WorldSession::HandleSetTaxiBenchmark); DEFINE_HANDLER(CMSG_SET_TITLE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Character::SetTitle, &WorldSession::HandleSetTitleOpcode); DEFINE_HANDLER(CMSG_SET_TRADE_CURRENCY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Trade::SetTradeCurrency, &WorldSession::HandleSetTradeCurrencyOpcode); @@ -1234,8 +1233,8 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_ITEM_TIME_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_KICK_REASON, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_LEARNED_SPELLS, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_LEARN_PVP_TALENT_FAILED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_LEARN_TALENT_FAILED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_LEARN_PVP_TALENTS_FAILED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_LEARN_TALENTS_FAILED, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_LEVEL_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_LEVEL_UP_INFO, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_LFG_BOOT_PLAYER, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1696,7 +1695,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_INSTANCE_OWNERSHIP, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_LAST_INSTANCE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_OBJECT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_TALENT_DATA, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_TALENT_DATA, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_TASK_PROGRESS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_WEEKLY_SPELL_USAGE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_WORLD_STATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index 571a1473aa1..d6ce4971a15 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -736,7 +736,6 @@ enum OpcodeClient : uint32 CMSG_BF_MGR_ENTRY_INVITE_RESPONSE = 0xBADD, CMSG_BF_MGR_QUEUE_INVITE_RESPONSE = 0xBADD, CMSG_BF_MGR_QUEUE_EXIT_REQUEST = 0xBADD, - CMSG_SET_SPECIALIZATION = 0xBADD, }; enum OpcodeServer : uint32 @@ -1174,8 +1173,8 @@ enum OpcodeServer : uint32 SMSG_ITEM_TIME_UPDATE = 0x2793, SMSG_KICK_REASON = 0x2821, SMSG_LEARNED_SPELLS = 0x2C4C, - SMSG_LEARN_PVP_TALENT_FAILED = 0x25E7, - SMSG_LEARN_TALENT_FAILED = 0x25E6, + SMSG_LEARN_PVP_TALENTS_FAILED = 0x25E7, + SMSG_LEARN_TALENTS_FAILED = 0x25E6, SMSG_LEVEL_UPDATE = 0x2587, SMSG_LEVEL_UP_INFO = 0x271C, SMSG_LFG_BOOT_PLAYER = 0x2A36, diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 53119742c4c..5b06c9ce03f 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -613,7 +613,6 @@ namespace WorldPackets namespace Talent { - class SetSpecialization; class LearnTalents; class ConfirmRespecWipe; } @@ -1433,7 +1432,6 @@ class TC_GAME_API WorldSession void HandleLearnTalentsOpcode(WorldPackets::Talent::LearnTalents& packet); void HandleConfirmRespecWipeOpcode(WorldPackets::Talent::ConfirmRespecWipe& confirmRespecWipe); void HandleUnlearnSkillOpcode(WorldPackets::Spells::UnlearnSkill& packet); - void HandleSetSpecializationOpcode(WorldPackets::Talent::SetSpecialization& packet); void HandleQuestgiverStatusQueryOpcode(WorldPackets::Quest::QuestGiverStatusQuery& packet); void HandleQuestgiverStatusMultipleQuery(WorldPackets::Quest::QuestGiverStatusMultipleQuery& packet); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index c0746016ff1..8aa8bfc404d 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5404,6 +5404,10 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_EFFECT_TALENT_SPEC_SELECT: { + ChrSpecializationEntry const* spec = sChrSpecializationStore.LookupEntry(m_misc.SpecializationId); + if (!spec || spec->ClassID != m_caster->getClass()) + return SPELL_FAILED_NO_SPEC; + // can't change during already started arena/battleground if (m_caster->GetTypeId() == TYPEID_PLAYER) if (Battleground const* bg = m_caster->ToPlayer()->GetBattleground()) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 523796eb56d..5917ed512c3 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -430,7 +430,6 @@ class TC_GAME_API Spell void EffectTitanGrip(SpellEffIndex effIndex); void EffectEnchantItemPrismatic(SpellEffIndex effIndex); void EffectPlayMusic(SpellEffIndex effIndex); - void EffectSpecCount(SpellEffIndex effIndex); void EffectActivateSpec(SpellEffIndex effIndex); void EffectPlaySound(SpellEffIndex effIndex); void EffectRemoveAura(SpellEffIndex effIndex); @@ -572,6 +571,9 @@ class TC_GAME_API Spell uint32 TalentId; uint32 GlyphSlot; + // SPELL_EFFECT_TALENT_SPEC_SELECT + uint32 SpecializationId; + // SPELL_EFFECT_SET_FOLLOWER_QUALITY // SPELL_EFFECT_INCREASE_FOLLOWER_ITEM_LEVEL // SPELL_EFFECT_INCREASE_FOLLOWER_EXPERIENCE diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index d7aa6769b8b..3e9dec9f485 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -226,7 +226,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectMilling, //158 SPELL_EFFECT_MILLING milling &Spell::EffectRenamePet, //159 SPELL_EFFECT_ALLOW_RENAME_PET allow rename pet once again &Spell::EffectForceCast, //160 SPELL_EFFECT_FORCE_CAST_2 - &Spell::EffectSpecCount, //161 SPELL_EFFECT_TALENT_SPEC_COUNT second talent spec (learn/revert) + &Spell::EffectNULL, //161 SPELL_EFFECT_TALENT_SPEC_COUNT second talent spec (learn/revert) &Spell::EffectActivateSpec, //162 SPELL_EFFECT_TALENT_SPEC_SELECT activate primary/secondary spec &Spell::EffectUnused, //163 SPELL_EFFECT_163 unused &Spell::EffectRemoveAura, //164 SPELL_EFFECT_REMOVE_AURA @@ -5357,17 +5357,6 @@ void Spell::EffectPlayMusic(SpellEffIndex /*effIndex*/) unitTarget->ToPlayer()->GetSession()->SendPacket(WorldPackets::Misc::PlayMusic(soundid).Write()); } -void Spell::EffectSpecCount(SpellEffIndex /*effIndex*/) -{ - if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) - return; - - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - unitTarget->ToPlayer()->UpdateTalentGroupCount(damage); -} - void Spell::EffectActivateSpec(SpellEffIndex /*effIndex*/) { if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) @@ -5376,7 +5365,7 @@ void Spell::EffectActivateSpec(SpellEffIndex /*effIndex*/) if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; - unitTarget->ToPlayer()->ActivateTalentGroup(damage-1); // damage is 1 or 2, spec is 0 or 1 + unitTarget->ToPlayer()->ActivateTalentGroup(sChrSpecializationStore.AssertEntry(m_misc.SpecializationId)); } void Spell::EffectPlaySound(SpellEffIndex /*effIndex*/) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 743237ada6e..6e8db741a3d 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -2843,7 +2843,7 @@ float SpellInfo::CalcProcPPM(Unit* caster, int32 itemLevel) const case SPELL_PPM_MOD_SPEC: { if (Player* plrCaster = caster->ToPlayer()) - if (plrCaster->GetSpecId(plrCaster->GetActiveTalentGroup()) == mod->Param) + if (plrCaster->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == mod->Param) ppm *= 1.0f + mod->Coeff; break; } diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index a4665973009..2c941a4f757 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -162,7 +162,7 @@ class spell_dru_eclipse_energize : public SpellScriptLoader Player* caster = GetCaster()->ToPlayer(); // No boomy, no deal. - if (caster->GetSpecId(caster->GetActiveTalentGroup()) != TALENT_SPEC_DRUID_BALANCE) + if (caster->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) != TALENT_SPEC_DRUID_BALANCE) return; switch (GetSpellInfo()->Id) |