#include "MenuManager.h" #include #ifdef EMSCRIPTEN #include #include #endif namespace ZL { extern bool inverseVertical; MenuManager::MenuManager(Renderer& iRenderer) : renderer(iRenderer) { } void MenuManager::setupMenu() { mainMenuRoot = loadUiFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE); shipSelectionRoot = loadUiFromFile("resources/config/ship_selection_menu.json", renderer, CONST_ZIP_FILE); connectingRoot = loadUiFromFile("resources/config/connecting.json", renderer, CONST_ZIP_FILE); connectionFailedRoot= loadUiFromFile("resources/config/connection_failed.json", renderer, CONST_ZIP_FILE); gameplayRoot = loadUiFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE); gameOverRoot = loadUiFromFile("resources/config/game_over.json", renderer, CONST_ZIP_FILE); connectionLostRoot = loadUiFromFile("resources/config/connection_lost.json", renderer, CONST_ZIP_FILE); enterMainMenu(); } bool MenuManager::shouldRenderSpace() const { return state == GameState::Gameplay || state == GameState::GameOver || state == GameState::ConnectionLost; } // ── State: MainMenu ────────────────────────────────────────────────────── void MenuManager::enterMainMenu() { state = GameState::MainMenu; uiManager.replaceRoot(mainMenuRoot); if (onMainMenuEntered) onMainMenuEntered(); uiManager.setButtonCallback("singleButton", [this](const std::string&) { enterShipSelectionSingle(); }); uiManager.setButtonCallback("multiplayerButton", [this](const std::string&) { enterShipSelectionMulti(); }); } // ── State: ShipSelectionSingle ─────────────────────────────────────────── void MenuManager::enterShipSelectionSingle() { state = GameState::ShipSelectionSingle; uiManager.replaceRoot(shipSelectionRoot); std::string initialNick; #ifdef EMSCRIPTEN char* savedNickC = emscripten_run_script_string("localStorage.getItem('spacegame_nick') || ''"); if (savedNickC) { initialNick = savedNickC; free(savedNickC); } #endif auto tf = uiManager.findTextField("nicknameInput"); if (tf) { if (!initialNick.empty()) tf->text = initialNick; #ifdef EMSCRIPTEN uiManager.setTextFieldCallback("nicknameInput", [](const std::string&, const std::string& value) { EM_ASM_({ try { localStorage.setItem('spacegame_nick', UTF8ToString($0)); } catch(e) {} }, value.c_str()); }); #endif } uiManager.setButtonCallback("spaceshipButton", [this, initialNick](const std::string&) { std::string nick = uiManager.getTextFieldValue("nicknameInput"); if (nick.empty()) nick = initialNick; if (nick.empty()) nick = "Player"; enterGameplay(); if (onSingleplayerPressed) onSingleplayerPressed(nick, 0); }); uiManager.setButtonCallback("cargoshipButton", [this, initialNick](const std::string&) { std::string nick = uiManager.getTextFieldValue("nicknameInput"); if (nick.empty()) nick = initialNick; if (nick.empty()) nick = "Player"; enterGameplay(); if (onSingleplayerPressed) onSingleplayerPressed(nick, 1); }); uiManager.setButtonCallback("backButton", [this](const std::string&) { enterMainMenu(); }); } // ── State: ShipSelectionMulti ───────────────────────────────────────────── void MenuManager::enterShipSelectionMulti() { state = GameState::ShipSelectionMulti; uiManager.replaceRoot(shipSelectionRoot); std::string initialNick; #ifdef EMSCRIPTEN char* savedNickC = emscripten_run_script_string("localStorage.getItem('spacegame_nick') || ''"); if (savedNickC) { initialNick = savedNickC; free(savedNickC); } #endif auto tf = uiManager.findTextField("nicknameInput"); if (tf) { if (!initialNick.empty()) tf->text = initialNick; #ifdef EMSCRIPTEN uiManager.setTextFieldCallback("nicknameInput", [](const std::string&, const std::string& value) { EM_ASM_({ try { localStorage.setItem('spacegame_nick', UTF8ToString($0)); } catch(e) {} }, value.c_str()); }); #endif } uiManager.setButtonCallback("spaceshipButton", [this, initialNick](const std::string&) { std::string nick = uiManager.getTextFieldValue("nicknameInput"); if (nick.empty()) nick = initialNick; if (nick.empty()) nick = "Player"; pendingMultiNick = nick; pendingMultiShipType = 0; enterConnecting(); if (onMultiplayerPressed) onMultiplayerPressed(nick, 0); }); uiManager.setButtonCallback("cargoshipButton", [this, initialNick](const std::string&) { std::string nick = uiManager.getTextFieldValue("nicknameInput"); if (nick.empty()) nick = initialNick; if (nick.empty()) nick = "Player"; pendingMultiNick = nick; pendingMultiShipType = 1; enterConnecting(); if (onMultiplayerPressed) onMultiplayerPressed(nick, 1); }); uiManager.setButtonCallback("backButton", [this](const std::string&) { enterMainMenu(); }); } // ── State: Connecting ──────────────────────────────────────────────────── void MenuManager::enterConnecting() { state = GameState::Connecting; uiManager.replaceRoot(connectingRoot); // No interactive elements — just a static "Connecting..." image } // ── State: ConnectionFailed ─────────────────────────────────────────────── void MenuManager::enterConnectionFailed() { state = GameState::ConnectionFailed; uiManager.replaceRoot(connectionFailedRoot); uiManager.setButtonCallback("connectionFailedReconnectButton", [this](const std::string&) { enterConnecting(); if (onMultiplayerPressed) onMultiplayerPressed(pendingMultiNick, pendingMultiShipType); }); uiManager.setButtonCallback("connectionFailedGoBack", [this](const std::string&) { enterMainMenu(); }); } // ── State: Gameplay ────────────────────────────────────────────────────── void MenuManager::enterGameplay() { state = GameState::Gameplay; uiManager.replaceRoot(gameplayRoot); uiManager.findButton("minusButton")->state = ButtonState::Disabled; if (auto btn = uiManager.findButton("takeButton")) btn->state = ButtonState::Disabled; /* auto velocityTv = uiManager.findTextView("velocityText"); if (velocityTv) { velocityTv->rect.x = 10.0f; velocityTv->rect.y = static_cast(Environment::height) - velocityTv->rect.h - 10.0f; }*/ uiManager.setButtonPressCallback("shootButton", [this](const std::string&) { if (onFirePressed) onFirePressed(); }); uiManager.setButtonPressCallback("shootButton2", [this](const std::string&) { if (onFirePressed) onFirePressed(); }); uiManager.setButtonPressCallback("plusButton", [this](const std::string&) { int newVel = Environment::shipState.selectedVelocity + 1; if (newVel > 4) newVel = 4; uiManager.findButton("minusButton")->state = ButtonState::Normal; if (newVel == 4) { uiManager.findButton("plusButton")->state = ButtonState::Disabled; } else { uiManager.findButton("plusButton")->state = ButtonState::Normal; } if (onVelocityChanged) onVelocityChanged(newVel); }); uiManager.setButtonPressCallback("minusButton", [this](const std::string&) { int newVel = Environment::shipState.selectedVelocity - 1; if (newVel < 0) newVel = 0; uiManager.findButton("plusButton")->state = ButtonState::Normal; if (newVel == 0) { uiManager.findButton("minusButton")->state = ButtonState::Disabled; } else { uiManager.findButton("minusButton")->state = ButtonState::Normal; } if (onVelocityChanged) onVelocityChanged(newVel); }); uiManager.setButtonPressCallback("takeButton", [this](const std::string&) { if (onTakeButtonPressed) onTakeButtonPressed(); }); uiManager.setButtonCallback("showPlayersButton", [this](const std::string&) { if (onShowPlayersPressed) onShowPlayersPressed(); }); //inverseMouseButton uiManager.setButtonPressCallback("inverseMouseButton", [this](const std::string&) { inverseVertical = !inverseVertical; std::cout << "Inverse mouse: " << (inverseVertical ? "ON" : "OFF") << std::endl; }); /* uiManager.setSliderCallback("velocitySlider", [this](const std::string&, float value) { int newVel = static_cast(roundf(value * 10)); if (newVel > 2) newVel = 2; if (newVel != Environment::shipState.selectedVelocity) { if (onVelocityChanged) onVelocityChanged(newVel); } });*/ } // ── State: GameOver ────────────────────────────────────────────────────── void MenuManager::enterGameOver(int score) { state = GameState::GameOver; uiManager.replaceRoot(gameOverRoot); uiManager.setText("scoreText", "Score: " + std::to_string(score)); uiManager.setButtonCallback("restartButton", [this](const std::string&) { if (onRestartPressed) onRestartPressed(); enterGameplay(); }); uiManager.setButtonCallback("gameOverExitButton", [this](const std::string&) { enterMainMenu(); }); } // ── State: ConnectionLost ───────────────────────────────────────────────── void MenuManager::enterConnectionLost() { state = GameState::ConnectionLost; uiManager.replaceRoot(connectionLostRoot); uiManager.setButtonCallback("reconnectButton", [this](const std::string&) { // TODO: reconnect logic }); uiManager.setButtonCallback("exitServerButton", [this](const std::string&) { enterMainMenu(); }); } // ── Public event API ────────────────────────────────────────────────────── void MenuManager::notifyConnected() { if (state == GameState::Connecting) { enterGameplay(); } } void MenuManager::notifyConnectionFailed() { if (state == GameState::Connecting) { enterConnectionFailed(); } } void MenuManager::showGameOver(int score) { if (state == GameState::Gameplay) { enterGameOver(score); } } void MenuManager::showConnectionLost() { if (state == GameState::Gameplay) { enterConnectionLost(); } } } // namespace ZL