feat: parse SMSG_GMTICKET_GETTICKET/SYSTEMSTATUS and SMSG_SPELLINSTAKILLLOG

Previously SMSG_GMTICKET_GETTICKET and SMSG_GMTICKET_SYSTEMSTATUS were
silently consumed. Now both are fully parsed:
- SMSG_GMTICKET_GETTICKET decodes all four status codes (no ticket,
  open ticket, closed, suspended), extracts ticket text, age and
  server-estimated wait time, and stores them on GameHandler.
- SMSG_GMTICKET_SYSTEMSTATUS shows a chat message when GM support
  goes offline/online.
- Added requestGmTicket() (sends CMSG_GMTICKET_GETTICKET) called
  automatically when the GM Ticket UI window is opened, so the player
  sees their existing open ticket text and wait time on first open.
- GM Ticket UI window now shows current-ticket status bar, estimated
  wait time, and hides the Delete button when no ticket is active.

Also implements SMSG_SPELLINSTAKILLLOG (previously silently consumed):
parses caster/victim/spellId for all expansions and emits combat text
when the local player is involved in an instant-kill spell event (e.g.
Execute, Obliterate).
This commit is contained in:
Kelsi 2026-03-12 22:14:46 -07:00
parent 9b60108fa6
commit dd38026b23
4 changed files with 150 additions and 8 deletions

View file

@ -20162,9 +20162,15 @@ void GameScreen::renderAchievementWindow(game::GameHandler& gameHandler) {
// ─── GM Ticket Window ─────────────────────────────────────────────────────────
void GameScreen::renderGmTicketWindow(game::GameHandler& gameHandler) {
// Fire a one-shot query when the window first becomes visible
if (showGmTicketWindow_ && !gmTicketWindowWasOpen_) {
gameHandler.requestGmTicket();
}
gmTicketWindowWasOpen_ = showGmTicketWindow_;
if (!showGmTicketWindow_) return;
ImGui::SetNextWindowSize(ImVec2(400, 260), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(440, 320), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos(ImVec2(300, 200), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("GM Ticket", &showGmTicketWindow_,
@ -20173,10 +20179,33 @@ void GameScreen::renderGmTicketWindow(game::GameHandler& gameHandler) {
return;
}
// Show GM support availability
if (!gameHandler.isGmSupportAvailable()) {
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "GM support is currently unavailable.");
ImGui::Spacing();
}
// Show existing open ticket if any
if (gameHandler.hasActiveGmTicket()) {
ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "You have an open GM ticket.");
const std::string& existingText = gameHandler.getGmTicketText();
if (!existingText.empty()) {
ImGui::TextWrapped("Current ticket: %s", existingText.c_str());
}
float waitHours = gameHandler.getGmTicketWaitHours();
if (waitHours > 0.0f) {
char waitBuf[64];
std::snprintf(waitBuf, sizeof(waitBuf), "Estimated wait: %.1f hours", waitHours);
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.4f, 1.0f), "%s", waitBuf);
}
ImGui::Separator();
ImGui::Spacing();
}
ImGui::TextWrapped("Describe your issue and a Game Master will contact you.");
ImGui::Spacing();
ImGui::InputTextMultiline("##gmticket_body", gmTicketBuf_, sizeof(gmTicketBuf_),
ImVec2(-1, 160));
ImVec2(-1, 120));
ImGui::Spacing();
bool hasText = (gmTicketBuf_[0] != '\0');
@ -20193,8 +20222,11 @@ void GameScreen::renderGmTicketWindow(game::GameHandler& gameHandler) {
showGmTicketWindow_ = false;
}
ImGui::SameLine();
if (ImGui::Button("Delete Ticket", ImVec2(100, 0))) {
gameHandler.deleteGmTicket();
if (gameHandler.hasActiveGmTicket()) {
if (ImGui::Button("Delete Ticket", ImVec2(110, 0))) {
gameHandler.deleteGmTicket();
showGmTicketWindow_ = false;
}
}
ImGui::End();