diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 3d0a689d..064ec58d 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -202,6 +202,9 @@ public: bool hasTarget() const { return targetGuid != 0; } void tabTarget(float playerX, float playerY, float playerZ); + // Inspection + void inspectTarget(); + // ---- Phase 1: Name queries ---- void queryPlayerName(uint64_t guid); void queryCreatureInfo(uint32_t entry, uint64_t guid); diff --git a/include/game/opcodes.hpp b/include/game/opcodes.hpp index cd5cced3..b0f01bb2 100644 --- a/include/game/opcodes.hpp +++ b/include/game/opcodes.hpp @@ -166,6 +166,8 @@ enum class Opcode : uint16_t { CMSG_USE_ITEM = 0x00AB, CMSG_AUTOEQUIP_ITEM = 0x10A, SMSG_INVENTORY_CHANGE_FAILURE = 0x112, + CMSG_INSPECT = 0x114, + SMSG_INSPECT_RESULTS = 0x115, // ---- Death/Respawn ---- CMSG_REPOP_REQUEST = 0x015A, diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index 509cae37..70900c78 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -664,6 +664,12 @@ public: static network::Packet build(uint64_t guid); }; +/** CMSG_INSPECT packet builder */ +class InspectPacket { +public: + static network::Packet build(uint64_t targetGuid); +}; + /** CMSG_NAME_QUERY packet builder */ class NameQueryPacket { public: diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index e130c7f7..d53b3f9b 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1504,6 +1504,32 @@ std::shared_ptr GameHandler::getTarget() const { return entityManager.getEntity(targetGuid); } +void GameHandler::inspectTarget() { + if (state != WorldState::IN_WORLD || !socket) { + LOG_WARNING("Cannot inspect: not in world or not connected"); + return; + } + + if (targetGuid == 0) { + addSystemChatMessage("You must target a player to inspect."); + return; + } + + auto target = getTarget(); + if (!target || target->getType() != ObjectType::PLAYER) { + addSystemChatMessage("You can only inspect players."); + return; + } + + auto packet = InspectPacket::build(targetGuid); + socket->send(packet); + + auto player = std::static_pointer_cast(target); + std::string name = player->getName().empty() ? "Target" : player->getName(); + addSystemChatMessage("Inspecting " + name + "..."); + LOG_INFO("Sent inspect request for player: ", name, " (GUID: 0x", std::hex, targetGuid, std::dec, ")"); +} + void GameHandler::releaseSpirit() { if (!playerDead_) return; if (socket && state == WorldState::IN_WORLD) { diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index f6af7778..b096d40f 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -1174,6 +1174,13 @@ network::Packet SetActiveMoverPacket::build(uint64_t guid) { return packet; } +network::Packet InspectPacket::build(uint64_t targetGuid) { + network::Packet packet(static_cast(Opcode::CMSG_INSPECT)); + packet.writeUInt64(targetGuid); + LOG_DEBUG("Built CMSG_INSPECT: target=0x", std::hex, targetGuid, std::dec); + return packet; +} + network::Packet NameQueryPacket::build(uint64_t playerGuid) { network::Packet packet(static_cast(Opcode::CMSG_NAME_QUERY)); packet.writeUInt64(playerGuid); diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 10445e8c..1740ed79 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -956,6 +956,13 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) { return; } + // /inspect command + if (cmdLower == "inspect") { + gameHandler.inspectTarget(); + chatInputBuffer[0] = '\0'; + return; + } + // Chat channel slash commands bool isChannelCommand = false; if (cmdLower == "s" || cmdLower == "say") {