From 31a722b3a6fff601310b503f18dd7130ffa02408 Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Thu, 28 Sep 2023 22:15:40 +0200 Subject: [PATCH] Core/Packets: more work on pet talents --- .../Implementation/CharacterDatabase.cpp | 2 +- src/server/game/DataStores/DBCEnums.h | 6 +- src/server/game/DataStores/DBCStructure.h | 12 +-- src/server/game/DataStores/DBCfmt.h | 2 +- .../Creature/TemporarySummon/NewPet.cpp | 90 +++++++++---------- .../Creature/TemporarySummon/NewPet.h | 4 +- src/server/game/Entities/Player/Player.cpp | 1 + src/server/game/Handlers/PetHandler.cpp | 32 ++----- src/server/game/Server/Packets/AllPackets.h | 1 + .../game/Server/Packets/CharacterPackets.cpp | 21 ----- .../game/Server/Packets/CharacterPackets.h | 28 ------ .../game/Server/Packets/TalentPackets.cpp | 51 +++++++++++ .../game/Server/Packets/TalentPackets.h | 66 ++++++++++++++ src/server/game/Server/WorldSession.h | 7 +- 14 files changed, 187 insertions(+), 136 deletions(-) create mode 100644 src/server/game/Server/Packets/TalentPackets.cpp create mode 100644 src/server/game/Server/Packets/TalentPackets.h diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index b86d95f8d2..081e2b7a6f 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -629,7 +629,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHAR_PETS, "DELETE FROM character_pet WHERE Guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_PET, "DELETE FROM character_pet WHERE PetNumber = ?", CONNECTION_BOTH); - PrepareStatement(CHAR_SEL_CHAR_PET, "SELECT PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar FROM character_pet WHERE Guid = ? ORDER BY Slot", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHAR_PET, "SELECT PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar, Talents FROM character_pet WHERE Guid = ? ORDER BY Slot", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHAR_PET, "UPDATE character_pet SET SavedHealth = ?, SavedPower = ?, LastSaveTime = ?, ReactState = ?, Slot = ?, HasBeenRenamed = ?, IsActive = ?, `Name` = ?, ActionBar = ?, Talents = ? WHERE PetNumber = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHAR_PET, "INSERT INTO character_pet (Guid, PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar, Talents) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH); diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index efcfbdff61..ac61b0ff73 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -598,9 +598,9 @@ enum class SpellItemEnchantmentFlags : uint32 DEFINE_ENUM_FLAG(SpellItemEnchantmentFlags); -#define MAX_TALENT_RANK 5 -#define MAX_PET_TALENT_RANK 3 // use in calculations, expected <= MAX_TALENT_RANK -#define MAX_TALENT_TABS 3 +static constexpr uint8 MAX_TALENT_RANK = 3; +static constexpr uint8 MAX_PET_TALENT_RANK = 3; // use in calculations, expected <= MAX_TALENT_RANK +static constexpr uint8 MAX_TALENT_TABS = 3; enum class SpellShapeshiftFormFlags : int32 { diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index e30d8fa6ed..6b49f4c632 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -2025,12 +2025,12 @@ struct TalentEntry uint32 TabID; // 1 index in TalentTab.dbc (TalentTabEntry) uint32 TierID; // 2 uint32 ColumnIndex; // 3 - uint32 SpellRank[MAX_TALENT_RANK]; // 4-8 - uint32 PrereqTalent[3]; // 9 - 11 (Talent.dbc) - uint32 PrereqRank[3]; // 12 - 14 part of prev field - //uint32 Flags; // 15 also need disable higest ranks on reset talent tree - //uint32 RequiredSpellID; // 16 - //uint64 CategoryMask[2]; // 17 - 18 its a 64 bit mask for pet 1 << m_categoryEnumID in CreatureFamily.dbc + uint32 SpellRank[MAX_TALENT_RANK]; // 4-6 + uint32 PrereqTalent[MAX_TALENT_RANK]; // 7 - 9 (Talent.dbc) + uint32 PrereqRank[MAX_TALENT_RANK]; // 10 - 12 part of prev field + uint32 Flags; // 13 also need disable higest ranks on reset talent tree + uint32 RequiredSpellID; // 14 + uint64 CategoryMask[2]; // 15 - 6 its a 64 bit mask for pet 1 << m_categoryEnumID in CreatureFamily.dbc }; #define MAX_MASTERY_SPELLS 2 diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 39b711ade3..9be7baae88 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -181,7 +181,7 @@ char const SpellVisualfmt[] = "dxxxxxxiixxxxxxxxxxxxxxxxxxxxxxxi"; char const SpellVisualKitfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"; char const StableSlotPricesfmt[] = "ni"; char const SummonPropertiesfmt[] = "niiiii"; -char const TalentEntryfmt[] = "niiiiiiiiiiiiiixxxx"; +char const TalentEntryfmt[] = "niiiiiiiiiiiiiiiiii"; char const TalentTabEntryfmt[] = "nxxiiixxxii"; char const TalentTreePrimarySpellsfmt[] = "diix"; char const TaxiNodesEntryfmt[] = "nifffsiiiff"; diff --git a/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp b/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp index ff265018a4..c53543fd49 100644 --- a/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp @@ -1,6 +1,6 @@ #include "NewPet.h" -#include "CharacterPackets.h" +#include "TalentPackets.h" #include "DBCStores.h" #include "GameTime.h" #include "Map.h" @@ -190,6 +190,9 @@ void NewPet::UpdatePlayerPetData(PlayerPetData* petData) void NewPet::LearnTalent(uint32 talentId, uint32 rank) { + if (rank >= MAX_TALENT_RANK) + return; + TalentEntry const* talentEntry = sTalentStore.LookupEntry(talentId); if (!talentEntry) return; @@ -200,12 +203,14 @@ void NewPet::LearnTalent(uint32 talentId, uint32 rank) _talents[talentId] = rank; - if (IsInWorld()) - SendTalentsInfoUpdateToSummoner(); + LearnSpell(talentEntry->SpellRank[rank]); } void NewPet::LoadTalents(std::string const& dataString) { + if (dataString.empty()) + return; + Tokenizer tokens(dataString, ' '); // Talents are stored in pairs (Id and rank) so ensure that there is always a pair @@ -217,6 +222,7 @@ void NewPet::LoadTalents(std::string const& dataString) uint32 talentId = atoi(*itr); ++itr; uint32 rank = atoi(*itr); + if (rank <= MAX_PET_TALENT_RANK) LearnTalent(talentId, rank); @@ -224,6 +230,25 @@ void NewPet::LoadTalents(std::string const& dataString) } } +void NewPet::SendTalentsInfoUpdateToSummoner() +{ + Unit const* summoner = GetInternalSummoner(); + if (!summoner || !summoner->IsPlayer()) + return; + + WorldPackets::Talent::TalentInfoUpdate packet; + packet.PetTalents = true; + packet.UnspentPoints = CalculateTalentPointsForLevel() - GetSpentTalentPoints(); + for (auto const& learnedTalents : _talents) + { + WorldPackets::Talent::TalentInfo& talent = packet.PetTalent.emplace_back(); + talent.TalentID = learnedTalents.first; + talent.Rank = learnedTalents.second; + } + + summoner->ToPlayer()->SendDirectMessage(packet.Write()); +} + // ------------- private methods void NewPet::SendSpellLearnedToSummoner(uint32 spellId) @@ -296,7 +321,7 @@ bool NewPet::UnlearnSpell(uint32 spellId, bool learnPreviousRank, bool clearActi if (!RemoveSpell(spellId, learnPreviousRank, clearActionbar)) return false; - if (IsInWorld()) + if (IsInWorld() && !sSpellMgr->AssertSpellInfo(spellId)->IsPassive()) SendSpellUnlearnedToSummoner(spellId); return true; @@ -412,17 +437,6 @@ bool NewPet::AddSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetS if (newspell.Active == ACT_ENABLED) ToggleAutocast(spellInfo, true); - /* - uint32 talentCost = sDBCManager.GetTalentSpellCost(spellId); - if (talentCost) - { - int32 free_points = GetMaxTalentPointsForLevel(getLevel()); - m_usedTalentCount += talentCost; - // update free talent points - free_points -= m_usedTalentCount; - SetFreeTalentPoints(free_points > 0 ? free_points : 0); - } - */ return true; } @@ -442,24 +456,10 @@ bool NewPet::RemoveSpell(uint32 spellId, bool learnPreviousRank, bool clearActio RemoveAurasDueToSpell(spellId); - /* - uint32 talentCost = sDBCManager.GetTalentSpellCost(spell_id); - if (talentCost > 0) - { - if (m_usedTalentCount > talentCost) - m_usedTalentCount -= talentCost; - else - m_usedTalentCount = 0; - // update free talent points - int32 free_points = GetMaxTalentPointsForLevel(getLevel()) - m_usedTalentCount; - SetFreeTalentPoints(free_points > 0 ? free_points : 0); - } - */ - if (learnPreviousRank) { - if (uint32 prev_id = sSpellMgr->GetPrevSpellInChain(spellId)) - LearnSpell(prev_id); + if (uint32 prevSpellId = sSpellMgr->GetPrevSpellInChain(spellId)) + LearnSpell(prevSpellId); else learnPreviousRank = false; } @@ -498,25 +498,6 @@ std::string NewPet::GenenerateTalentsDataString() const return ss.str(); } -void NewPet::SendTalentsInfoUpdateToSummoner() -{ - Unit const* summoner = GetInternalSummoner(); - if (!summoner || !summoner->IsPlayer()) - return; - - WorldPackets::Character::TalentInfoUpdate packet; - packet.PetTalents = true; - packet.UnspentPoints = CalculateTalentPointsForLevel(); - for (auto const& talent : _talents) - { - WorldPackets::Character::PetTalentInfo& talentInfo = packet.PetTalent.emplace_back(); - talentInfo.TalentID = talent.first; - talentInfo.Rank = talent.second; - } - - summoner->ToPlayer()->SendDirectMessage(packet.Write()); -} - uint32 NewPet::CalculateTalentPointsForLevel() const { uint32 points = 0; @@ -528,3 +509,12 @@ uint32 NewPet::CalculateTalentPointsForLevel() const return points; } + +uint32 NewPet::GetSpentTalentPoints() const +{ + uint32 points = 0; + for (auto const& pair : _talents) + points += pair.second + 1; + + return points; +} diff --git a/src/server/game/Entities/Creature/TemporarySummon/NewPet.h b/src/server/game/Entities/Creature/TemporarySummon/NewPet.h index 3bbc47ac58..8e253db8f1 100644 --- a/src/server/game/Entities/Creature/TemporarySummon/NewPet.h +++ b/src/server/game/Entities/Creature/TemporarySummon/NewPet.h @@ -78,6 +78,8 @@ class TC_GAME_API NewPet final : public NewGuardian void LearnTalent(uint32 talentId, uint32 rank); // Learns all talents that have been saved in the database void LoadTalents(std::string const& dataString); + // Sends a pet talent info update packet to the summoner + void SendTalentsInfoUpdateToSummoner(); private: void SendSpellLearnedToSummoner(uint32 spellId); @@ -89,8 +91,8 @@ class TC_GAME_API NewPet final : public NewGuardian bool RemoveSpell(uint32 spellId, bool learnPreviousRank, bool clearActionbar = true); std::string GenerateActionBarDataString() const; std::string GenenerateTalentsDataString() const; - void SendTalentsInfoUpdateToSummoner(); uint32 CalculateTalentPointsForLevel() const; + uint32 GetSpentTalentPoints() const; bool _isClassPet; PetSpellMap _spells; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9149725222..ad4e8fdf57 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -17768,6 +17768,7 @@ void Player::_LoadPets(PreparedQueryResult result) petData->IsActive = fields[11].GetBool(); petData->Name = fields[12].GetString(); petData->ActionBar = fields[13].GetString(); + petData->Talents = fields[14].GetString(); petData->Status = PlayerPetDataStatus::UpToDate; _playerPetDataMap.emplace(std::make_pair(petData->Slot, petData->CreatureId), std::move(petData)); diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 4ba038b349..1002040a0b 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -35,6 +35,7 @@ #include "SpellHistory.h" #include "SpellInfo.h" #include "SpellMgr.h" +#include "TalentPackets.h" #include "Util.h" #include "WorldPacket.h" @@ -840,8 +841,6 @@ void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Dec void WorldSession::HandlePetLearnTalent(WorldPacket& recvData) { - TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_PET_LEARN_TALENT"); - ObjectGuid guid; uint32 talentId, requestedRank; recvData >> guid >> talentId >> requestedRank; @@ -851,31 +850,16 @@ void WorldSession::HandlePetLearnTalent(WorldPacket& recvData) pet->LearnTalent(talentId, requestedRank); } -void WorldSession::HandleLearnPreviewTalentsPet(WorldPacket& recvData) +void WorldSession::HandleLearnPreviewTalentsPet(WorldPackets::Talent::LeanPreviewTalentsPet& packet) { - TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_LEARN_PREVIEW_TALENTS_PET"); - - ObjectGuid guid; - recvData >> guid; - - uint32 talentsCount; - recvData >> talentsCount; - - uint32 talentId, talentRank; - - // Client has max 19 talents, rounded up : 25 - uint32 const MaxTalentsCount = 25; - - for (uint32 i = 0; i < talentsCount && i < MaxTalentsCount; ++i) - { - recvData >> talentId >> talentRank; - - _player->LearnPetTalent(guid, talentId, talentRank); - } + NewPet* pet = _player->GetActivelyControlledSummon(); + if (!pet || pet->GetGUID() != packet.PetGUID) + return; - _player->SendTalentsInfoData(true); + for (WorldPackets::Talent::TalentInfo& talent : packet.Talents) + pet->LearnTalent(talent.TalentID, talent.Rank); - recvData.rfinish(); + pet->SendTalentsInfoUpdateToSummoner(); } void WorldSession::UpdatePetSlot(uint32 petNumberA, uint8 oldPetSlot, uint8 newPetSlot) diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h index 0a3e17613a..bc9d9e0310 100644 --- a/src/server/game/Server/Packets/AllPackets.h +++ b/src/server/game/Server/Packets/AllPackets.h @@ -41,6 +41,7 @@ #include "QueryPackets.h" #include "SpellPackets.h" #include "SystemPackets.h" +#include "TalentPackets.h" #include "TicketPackets.h" #include "TotemPackets.h" #include "TradePackets.h" diff --git a/src/server/game/Server/Packets/CharacterPackets.cpp b/src/server/game/Server/Packets/CharacterPackets.cpp index b0a36730e4..14cbcfdd41 100644 --- a/src/server/game/Server/Packets/CharacterPackets.cpp +++ b/src/server/game/Server/Packets/CharacterPackets.cpp @@ -333,24 +333,3 @@ WorldPacket const* WorldPackets::Character::FailedPlayerCondition::Write() return &_worldPacket; } - -WorldPacket const* WorldPackets::Character::TalentInfoUpdate::Write() -{ - _worldPacket << bool(PetTalents); - _worldPacket << uint32(UnspentPoints); - if (PetTalents) - { - _worldPacket << uint8(PetTalent.size()); - for (PetTalentInfo const& talentData : PetTalent) - { - _worldPacket << uint32(talentData.TalentID); - _worldPacket << uint8(talentData.Rank); - } - } - else - { - - } - - return &_worldPacket; -} diff --git a/src/server/game/Server/Packets/CharacterPackets.h b/src/server/game/Server/Packets/CharacterPackets.h index e6f1372341..52e0179d5f 100644 --- a/src/server/game/Server/Packets/CharacterPackets.h +++ b/src/server/game/Server/Packets/CharacterPackets.h @@ -266,34 +266,6 @@ namespace WorldPackets int32 ConditionID = 0; }; - - struct PetTalentInfo - { - uint32 TalentID = 0; - uint8 Rank = 0; - }; - - struct TalentGroupInfo - { - int32 SpecID = 0; - uint16 GlyphIDs[6] = { }; - std::vector TalentIDs; - }; - - class TalentInfoUpdate final : public ServerPacket - { - public: - TalentInfoUpdate() : ServerPacket(SMSG_TALENTS_INFO, 4) { } - - WorldPacket const* Write() override; - - bool PetTalents = false; - uint32 UnspentPoints = 0; - std::vector TalentGroups; - std::vector PetTalent; - }; - - } } diff --git a/src/server/game/Server/Packets/TalentPackets.cpp b/src/server/game/Server/Packets/TalentPackets.cpp new file mode 100644 index 0000000000..c81c8aa564 --- /dev/null +++ b/src/server/game/Server/Packets/TalentPackets.cpp @@ -0,0 +1,51 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * 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 . + */ + +#include "TalentPackets.h" + +void WorldPackets::Talent::LeanPreviewTalentsPet::Read() +{ + _worldPacket >> PetGUID; + + Talents.resize(_worldPacket.read()); + for (TalentInfo& talent : Talents) + { + _worldPacket >> talent.TalentID; + _worldPacket >> talent.Rank; + } +} + +WorldPacket const* WorldPackets::Talent::TalentInfoUpdate::Write() +{ + _worldPacket << bool(PetTalents); + _worldPacket << uint32(UnspentPoints); + if (PetTalents) + { + _worldPacket << uint8(PetTalent.size()); + for (TalentInfo const& talentData : PetTalent) + { + _worldPacket << uint32(talentData.TalentID); + _worldPacket << uint8(talentData.Rank); + } + } + else + { + + } + + 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 0000000000..20cde1b944 --- /dev/null +++ b/src/server/game/Server/Packets/TalentPackets.h @@ -0,0 +1,66 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * 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 . + */ + +#ifndef TalentPackets_h__ +#define TalentPackets_h__ + +#include "Packet.h" + +namespace WorldPackets +{ + namespace Talent + { + struct TalentInfo + { + uint32 TalentID = 0; + uint32 Rank = 0; + }; + + class LeanPreviewTalentsPet final : public ClientPacket + { + public: + LeanPreviewTalentsPet(WorldPacket&& packet) : ClientPacket(CMSG_LEARN_PREVIEW_TALENTS_PET, std::move(packet)) { } + + ObjectGuid PetGUID; + Array Talents; + + void Read() override; + }; + + struct TalentGroupInfo + { + int32 SpecID = 0; + uint16 GlyphIDs[6] = { }; + std::vector TalentIDs; + }; + + class TalentInfoUpdate final : public ServerPacket + { + public: + TalentInfoUpdate() : ServerPacket(SMSG_TALENTS_INFO, 4) { } + + WorldPacket const* Write() override; + + bool PetTalents = false; + uint32 UnspentPoints = 0; + std::vector TalentGroups; + std::vector PetTalent; + }; + } +} + +#endif // TalentPackets_h__ diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 8e4cd83d13..0b145c8e4c 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -238,6 +238,11 @@ namespace WorldPackets class UpdateMissileTrajectory; } + namespace Talent + { + class LeanPreviewTalentsPet; + } + namespace Ticket { class Complaint; @@ -1073,7 +1078,7 @@ class TC_GAME_API WorldSession void HandlePetSpellAutocastOpcode(WorldPacket& recvPacket); void HandlePetCastSpellOpcode(WorldPacket& recvPacket); void HandlePetLearnTalent(WorldPacket& recvPacket); - void HandleLearnPreviewTalentsPet(WorldPacket& recvPacket); + void HandleLearnPreviewTalentsPet(WorldPackets::Talent::LeanPreviewTalentsPet& packet); void HandleSetActionBarToggles(WorldPacket& recvData);