From 74b78cd10bfc1e2a505319ab9aa6ff71f2c5e1dc Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 26 Feb 2026 10:41:29 -0800 Subject: [PATCH] Add pet tracking via SMSG_PET_SPELLS and dismiss pet button --- include/game/game_handler.hpp | 5 +++++ include/game/world_packets.hpp | 6 ++++++ src/game/game_handler.cpp | 16 ++++++++++++++++ src/game/world_packets.cpp | 7 +++++++ src/ui/game_screen.cpp | 13 +++++++++++-- 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 57949bcd..265b2fe9 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -441,6 +441,9 @@ public: void castSpell(uint32_t spellId, uint64_t targetGuid = 0); void cancelCast(); void cancelAura(uint32_t spellId); + void dismissPet(); + bool hasPet() const { return petGuid_ != 0; } + uint64_t getPetGuid() const { return petGuid_; } const std::unordered_set& getKnownSpells() const { return knownSpells; } bool isCasting() const { return casting; } bool isGameObjectInteractionCasting() const { @@ -1137,6 +1140,7 @@ private: void handleGuildInvite(network::Packet& packet); void handleGuildCommandResult(network::Packet& packet); void handlePetitionShowlist(network::Packet& packet); + void handlePetSpells(network::Packet& packet); void handleTurnInPetitionResults(network::Packet& packet); // ---- Character creation handler ---- @@ -1475,6 +1479,7 @@ private: std::array actionBar{}; std::vector playerAuras; std::vector targetAuras; + uint64_t petGuid_ = 0; // ---- Phase 4: Group ---- GroupListData partyData; diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index f7239c54..cd34ab86 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -1698,6 +1698,12 @@ public: static network::Packet build(uint32_t spellId); }; +/** CMSG_PET_ACTION packet builder */ +class PetActionPacket { +public: + static network::Packet build(uint64_t petGuid, uint32_t action); +}; + /** SMSG_CAST_FAILED data */ struct CastFailedData { uint8_t castCount = 0; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index d48f80ef..bbc477c5 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1836,6 +1836,9 @@ void GameHandler::handlePacket(network::Packet& packet) { case Opcode::SMSG_GUILD_COMMAND_RESULT: handleGuildCommandResult(packet); break; + case Opcode::SMSG_PET_SPELLS: + handlePetSpells(packet); + break; case Opcode::SMSG_PETITION_SHOWLIST: handlePetitionShowlist(packet); break; @@ -3322,6 +3325,7 @@ void GameHandler::selectCharacter(uint64_t characterGuid) { actionBar = {}; playerAuras.clear(); targetAuras.clear(); + petGuid_ = 0; playerXp_ = 0; playerNextLevelXp_ = 0; serverPlayerLevel_ = 1; @@ -9261,6 +9265,18 @@ void GameHandler::cancelAura(uint32_t spellId) { socket->send(packet); } +void GameHandler::handlePetSpells(network::Packet& packet) { + if (packet.getSize() - packet.getReadPos() < 8) return; + petGuid_ = packet.readUInt64(); + LOG_DEBUG("SMSG_PET_SPELLS: petGuid=0x", std::hex, petGuid_, std::dec); +} + +void GameHandler::dismissPet() { + if (petGuid_ == 0 || state != WorldState::IN_WORLD || !socket) return; + auto packet = PetActionPacket::build(petGuid_, 0x07000000); + socket->send(packet); +} + void GameHandler::setActionBarSlot(int slot, ActionBarSlot::Type type, uint32_t id) { if (slot < 0 || slot >= ACTION_BAR_SLOTS) return; actionBar[slot].type = type; diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 6505ea91..acc7dea6 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -2890,6 +2890,13 @@ network::Packet CancelAuraPacket::build(uint32_t spellId) { return packet; } +network::Packet PetActionPacket::build(uint64_t petGuid, uint32_t action) { + network::Packet packet(wireOpcode(Opcode::CMSG_PET_ACTION)); + packet.writeUInt64(petGuid); + packet.writeUInt32(action); + return packet; +} + bool CastFailedParser::parse(network::Packet& packet, CastFailedData& data) { data.castCount = packet.readUInt8(); data.spellId = packet.readUInt32(); diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index d8efa743..d0b31c50 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -4720,14 +4720,13 @@ void GameScreen::renderGuildRoster(game::GameHandler& gameHandler) { void GameScreen::renderBuffBar(game::GameHandler& gameHandler) { const auto& auras = gameHandler.getPlayerAuras(); - if (auras.empty()) return; // Count non-empty auras int activeCount = 0; for (const auto& a : auras) { if (!a.isEmpty()) activeCount++; } - if (activeCount == 0) return; + if (activeCount == 0 && !gameHandler.hasPet()) return; auto* assetMgr = core::Application::getInstance().getAssetManager(); @@ -4813,6 +4812,16 @@ void GameScreen::renderBuffBar(game::GameHandler& gameHandler) { ImGui::PopID(); shown++; } + // Dismiss Pet button + if (gameHandler.hasPet()) { + if (shown > 0) ImGui::Spacing(); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.2f, 0.2f, 0.9f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.8f, 0.3f, 0.3f, 1.0f)); + if (ImGui::Button("Dismiss Pet", ImVec2(-1, 0))) { + gameHandler.dismissPet(); + } + ImGui::PopStyleColor(2); + } } ImGui::End();