Implement mailbox interaction and expansion-aware mail system

Fix mailbox right-click (transposed CMSG_GAMEOBJECT_USE opcode, missing
mail opcodes in Turtle WoW JSON, decorative GO type filtering). Add
expansion-aware mail packet handling via PacketParsers: Classic format
(single item, no msgSize prefix, Vanilla field order) vs WotLK format
(attachment arrays, enchant slots). Fix CMSG_MAIL_TAKE_ITEM and
CMSG_MAIL_DELETE for Vanilla (no trailing fields). Add pulsing "New
Mail" indicator below minimap, SMSG_RECEIVED_MAIL and
MSG_QUERY_NEXT_MAIL_TIME handlers, and async sender name backfill.
This commit is contained in:
Kelsi 2026-02-16 18:46:44 -08:00
parent bbcc18aa22
commit 1cfe186c62
8 changed files with 421 additions and 126 deletions

View file

@ -770,6 +770,7 @@ public:
bool isMailComposeOpen() const { return showMailCompose_; }
void openMailCompose() { showMailCompose_ = true; }
void closeMailCompose() { showMailCompose_ = false; }
bool hasNewMail() const { return hasNewMail_; }
void closeMailbox();
void sendMail(const std::string& recipient, const std::string& subject,
const std::string& body, uint32_t money, uint32_t cod = 0);
@ -1009,6 +1010,7 @@ private:
void handleMailListResult(network::Packet& packet);
void handleSendMailResult(network::Packet& packet);
void handleReceivedMail(network::Packet& packet);
void handleQueryNextMailTime(network::Packet& packet);
// ---- Taxi handlers ----
void handleShowTaxiNodes(network::Packet& packet);
@ -1351,6 +1353,7 @@ private:
std::vector<MailMessage> mailInbox_;
int selectedMailIndex_ = -1;
bool showMailCompose_ = false;
bool hasNewMail_ = false;
// Vendor
bool vendorWindowOpen = false;

View file

@ -167,6 +167,28 @@ public:
return LeaveChannelPacket::build(channelName);
}
// --- Mail ---
/** Build CMSG_SEND_MAIL */
virtual network::Packet buildSendMail(uint64_t mailboxGuid, const std::string& recipient,
const std::string& subject, const std::string& body,
uint32_t money, uint32_t cod) {
return SendMailPacket::build(mailboxGuid, recipient, subject, body, money, cod);
}
/** Parse SMSG_MAIL_LIST_RESULT into a vector of MailMessage */
virtual bool parseMailList(network::Packet& packet, std::vector<MailMessage>& inbox);
/** Build CMSG_MAIL_TAKE_ITEM */
virtual network::Packet buildMailTakeItem(uint64_t mailboxGuid, uint32_t mailId, uint32_t itemSlot) {
return MailTakeItemPacket::build(mailboxGuid, mailId, itemSlot);
}
/** Build CMSG_MAIL_DELETE */
virtual network::Packet buildMailDelete(uint64_t mailboxGuid, uint32_t mailId, uint32_t mailTemplateId) {
return MailDeletePacket::build(mailboxGuid, mailId, mailTemplateId);
}
// --- Utility ---
/** Read a packed GUID from the packet */
@ -252,6 +274,12 @@ public:
bool parseGuildQueryResponse(network::Packet& packet, GuildQueryResponseData& data) override;
network::Packet buildJoinChannel(const std::string& channelName, const std::string& password) override;
network::Packet buildLeaveChannel(const std::string& channelName) override;
network::Packet buildSendMail(uint64_t mailboxGuid, const std::string& recipient,
const std::string& subject, const std::string& body,
uint32_t money, uint32_t cod) override;
bool parseMailList(network::Packet& packet, std::vector<MailMessage>& inbox) override;
network::Packet buildMailTakeItem(uint64_t mailboxGuid, uint32_t mailId, uint32_t itemSlot) override;
network::Packet buildMailDelete(uint64_t mailboxGuid, uint32_t mailId, uint32_t mailTemplateId) override;
};
/**