mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Add chat tabs, networked text emotes, channel system, and chat bubbles
Chat tabs filter messages into General/Combat/Whispers/Trade tabs. Text emotes now send CMSG_TEXT_EMOTE to server and display incoming emotes from other players. Channel system auto-joins General/Trade on login with /join, /leave, and /1-/9 shortcuts. Chat bubbles render as ImGui overlays above entities for SAY/YELL messages with fade-out animation.
This commit is contained in:
parent
ca3150e43d
commit
9bcead6a0f
14 changed files with 670 additions and 23 deletions
|
|
@ -229,6 +229,15 @@ public:
|
|||
* @param target Target name (for whispers, empty otherwise)
|
||||
*/
|
||||
void sendChatMessage(ChatType type, const std::string& message, const std::string& target = "");
|
||||
void sendTextEmote(uint32_t textEmoteId, uint64_t targetGuid = 0);
|
||||
void joinChannel(const std::string& channelName, const std::string& password = "");
|
||||
void leaveChannel(const std::string& channelName);
|
||||
const std::vector<std::string>& getJoinedChannels() const { return joinedChannels_; }
|
||||
std::string getChannelByIndex(int index) const;
|
||||
|
||||
// Chat bubble callback: (senderGuid, message, isYell)
|
||||
using ChatBubbleCallback = std::function<void(uint64_t, const std::string&, bool)>;
|
||||
void setChatBubbleCallback(ChatBubbleCallback cb) { chatBubbleCallback_ = std::move(cb); }
|
||||
|
||||
/**
|
||||
* Get chat history (recent messages)
|
||||
|
|
@ -845,6 +854,9 @@ private:
|
|||
* Handle SMSG_MESSAGECHAT from server
|
||||
*/
|
||||
void handleMessageChat(network::Packet& packet);
|
||||
void handleTextEmote(network::Packet& packet);
|
||||
void handleChannelNotify(network::Packet& packet);
|
||||
void autoJoinDefaultChannels();
|
||||
|
||||
// ---- Phase 1 handlers ----
|
||||
void handleNameQueryResponse(network::Packet& packet);
|
||||
|
|
@ -1031,6 +1043,8 @@ private:
|
|||
// Chat
|
||||
std::deque<MessageChatData> chatHistory; // Recent chat messages
|
||||
size_t maxChatHistory = 100; // Maximum chat messages to keep
|
||||
std::vector<std::string> joinedChannels_; // Active channel memberships
|
||||
ChatBubbleCallback chatBubbleCallback_;
|
||||
|
||||
// Targeting
|
||||
uint64_t targetGuid = 0;
|
||||
|
|
|
|||
|
|
@ -351,6 +351,19 @@ enum class LogicalOpcode : uint16_t {
|
|||
SMSG_ARENA_ERROR,
|
||||
MSG_INSPECT_ARENA_TEAMS,
|
||||
|
||||
// ---- Emotes ----
|
||||
CMSG_EMOTE,
|
||||
SMSG_EMOTE,
|
||||
CMSG_TEXT_EMOTE,
|
||||
SMSG_TEXT_EMOTE,
|
||||
|
||||
// ---- Channels ----
|
||||
CMSG_JOIN_CHANNEL,
|
||||
CMSG_LEAVE_CHANNEL,
|
||||
SMSG_CHANNEL_NOTIFY,
|
||||
CMSG_CHANNEL_LIST,
|
||||
SMSG_CHANNEL_LIST,
|
||||
|
||||
// Sentinel
|
||||
COUNT
|
||||
};
|
||||
|
|
|
|||
|
|
@ -670,6 +670,115 @@ public:
|
|||
*/
|
||||
const char* getChatTypeString(ChatType type);
|
||||
|
||||
// ============================================================
|
||||
// Text Emotes
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* CMSG_TEXT_EMOTE packet builder
|
||||
*/
|
||||
class TextEmotePacket {
|
||||
public:
|
||||
static network::Packet build(uint32_t textEmoteId, uint64_t targetGuid = 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_TEXT_EMOTE data
|
||||
*/
|
||||
struct TextEmoteData {
|
||||
uint64_t senderGuid = 0;
|
||||
uint32_t textEmoteId = 0;
|
||||
uint32_t emoteNum = 0;
|
||||
std::string targetName;
|
||||
|
||||
bool isValid() const { return senderGuid != 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_TEXT_EMOTE parser
|
||||
*/
|
||||
class TextEmoteParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, TextEmoteData& data);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Channel System
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* CMSG_JOIN_CHANNEL packet builder
|
||||
*/
|
||||
class JoinChannelPacket {
|
||||
public:
|
||||
static network::Packet build(const std::string& channelName, const std::string& password = "");
|
||||
};
|
||||
|
||||
/**
|
||||
* CMSG_LEAVE_CHANNEL packet builder
|
||||
*/
|
||||
class LeaveChannelPacket {
|
||||
public:
|
||||
static network::Packet build(const std::string& channelName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Channel notification types
|
||||
*/
|
||||
enum class ChannelNotifyType : uint8_t {
|
||||
YOU_JOINED = 0x00,
|
||||
YOU_LEFT = 0x01,
|
||||
WRONG_PASSWORD = 0x02,
|
||||
NOT_MEMBER = 0x03,
|
||||
NOT_MODERATOR = 0x04,
|
||||
PASSWORD_CHANGED = 0x05,
|
||||
OWNER_CHANGED = 0x06,
|
||||
PLAYER_NOT_FOUND = 0x07,
|
||||
NOT_OWNER = 0x08,
|
||||
CHANNEL_OWNER = 0x09,
|
||||
MODE_CHANGE = 0x0A,
|
||||
ANNOUNCEMENTS_ON = 0x0B,
|
||||
ANNOUNCEMENTS_OFF = 0x0C,
|
||||
MODERATION_ON = 0x0D,
|
||||
MODERATION_OFF = 0x0E,
|
||||
MUTED = 0x0F,
|
||||
PLAYER_KICKED = 0x10,
|
||||
BANNED = 0x11,
|
||||
PLAYER_BANNED = 0x12,
|
||||
PLAYER_UNBANNED = 0x13,
|
||||
PLAYER_NOT_BANNED = 0x14,
|
||||
PLAYER_ALREADY_MEMBER = 0x15,
|
||||
INVITE = 0x16,
|
||||
INVITE_WRONG_FACTION = 0x17,
|
||||
WRONG_FACTION = 0x18,
|
||||
INVALID_NAME = 0x19,
|
||||
NOT_MODERATED = 0x1A,
|
||||
PLAYER_INVITED = 0x1B,
|
||||
PLAYER_INVITE_BANNED = 0x1C,
|
||||
THROTTLED = 0x1D,
|
||||
NOT_IN_AREA = 0x1E,
|
||||
NOT_IN_LFG = 0x1F,
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_CHANNEL_NOTIFY data
|
||||
*/
|
||||
struct ChannelNotifyData {
|
||||
ChannelNotifyType notifyType = ChannelNotifyType::YOU_JOINED;
|
||||
std::string channelName;
|
||||
uint64_t senderGuid = 0;
|
||||
|
||||
bool isValid() const { return !channelName.empty(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* SMSG_CHANNEL_NOTIFY parser
|
||||
*/
|
||||
class ChannelNotifyParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, ChannelNotifyData& data);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Server Info Commands
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ public:
|
|||
void cancelEmote();
|
||||
bool isEmoteActive() const { return emoteActive; }
|
||||
static std::string getEmoteText(const std::string& emoteName, const std::string* targetName = nullptr);
|
||||
static uint32_t getEmoteDbcId(const std::string& emoteName);
|
||||
|
||||
// Targeting support
|
||||
void setTargetPosition(const glm::vec3* pos);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,16 @@ private:
|
|||
int lastChatType = 0; // Track chat type changes
|
||||
bool chatInputMoveCursorToEnd = false;
|
||||
|
||||
// Chat tabs
|
||||
int activeChatTab_ = 0;
|
||||
struct ChatTab {
|
||||
std::string name;
|
||||
uint32_t typeMask; // bitmask of ChatType values to show
|
||||
};
|
||||
std::vector<ChatTab> chatTabs_;
|
||||
void initChatTabs();
|
||||
bool shouldShowMessage(const game::MessageChatData& msg, int tabIndex) const;
|
||||
|
||||
// UI state
|
||||
bool showEntityWindow = false;
|
||||
bool showChatWindow = true;
|
||||
|
|
@ -170,6 +180,7 @@ private:
|
|||
void renderMinimapMarkers(game::GameHandler& gameHandler);
|
||||
void renderGuildRoster(game::GameHandler& gameHandler);
|
||||
void renderGuildInvitePopup(game::GameHandler& gameHandler);
|
||||
void renderChatBubbles(game::GameHandler& gameHandler);
|
||||
|
||||
/**
|
||||
* Inventory screen
|
||||
|
|
@ -209,6 +220,17 @@ private:
|
|||
// Gender placeholder replacement
|
||||
std::string replaceGenderPlaceholders(const std::string& text, game::GameHandler& gameHandler);
|
||||
|
||||
// Chat bubbles
|
||||
struct ChatBubble {
|
||||
uint64_t senderGuid = 0;
|
||||
std::string message;
|
||||
float timeRemaining = 0.0f;
|
||||
float totalDuration = 0.0f;
|
||||
bool isYell = false;
|
||||
};
|
||||
std::vector<ChatBubble> chatBubbles_;
|
||||
bool chatBubbleCallbackSet_ = false;
|
||||
|
||||
// Left-click targeting: distinguish click from camera drag
|
||||
glm::vec2 leftClickPressPos_ = glm::vec2(0.0f);
|
||||
bool leftClickWasPress_ = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue