From ff6155e2f376e567a32e89c07a5439907f959516 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 7 Feb 2026 21:12:54 -0800 Subject: [PATCH] Fix chat focus and spirit healer confirm --- include/game/opcodes.hpp | 1 + include/game/world_packets.hpp | 6 ++++++ src/game/game_handler.cpp | 21 +++++++++++++++++++-- src/game/world_packets.cpp | 6 ++++++ src/ui/game_screen.cpp | 25 ++++++++----------------- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/include/game/opcodes.hpp b/include/game/opcodes.hpp index b8d89dcc..56cac724 100644 --- a/include/game/opcodes.hpp +++ b/include/game/opcodes.hpp @@ -242,6 +242,7 @@ enum class Opcode : uint16_t { // ---- Death/Respawn ---- CMSG_REPOP_REQUEST = 0x015A, CMSG_SPIRIT_HEALER_ACTIVATE = 0x0176, + SMSG_SPIRIT_HEALER_CONFIRM = 0x0222, // ---- Teleport / Transfer ---- MSG_MOVE_TELEPORT_ACK = 0x0C7, diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index e5f3e82d..ff8d9ddd 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -1507,6 +1507,12 @@ public: static network::Packet build(uint64_t npcGuid); }; +/** CMSG_QUESTGIVER_HELLO packet builder */ +class QuestgiverHelloPacket { +public: + static network::Packet build(uint64_t npcGuid); +}; + /** CMSG_GOSSIP_SELECT_OPTION packet builder */ class GossipSelectOptionPacket { public: diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index b90d54b6..e3ac5247 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -507,6 +507,20 @@ void GameHandler::handlePacket(network::Packet& packet) { case Opcode::SMSG_GOSSIP_COMPLETE: handleGossipComplete(packet); break; + case Opcode::SMSG_SPIRIT_HEALER_CONFIRM: { + if (packet.getSize() - packet.getReadPos() < 8) { + LOG_WARNING("SMSG_SPIRIT_HEALER_CONFIRM too short"); + break; + } + uint64_t healerGuid = packet.readUInt64(); + LOG_INFO("Spirit healer confirm from 0x", std::hex, healerGuid, std::dec); + if (playerDead_ && socket && state == WorldState::IN_WORLD) { + auto activate = SpiritHealerActivatePacket::build(healerGuid); + socket->send(activate); + LOG_INFO("Confirmed spirit healer activation"); + } + break; + } case Opcode::SMSG_LIST_INVENTORY: handleListInventory(packet); break; @@ -2667,11 +2681,14 @@ void GameHandler::releaseSpirit() { } void GameHandler::activateSpiritHealer(uint64_t npcGuid) { - if (!playerDead_) return; if (state != WorldState::IN_WORLD || !socket) return; + auto gossipPacket = GossipHelloPacket::build(npcGuid); + socket->send(gossipPacket); + auto questHelloPacket = QuestgiverHelloPacket::build(npcGuid); + socket->send(questHelloPacket); auto packet = SpiritHealerActivatePacket::build(npcGuid); socket->send(packet); - LOG_INFO("Sent CMSG_SPIRIT_HEALER_ACTIVATE to 0x", std::hex, npcGuid, std::dec); + LOG_INFO("Sent spirit healer activation sequence to 0x", std::hex, npcGuid, std::dec); } void GameHandler::tabTarget(float playerX, float playerY, float playerZ) { diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 6de69772..8c402aa2 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -2289,6 +2289,12 @@ network::Packet GossipHelloPacket::build(uint64_t npcGuid) { return packet; } +network::Packet QuestgiverHelloPacket::build(uint64_t npcGuid) { + network::Packet packet(static_cast(Opcode::CMSG_QUESTGIVER_HELLO)); + packet.writeUInt64(npcGuid); + return packet; +} + network::Packet GossipSelectOptionPacket::build(uint64_t npcGuid, uint32_t menuId, uint32_t optionId, const std::string& code) { network::Packet packet(static_cast(Opcode::CMSG_GOSSIP_SELECT_OPTION)); packet.writeUInt64(npcGuid); diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index d8a32c45..4586ab43 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -395,7 +395,9 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) { ImGui::SetNextWindowPos(chatWindowPos_, ImGuiCond_FirstUseEver); } ImGuiWindowFlags flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize; - if (chatWindowLocked) flags |= ImGuiWindowFlags_NoMove; + if (chatWindowLocked) { + flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar; + } ImGui::Begin("Chat", nullptr, flags); if (!chatWindowLocked) { @@ -406,6 +408,7 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) { const auto& chatHistory = gameHandler.getChatHistory(); ImGui::BeginChild("ChatHistory", ImVec2(0, -70), true, ImGuiWindowFlags_HorizontalScrollbar); + bool chatHistoryHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); for (const auto& msg : chatHistory) { ImVec4 color = getChatTypeColor(msg.type); @@ -435,14 +438,10 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) { ImGui::EndChild(); - // Click on chat history area → focus the input field - if (ImGui::IsItemClicked()) { - refocusChatInput = true; - } - ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); + float controlsTopY = ImGui::GetCursorScreenPos().y; // Lock toggle ImGui::Checkbox("Lock", &chatWindowLocked); @@ -514,18 +513,10 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) { chatInputActive = false; } - // Click on empty chat window area (receive panel/background) → focus input - // Ignore title bar (move) and interactive items like Lock. + // Click in chat history area (received messages) → focus input. { - ImGuiIO& io = ImGui::GetIO(); - if (ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && - io.MouseClicked[0] && - !ImGui::IsAnyItemHovered()) { - ImVec2 winPos = ImGui::GetWindowPos(); - float titleBarH = ImGui::GetFrameHeight(); - if (io.MousePos.y > winPos.y + titleBarH) { - refocusChatInput = true; - } + if (chatHistoryHovered && ImGui::IsMouseClicked(0)) { + refocusChatInput = true; } }