feat: add LFG role check confirmation popup with CMSG_LFG_SET_ROLES

When the dungeon finder initiates a role check (SMSG_LFG_ROLE_CHECK_UPDATE
state=2), show a centered popup with Tank/Healer/DPS checkboxes and
Accept/Leave Queue buttons. Accept sends CMSG_LFG_SET_ROLES with the
selected role mask. Previously only showed passive "Role check in progress"
text with no way to respond.
This commit is contained in:
Kelsi 2026-03-20 16:10:29 -07:00
parent df7feed648
commit 23ebfc7e85
4 changed files with 79 additions and 0 deletions

View file

@ -1442,6 +1442,7 @@ public:
// roles bitmask: 0x02=tank, 0x04=healer, 0x08=dps; pass LFGDungeonEntry ID
void lfgJoin(uint32_t dungeonId, uint8_t roles);
void lfgLeave();
void lfgSetRoles(uint8_t roles);
void lfgAcceptProposal(uint32_t proposalId, bool accept);
void lfgSetBootVote(bool vote);
void lfgTeleport(bool toLfgDungeon = true);

View file

@ -388,6 +388,7 @@ private:
void renderBgInvitePopup(game::GameHandler& gameHandler);
void renderBfMgrInvitePopup(game::GameHandler& gameHandler);
void renderLfgProposalPopup(game::GameHandler& gameHandler);
void renderLfgRoleCheckPopup(game::GameHandler& gameHandler);
void renderChatBubbles(game::GameHandler& gameHandler);
void renderMailWindow(game::GameHandler& gameHandler);
void renderMailComposeWindow(game::GameHandler& gameHandler);

View file

@ -17172,6 +17172,17 @@ void GameHandler::lfgLeave() {
LOG_INFO("Sent CMSG_LFG_LEAVE");
}
void GameHandler::lfgSetRoles(uint8_t roles) {
if (state != WorldState::IN_WORLD || !socket) return;
const uint32_t wire = wireOpcode(Opcode::CMSG_LFG_SET_ROLES);
if (wire == 0xFFFF) return;
network::Packet pkt(static_cast<uint16_t>(wire));
pkt.writeUInt8(roles);
socket->send(pkt);
LOG_INFO("Sent CMSG_LFG_SET_ROLES: roles=", static_cast<int>(roles));
}
void GameHandler::lfgAcceptProposal(uint32_t proposalId, bool accept) {
if (!socket) return;

View file

@ -720,6 +720,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
renderBgInvitePopup(gameHandler);
renderBfMgrInvitePopup(gameHandler);
renderLfgProposalPopup(gameHandler);
renderLfgRoleCheckPopup(gameHandler);
renderGuildRoster(gameHandler);
renderSocialFrame(gameHandler);
renderBuffBar(gameHandler);
@ -14201,6 +14202,71 @@ void GameScreen::renderLfgProposalPopup(game::GameHandler& gameHandler) {
ImGui::PopStyleColor(3);
}
void GameScreen::renderLfgRoleCheckPopup(game::GameHandler& gameHandler) {
using LfgState = game::GameHandler::LfgState;
if (gameHandler.getLfgState() != LfgState::RoleCheck) return;
auto* window = core::Application::getInstance().getWindow();
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
ImGui::SetNextWindowPos(ImVec2(screenW / 2.0f - 160.0f, screenH / 2.0f - 80.0f), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(320.0f, 0.0f), ImGuiCond_Always);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.08f, 0.08f, 0.18f, 0.96f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.3f, 0.5f, 0.9f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImVec4(0.1f, 0.1f, 0.3f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 6.0f);
const ImGuiWindowFlags flags =
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse;
if (ImGui::Begin("Role Check##LfgRoleCheck", nullptr, flags)) {
ImGui::TextColored(ImVec4(0.4f, 0.7f, 1.0f, 1.0f), "Confirm your role:");
ImGui::Spacing();
// Role checkboxes
bool isTank = (lfgRoles_ & 0x02) != 0;
bool isHealer = (lfgRoles_ & 0x04) != 0;
bool isDps = (lfgRoles_ & 0x08) != 0;
if (ImGui::Checkbox("Tank", &isTank)) lfgRoles_ = (lfgRoles_ & ~0x02) | (isTank ? 0x02 : 0);
ImGui::SameLine(120.0f);
if (ImGui::Checkbox("Healer", &isHealer)) lfgRoles_ = (lfgRoles_ & ~0x04) | (isHealer ? 0x04 : 0);
ImGui::SameLine(220.0f);
if (ImGui::Checkbox("DPS", &isDps)) lfgRoles_ = (lfgRoles_ & ~0x08) | (isDps ? 0x08 : 0);
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
bool hasRole = (lfgRoles_ & 0x0E) != 0;
if (!hasRole) ImGui::BeginDisabled();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.4f, 0.15f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.2f, 0.6f, 0.2f, 1.0f));
if (ImGui::Button("Accept", ImVec2(140.0f, 28.0f))) {
gameHandler.lfgSetRoles(lfgRoles_);
}
ImGui::PopStyleColor(2);
if (!hasRole) ImGui::EndDisabled();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.15f, 0.15f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.6f, 0.2f, 0.2f, 1.0f));
if (ImGui::Button("Leave Queue", ImVec2(140.0f, 28.0f))) {
gameHandler.lfgLeave();
}
ImGui::PopStyleColor(2);
}
ImGui::End();
ImGui::PopStyleVar();
ImGui::PopStyleColor(3);
}
void GameScreen::renderGuildRoster(game::GameHandler& gameHandler) {
// Guild Roster toggle (customizable keybind)
if (!chatInputActive && !ImGui::GetIO().WantTextInput &&