#include "stdafx.h" // Non-RmlUi includes that depend on Windows macros #include "UI.h" #include "../../MinecraftServer.h" #include "../../MultiPlayerLocalPlayer.h" // RmlUi includes (Windows macros temporarily suppressed) #pragma push_macro("byte") #pragma push_macro("GetNextSibling") #pragma push_macro("GetFirstChild") #undef byte #undef GetNextSibling #undef GetFirstChild #include "UIScene_PauseMenu.h" #include "RmlManager.h" #include "../../Windows64/KeyboardMouseInput.h" #include #include #pragma pop_macro("GetFirstChild") #pragma pop_macro("GetNextSibling") #pragma pop_macro("byte") UIScene_PauseMenu::UIScene_PauseMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) , m_document(nullptr) , m_bIgnoreInput(false) , m_bExitDialog(false) , m_bSaveDialog(false) { Rml::Context* ctx = RmlManager::Get().GetContext(); if (!ctx) { app.DebugPrintf("[RmlPauseMenu] No RmlUi context available\n"); return; } m_document = ctx->LoadDocument("PauseMenu.rml"); if (m_document) { app.DebugPrintf("[RmlPauseMenu] Document loaded OK, showing...\n"); m_document->Show(); app.DebugPrintf("[RmlPauseMenu] Document shown, visible=%d, context docs=%d\n", m_document->IsVisible(), ctx->GetNumDocuments()); m_document->GetElementById("resume")->AddEventListener(Rml::EventId::Click, this); m_document->GetElementById("help_options")->AddEventListener(Rml::EventId::Click, this); m_document->GetElementById("achievements")->AddEventListener(Rml::EventId::Click, this); m_document->GetElementById("save_game")->AddEventListener(Rml::EventId::Click, this); m_document->GetElementById("exit_game")->AddEventListener(Rml::EventId::Click, this); m_document->GetElementById("exit_save")->AddEventListener(Rml::EventId::Click, this); m_document->GetElementById("exit_nosave")->AddEventListener(Rml::EventId::Click, this); m_document->GetElementById("exit_cancel")->AddEventListener(Rml::EventId::Click, this); m_document->GetElementById("save_cancel")->AddEventListener(Rml::EventId::Click, this); m_document->GetElementById("save_confirm")->AddEventListener(Rml::EventId::Click, this); m_document->GetElementById("resume")->Focus(); } else { app.DebugPrintf("[RmlPauseMenu] Failed to load PauseMenu.rml\n"); } ui.HidePressStart(); // parentLayer->addComponent(iPad,eUIComponent_Logo); if (g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1) { app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), eXuiServerAction_PauseServer, (void*)TRUE); } TelemetryManager->RecordMenuShown(m_iPad, eUIScene_PauseMenu, 0); TelemetryManager->RecordPauseOrInactive(m_iPad); } UIScene_PauseMenu::~UIScene_PauseMenu() { if (m_document) { m_document->GetElementById("resume")->RemoveEventListener(Rml::EventId::Click, this); m_document->GetElementById("help_options")->RemoveEventListener(Rml::EventId::Click, this); m_document->GetElementById("achievements")->RemoveEventListener(Rml::EventId::Click, this); m_document->GetElementById("save_game")->RemoveEventListener(Rml::EventId::Click, this); m_document->GetElementById("exit_game")->RemoveEventListener(Rml::EventId::Click, this); m_document->GetElementById("exit_save")->RemoveEventListener(Rml::EventId::Click, this); m_document->GetElementById("exit_nosave")->RemoveEventListener(Rml::EventId::Click, this); m_document->GetElementById("exit_cancel")->RemoveEventListener(Rml::EventId::Click, this); m_document->GetElementById("save_cancel")->RemoveEventListener(Rml::EventId::Click, this); m_document->GetElementById("save_confirm")->RemoveEventListener(Rml::EventId::Click, this); m_document->Close(); m_document = nullptr; } m_parentLayer->showComponent(m_iPad, eUIComponent_Panorama, false); m_parentLayer->showComponent(m_iPad, eUIComponent_MenuBackground, false); m_parentLayer->showComponent(m_iPad, eUIComponent_Logo, false); } void UIScene_PauseMenu::gainFocus() { if (!bHasFocus && stealsFocus()) { bHasFocus = true; updateTooltips(); updateComponents(); if (!m_bFocussedOnce) { // No Iggy set-focus call needed for RmlUi scene } handleGainFocus(m_bFocussedOnce); if (bHasFocus) m_bFocussedOnce = true; if (m_document) m_document->Show(); } } void UIScene_PauseMenu::handleLoseFocus() { if (m_document) m_document->Hide(); } void UIScene_PauseMenu::reloadMovie(bool force) { } void UIScene_PauseMenu::tick() { // Signal the base class that we've ticked once, so it sets m_bCanHandleInput // (the Iggy while-loop that normally sets this never runs without a loaded SWF) m_hasTickedOnce = true; UIScene::tick(); Rml::Context* ctx = RmlManager::Get().GetContext(); if (!ctx || !bHasFocus) return; // Forward mouse position (for hover effects and correct click targeting) ctx->ProcessMouseMove(g_KBMInput.GetMouseX(), g_KBMInput.GetMouseY(), 0); // Forward mouse button presses/releases if (g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT)) ctx->ProcessMouseButtonDown(0, 0); if (g_KBMInput.IsMouseButtonReleased(KeyboardMouseInput::MOUSE_LEFT)) ctx->ProcessMouseButtonUp(0, 0); if (g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_RIGHT)) ctx->ProcessMouseButtonDown(1, 0); if (g_KBMInput.IsMouseButtonReleased(KeyboardMouseInput::MOUSE_RIGHT)) ctx->ProcessMouseButtonUp(1, 0); // Forward scroll wheel int wheelDelta = g_KBMInput.GetMouseWheel(); if (wheelDelta != 0) ctx->ProcessMouseWheel(wheelDelta / -120, 0); ctx->Update(); } void UIScene_PauseMenu::render(S32 width, S32 height, C4JRender::eViewportType viewport) { } void UIScene_PauseMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) { if (m_bIgnoreInput) return; if (pressed) { if (iPad == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsLocalGame()) { app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), eXuiServerAction_PauseServer, (void*)FALSE); } } Rml::Context* ctx = RmlManager::Get().GetContext(); if (!ctx) return; // Translate game actions to RmlUi key events. // Mouse clicks are forwarded in tick() via ProcessMouseButtonDown/Up, // so here we only handle keyboard-originated actions. if (!pressed) return; switch (key) { case ACTION_MENU_UP: ctx->ProcessKeyDown(Rml::Input::KI_UP, 0); handled = true; break; case ACTION_MENU_DOWN: ctx->ProcessKeyDown(Rml::Input::KI_DOWN, 0); handled = true; break; case ACTION_MENU_OK: // Only forward as keyboard Enter if actually coming from keyboard, // not from mouse click (mouse is handled in tick()). if (g_KBMInput.IsKeyPressed(VK_RETURN)) { ctx->ProcessKeyDown(Rml::Input::KI_RETURN, 0); handled = true; } break; case ACTION_MENU_CANCEL: case ACTION_MENU_PAUSEMENU: if (m_bExitDialog) { OnExitCancel(); handled = true; } else if (m_bSaveDialog) { OnSaveCancel(); handled = true; } else { navigateBack(); handled = true; } break; default: break; } } void UIScene_PauseMenu::ProcessEvent(Rml::Event& event) { if (m_bIgnoreInput) return; const Rml::String& id = event.GetCurrentElement()->GetId(); if (event == Rml::EventId::Click) { if (id == "resume") OnResume(); else if (id == "help_options") OnHelpAndOptions(); else if (id == "achievements") OnAchievements(); else if (id == "save_game") OnSaveGame(); else if (id == "exit_game") OnExitGame(); else if (id == "exit_save") OnExitSave(); else if (id == "exit_nosave") OnExitNoSave(); else if (id == "exit_cancel") OnExitCancel(); else if (id == "save_confirm") OnSaveConfirm(); else if (id == "save_cancel") OnSaveCancel(); } } void UIScene_PauseMenu::ShowScene(bool show) { if (m_document) { if (show) m_document->Show(); else m_document->Hide(); } } void UIScene_PauseMenu::navigateBack() { if (m_iPad == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsLocalGame()) { app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), eXuiServerAction_PauseServer, (void*)FALSE); } ui.PlayUISFX(eSFX_Back); UIScene::navigateBack(); } void UIScene_PauseMenu::OnResume() { ui.PlayUISFX(eSFX_Back); navigateBack(); } void UIScene_PauseMenu::OnHelpAndOptions() { ui.NavigateToScene(m_iPad, eUIScene_HelpAndOptionsMenu); } void UIScene_PauseMenu::OnAchievements() { ui.NavigateToScene(m_iPad, eUIScene_AchievementsMenu); } void UIScene_PauseMenu::ShowSaveDialog(bool show) { if (!m_document) return; auto* saveDialog = m_document->GetElementById("save_dialog"); auto* buttons = m_document->GetElementById("buttons"); if (show) { if (buttons) buttons->SetProperty("display", "none"); if (saveDialog) saveDialog->SetProperty("display", "block"); auto* saveText = m_document->GetElementById("save_text"); if (!ProfileManager.IsFullVersion()) { if (saveText) saveText->SetInnerRML("You need to unlock the full game to save."); } else { if (saveText) saveText->SetInnerRML("Overwrite existing save?"); } m_document->GetElementById("save_cancel")->Focus(); m_bSaveDialog = true; } else { if (saveDialog) saveDialog->SetProperty("display", "none"); if (buttons) buttons->SetProperty("display", "block"); m_document->GetElementById("save_game")->Focus(); m_bSaveDialog = false; } } void UIScene_PauseMenu::OnSaveGame() { if (m_iPad != ProfileManager.GetPrimaryPad()) return; if (!ProfileManager.IsFullVersion()) { ShowSaveDialog(true); return; } bool bSaveExists; StorageManager.DoesSaveExist(&bSaveExists); if (bSaveExists) { ShowSaveDialog(true); } else { app.SetAction(m_iPad, eAppAction_SaveGame); } } void UIScene_PauseMenu::OnSaveConfirm() { ShowSaveDialog(false); if (!ProfileManager.IsFullVersion()) { ProfileManager.DisplayFullVersionPurchase(false, m_iPad, eSen_UpsellID_Full_Version_Of_Game); } else { app.SetAction(m_iPad, eAppAction_SaveGame); } } void UIScene_PauseMenu::OnSaveCancel() { ShowSaveDialog(false); } void UIScene_PauseMenu::ShowExitDialog(bool show) { if (!m_document) return; auto* exitDialog = m_document->GetElementById("exit_dialog"); auto* buttons = m_document->GetElementById("buttons"); if (show) { Minecraft* pMinecraft = Minecraft::GetInstance(); if (buttons) buttons->SetProperty("display", "none"); if (exitDialog) exitDialog->SetProperty("display", "block"); // Configure dialog for current state auto* exitSave = m_document->GetElementById("exit_save"); auto* exitNosave = m_document->GetElementById("exit_nosave"); auto* exitCancel = m_document->GetElementById("exit_cancel"); auto* exitText = m_document->GetElementById("exit_text"); if (ProfileManager.IsFullVersion() && m_iPad == ProfileManager.GetPrimaryPad()) { // Full version primary pad: show Save & Exit + Exit without Saving + Cancel if (exitSave) exitSave->SetProperty("display", "inline-block"); if (exitNosave) exitNosave->SetProperty("display", "inline-block"); if (g_NetworkManager.GetPlayerCount() > 1) { if (exitText) exitText->SetInnerRML("Exit Game (disconnect other players)?"); } else { if (exitText) exitText->SetInnerRML("Exit Game?"); } if (exitCancel) exitCancel->Focus(); } else if (!ProfileManager.IsFullVersion() && m_iPad == ProfileManager.GetPrimaryPad()) { // Trial version: simple OK/Cancel, "progress will be lost" if (exitSave) exitSave->SetProperty("display", "none"); if (exitNosave) exitNosave->SetProperty("display", "inline-block"); if (exitNosave) exitNosave->SetInnerRML("Yes"); if (exitText) exitText->SetInnerRML("Exit Game (progress will be lost)?"); if (exitCancel) exitCancel->Focus(); } m_bExitDialog = true; } else { if (exitDialog) exitDialog->SetProperty("display", "none"); if (buttons) buttons->SetProperty("display", "block"); // Restore default button state auto* exitSave = m_document->GetElementById("exit_save"); auto* exitNosave = m_document->GetElementById("exit_nosave"); if (exitSave) exitSave->SetProperty("display", "inline-block"); if (exitNosave) exitNosave->SetInnerRML("Exit without Saving"); if (exitNosave) exitNosave->SetProperty("display", "inline-block"); m_document->GetElementById("exit_game")->Focus(); m_bExitDialog = false; } } void UIScene_PauseMenu::OnExitGame() { Minecraft* pMinecraft = Minecraft::GetInstance(); // Non-primary pad: exit directly without dialog if (m_iPad != ProfileManager.GetPrimaryPad()) { if (pMinecraft->localplayers[m_iPad] != nullptr) TelemetryManager->RecordLevelExit(m_iPad, eSen_LevelExitStatus_Exited); app.SetAction(m_iPad, eAppAction_ExitPlayer); return; } // Primary pad: show inline confirmation dialog (no Iggy MessageBox) ShowExitDialog(true); } void UIScene_PauseMenu::OnExitSave() { ShowExitDialog(false); SetIgnoreInput(true); Minecraft* pMinecraft = Minecraft::GetInstance(); if (pMinecraft->localplayers[m_iPad] != nullptr) { int playTime = static_cast(pMinecraft->localplayers[m_iPad]->getSessionTimer()); TelemetryManager->RecordLevelExit(m_iPad, eSen_LevelExitStatus_Exited); } MinecraftServer::getInstance()->setSaveOnExit(true); app.SetAction(m_iPad, eAppAction_ExitWorld); } void UIScene_PauseMenu::OnExitNoSave() { ShowExitDialog(false); SetIgnoreInput(true); Minecraft* pMinecraft = Minecraft::GetInstance(); if (pMinecraft->localplayers[m_iPad] != nullptr) { int playTime = static_cast(pMinecraft->localplayers[m_iPad]->getSessionTimer()); TelemetryManager->RecordLevelExit(m_iPad, eSen_LevelExitStatus_Exited); } app.SetAction(m_iPad, eAppAction_ExitWorld); } void UIScene_PauseMenu::OnExitCancel() { ShowExitDialog(false); }