diff options
author | Intel <chemicstry@gmail.com> | 2014-11-12 03:58:17 +0200 |
---|---|---|
committer | Intel <chemicstry@gmail.com> | 2014-11-12 21:43:48 +0200 |
commit | da52c8d54ec341cc55f963fe0397fcc5e98b06e9 (patch) | |
tree | 6b2249c92c3567ae560f9c2c989114c9d1d5e124 | |
parent | 620f23d9dcb94b21c2b3402765817362e2b82b2f (diff) |
Core/Talents:
Fixed SMSG_TALENTS_INFO packet
Fixed talents saving to DB
Renamed 'talent spec' to 'talent group' since this name was shadowing character specializations (and new name is correct according to JAM)
Enabled loading of MinorTalent.dbc (will be used later)
Added additional specialization check in LearnTalent
24 files changed, 337 insertions, 385 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index 30934a4a81f..29f14fa7b39 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -763,7 +763,7 @@ DROP TABLE IF EXISTS `character_glyphs`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `character_glyphs` ( `guid` bigint(20) unsigned NOT NULL, - `spec` tinyint(3) unsigned NOT NULL DEFAULT '0', + `talentGroup` tinyint(3) unsigned NOT NULL DEFAULT '0', `glyph1` smallint(5) unsigned DEFAULT '0', `glyph2` smallint(5) unsigned DEFAULT '0', `glyph3` smallint(5) unsigned DEFAULT '0', @@ -773,7 +773,7 @@ CREATE TABLE `character_glyphs` ( `glyph7` smallint(5) unsigned DEFAULT '0', `glyph8` smallint(5) unsigned DEFAULT '0', `glyph9` smallint(5) unsigned DEFAULT '0', - PRIMARY KEY (`guid`,`spec`) + PRIMARY KEY (`guid`,`talentGroup`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -1276,8 +1276,8 @@ DROP TABLE IF EXISTS `character_talent`; CREATE TABLE `character_talent` ( `guid` bigint(20) unsigned NOT NULL, `spell` mediumint(8) unsigned NOT NULL, - `spec` tinyint(3) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`guid`,`spell`,`spec`) + `talentGroup` tinyint(3) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`guid`,`spell`,`talentGroup`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -1383,8 +1383,8 @@ CREATE TABLE `characters` ( `power4` int(10) unsigned NOT NULL DEFAULT '0', `power5` int(10) unsigned NOT NULL DEFAULT '0', `latency` mediumint(8) unsigned NOT NULL DEFAULT '0', - `speccount` tinyint(3) unsigned NOT NULL DEFAULT '1', - `activespec` tinyint(3) unsigned NOT NULL DEFAULT '0', + `talentGroupsCount` tinyint(3) unsigned NOT NULL DEFAULT '1', + `activeTalentGroup` tinyint(3) unsigned NOT NULL DEFAULT '0', `exploredZones` longtext, `equipmentCache` longtext, `knownTitles` longtext, diff --git a/sql/updates/characters/2014_11_12_00_characters.sql b/sql/updates/characters/2014_11_12_00_characters.sql new file mode 100644 index 00000000000..68aaf99d5d0 --- /dev/null +++ b/sql/updates/characters/2014_11_12_00_characters.sql @@ -0,0 +1,4 @@ +ALTER TABLE `character_glyphs` CHANGE `spec` `talentGroup` tinyint(3) unsigned NOT NULL DEFAULT '0'; +ALTER TABLE `character_talent` CHANGE `spec` `talentGroup` tinyint(3) unsigned NOT NULL DEFAULT '0'; +ALTER TABLE `characters` CHANGE `speccount` `talentGroupsCount` tinyint(3) unsigned NOT NULL DEFAULT '1'; +ALTER TABLE `characters` CHANGE `activespec` `activeTalentGroup` tinyint(3) unsigned NOT NULL DEFAULT '0'; diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 35956191f5e..d1aa2f77cf8 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -1070,7 +1070,7 @@ void Battleground::AddPlayer(Player* player) BattlegroundPlayer bp; bp.OfflineRemoveTime = 0; bp.Team = team; - bp.ActiveSpec = player->GetTalentSpec(player->GetActiveSpec()); + bp.ActiveSpec = player->GetActiveTalentSpec(); // Add to list/maps m_Players[player->GetGUID()] = bp; diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 598c2e5d5f3..333b7f5a2c2 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -163,6 +163,8 @@ DBCStorage <MapEntry> sMapStore(MapEntryfmt); DBCStorage <MapDifficultyEntry> sMapDifficultyStore(MapDifficultyEntryfmt); // only for loading MapDifficultyMap sMapDifficultyMap; +DBCStorage <MinorTalentEntry> sMinorTalentStore(MinorTalentEntryfmt); + DBCStorage <MovieEntry> sMovieStore(MovieEntryfmt); DBCStorage <MountCapabilityEntry> sMountCapabilityStore(MountCapabilityfmt); DBCStorage <MountTypeEntry> sMountTypeStore(MountTypefmt); @@ -570,7 +572,7 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sSpellShapeshiftStore, dbcPath, "SpellShapeshift.dbc");//19116 LoadDBC(availableDbcLocales, bad_dbc_files, sSpellShapeshiftFormStore, dbcPath, "SpellShapeshiftForm.dbc");//19116 //LoadDBC(availableDbcLocales, bad_dbc_files, sStableSlotPricesStore, dbcPath, "StableSlotPrices.dbc"); - LoadDBC(availableDbcLocales, bad_dbc_files, sSummonPropertiesStore, dbcPath, "SummonProperties.dbc");//15595 + LoadDBC(availableDbcLocales, bad_dbc_files, sSummonPropertiesStore, dbcPath, "SummonProperties.dbc");//19116 // Must be done when sSkillLineAbilityStore, sSpellStore, sSpellLevelsStore and sCreatureFamilyStore are all loaded /* TODO: Requires spells attributes from SpellMisc.db2 is loaded after dbc @@ -608,8 +610,6 @@ void LoadDBCStores(const std::string& dataPath) } */ - LoadDBC(availableDbcLocales, bad_dbc_files, sTalentStore, dbcPath, "Talent.dbc");//15595 - // Create Spelldifficulty searcher /* TODO: 6.x update to new spell diffs for (uint32 i = 0; i < sSpellDifficultyStore.GetNumRows(); ++i) @@ -640,6 +640,9 @@ void LoadDBCStores(const std::string& dataPath) sSpellMgr->SetSpellDifficultyId(uint32(newEntry.SpellID[x]), spellDiff->ID); }*/ + LoadDBC(availableDbcLocales, bad_dbc_files, sTalentStore, dbcPath, "Talent.dbc");//19116 + LoadDBC(availableDbcLocales, bad_dbc_files, sMinorTalentStore, dbcPath, "MinorTalent.dbc");//19116 + for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i) { TalentEntry const* talentInfo = sTalentStore.LookupEntry(i); @@ -649,35 +652,6 @@ void LoadDBCStores(const std::string& dataPath) sTalentBySpellIDMap[talentInfo->SpellID] = talentInfo; } - // create talent spells set - /* TODO: 6.x update to new talent system - - // prepare fast data access to bit pos of talent ranks for use at inspecting - { - // now have all max ranks (and then bit amount used for store talent ranks in inspect) - for (uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId) - { - TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentTabId); - if (!talentTabInfo) - continue; - - // prevent memory corruption; otherwise cls will become 12 below - if ((talentTabInfo->ClassMask & CLASSMASK_ALL_PLAYABLE) == 0) - continue; - - // store class talent tab pages - for (uint32 cls = 1; cls < MAX_CLASSES; ++cls) - if (talentTabInfo->ClassMask & (1 << (cls - 1))) - sTalentTabPages[cls][talentTabInfo->tabpage] = talentTabId; - } - } - - LoadDBC(availableDbcLocales, bad_dbc_files, sTalentTreePrimarySpellsStore, dbcPath, "TalentTreePrimarySpells.dbc"); - for (uint32 i = 0; i < sTalentTreePrimarySpellsStore.GetNumRows(); ++i) - if (TalentTreePrimarySpellsEntry const* talentSpell = sTalentTreePrimarySpellsStore.LookupEntry(i)) - sTalentTreePrimarySpellsMap[talentSpell->TalentTree].push_back(talentSpell->SpellId); - sTalentTreePrimarySpellsStore.Clear();*/ - LoadDBC(availableDbcLocales, bad_dbc_files, sTaxiNodesStore, dbcPath, "TaxiNodes.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sTaxiPathStore, dbcPath, "TaxiPath.dbc");//15595 for (uint32 i = 1; i < sTaxiPathStore.GetNumRows(); ++i) diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index bb75a5ed6b5..c59782983ec 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -179,6 +179,7 @@ extern DBCStorage <LiquidTypeEntry> sLiquidTypeStore; extern DBCStorage <LockEntry> sLockStore; extern DBCStorage <MailTemplateEntry> sMailTemplateStore; extern DBCStorage <MapEntry> sMapStore; +extern DBCStorage <MinorTalentEntry> sMinorTalentStore; extern DBCStorage <MountCapabilityEntry> sMountCapabilityStore; extern DBCStorage <MountTypeEntry> sMountTypeStore; extern DBCStorage <NameGenEntry> sNameGenStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 2556c141d8d..e1f6344e067 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1608,6 +1608,14 @@ struct MapDifficultyEntry //char* LockID; // 6 }; +struct MinorTalentEntry +{ + uint32 ID; // 0 + uint32 SpecID; // 1 + uint32 SpellID; // 2 + uint32 OrderIndex; // 3 +}; + struct MountCapabilityEntry { uint32 ID; // 0 diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 4c590e8e6ee..640d10aae19 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -107,6 +107,7 @@ char const PhaseGroupfmt[] = "nii"; char const MailTemplateEntryfmt[] = "nxs"; char const MapEntryfmt[] = "nxiixxsixxixiffxiiiixx"; char const MapDifficultyEntryfmt[] = "diisiix"; +char const MinorTalentEntryfmt[] = "niii"; char const MovieEntryfmt[] = "nxxxx"; char const MountCapabilityfmt[] = "niiiiiii"; char const MountTypefmt[] = "niiiiiiiiiiiiiiiiiiiiiiii"; @@ -156,9 +157,7 @@ char const SpellShapeshiftEntryfmt[] = "niiiix"; char const SpellShapeshiftFormfmt[] = "nxxiixiiiiiiiiiiiiixx"; char const StableSlotPricesfmt[] = "ni"; char const SummonPropertiesfmt[] = "niiiii"; -char const TalentEntryfmt[] = "niiiiiiiiixxixxxxxx"; -char const TalentTabEntryfmt[] = "nxxiiixxxii"; -char const TalentTreePrimarySpellsfmt[] = "diix"; +char const TalentEntryfmt[] = "niiiiiiiiix"; char const TaxiNodesEntryfmt[] = "nifffsiiixx"; char const TaxiPathEntryfmt[] = "niii"; char const TaxiPathNodeEntryfmt[] = "diiifffiiii"; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 0fe830f861e..52045f46472 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -341,7 +341,8 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c if (owner->GetGroup()) owner->SetGroupUpdateFlag(GROUP_UPDATE_PET); - owner->SendTalentsInfoData(true); + // TODO: 6.x remove/update pet talents + //owner->SendTalentsInfoData(true); if (getPetType() == HUNTER_PET) { @@ -1735,6 +1736,7 @@ void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* onlinePet /*= NULL*/) void Pet::InitTalentForLevel() { + /* TODO: 6.x remove/update pet talents uint8 level = getLevel(); uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level); // Reset talents in case low level (on level down) or wrong points for level (hunter can unlearn TP increase talent) @@ -1745,6 +1747,7 @@ void Pet::InitTalentForLevel() if (!m_loading) GetOwner()->SendTalentsInfoData(true); + */ } uint8 Pet::GetMaxTalentPointsForLevel(uint8 level) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c0753f72942..e412aa43098 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -32,6 +32,7 @@ #include "ChannelMgr.h" #include "CharacterDatabaseCleaner.h" #include "CharacterPackets.h" +#include "TalentPackets.h" #include "Chat.h" #include "Common.h" #include "ConditionMgr.h" @@ -2985,7 +2986,7 @@ void Player::GiveLevel(uint8 level) void Player::InitTalentForLevel() { if (!GetSession()->PlayerLoading()) - SendTalentsInfoData(false); // update at client + SendTalentsInfoData(); // update at client } void Player::InitStatsForLevel(bool reapplyMods) @@ -3308,20 +3309,29 @@ void DeleteSpellFromAllPlayers(uint32 spellId) } } -bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) +bool Player::AddTalent(uint32 talentId, uint8 spec) { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + TalentEntry const* talentEntry = sTalentStore.LookupEntry(talentId); + + // Check if talent exists in Talent.dbc + if (!talentEntry) + { + TC_LOG_ERROR("spells", "Player::addTalent: Talent %u not found", talentId); + return false; + } + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talentEntry->SpellID); if (!spellInfo) { // do character spell book cleanup (all characters) - if (!IsInWorld() && !learning) // spell load case + if (!IsInWorld()) // spell load case { - TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId); + TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", talentEntry->SpellID); - DeleteSpellFromAllPlayers(spellId); + DeleteSpellFromAllPlayers(talentEntry->SpellID); } else - TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId); + TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request.", talentEntry->SpellID); return false; } @@ -3329,35 +3339,25 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) if (!SpellMgr::IsSpellValid(spellInfo, this, false)) { // do character spell book cleanup (all characters) - if (!IsInWorld() && !learning) // spell load case + if (!IsInWorld()) // spell load case { - TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", spellId); + TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", talentEntry->SpellID); - DeleteSpellFromAllPlayers(spellId); + DeleteSpellFromAllPlayers(talentEntry->SpellID); } else - TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed.", spellId); - - return false; - } + TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed.", talentEntry->SpellID); - TalentEntry const* talentEntry = GetTalentBySpellID(spellId); - - // Check if talent exists in Talent.dbc - if (!talentEntry) { - TC_LOG_ERROR("spells", "Player::addTalent: Learning non-talent spell %u not allowed.", spellId); return false; } - TalentSpecInfo* talentSpecInfo = GetTalentSpecInfo(spec); + TalentGroupInfo* talentGroupInfo = GetTalentGroupInfo(spec); // Check if player already has this talent - if (talentSpecInfo->HasTalent(spellId)) + if (talentGroupInfo->HasTalent(talentId)) return false; - talentSpecInfo->Talents[talentEntry->TierID].SpellID = spellId; - if (learning) - talentSpecInfo->Talents[talentEntry->TierID].State = PLAYERSPELL_NEW; + talentGroupInfo->Talents[talentEntry->TierID] = talentId; return true; } @@ -3707,7 +3707,7 @@ bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const bool Player::IsCurrentSpecMasterySpell(SpellInfo const* spellInfo) const { - if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetTalentSpec(GetActiveSpec()))) + if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec())) return spellInfo->Id == chrSpec->MasterySpellID[0] || spellInfo->Id == chrSpec->MasterySpellID[1]; return false; @@ -4121,7 +4121,8 @@ bool Player::ResetTalents(bool no_cost) RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true); - uint8 spec = GetActiveSpec(); + uint8 group = GetActiveTalentGroup(); + uint32 specID = GetActiveTalentSpec(); for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) { @@ -4147,13 +4148,10 @@ bool Player::ResetTalents(bool no_cost) for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (_spellEntry->Effects[i].TriggerSpell > 0 && _spellEntry->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL) RemoveSpell(_spellEntry->Effects[i].TriggerSpell, true); - - // Reset talents store - GetTalentSpecInfo(spec)->Reset(); } // Remove all specialization specific spells and give default ones which were overriden - auto specSpells = sSpecializationSpellsBySpecStore.find(GetTalentSpec(GetActiveSpec())); + auto specSpells = sSpecializationSpellsBySpecStore.find(specID); if (specSpells != sSpecializationSpellsBySpecStore.end()) { for (auto it = specSpells->second.begin(); it != specSpells->second.end(); ++it) @@ -4168,12 +4166,14 @@ bool Player::ResetTalents(bool no_cost) } // Unlearn masteries - ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetTalentSpec(spec)); + ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(specID); for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i) if (uint32 mastery = chrSpec->MasterySpellID[i]) RemoveAurasDueToSpell(mastery); - SetTalentSpec(spec, 0); + // Reset talents store + GetTalentGroupInfo(group)->Reset(); + SetTalentSpec(group, 0); SQLTransaction trans = CharacterDatabase.BeginTransaction(); _SaveTalents(trans); @@ -4265,9 +4265,9 @@ bool Player::HasSpell(uint32 spell) const !itr->second->disabled); } -bool Player::HasTalent(uint32 spell, uint8 spec) +bool Player::HasTalent(uint32 talentId, uint8 group) { - return GetTalentSpecInfo(spec)->HasTalent(spell); + return GetTalentGroupInfo(group)->HasTalent(talentId); } bool Player::HasActiveSpell(uint32 spell) const @@ -6269,7 +6269,7 @@ void Player::SendActionButtons(uint32 state) const data << uint8(state); GetSession()->SendPacket(&data); - TC_LOG_INFO("network", "Action Buttons for '%s' spec '%u' Sent", GetGUID().ToString().c_str(), GetActiveSpec()); + TC_LOG_INFO("network", "Action Buttons for '%s' group '%u' Sent", GetGUID().ToString().c_str(), GetActiveTalentGroup()); } bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type) @@ -9684,6 +9684,7 @@ void Player::SendTalentWipeConfirm(ObjectGuid guid) void Player::ResetPetTalents() { + /* TODO: 6.x remove/update pet talents // This needs another gossip option + NPC text as a confirmation. // The confirmation gossip listid has the text: "Yes, please do." Pet* pet = GetPet(); @@ -9699,6 +9700,7 @@ void Player::ResetPetTalents() } pet->resetTalents(); SendTalentsInfoData(true); + */ } /*********************************************************/ @@ -14117,7 +14119,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool break; } case GOSSIP_OPTION_LEARNDUALSPEC: - if (!(GetSpecsCount() == 1 && creature->isCanTrainingAndResetTalentsOf(this) && !(getLevel() < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)))) + if (!(GetTalentGroupsCount() == 1 && creature->isCanTrainingAndResetTalentsOf(this) && !(getLevel() < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)))) canTalk = false; break; case GOSSIP_OPTION_UNLEARNTALENTS: @@ -14331,7 +14333,7 @@ void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 men GetSession()->SendTrainerList(guid); break; case GOSSIP_OPTION_LEARNDUALSPEC: - if (GetSpecsCount() == 1 && getLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)) + 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 @@ -17431,19 +17433,19 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) //mails are loaded only when needed ;-) - when player in game click on mailbox. //_LoadMail(); - SetSpecsCount(fields[53].GetUInt8()); - SetActiveSpec(fields[54].GetUInt8()); + SetTalentGroupsCount(fields[53].GetUInt8()); + SetActiveTalentGroup(fields[54].GetUInt8()); // sanity check - if (GetSpecsCount() > MAX_TALENT_SPECS || GetActiveSpec() > MAX_TALENT_SPEC || GetSpecsCount() < MIN_TALENT_SPECS) + if (GetTalentGroupsCount() > MAX_TALENT_GROUPS || GetActiveTalentGroup() > MAX_TALENT_GROUP || GetTalentGroupsCount() < MIN_TALENT_GROUPS) { - SetActiveSpec(0); - TC_LOG_ERROR("entities.player", "Player %s (%s) has SpecCount = %u and ActiveSpec = %u.", GetName().c_str(), GetGUID().ToString().c_str(), GetSpecsCount(), GetActiveSpec()); + TC_LOG_ERROR("entities.player", "Player %s (%s) has SpecCount = %u and ActiveSpec = %u.", GetName().c_str(), GetGUID().ToString().c_str(), GetTalentGroupsCount(), GetActiveTalentGroup()); + SetActiveTalentGroup(0); } // Only load selected specializations, learning mastery spells requires this - Tokenizer talentSpecs(fields[26].GetString(), ' ', MAX_TALENT_SPECS); - for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) + Tokenizer talentSpecs(fields[26].GetString(), ' ', MAX_TALENT_GROUPS); + for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) { if (i >= talentSpecs.size()) break; @@ -17804,7 +17806,7 @@ void Player::_LoadGlyphAuras() { for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) { - if (uint32 glyph = GetGlyph(GetActiveSpec(), i)) + if (uint32 glyph = GetGlyph(GetActiveTalentGroup(), i)) { if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph)) { @@ -19104,7 +19106,7 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt32(index++, GetTalentResetTime()); ss.str(""); - for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) + for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) ss << GetTalentSpec(i) << " "; stmt->setString(index++, ss.str()); stmt->setUInt16(index++, (uint16)m_ExtraFlags); @@ -19141,8 +19143,8 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt32(index++, GetSession()->GetLatency()); - stmt->setUInt8(index++, GetSpecsCount()); - stmt->setUInt8(index++, GetActiveSpec()); + stmt->setUInt8(index++, GetTalentGroupsCount()); + stmt->setUInt8(index++, GetActiveTalentGroup()); ss.str(""); for (uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i) @@ -19233,7 +19235,7 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt32(index++, GetTalentResetTime()); ss.str(""); - for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) + for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) ss << GetTalentSpec(i) << " "; stmt->setString(index++, ss.str()); stmt->setUInt16(index++, (uint16)m_ExtraFlags); @@ -19270,8 +19272,8 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt32(index++, GetSession()->GetLatency()); - stmt->setUInt8(index++, GetSpecsCount()); - stmt->setUInt8(index++, GetActiveSpec()); + stmt->setUInt8(index++, GetTalentGroupsCount()); + stmt->setUInt8(index++, GetActiveTalentGroup()); ss.str(""); for (uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i) @@ -19377,7 +19379,7 @@ void Player::_SaveActions(SQLTransaction& trans) case ACTIONBUTTON_NEW: stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACTION); stmt->setUInt64(0, GetGUID().GetCounter()); - stmt->setUInt8(1, GetActiveSpec()); + stmt->setUInt8(1, GetActiveTalentGroup()); stmt->setUInt8(2, itr->first); stmt->setUInt32(3, itr->second.GetAction()); stmt->setUInt8(4, uint8(itr->second.GetType())); @@ -19392,7 +19394,7 @@ void Player::_SaveActions(SQLTransaction& trans) stmt->setUInt8(1, uint8(itr->second.GetType())); stmt->setUInt64(2, GetGUID().GetCounter()); stmt->setUInt8(3, itr->first); - stmt->setUInt8(4, GetActiveSpec()); + stmt->setUInt8(4, GetActiveTalentGroup()); trans->Append(stmt); itr->second.uState = ACTIONBUTTON_UNCHANGED; @@ -19402,7 +19404,7 @@ void Player::_SaveActions(SQLTransaction& trans) stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACTION_BY_BUTTON_SPEC); stmt->setUInt64(0, GetGUID().GetCounter()); stmt->setUInt8(1, itr->first); - stmt->setUInt8(2, GetActiveSpec()); + stmt->setUInt8(2, GetActiveTalentGroup()); trans->Append(stmt); m_actionButtons.erase(itr++); @@ -23054,7 +23056,7 @@ void Player::SendInitialPacketsBeforeAddToMap() // SMSG_SET_FLAT_SPELL_MODIFIER // SMSG_UPDATE_AURA_DURATION - SendTalentsInfoData(false); + SendTalentsInfoData(); data.Initialize(SMSG_WORLD_SERVER_INFO, 1 + 1 + 4 + 4); data.WriteBit(0); // HasRestrictedLevel @@ -24802,7 +24804,7 @@ void Player::InitGlyphsForLevel() void Player::SetGlyph(uint8 slot, uint32 glyph) { - _talentMgr->SpecInfo[GetActiveSpec()].Glyphs[slot] = glyph; + _talentMgr->GroupInfo[GetActiveTalentGroup()].Glyphs[slot] = glyph; SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, glyph); } @@ -25487,7 +25489,7 @@ void Player::CompletedAchievement(AchievementEntry const* entry) bool Player::LearnTalent(uint32 talentId) { - uint8 spec = GetActiveSpec(); + uint8 group = GetActiveTalentGroup(); TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); @@ -25498,13 +25500,15 @@ bool Player::LearnTalent(uint32 talentId) if (getClass() != talentInfo->ClassID) return false; - // Check player level. - if (getLevel() < (15*talentInfo->TierID + 15)) + // 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) return false; // Check if such tier talent hasn't been picked already - TalentSpecInfo* talentSpecInfo = GetTalentSpecInfo(spec); - if (talentSpecInfo->Talents[talentInfo->TierID].SpellID != 0) + TalentGroupInfo* talentGroupInfo = GetTalentGroupInfo(group); + if (talentGroupInfo->Talents[talentInfo->TierID]) return false; // spell not set in talent.dbc @@ -25519,16 +25523,10 @@ bool Player::LearnTalent(uint32 talentId) if (HasSpell(spellid)) return false; - // learn! (other talent ranks will unlearned at learning) - LearnSpell(spellid, false); - AddTalent(spellid, spec, true); - - TC_LOG_INFO("misc", "TalentID: %u Spell: %u Spec: %u\n", talentId, spellid, spec); - - // set talent tree for player - if (!GetTalentSpec(spec)) + // set talent spec for player + if (!GetTalentSpec(group)) { - SetTalentSpec(spec, talentInfo->SpecID); + SetTalentSpec(group, talentInfo->SpecID); // Replace default spells by specialization spells auto specSpells = sSpecializationSpellsBySpecStore.find(talentInfo->SpecID); @@ -25553,6 +25551,16 @@ bool Player::LearnTalent(uint32 talentId) } } + // Check talent spec + if (talentInfo->SpecID != GetTalentSpec(group)) + 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); + return true; } @@ -25630,160 +25638,38 @@ bool Player::CanSeeSpellClickOn(Creature const* c) const return false; } -void Player::BuildPlayerTalentsInfoData(WorldPacket* data) -{ - /* TODO: 6.x update with new talent system (and move to packet class) - *data << uint32(GetFreeTalentPoints()); // unspentTalentPoints - *data << uint8(GetSpecsCount()); // talent group count (0, 1 or 2) - *data << uint8(GetActiveSpec()); // talent group index (0 or 1) - - if (GetSpecsCount()) - { - if (GetSpecsCount() > MAX_TALENT_SPECS) - SetSpecsCount(MAX_TALENT_SPECS); - - // loop through all specs (only 1 for now) - for (uint8 specIdx = 0; specIdx < GetSpecsCount(); ++specIdx) - { - *data << uint32(GetPrimaryTalentTree(specIdx)); - uint8 talentIdCount = 0; - size_t pos = data->wpos(); - *data << uint8(talentIdCount); // [PH], talentIdCount - - // find class talent tabs (all players have 3 talent tabs) - uint32 const* talentTabIds = GetTalentTabPages(getClass()); - - for (uint8 i = 0; i < MAX_TALENT_TABS; ++i) - { - uint32 talentTabId = talentTabIds[i]; - - for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) - { - TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); - if (!talentInfo) - continue; - - // skip another tab talents - if (talentInfo->TalentTab != talentTabId) - continue; - - // find max talent rank (0~4) - int8 curtalent_maxrank = -1; - for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank) - { - if (talentInfo->RankID[rank] && HasTalent(talentInfo->RankID[rank], specIdx)) - { - curtalent_maxrank = rank; - break; - } - } - - // not learned talent - if (curtalent_maxrank < 0) - continue; - - *data << uint32(talentInfo->TalentID); // Talent.dbc - *data << uint8(curtalent_maxrank); // talentMaxRank (0-4) - - ++talentIdCount; - } - } - - data->put<uint8>(pos, talentIdCount); // put real count - - *data << uint8(MAX_GLYPH_SLOT_INDEX); // glyphs count - - for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) - *data << uint16(GetGlyph(specIdx, i)); // GlyphProperties.dbc - } - } - */ -} - -void Player::BuildPetTalentsInfoData(WorldPacket* data) +void Player::SendTalentsInfoData() { - /* TODO: 6.x update with new talent system (and move to packet class) - uint32 unspentTalentPoints = 0; - size_t pointsPos = data->wpos(); - *data << uint32(unspentTalentPoints); // [PH], unspentTalentPoints - - uint8 talentIdCount = 0; - size_t countPos = data->wpos(); - *data << uint8(talentIdCount); // [PH], talentIdCount - - Pet* pet = GetPet(); - if (!pet) - return; + WorldPackets::Talent::UpdateTalentData packet; - unspentTalentPoints = pet->GetFreeTalentPoints(); + packet.Info.ActiveGroup = GetActiveTalentGroup(); + + uint8 groupsCount = GetTalentGroupsCount(); - data->put<uint32>(pointsPos, unspentTalentPoints); // put real points - - CreatureTemplate const* ci = pet->GetCreatureTemplate(); - if (!ci) - return; - - CreatureFamilyEntry const* pet_family = sCreatureFamilyStore.LookupEntry(ci->family); - if (!pet_family || pet_family->PetTalentType < 0) - return; - - for (uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId) + for (uint8 i = 0; i < groupsCount; ++i) { - TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentTabId); - if (!talentTabInfo) - continue; - - if (!((1 << pet_family->PetTalentType) & talentTabInfo->petTalentMask)) - continue; - - for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) + TalentGroupInfo* groupInfo = GetTalentGroupInfo(i); + WorldPackets::Talent::TalentGroupInfo groupInfoPkt; + + groupInfoPkt.SpecID = groupInfo->SpecID; + + groupInfoPkt.TalentIDs.reserve(MAX_TALENT_TIERS); + for (uint32 x = 0; x < MAX_TALENT_TIERS; ++x) { - TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); - if (!talentInfo) - continue; - - // skip another tab talents - if (talentInfo->TalentTab != talentTabId) - continue; - - // find max talent rank (0~4) - int8 curtalent_maxrank = -1; - for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank) - { - if (talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank])) - { - curtalent_maxrank = rank; - break; - } - } - - // not learned talent - if (curtalent_maxrank < 0) - continue; - - *data << uint32(talentInfo->TalentID); // Talent.dbc - *data << uint8(curtalent_maxrank); // talentMaxRank (0-4) + // Do not send empty talents + if (!groupInfo->Talents[x]) + break; - ++talentIdCount; + groupInfoPkt.TalentIDs.push_back(groupInfo->Talents[x]); } + + for (uint32 x = 0; x < MAX_GLYPH_SLOT_INDEX; ++x) + groupInfoPkt.GlyphIDs[x] = groupInfo->Glyphs[x]; + + packet.Info.TalentGroups.push_back(groupInfoPkt); + } - data->put<uint8>(countPos, talentIdCount); // put real count - - break; - }*/ -} - -void Player::SendTalentsInfoData(bool pet) -{ - /* TODO: 6.x update with new talent system (and move to packet class) - WorldPacket data(SMSG_TALENTS_INFO, 50); - data << uint8(pet ? 1 : 0); - if (pet) - BuildPetTalentsInfoData(&data); - else - BuildPlayerTalentsInfoData(&data); - GetSession()->SendPacket(&data); - */ + GetSession()->SendPacket(packet.Write()); } void Player::BuildEnchantmentsInfoData(WorldPacket* data) @@ -26064,7 +25950,7 @@ void Player::SetMap(Map* map) void Player::_LoadGlyphs(PreparedQueryResult result) { - // SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6, glyph7, glyph8, glyph9 FROM character_glyphs WHERE guid = '%u' + // SELECT group, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6, glyph7, glyph8, glyph9 FROM character_glyphs WHERE guid = '%u' if (!result) return; @@ -26072,12 +25958,12 @@ void Player::_LoadGlyphs(PreparedQueryResult result) { Field* fields = result->Fetch(); - uint8 spec = fields[0].GetUInt8(); - if (spec >= GetSpecsCount()) + uint8 group = fields[0].GetUInt8(); + if (group >= GetTalentGroupsCount()) continue; for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) - _talentMgr->SpecInfo[spec].Glyphs[i] = fields[i + 1].GetUInt16(); + _talentMgr->GroupInfo[group].Glyphs[i] = fields[i + 1].GetUInt16(); } while (result->NextRow()); } @@ -26089,17 +25975,17 @@ void Player::_SaveGlyphs(SQLTransaction& trans) trans->Append(stmt); - for (uint8 spec = 0; spec < GetSpecsCount(); ++spec) + for (uint8 group = 0; group < GetTalentGroupsCount(); ++group) { uint8 index = 0; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_GLYPHS); stmt->setUInt64(index++, GetGUID().GetCounter()); - stmt->setUInt8(index++, spec); + stmt->setUInt8(index++, group); for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) - stmt->setUInt16(index++, uint16(GetGlyph(spec, i))); + stmt->setUInt16(index++, uint16(GetGlyph(group, i))); trans->Append(stmt); } @@ -26111,57 +25997,40 @@ void Player::_LoadTalents(PreparedQueryResult result) if (result) { do - AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt8(), false); + AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt8()); while (result->NextRow()); } } void Player::_SaveTalents(SQLTransaction& trans) { - PreparedStatement* stmt = NULL; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_TALENT); + stmt->setUInt64(0, GetGUID().GetCounter()); + trans->Append(stmt); - for (uint8 spec = 0; spec < MAX_TALENT_SPECS; ++spec) + for (uint8 group = 0; group < MAX_TALENT_GROUPS; ++group) { - TalentSpecInfo* talentSpecInfo = GetTalentSpecInfo(spec); + TalentGroupInfo* talentGroupInfo = GetTalentGroupInfo(group); for (uint32 tier = 0; tier < MAX_TALENT_TIERS; ++tier) { - PlayerTalent* talent = &talentSpecInfo->Talents[tier]; - - if (talent->State == PLAYERSPELL_REMOVED || talent->State == PLAYERSPELL_CHANGED) - { - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC); - stmt->setUInt64(0, GetGUID().GetCounter()); - stmt->setUInt32(1, talent->SpellID); - stmt->setUInt8(2, spec); - trans->Append(stmt); - } - - if (talent->State == PLAYERSPELL_NEW || talent->State == PLAYERSPELL_CHANGED) - { - stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_TALENT); - stmt->setUInt64(0, GetGUID().GetCounter()); - stmt->setUInt32(1, talent->SpellID); - stmt->setUInt8(2, spec); - trans->Append(stmt); - } - - if (talent->State == PLAYERSPELL_REMOVED) - talent->SpellID = 0; - - talent->State = PLAYERSPELL_UNCHANGED; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_TALENT); + stmt->setUInt64(0, GetGUID().GetCounter()); + stmt->setUInt32(1, talentGroupInfo->Talents[tier]); + stmt->setUInt8(2, group); + trans->Append(stmt); } } } -void Player::UpdateSpecCount(uint8 count) +void Player::UpdateTalentGroupCount(uint8 count) { - uint32 curCount = GetSpecsCount(); + uint32 curCount = GetTalentGroupsCount(); if (curCount == count) return; - if (GetActiveSpec() >= count) - ActivateSpec(0); + if (GetActiveTalentGroup() >= count) + ActivateTalentGroup(0); SQLTransaction trans = CharacterDatabase.BeginTransaction(); PreparedStatement* stmt = NULL; @@ -26187,7 +26056,7 @@ void Player::UpdateSpecCount(uint8 count) _SaveActions(trans); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC); - stmt->setUInt8(0, GetActiveSpec()); + stmt->setUInt8(0, GetActiveTalentGroup()); stmt->setUInt64(1, GetGUID().GetCounter()); trans->Append(stmt); @@ -26195,17 +26064,17 @@ void Player::UpdateSpecCount(uint8 count) CharacterDatabase.CommitTransaction(trans); - SetSpecsCount(count); + SetTalentGroupsCount(count); - SendTalentsInfoData(false); + SendTalentsInfoData(); } -void Player::ActivateSpec(uint8 spec) +void Player::ActivateTalentGroup(uint8 group) { - if (GetActiveSpec() == spec) + if (GetActiveTalentGroup() == group) return; - if (spec > GetSpecsCount()) + if (group > GetTalentGroupsCount()) return; if (IsNonMeleeSpellCast(false)) @@ -26251,7 +26120,7 @@ void Player::ActivateSpec(uint8 spec) } // Unlearn specialization specific spells - auto specSpells = sSpecializationSpellsBySpecStore.find(GetTalentSpec(GetActiveSpec())); + auto specSpells = sSpecializationSpellsBySpecStore.find(GetActiveTalentSpec()); if (specSpells != sSpecializationSpellsBySpecStore.end()) { for (auto it = specSpells->second.begin(); it != specSpells->second.end(); ++it) @@ -26265,7 +26134,7 @@ void Player::ActivateSpec(uint8 spec) } // Unlearn mastery spells - ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetTalentSpec(GetActiveSpec())); + ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec()); for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i) if (chrSpec->MasterySpellID[i]) RemoveSpell(chrSpec->MasterySpellID[i], true); @@ -26273,12 +26142,12 @@ void Player::ActivateSpec(uint8 spec) // set glyphs for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) // remove secondary glyph - if (uint32 oldglyph = GetGlyph(GetActiveSpec(), slot)) + if (uint32 oldglyph = GetGlyph(GetActiveTalentGroup(), slot)) if (GlyphPropertiesEntry const* old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph)) RemoveAurasDueToSpell(old_gp->SpellID); - // Activate new spec - SetActiveSpec(spec); + // Activate new group + SetActiveTalentGroup(group); for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) { @@ -26291,14 +26160,11 @@ void Player::ActivateSpec(uint8 spec) if (getClass() != talentInfo->ClassID) continue; - if (HasTalent(talentInfo->SpellID, GetActiveSpec())) - { - LearnSpell(talentInfo->SpellID, false); - } + LearnSpell(talentInfo->SpellID, false); } // Replace default spells with specialization specific spells - auto newSpecSpells = sSpecializationSpellsBySpecStore.find(GetTalentSpec(spec)); + auto newSpecSpells = sSpecializationSpellsBySpecStore.find(GetActiveTalentSpec()); if (newSpecSpells != sSpecializationSpellsBySpecStore.end()) { for (auto it = newSpecSpells->second.begin(); it != newSpecSpells->second.end(); ++it) @@ -26314,7 +26180,7 @@ void Player::ActivateSpec(uint8 spec) } if (CanUseMastery()) - if (ChrSpecializationEntry const* newChrSpec = sChrSpecializationStore.LookupEntry(GetTalentSpec(spec))) + 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); @@ -26322,7 +26188,7 @@ void Player::ActivateSpec(uint8 spec) // set glyphs for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) { - uint32 glyph = GetGlyph(GetActiveSpec(), slot); + uint32 glyph = GetGlyph(GetActiveTalentGroup(), slot); // apply primary glyph if (glyph) @@ -26337,7 +26203,7 @@ void Player::ActivateSpec(uint8 spec) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC); stmt->setUInt64(0, GetGUID().GetCounter()); - stmt->setUInt8(1, GetActiveSpec()); + stmt->setUInt8(1, GetActiveTalentGroup()); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) _LoadActions(result); } @@ -26350,7 +26216,7 @@ void Player::ActivateSpec(uint8 spec) SetPower(pw, 0); - if (!sChrSpecializationStore.LookupEntry(GetTalentSpec(spec))) + if (!sChrSpecializationStore.LookupEntry(GetActiveTalentSpec())) ResetTalents(true); } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 54d542011a1..11167097e89 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -115,12 +115,6 @@ 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; - uint32 SpellID : 32; -}; - extern uint32 const MasterySpells[MAX_CLASSES]; enum TalentSpecialization // talent tabs @@ -1217,46 +1211,52 @@ private: bool _isPvP; }; -struct TalentSpecInfo +struct TalentGroupInfo { - PlayerTalent Talents[MAX_TALENT_TIERS]; + uint32 Talents[MAX_TALENT_TIERS]; uint32 Glyphs[MAX_GLYPH_SLOT_INDEX]; - uint32 TalentSpec; + uint32 SpecID; - bool HasTalent(uint32 spellId) + bool HasTalent(uint32 talentId) { for (uint32 i = 0; i < MAX_TALENT_TIERS; ++i) - if (Talents[i].SpellID == spellId) + if (Talents[i] == talentId) return true; return false; } + uint32 TalentCount() + { + for (uint32 i = 0; i < MAX_TALENT_TIERS; ++i) + if (!Talents[i]) + return i; + return MAX_TALENT_TIERS; + } + void Reset() { - for (uint32 i = 0; i < MAX_TALENT_TIERS; ++i) { - Talents[i].State = PLAYERSPELL_REMOVED; - Talents[i].SpellID = 0; - } + for (uint32 i = 0; i < MAX_TALENT_TIERS; ++i) + Talents[i] = 0; } }; struct PlayerTalentInfo { - PlayerTalentInfo() : ResetTalentsCost(0), ResetTalentsTime(0), ActiveSpec(0), SpecsCount(1) + PlayerTalentInfo() : ResetTalentsCost(0), ResetTalentsTime(0), ActiveGroup(0), GroupsCount(1) { - for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) + for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i) { - memset(SpecInfo[i].Talents, 0, sizeof(PlayerTalent)*MAX_TALENT_TIERS); - memset(SpecInfo[i].Glyphs, 0, MAX_GLYPH_SLOT_INDEX * sizeof(uint32)); - SpecInfo[i].TalentSpec = 0; + 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; } } - TalentSpecInfo SpecInfo[MAX_TALENT_SPECS]; + TalentGroupInfo GroupInfo[MAX_TALENT_GROUPS]; uint32 ResetTalentsCost; time_t ResetTalentsTime; - uint8 ActiveSpec; - uint8 SpecsCount; + uint8 ActiveGroup; + uint8 GroupsCount; private: PlayerTalentInfo(PlayerTalentInfo const&); @@ -1825,35 +1825,34 @@ class Player : public Unit, public GridObject<Player> void SetTalentResetCost(uint32 cost) { _talentMgr->ResetTalentsCost = cost; } uint32 GetTalentResetTime() const { return _talentMgr->ResetTalentsTime; } void SetTalentResetTime(time_t time_) { _talentMgr->ResetTalentsTime = time_; } - uint8 GetActiveSpec() const { return _talentMgr->ActiveSpec; } - void SetActiveSpec(uint8 spec){ _talentMgr->ActiveSpec = spec; } - uint8 GetSpecsCount() const { return _talentMgr->SpecsCount; } - void SetSpecsCount(uint8 count) { _talentMgr->SpecsCount = count; } - uint32 GetTalentSpec(uint8 spec) const { return _talentMgr->SpecInfo[spec].TalentSpec; } - void SetTalentSpec(uint8 spec, uint32 talentSpec) const { _talentMgr->SpecInfo[spec].TalentSpec = talentSpec; } + 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; } bool ResetTalents(bool no_cost = false); uint32 GetNextResetTalentsCost() const; void InitTalentForLevel(); - void BuildPlayerTalentsInfoData(WorldPacket* data); - void BuildPetTalentsInfoData(WorldPacket* data); - void SendTalentsInfoData(bool pet); + void SendTalentsInfoData(); bool LearnTalent(uint32 talentId); - bool AddTalent(uint32 spellId, uint8 spec, bool learning); - bool HasTalent(uint32 spell_id, uint8 spec); + bool AddTalent(uint32 talentId, uint8 spec); + bool HasTalent(uint32 talentId, uint8 spec); // Dual Spec - void UpdateSpecCount(uint8 count); - void ActivateSpec(uint8 spec); + void UpdateTalentGroupCount(uint8 count); + void ActivateTalentGroup(uint8 group); void InitGlyphsForLevel(); void SetGlyphSlot(uint8 slot, uint32 slottype) { SetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot, slottype); } uint32 GetGlyphSlot(uint8 slot) const { return GetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot); } void SetGlyph(uint8 slot, uint32 glyph); - uint32 GetGlyph(uint8 spec, uint8 slot) const { return _talentMgr->SpecInfo[spec].Glyphs[slot]; } + uint32 GetGlyph(uint8 group, uint8 slot) const { return _talentMgr->GroupInfo[group].Glyphs[slot]; } - TalentSpecInfo* GetTalentSpecInfo(uint8 spec) { return &_talentMgr->SpecInfo[spec]; } + TalentGroupInfo* GetTalentGroupInfo(uint8 group) { return &_talentMgr->GroupInfo[group]; } 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 37b4d4d97a0..ea15daed587 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -526,7 +526,7 @@ void Player::UpdateMastery() value += GetRatingBonusValue(CR_MASTERY); SetFloatValue(PLAYER_MASTERY, value); - ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetTalentSpec(GetActiveSpec())); + ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec()); if (!chrSpec) return; diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 6e9560ac22f..7d892371931 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1055,7 +1055,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS)) { pCurrChar->ResetTalents(true); - pCurrChar->SendTalentsInfoData(false); // original talents send already in to SendInitialPacketsBeforeAddToMap, resend reset state + pCurrChar->SendTalentsInfoData(); // original talents send already in to SendInitialPacketsBeforeAddToMap, resend reset state SendNotification(LANG_RESET_TALENTS); } @@ -1416,13 +1416,13 @@ void WorldSession::HandleRemoveGlyph(WorldPacket& recvData) return; } - if (uint32 glyph = _player->GetGlyph(_player->GetActiveSpec(), slot)) + if (uint32 glyph = _player->GetGlyph(_player->GetActiveTalentGroup(), slot)) { if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph)) { _player->RemoveAurasDueToSpell(gp->SpellID); _player->SetGlyph(slot, 0); - _player->SendTalentsInfoData(false); + _player->SendTalentsInfoData(); } } } diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index b28068538ce..6f2cf949dfe 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -1242,6 +1242,7 @@ void WorldSession::HandleInspectOpcode(WorldPacket& recvData) WorldPacket data(SMSG_INSPECT_TALENT, 8 + 4 + 1 + 1 + talent_points + 8 + 4 + 8 + 4); data << player->GetGUID(); + /* TODO: 6.x update packet structure (BuildPlayerTalentsInfoData no longer exists) if (sWorld->getBoolConfig(CONFIG_TALENTS_INSPECTING) || _player->IsGameMaster()) player->BuildPlayerTalentsInfoData(&data); else @@ -1250,6 +1251,7 @@ void WorldSession::HandleInspectOpcode(WorldPacket& recvData) data << uint8(0); // talentGroupCount data << uint8(0); // talentGroupIndex } + */ player->BuildEnchantmentsInfoData(&data); if (Guild* guild = sGuildMgr->GetGuildById(player->GetGuildId())) diff --git a/src/server/game/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp index c3f9dfc2868..ef1e9031cb9 100644 --- a/src/server/game/Handlers/SkillHandler.cpp +++ b/src/server/game/Handlers/SkillHandler.cpp @@ -111,7 +111,7 @@ void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket& recvData) return; } - _player->SendTalentsInfoData(false); + _player->SendTalentsInfoData(); unit->CastSpell(_player, 14867, true); //spell: "Untalent Visual Effect" } diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index e2c82763161..9e8b5ca00f3 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -815,10 +815,10 @@ enum SpellAttr13 SPELL_ATTR13_UNK23 = 0x00800000 // 23 }; -#define MIN_TALENT_SPEC 0 -#define MAX_TALENT_SPEC 1 -#define MIN_TALENT_SPECS 1 -#define MAX_TALENT_SPECS 2 +#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 diff --git a/src/server/game/Server/Packets/TalentPackets.cpp b/src/server/game/Server/Packets/TalentPackets.cpp new file mode 100644 index 00000000000..3410fb273f7 --- /dev/null +++ b/src/server/game/Server/Packets/TalentPackets.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "TalentPackets.h" + +WorldPacket const* WorldPackets::Talent::UpdateTalentData::Write() +{ + _worldPacket << Info.ActiveGroup; + _worldPacket << uint32(Info.TalentGroups.size()); + + for (auto& talentGroupInfo : Info.TalentGroups) + { + _worldPacket << talentGroupInfo.SpecID; + _worldPacket << uint32(talentGroupInfo.TalentIDs.size()); + + for (uint32 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) + _worldPacket << talentGroupInfo.GlyphIDs[i]; + + for (uint16 talentID : talentGroupInfo.TalentIDs) + _worldPacket << talentID; + } + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/TalentPackets.h b/src/server/game/Server/Packets/TalentPackets.h new file mode 100644 index 00000000000..c756c6962bb --- /dev/null +++ b/src/server/game/Server/Packets/TalentPackets.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TalentPackets_h__ +#define TalentPackets_h__ + +#include "Packet.h" +#include "Player.h" +#include <vector> + +namespace WorldPackets +{ + namespace Talent + { + struct TalentGroupInfo + { + uint32 SpecID; + std::vector<uint16> TalentIDs; + uint16 GlyphIDs[MAX_GLYPH_SLOT_INDEX]; + }; + + struct TalentInfoUpdate + { + uint8 ActiveGroup; + std::vector<TalentGroupInfo> TalentGroups; + }; + + class UpdateTalentData final : public ServerPacket + { + public: + UpdateTalentData() : ServerPacket(SMSG_TALENTS_INFO, 2+4+4+4+12) { } + + WorldPacket const* Write() override; + + TalentInfoUpdate Info; + }; + } +} + +#endif // TalentPackets_h__ diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 4e31af725a9..292f126d8a4 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1246,7 +1246,7 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const // Also do it for Glyphs for (uint32 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) { - if (uint32 glyphId = plrTarget->GetGlyph(plrTarget->GetActiveSpec(), i)) + if (uint32 glyphId = plrTarget->GetGlyph(plrTarget->GetActiveTalentGroup(), i)) { if (GlyphPropertiesEntry const* glyph = sGlyphPropertiesStore.LookupEntry(glyphId)) { diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index c3ba18cad2c..3579ba73c31 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -4034,7 +4034,7 @@ void Spell::EffectApplyGlyph(SpellEffIndex effIndex) } // remove old glyph - if (uint32 oldGlyph = player->GetGlyph(player->GetActiveSpec(), m_glyphIndex)) + if (uint32 oldGlyph = player->GetGlyph(player->GetActiveTalentGroup(), m_glyphIndex)) { if (GlyphPropertiesEntry const* oldGlyphProperties = sGlyphPropertiesStore.LookupEntry(oldGlyph)) { @@ -4045,16 +4045,16 @@ void Spell::EffectApplyGlyph(SpellEffIndex effIndex) player->CastSpell(m_caster, newGlyphProperties->SpellID, true); player->SetGlyph(m_glyphIndex, newGlyph); - player->SendTalentsInfoData(false); + player->SendTalentsInfoData(); } } - else if (uint32 oldGlyph = player->GetGlyph(player->GetActiveSpec(), m_glyphIndex)) // Removing the glyph, get the old one + else if (uint32 oldGlyph = player->GetGlyph(player->GetActiveTalentGroup(), m_glyphIndex)) // Removing the glyph, get the old one { if (GlyphPropertiesEntry const* oldGlyphProperties = sGlyphPropertiesStore.LookupEntry(oldGlyph)) { player->RemoveAurasDueToSpell(oldGlyphProperties->SpellID); player->SetGlyph(m_glyphIndex, 0); - player->SendTalentsInfoData(false); + player->SendTalentsInfoData(); } } } @@ -5494,7 +5494,7 @@ void Spell::EffectSpecCount(SpellEffIndex /*effIndex*/) if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; - unitTarget->ToPlayer()->UpdateSpecCount(damage); + unitTarget->ToPlayer()->UpdateTalentGroupCount(damage); } void Spell::EffectActivateSpec(SpellEffIndex /*effIndex*/) @@ -5505,7 +5505,7 @@ void Spell::EffectActivateSpec(SpellEffIndex /*effIndex*/) if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; - unitTarget->ToPlayer()->ActivateSpec(damage-1); // damage is 1 or 2, spec is 0 or 1 + unitTarget->ToPlayer()->ActivateTalentGroup(damage-1); // damage is 1 or 2, spec is 0 or 1 } void Spell::EffectPlaySound(SpellEffIndex effIndex) diff --git a/src/server/game/Tools/CharacterDatabaseCleaner.cpp b/src/server/game/Tools/CharacterDatabaseCleaner.cpp index a63a8ae6cdd..ec96d092393 100644 --- a/src/server/game/Tools/CharacterDatabaseCleaner.cpp +++ b/src/server/game/Tools/CharacterDatabaseCleaner.cpp @@ -147,7 +147,7 @@ bool CharacterDatabaseCleaner::TalentCheck(uint32 talent_id) void CharacterDatabaseCleaner::CleanCharacterTalent() { - CharacterDatabase.DirectPExecute("DELETE FROM character_talent WHERE spec > %u", MAX_TALENT_SPECS); + CharacterDatabase.DirectPExecute("DELETE FROM character_talent WHERE spec > %u", MAX_TALENT_GROUPS); CheckUnique("spell", "character_talent", &TalentCheck); } diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp index 81499567b72..2cf97403595 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->GetActiveSpec(), true); + player->AddTalent(talentInfo->SpellID, player->GetActiveTalentGroup()); } handler->SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS); diff --git a/src/server/scripts/Commands/cs_reset.cpp b/src/server/scripts/Commands/cs_reset.cpp index 9d2621c0010..4a58a7f7e5d 100644 --- a/src/server/scripts/Commands/cs_reset.cpp +++ b/src/server/scripts/Commands/cs_reset.cpp @@ -204,6 +204,7 @@ public: Player* target; ObjectGuid targetGuid; std::string targetName; + /* TODO: 6.x remove/update pet talents if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName)) { // Try reset talents as Hunter Pet @@ -227,19 +228,22 @@ public: handler->SetSentErrorMessage(true); return false; } + */ if (target) { target->ResetTalents(true); - target->SendTalentsInfoData(false); + target->SendTalentsInfoData(); ChatHandler(target->GetSession()).SendSysMessage(LANG_RESET_TALENTS); if (!handler->GetSession() || handler->GetSession()->GetPlayer() != target) handler->PSendSysMessage(LANG_RESET_TALENTS_ONLINE, handler->GetNameLink(target).c_str()); + /* TODO: 6.x remove/update pet talents Pet* pet = target->GetPet(); Pet::resetTalentsForAllPetsOf(target, pet); if (pet) target->SendTalentsInfoData(true); + */ return true; } else if (!targetGuid.IsEmpty()) diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index ed0709c6ad9..184e12cdccb 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -168,7 +168,7 @@ class spell_dru_eclipse_energize : public SpellScriptLoader Player* caster = GetCaster()->ToPlayer(); // No boomy, no deal. - if (caster->GetTalentSpec(caster->GetActiveSpec()) != TALENT_SPEC_DRUID_BALANCE) + if (caster->GetActiveTalentSpec() != TALENT_SPEC_DRUID_BALANCE) return; switch (GetSpellInfo()->Id) diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index e91aa10c52d..9c27f876054 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -74,7 +74,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() "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, instance_mode_mask, " "totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, " - "health, power1, power2, power3, power4, power5, instance_id, speccount, activespec, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels " + "health, power1, power2, power3, power4, power5, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels " "FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH); @@ -105,7 +105,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, bag, slot, " "item, itemEntry FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activespec AND a.guid = ? ORDER BY button", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activeTalentGroup AND a.guid = ? ORDER BY button", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_MAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = ? AND (checked & 1) = 0", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?", CONNECTION_SYNCH); @@ -122,8 +122,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER_EQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, ignore_mask, item0, item1, item2, item3, item4, item5, item6, item7, item8, " "item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = ? ORDER BY setindex", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_BGDATA, "SELECT instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell FROM character_battleground_data WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_GLYPHS, "SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6, glyph7, glyph8, glyph9 FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_TALENTS, "SELECT spell, spec FROM character_talent WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_GLYPHS, "SELECT talentGroup, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6, glyph7, glyph8, glyph9 FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_TALENTS, "SELECT spell, talentGroup FROM character_talent WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_SKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_RANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_BANNED, "SELECT guid FROM character_banned WHERE guid = ? AND active = 1", CONNECTION_ASYNC); @@ -376,13 +376,13 @@ void CharacterDatabaseConnection::DoPrepareStatements() "extra_flags, stable_slots, at_login, zone, " "death_expire_time, taxi_path, totalKills, " "todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, " - "power4, power5, latency, speccount, activespec, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels) VALUES " + "power4, power5, latency, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels) VALUES " "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,playerBytes=?,playerBytes2=?,playerFlags=?," "map=?,instance_id=?,instance_mode_mask=?,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=?," "totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?," - "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,latency=?,speccount=?,activespec=?,exploredZones=?," + "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,latency=?,talentGroupsCount=?,activeTalentGroup=?,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); @@ -557,9 +557,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER, "DELETE FROM petition_sign WHERE ownerguid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PETITION_BY_OWNER_AND_TYPE, "DELETE FROM petition WHERE ownerguid = ? AND type = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER_AND_TYPE, "DELETE FROM petition_sign WHERE ownerguid = ? AND type = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_CHAR_GLYPHS, "INSERT INTO character_glyphs (guid, spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6, glyph7, glyph8, glyph9) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC, "DELETE FROM character_talent WHERE guid = ? and spell = ? and spec = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_CHAR_TALENT, "INSERT INTO character_talent (guid, spell, spec) VALUES (?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHAR_GLYPHS, "INSERT INTO character_glyphs (guid, talentGroup, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6, glyph7, glyph8, glyph9) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC, "DELETE FROM character_talent WHERE guid = ? and spell = ? and talentGroup = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHAR_TALENT, "INSERT INTO character_talent (guid, spell, talentGroup) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, "DELETE FROM character_action WHERE spec<>? AND guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHAR_LIST_SLOT, "UPDATE characters SET slot = ? WHERE guid = ? AND account = ?", CONNECTION_ASYNC); |