aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/characters/3.4.x/2023_11_19_00_characters.sql4
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp12
-rw-r--r--src/server/game/Entities/Player/Player.cpp90
-rw-r--r--src/server/game/Entities/Player/Player.h9
-rw-r--r--src/server/game/Spells/Spell.h3
-rw-r--r--src/server/game/Spells/SpellEffects.cpp17
6 files changed, 55 insertions, 80 deletions
diff --git a/sql/updates/characters/3.4.x/2023_11_19_00_characters.sql b/sql/updates/characters/3.4.x/2023_11_19_00_characters.sql
new file mode 100644
index 00000000000..c20683eff7f
--- /dev/null
+++ b/sql/updates/characters/3.4.x/2023_11_19_00_characters.sql
@@ -0,0 +1,4 @@
+ALTER TABLE `characters_10x`.`characters`
+ DROP COLUMN `activeTalentGroup`,
+ CHANGE `primarySpecialization` `activeTalentGroup` TINYINT UNSIGNED DEFAULT 0 NOT NULL,
+ ADD COLUMN `bonusTalentGroups` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `activeTalentGroup`;
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index e6b325b36d7..a47068200b9 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -94,9 +94,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHARACTER, "SELECT c.guid, account, name, race, class, gender, level, xp, money, inventorySlots, bankSlots, restState, playerFlags, playerFlagsEx, "
"position_x, position_y, position_z, map, orientation, taximask, createTime, createMode, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, "
- "resettalents_time, primarySpecialization, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, summonedPetNumber, at_login, zone, online, death_expire_time, taxi_path, dungeonDifficulty, "
+ "resettalents_time, activeTalentGroup, bonusTalentGroups, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, summonedPetNumber, at_login, zone, online, death_expire_time, taxi_path, dungeonDifficulty, "
"totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, "
- "health, power1, power2, power3, power4, power5, power6, power7, power8, power9, power10, instance_id, activeTalentGroup, lootSpecId, exploredZones, knownTitles, actionBars, "
+ "health, power1, power2, power3, power4, power5, power6, power7, power8, power9, power10, instance_id, lootSpecId, exploredZones, knownTitles, actionBars, "
"raidDifficulty, legacyRaidDifficulty, fishingSteps, honor, honorLevel, honorRestState, honorRestBonus, numRespecs, "
"personalTabardEmblemStyle, personalTabardEmblemColor, personalTabardBorderStyle, personalTabardBorderColor, personalTabardBackgroundColor "
"FROM characters c LEFT JOIN character_fishingsteps cfs ON c.guid = cfs.guid WHERE c.guid = ?", CONNECTION_ASYNC);
@@ -475,17 +475,17 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_CHARACTER, "INSERT INTO characters (guid, account, name, race, class, gender, level, xp, money, inventorySlots, bankSlots, restState, playerFlags, playerFlagsEx, "
"map, instance_id, dungeonDifficulty, raidDifficulty, legacyRaidDifficulty, position_x, position_y, position_z, orientation, trans_x, trans_y, trans_z, trans_o, transguid, "
"taximask, createTime, createMode, cinematic, "
- "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, primarySpecialization, "
+ "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, activeTalentGroup, bonusTalentGroups,"
"extra_flags, summonedPetNumber, at_login, "
"death_expire_time, taxi_path, totalKills, "
"todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, "
- "power4, power5, power6, power7, power8, power9, power10, latency, activeTalentGroup, lootSpecId, exploredZones, equipmentCache, knownTitles, actionBars, lastLoginBuild) VALUES "
+ "power4, power5, power6, power7, power8, power9, power10, latency, lootSpecId, exploredZones, equipmentCache, knownTitles, actionBars, lastLoginBuild) VALUES "
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,inventorySlots=?,bankSlots=?,restState=?,playerFlags=?,playerFlagsEx=?,"
"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=?,numRespecs=?,primarySpecialization=?,extra_flags=?,summonedPetNumber=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?,"
+ "logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,numRespecs=?,activeTalentGroup=?,bonusTalentGroups=?,extra_flags=?,summonedPetNumber=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?,"
"totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?,"
- "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,power6=?,power7=?,power8=?,power9=?,power10=?,latency=?,activeTalentGroup=?,lootSpecId=?,exploredZones=?,"
+ "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,power6=?,power7=?,power8=?,power9=?,power10=?,latency=?,lootSpecId=?,exploredZones=?,"
"equipmentCache=?,knownTitles=?,actionBars=?,online=?,honor=?,honorLevel=?,honorRestState=?,honorRestBonus=?,lastLoginBuild=? 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/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index f69d5b80ba9..3e41f2aa05d 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -2358,7 +2358,6 @@ void Player::GiveLevel(uint8 level)
UpdateSkillsForLevel();
LearnDefaultSkills();
- LearnSpecializationSpells();
// save base values (bonuses already included in stored stats
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
@@ -17123,7 +17122,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
uint8 is_logout_resting;
uint32 resettalents_cost;
time_t resettalents_time;
- uint32 primarySpecialization;
+ uint8 activeTalentGroup;
+ uint8 bonusTalentGroups;
float trans_x;
float trans_y;
float trans_z;
@@ -17146,7 +17146,6 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
uint32 health;
std::array<uint32, MAX_POWERS_PER_CLASS> powers;
uint32 instance_id;
- uint8 activeTalentGroup;
uint32 lootSpecId;
std::string exploredZones;
std::string knownTitles;
@@ -17198,7 +17197,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
is_logout_resting = fields[i++].GetUInt8();
resettalents_cost = fields[i++].GetUInt32();
resettalents_time = fields[i++].GetInt64();
- primarySpecialization = fields[i++].GetUInt32();
+ activeTalentGroup = fields[i++].GetUInt8();
+ bonusTalentGroups = fields[i++].GetUInt8();
trans_x = fields[i++].GetFloat();
trans_y = fields[i++].GetFloat();
trans_z = fields[i++].GetFloat();
@@ -17222,7 +17222,6 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
for (uint32& power : powers)
power = fields[i++].GetUInt32();
instance_id = fields[i++].GetUInt32();
- activeTalentGroup = fields[i++].GetUInt8();
lootSpecId = fields[i++].GetUInt32();
exploredZones = fields[i++].GetString();
knownTitles = fields[i++].GetString();
@@ -17710,8 +17709,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
UpdateSkillsForLevel(); //update skills after load, to make sure they are correctly update at player load
SetNumRespecs(fields.numRespecs);
- SetPrimarySpecialization(fields.primarySpecialization);
SetActiveTalentGroup(fields.activeTalentGroup);
+ SetBonusTalentGroupCount(fields.bonusTalentGroups);
uint32 lootSpecId = fields.lootSpecId;
if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(lootSpecId))
@@ -17727,8 +17726,6 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
GetSession()->GetCollectionMgr()->LoadItemAppearances();
GetSession()->GetCollectionMgr()->LoadTransmogIllusions();
- LearnSpecializationSpells();
-
_LoadGlyphs(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GLYPHS));
_LoadAuras(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURAS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURA_EFFECTS), time_diff);
_LoadGlyphAuras();
@@ -19469,7 +19466,8 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
//save, but in tavern/city
stmt->setUInt32(index++, GetTalentResetCost());
stmt->setInt64(index++, GetTalentResetTime());
- stmt->setUInt32(index++, AsUnderlyingType(GetPrimarySpecialization()));
+ stmt->setUInt8(index++, GetActiveTalentGroup());
+ stmt->setUInt8(index++, GetBonusTalentGroupCount());
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
stmt->setUInt32(index++, 0); // summonedPetNumber
stmt->setUInt16(index++, (uint16)m_atLoginFlags);
@@ -19491,9 +19489,6 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
stmt->setUInt32(index++, m_unitData->Power[i]);
stmt->setUInt32(index++, GetSession()->GetLatency());
-
- stmt->setUInt8(index++, GetActiveTalentGroup());
-
stmt->setUInt32(index++, GetLootSpecId());
ss.str("");
@@ -19602,7 +19597,8 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
stmt->setUInt32(index++, GetTalentResetCost());
stmt->setInt64(index++, GetTalentResetTime());
stmt->setUInt8(index++, GetNumRespecs());
- stmt->setUInt32(index++, AsUnderlyingType(GetPrimarySpecialization()));
+ stmt->setUInt8(index++, GetActiveTalentGroup());
+ stmt->setUInt8(index++, GetBonusTalentGroupCount());
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
if (PetStable const* petStable = GetPetStable())
stmt->setUInt32(index++, petStable->GetCurrentPet() && petStable->GetCurrentPet()->Health > 0 ? petStable->GetCurrentPet()->PetNumber : 0); // summonedPetNumber
@@ -19628,9 +19624,6 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
stmt->setUInt32(index++, m_unitData->Power[i]);
stmt->setUInt32(index++, GetSession()->GetLatency());
-
- stmt->setUInt8(index++, GetActiveTalentGroup());
-
stmt->setUInt32(index++, GetLootSpecId());
ss.str("");
@@ -26378,9 +26371,7 @@ void Player::SendTalentsInfoData()
uint8 activeGroup = GetActiveTalentGroup();
packet.ActiveGroup = activeGroup;
- printf("sending talent info data for group %u\n", activeGroup);
-
- for (uint8 i = 0; i < MAX_SPECIALIZATIONS; ++i)
+ for (uint8 i = 0; i < (1 + GetBonusTalentGroupCount()); ++i)
{
WorldPackets::Talent::TalentGroupInfo& groupInfo = packet.TalentGroupInfos.emplace_back();
groupInfo.SpecID = MAX_SPECIALIZATIONS;
@@ -27036,9 +27027,6 @@ void Player::ActivateTalentGroup(uint8 talentGroup)
ApplyTraitConfig(m_activePlayerData->ActiveCombatTraitConfigID, false);
- // Remove spec specific spells
- RemoveSpecializationSpells();
-
for (uint32 glyphId : GetGlyphs(GetActiveTalentGroup()))
RemoveAurasDueToSpell(sGlyphPropertiesStore.AssertEntry(glyphId)->SpellID);
@@ -27067,8 +27055,6 @@ void Player::ActivateTalentGroup(uint8 talentGroup)
}
}
- LearnSpecializationSpells();
-
InitTalentForLevel();
StartLoadingActionButtons();
@@ -28676,48 +28662,6 @@ void Player::RemoveOverrideSpell(uint32 overridenSpellId, uint32 newSpellId)
m_overrideSpells.erase(overrides);
}
-void Player::LearnSpecializationSpells()
-{
- if (std::vector<SpecializationSpellsEntry const*> const* specSpells = sDB2Manager.GetSpecializationSpells(AsUnderlyingType(GetPrimarySpecialization())))
- {
- for (size_t j = 0; j < specSpells->size(); ++j)
- {
- SpecializationSpellsEntry const* specSpell = (*specSpells)[j];
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(specSpell->SpellID, DIFFICULTY_NONE);
- if (!spellInfo || spellInfo->SpellLevel > GetLevel())
- continue;
-
- LearnSpell(specSpell->SpellID, true);
- if (specSpell->OverridesSpellID)
- AddOverrideSpell(specSpell->OverridesSpellID, specSpell->SpellID);
- }
- }
-}
-
-void Player::RemoveSpecializationSpells()
-{
- for (uint32 i = 0; i < MAX_SPECIALIZATIONS; ++i)
- {
- if (ChrSpecializationEntry const* specialization = sDB2Manager.GetChrSpecializationByIndex(GetClass(), i))
- {
- if (std::vector<SpecializationSpellsEntry const*> const* specSpells = sDB2Manager.GetSpecializationSpells(specialization->ID))
- {
- for (size_t j = 0; j < specSpells->size(); ++j)
- {
- SpecializationSpellsEntry const* specSpell = (*specSpells)[j];
- RemoveSpell(specSpell->SpellID, true);
- if (specSpell->OverridesSpellID)
- RemoveOverrideSpell(specSpell->OverridesSpellID, specSpell->SpellID);
- }
- }
-
- for (uint32 j = 0; j < MAX_MASTERY_SPELLS; ++j)
- if (uint32 mastery = specialization->MasterySpellID[j])
- RemoveAurasDueToSpell(mastery);
- }
- }
-}
-
void Player::AddSpellCategoryCooldownMod(int32 spellCategoryId, int32 mod)
{
int32 categoryIndex = m_activePlayerData->CategoryCooldownMods.FindIndexIf([spellCategoryId](UF::CategoryCooldownMod const& mod)
@@ -28778,6 +28722,20 @@ uint32 Player::GetDefaultSpecId() const
{
return ASSERT_NOTNULL(sDB2Manager.GetDefaultChrSpecializationForClass(GetClass()))->ID;
}
+void Player::SetBonusTalentGroupCount(uint8 amount)
+{
+ if (_specializationInfo.BonusGroups == amount)
+ return;
+
+ _specializationInfo.BonusGroups = std::min<uint8>(amount, MAX_SPECIALIZATIONS - 1);
+ if (GetActiveTalentGroup() > amount)
+ {
+ ResetTalents(true);
+ ActivateTalentGroup(0);
+ }
+ else
+ SendTalentsInfoData();
+}
ChrSpecializationEntry const* Player::GetPrimarySpecializationEntry() const
{
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index c9b6e4fd7bc..0a6dc73fda6 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1065,13 +1065,14 @@ enum TalentLearnResult : int32
struct TC_GAME_API SpecializationInfo
{
- SpecializationInfo() : ResetTalentsCost(0), ResetTalentsTime(0), ActiveGroup(0) { }
+ SpecializationInfo() : ResetTalentsCost(0), ResetTalentsTime(0), ActiveGroup(0), BonusGroups(0) { }
PlayerTalentMap Talents[MAX_SPECIALIZATIONS];
std::vector<uint32> Glyphs[MAX_SPECIALIZATIONS];
uint32 ResetTalentsCost;
time_t ResetTalentsTime;
uint8 ActiveGroup;
+ uint8 BonusGroups;
private:
SpecializationInfo(SpecializationInfo const&) = delete;
@@ -1793,8 +1794,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SetOverrideSpellsId(int32 overrideSpellsId) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::OverrideSpellsID), overrideSpellsId); }
void AddOverrideSpell(uint32 overridenSpellId, uint32 newSpellId);
void RemoveOverrideSpell(uint32 overridenSpellId, uint32 newSpellId);
- void LearnSpecializationSpells();
- void RemoveSpecializationSpells();
void AddSpellCategoryCooldownMod(int32 spellCategoryId, int32 mod);
void RemoveSpellCategoryCooldownMod(int32 spellCategoryId, int32 mod);
void SetSpellFavorite(uint32 spellId, bool favorite);
@@ -1822,7 +1821,9 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
ChrSpecialization GetPrimarySpecialization() const { return ChrSpecialization(*m_playerData->CurrentSpecID); }
void SetPrimarySpecialization(uint32 spec) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_playerData).ModifyValue(&UF::PlayerData::CurrentSpecID), spec); }
uint8 GetActiveTalentGroup() const { return _specializationInfo.ActiveGroup; }
- void SetActiveTalentGroup(uint8 group){ _specializationInfo.ActiveGroup = group; }
+ void SetActiveTalentGroup(uint8 group) { _specializationInfo.ActiveGroup = group; }
+ uint8 GetBonusTalentGroupCount() const { return _specializationInfo.BonusGroups; }
+ void SetBonusTalentGroupCount(uint8 amount);
uint32 GetDefaultSpecId() const;
ChrSpecializationEntry const* GetPrimarySpecializationEntry() const;
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index b16bf1eb5e6..f8026844fbc 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -370,7 +370,8 @@ class TC_GAME_API Spell
void EffectTitanGrip();
void EffectEnchantItemPrismatic();
void EffectPlayMusic();
- void EffectActivateSpec();
+ void EffectTalentSpecCount();
+ void EffectTalentSpecSelect();
void EffectPlaySound();
void EffectRemoveAura();
void EffectDamageFromMaxHealthPCT();
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 389e27c459e..69cecbbaf57 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -246,8 +246,8 @@ NonDefaultConstructible<SpellEffectHandlerFn> SpellEffectHandlers[TOTAL_SPELL_EF
&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::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::EffectTalentSpecCount, //161 SPELL_EFFECT_TALENT_SPEC_COUNT second talent spec (learn/revert)
+ &Spell::EffectTalentSpecSelect, //162 SPELL_EFFECT_TALENT_SPEC_SELECT activate primary/secondary spec
&Spell::EffectNULL, //163 SPELL_EFFECT_OBLITERATE_ITEM
&Spell::EffectRemoveAura, //164 SPELL_EFFECT_REMOVE_AURA
&Spell::EffectDamageFromMaxHealthPCT, //165 SPELL_EFFECT_DAMAGE_FROM_MAX_HEALTH_PCT
@@ -4982,7 +4982,18 @@ void Spell::EffectPlayMusic()
unitTarget->ToPlayer()->SendDirectMessage(WorldPackets::Misc::PlayMusic(soundid).Write());
}
-void Spell::EffectActivateSpec()
+void Spell::EffectTalentSpecCount()
+{
+ if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
+ return;
+
+ if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ unitTarget->ToPlayer()->SetBonusTalentGroupCount(static_cast<uint8>(effectInfo->BasePoints));
+}
+
+void Spell::EffectTalentSpecSelect()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;