aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/DataStores/DBCStores.cpp5
-rw-r--r--src/server/game/DataStores/DBCStores.h3
-rw-r--r--src/server/game/Entities/Player/Player.cpp61
-rw-r--r--src/server/game/Entities/Player/Player.h1
-rw-r--r--src/server/game/Handlers/SkillHandler.cpp34
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h13
-rw-r--r--src/server/game/Server/Packets/TalentPackets.cpp5
-rw-r--r--src/server/game/Server/Packets/TalentPackets.h10
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp5
-rw-r--r--src/server/game/Server/WorldSession.h6
10 files changed, 107 insertions, 36 deletions
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index a4dd3db66a9..ed909a4f14e 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -75,6 +75,7 @@ DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt);
DBCStorage <ChrRacesEntry> sChrRacesStore(ChrRacesEntryfmt);
DBCStorage <ChrPowerTypesEntry> sChrPowerTypesStore(ChrClassesXPowerTypesfmt);
DBCStorage <ChrSpecializationEntry> sChrSpecializationStore(ChrSpecializationEntryfmt);
+ChrSpecializationByIndexArray sChrSpecializationByIndexStore;
SpecializationSpellsBySpecStore sSpecializationSpellsBySpecStore;
DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore(CinematicSequencesEntryfmt);
DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore(CreatureDisplayInfofmt);
@@ -352,6 +353,10 @@ void LoadDBCStores(const std::string& dataPath)
}
LoadDBC(availableDbcLocales, bad_dbc_files, sChrSpecializationStore, dbcPath, "ChrSpecialization.dbc");
+ memset(sChrSpecializationByIndexStore, 0, sizeof(sChrSpecializationByIndexStore));
+ for (uint32 i = 0; i < sChrSpecializationStore.GetNumRows(); ++i)
+ if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(i))
+ sChrSpecializationByIndexStore[chrSpec->ClassID][chrSpec->OrderIndex] = chrSpec;
LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicSequencesStore, dbcPath, "CinematicSequences.dbc");//19116
LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoStore, dbcPath, "CreatureDisplayInfo.dbc");//19116
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index bb7885c8bc2..e9a9b3c692f 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -22,6 +22,7 @@
#include "Common.h"
#include "DBCStore.h"
#include "DBCStructure.h"
+#include "SharedDefines.h"
#include <list>
@@ -90,6 +91,7 @@ SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, u
typedef std::set<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;
extern DBCStorage <AchievementEntry> sAchievementStore;
@@ -110,6 +112,7 @@ extern DBCStorage <ChrClassesEntry> sChrClassesStore;
extern DBCStorage <ChrRacesEntry> sChrRacesStore;
extern DBCStorage <ChrPowerTypesEntry> sChrPowerTypesStore;
extern DBCStorage <ChrSpecializationEntry> sChrSpecializationStore;
+extern ChrSpecializationByIndexArray sChrSpecializationByIndexStore;
extern DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore;
extern DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore;
extern DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index c0aac237d0d..3ba4f662426 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -25427,6 +25427,10 @@ 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);
@@ -25460,34 +25464,6 @@ bool Player::LearnTalent(uint32 talentId)
if (HasSpell(spellid))
return false;
- // set talent spec for player
- if (!GetTalentSpec(group))
- {
- SetTalentSpec(group, talentInfo->SpecID);
-
- // Replace default spells by specialization spells
- auto specSpells = sSpecializationSpellsBySpecStore.find(talentInfo->SpecID);
- if (specSpells != sSpecializationSpellsBySpecStore.end())
- {
- for (auto it = specSpells->second.begin(); it != specSpells->second.end(); ++it)
- {
- SpecializationSpellsEntry const* specSpell = *it;
- if (HasSpell(specSpell->OverridesSpellID)) {
- RemoveSpell(specSpell->OverridesSpellID, true);
- LearnSpell(specSpell->SpellID, false);
- }
- }
- }
-
- if (CanUseMastery()) {
- ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(talentInfo->SpecID);
- 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);
- }
- }
-
// Check talent spec
if (talentInfo->SpecID != GetTalentSpec(group))
return false;
@@ -25501,6 +25477,35 @@ bool Player::LearnTalent(uint32 talentId)
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 (HasSpell(specSpell->OverridesSpellID)) {
+ RemoveSpell(specSpell->OverridesSpellID, true);
+ LearnSpell(specSpell->SpellID, false);
+ }
+ }
+ }
+
+ if (CanUseMastery()) {
+ 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);
+ }
+
+ SendTalentsInfoData();
+}
+
void Player::AddKnownCurrency(uint32 itemId)
{
if (CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId))
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index a5698740092..39eace840f3 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1841,6 +1841,7 @@ class Player : public Unit, public GridObject<Player>
bool LearnTalent(uint32 talentId);
bool AddTalent(uint32 talentId, uint8 spec);
bool HasTalent(uint32 talentId, uint8 spec);
+ void LearnTalentSpecialization(uint32 talentSpec);
// Dual Spec
void UpdateTalentGroupCount(uint8 count);
diff --git a/src/server/game/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp
index ef1e9031cb9..f0c1b28117b 100644
--- a/src/server/game/Handlers/SkillHandler.cpp
+++ b/src/server/game/Handlers/SkillHandler.cpp
@@ -26,6 +26,7 @@
#include "UpdateMask.h"
#include "WorldPacket.h"
#include "WorldSession.h"
+#include "TalentPackets.h"
void WorldSession::HandleLearnTalentOpcode(WorldPacket& recvData)
{
@@ -126,3 +127,36 @@ void WorldSession::HandleUnlearnSkillOpcode(WorldPacket& recvData)
GetPlayer()->SetSkill(skillId, 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 = sChrSpecializationByIndexStore[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/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 9e8b5ca00f3..68c30716ad9 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -815,12 +815,13 @@ enum SpellAttr13
SPELL_ATTR13_UNK23 = 0x00800000 // 23
};
-#define MIN_TALENT_GROUP 0
-#define MAX_TALENT_GROUP 1
-#define MIN_TALENT_GROUPS 1
-#define MAX_TALENT_GROUPS 2
-#define MAX_GLYPH_SLOT_INDEX 9
-#define REQ_PRIMARY_TREE_TALENTS 31
+#define MIN_TALENT_GROUP 0
+#define MAX_TALENT_GROUP 1
+#define MIN_TALENT_GROUPS 1
+#define MAX_TALENT_GROUPS 2
+#define MAX_GLYPH_SLOT_INDEX 9
+#define MIN_SPECIALIZATION_LEVEL 10
+#define MAX_SPECIALIZATIONS 4
// Custom values
enum SpellClickUserTypes
diff --git a/src/server/game/Server/Packets/TalentPackets.cpp b/src/server/game/Server/Packets/TalentPackets.cpp
index 3410fb273f7..4855f663662 100644
--- a/src/server/game/Server/Packets/TalentPackets.cpp
+++ b/src/server/game/Server/Packets/TalentPackets.cpp
@@ -36,3 +36,8 @@ WorldPacket const* WorldPackets::Talent::UpdateTalentData::Write()
return &_worldPacket;
}
+
+void WorldPackets::Talent::SetSpecialization::Read()
+{
+ _worldPacket >> SpecGroupIndex;
+}
diff --git a/src/server/game/Server/Packets/TalentPackets.h b/src/server/game/Server/Packets/TalentPackets.h
index c756c6962bb..82d2e7985c4 100644
--- a/src/server/game/Server/Packets/TalentPackets.h
+++ b/src/server/game/Server/Packets/TalentPackets.h
@@ -48,6 +48,16 @@ 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;
+ };
}
}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 417a7420361..c7b3b549468 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -24,6 +24,7 @@
#include "Packets/MiscPackets.h"
#include "Packets/MovementPackets.h"
#include "Packets/QueryPackets.h"
+#include "Packets/TalentPackets.h"
#include "Packets/TradePackets.h"
template<class PacketClass, void(WorldSession::*HandlerFunction)(PacketClass&)>
@@ -591,7 +592,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_SAVED_INSTANCE_EXTEND, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetSavedInstanceExtend );
DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_SELECTION, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::HandleSetSelectionOpcode );
DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_SKILL_CHEAT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
- DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_SPECIALIZATION, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
+ DEFINE_HANDLER(CMSG_SET_SPECIALIZATION, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Talent::SetSpecialization, &WorldSession::HandleSetSpecializationOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_TAXI_BENCHMARK_MODE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTaxiBenchmarkOpcode );
DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_TITLE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTitleOpcode );
DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_TRADE_CURRENCY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
@@ -1343,7 +1344,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SUSPEND_COMMS, STATUS_UNHANDLED);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SUSPEND_TOKEN, STATUS_UNHANDLED);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TALENTS_ERROR, STATUS_UNHANDLED);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_TALENTS_INFO, STATUS_UNHANDLED);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_TALENTS_INFO, STATUS_NEVER);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TALENTS_INVOLUNTARILY_RESET, STATUS_UNHANDLED);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TAXINODE_STATUS, STATUS_UNHANDLED);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TEST_DROP_RATE_RESULT, STATUS_UNHANDLED);
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 445e7412629..59049d12107 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -106,6 +106,11 @@ namespace WorldPackets
{
class QueryGuildInfo;
}
+
+ namespace Talent
+ {
+ class SetSpecialization;
+ }
namespace Trade
{
@@ -787,6 +792,7 @@ class WorldSession
void HandleLearnPreviewTalents(WorldPacket& recvPacket);
void HandleTalentWipeConfirmOpcode(WorldPacket& recvPacket);
void HandleUnlearnSkillOpcode(WorldPacket& recvPacket);
+ void HandleSetSpecializationOpcode(WorldPackets::Talent::SetSpecialization& packet);
void HandleQuestgiverStatusQueryOpcode(WorldPacket& recvPacket);
void HandleQuestgiverStatusMultipleQuery(WorldPacket& recvPacket);