From 1265d87bc5eb6626b9dada95b8dd66abf3bbf1e4 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sun, 22 Feb 2026 18:23:27 +0300 Subject: [PATCH] Updated UI --- proj-windows/CMakeLists.txt | 6 +- src/Game.cpp | 297 +++++------------------------------- src/Game.h | 19 +-- src/MenuManager.cpp | 196 ++++++++++++++++++++++++ src/MenuManager.h | 38 +++++ src/Space.cpp | 0 src/Space.h | 152 ++++++++++++++++++ src/UiManager.cpp | 137 ++++++++++------- src/UiManager.h | 7 +- src/network/LocalClient.cpp | 6 +- 10 files changed, 526 insertions(+), 332 deletions(-) create mode 100644 src/MenuManager.cpp create mode 100644 src/MenuManager.h create mode 100644 src/Space.cpp create mode 100644 src/Space.h diff --git a/proj-windows/CMakeLists.txt b/proj-windows/CMakeLists.txt index 3d790e0..6210c4b 100644 --- a/proj-windows/CMakeLists.txt +++ b/proj-windows/CMakeLists.txt @@ -63,6 +63,10 @@ add_executable(space-game001 ../src/network/WebSocketClientEmscripten.cpp ../src/render/TextRenderer.h ../src/render/TextRenderer.cpp + ../src/MenuManager.h + ../src/MenuManager.cpp + ../src/Space.h + ../src/Space.cpp ) # Установка проекта по умолчанию для Visual Studio @@ -84,7 +88,7 @@ target_compile_definitions(space-game001 PRIVATE WIN32_LEAN_AND_MEAN PNG_ENABLED SDL_MAIN_HANDLED - NETWORK + #NETWORK # SIMPLIFIED ) diff --git a/src/Game.cpp b/src/Game.cpp index 993dbcb..5600e8b 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -34,7 +34,9 @@ namespace ZL const char* CONST_ZIP_FILE = ""; #endif - static bool g_exitBgAnimating = false; + bool g_exitBgAnimating = false; + + bool firePressed = false; float x = 0; @@ -246,6 +248,7 @@ namespace ZL , newTickCount(0) , lastTickCount(0) , planetObject(renderer, taskManager, mainThreadHandler) + , menuManager(renderer) { projectiles.reserve(maxProjectiles); for (int i = 0; i < maxProjectiles; ++i) { @@ -307,156 +310,29 @@ namespace ZL explosionEmitter.setEmissionPoints(std::vector()); projectileEmitter.setEmissionPoints(std::vector()); - uiManager.loadFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE); - std::function loadGameplayUI; - loadGameplayUI = [this]() { - uiManager.loadFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE); + menuManager.onRestartPressed = [this]() { + this->shipAlive = true; + this->gameOver = false; + this->showExplosion = false; + this->explosionEmitter.setEmissionPoints(std::vector()); + Environment::shipState.position = Vector3f{ 0, 0, 45000.f }; + Environment::shipState.velocity = 0.0f; + Environment::shipState.rotation = Eigen::Matrix3f::Identity(); + Environment::inverseShipMatrix = Eigen::Matrix3f::Identity(); + Environment::zoom = DEFAULT_ZOOM; + Environment::tapDownHold = false; - auto velocityTv = uiManager.findTextView("velocityText"); - if (velocityTv) { - velocityTv->rect.x = 10.0f; - velocityTv->rect.y = static_cast(Environment::height) - velocityTv->rect.h - 10.0f; - } - else { - std::cerr << "Failed to find velocityText in UI" << std::endl; - } + std::cerr << "Game restarted\n"; + }; - uiManager.startAnimationOnNode("backgroundNode", "bgScroll"); - static bool isExitButtonAnimating = false; - uiManager.setAnimationCallback("settingsButton", "buttonsExit", [this]() { - std::cerr << "Settings button animation finished -> переход в настройки" << std::endl; - if (uiManager.pushMenuFromFile("resources/config/settings.json", this->renderer, CONST_ZIP_FILE)) { - uiManager.setButtonCallback("Opt1", [this](const std::string& n) { - std::cerr << "Opt1 pressed: " << n << std::endl; - }); - uiManager.setButtonCallback("Opt2", [this](const std::string& n) { - std::cerr << "Opt2 pressed: " << n << std::endl; - }); - uiManager.setButtonCallback("backButton", [this](const std::string& n) { - uiManager.stopAllAnimations(); - uiManager.popMenu(); - }); - } - else { - std::cerr << "Failed to open settings menu after animations" << std::endl; - } - }); - - uiManager.setAnimationCallback("exitButton", "bgScroll", []() { - std::cerr << "Exit button bgScroll animation finished" << std::endl; - g_exitBgAnimating = false; - }); - - uiManager.setButtonCallback("playButton", [this](const std::string& name) { - std::cerr << "Play button pressed: " << name << std::endl; - }); - - uiManager.setButtonCallback("settingsButton", [this](const std::string& name) { - std::cerr << "Settings button pressed: " << name << std::endl; - uiManager.startAnimationOnNode("playButton", "buttonsExit"); - uiManager.startAnimationOnNode("settingsButton", "buttonsExit"); - uiManager.startAnimationOnNode("exitButton", "buttonsExit"); - }); - - uiManager.setButtonCallback("exitButton", [this](const std::string& name) { - std::cerr << "Exit button pressed: " << name << std::endl; - - if (!g_exitBgAnimating) { - std::cerr << "start repeat anim bgScroll on exitButton" << std::endl; - g_exitBgAnimating = true; - uiManager.startAnimationOnNode("exitButton", "bgScroll"); - } - else { - std::cerr << "stop repeat anim bgScroll on exitButton" << std::endl; - g_exitBgAnimating = false; - uiManager.stopAnimationOnNode("exitButton", "bgScroll"); - - auto exitButton = uiManager.findButton("exitButton"); - if (exitButton) { - exitButton->animOffsetX = 0.0f; - exitButton->animOffsetY = 0.0f; - exitButton->animScaleX = 1.0f; - exitButton->animScaleY = 1.0f; - exitButton->buildMesh(); - } - } - }); - - uiManager.setButtonCallback("shootButton", [this](const std::string& name) { - firePressed = true; - }); - uiManager.setButtonCallback("shootButton2", [this](const std::string& name) { - firePressed = true; - }); - uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) { - int newVel = roundf(value * 10); - if (newVel > 2) - { - newVel = 2; - } - - if (newVel != Environment::shipState.selectedVelocity) { - newShipVelocity = newVel; - } - }); + menuManager.onVelocityChanged = [this](float newVelocity) { + newShipVelocity = newVelocity; + //Environment::shipState.velocity = newVelocity; + //std::cerr << "Ship velocity changed: " << newVelocity << "\n"; }; + menuManager.setupMenu(); - uiManager.setButtonCallback("singleButton", [loadGameplayUI](const std::string& name) { - std::cerr << "Single button pressed: " << name << " -> load gameplay UI\n"; - loadGameplayUI(); - }); - /*uiManager.setButtonCallback("multiplayerButton", [loadGameplayUI](const std::string& name) { - std::cerr << "Multiplayer button pressed: " << name << " -> load gameplay UI\n"; - loadGameplayUI(); - });*/ - uiManager.setButtonCallback("multiplayerButton", [this](const std::string& name) { - std::cerr << "Multiplayer button pressed → opening multiplayer menu\n"; - - uiManager.startAnimationOnNode("playButton", "buttonsExit"); - uiManager.startAnimationOnNode("settingsButton", "buttonsExit"); - uiManager.startAnimationOnNode("multiplayerButton", "buttonsExit"); - uiManager.startAnimationOnNode("exitButton", "buttonsExit"); - - if (uiManager.pushMenuFromFile("resources/config/multiplayer_menu.json", this->renderer, CONST_ZIP_FILE)) { - - // Callback для кнопки подключения - uiManager.setButtonCallback("connectButton", [this](const std::string& buttonName) { - std::string serverAddress = uiManager.getTextFieldValue("serverInputField"); - - if (serverAddress.empty()) { - uiManager.setText("statusText", "Please enter server address"); - return; - } - - uiManager.setText("statusText", "Connecting to " + serverAddress + "..."); - std::cerr << "Connecting to server: " << serverAddress << std::endl; - - // Здесь добавить вашу логику подключения к серверу - // connectToServer(serverAddress); - }); - - // Callback для кнопки назад - uiManager.setButtonCallback("backButton", [this](const std::string& buttonName) { - uiManager.popMenu(); - }); - - // Callback для отслеживания ввода текста - uiManager.setTextFieldCallback("serverInputField", - [this](const std::string& fieldName, const std::string& newText) { - std::cout << "Server input field changed to: " << newText << std::endl; - }); - - std::cerr << "Multiplayer menu loaded successfully\n"; - } - else { - std::cerr << "Failed to load multiplayer menu\n"; - } - }); - uiManager.setButtonCallback("exitButton", [](const std::string& name) { - std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n"; - Environment::exitGameLoop = true; - }); cubemapTexture = std::make_shared( std::array{ @@ -756,7 +632,7 @@ namespace ZL renderer.DisableVertexAttribArray(vPositionName); renderer.DisableVertexAttribArray(vTexCoordName); glEnable(GL_BLEND); - uiManager.draw(renderer); + menuManager.uiManager.draw(renderer); glDisable(GL_BLEND); renderer.shaderManager.PopShader(); CheckGlError(); @@ -1445,41 +1321,7 @@ namespace ZL std::cerr << "GAME OVER: collision with planet (moved back and exploded)\n"; - if (!uiGameOverShown) { - if (uiManager.pushMenuFromFile("resources/config/game_over.json", this->renderer, CONST_ZIP_FILE)) { - uiManager.setButtonCallback("restartButton", [this](const std::string& name) { - - std::string respawnMsg = "RESPAWN:" + std::to_string(networkClient->GetClientId()); - networkClient->Send(respawnMsg); - - this->shipAlive = true; - this->gameOver = false; - this->uiGameOverShown = false; - this->showExplosion = false; - this->explosionEmitter.setEmissionPoints(std::vector()); - this->deadRemotePlayers.clear(); - - Environment::shipState.position = Vector3f{ 0, 0, 45000.f }; - Environment::shipState.velocity = 0.0f; - Environment::shipState.rotation = Eigen::Matrix3f::Identity(); - Environment::inverseShipMatrix = Eigen::Matrix3f::Identity(); - Environment::zoom = DEFAULT_ZOOM; - Environment::tapDownHold = false; - - uiManager.popMenu(); - std::cerr << "Game restarted\n"; - }); - - uiManager.setButtonCallback("gameOverExitButton", [this](const std::string& name) { - Environment::exitGameLoop = true; - }); - - uiGameOverShown = true; - } - else { - std::cerr << "Failed to load game_over.json\n"; - } - } + menuManager.showGameOver(); } else { bool stoneCollided = false; @@ -1554,34 +1396,7 @@ namespace ZL planetObject.planetStones.statuses[collidedTriIdx] = ChunkStatus::Empty; } - if (!uiGameOverShown) { - if (uiManager.pushMenuFromFile("resources/config/game_over.json", this->renderer, CONST_ZIP_FILE)) { - uiManager.setButtonCallback("restartButton", [this](const std::string& name) { - this->shipAlive = true; - this->gameOver = false; - this->uiGameOverShown = false; - this->showExplosion = false; - this->explosionEmitter.setEmissionPoints(std::vector()); - Environment::shipState.position = Vector3f{ 0, 0, 45000.f }; - Environment::shipState.velocity = 0.0f; - Environment::shipState.rotation = Eigen::Matrix3f::Identity(); - Environment::inverseShipMatrix = Eigen::Matrix3f::Identity(); - Environment::zoom = DEFAULT_ZOOM; - Environment::tapDownHold = false; - uiManager.popMenu(); - std::cerr << "Game restarted\n"; - }); - - uiManager.setButtonCallback("gameOverExitButton", [this](const std::string& name) { - Environment::exitGameLoop = true; - }); - - uiGameOverShown = true; - } - else { - std::cerr << "Failed to load game_over.json\n"; - } - } + menuManager.showGameOver(); } } } @@ -1643,14 +1458,14 @@ namespace ZL // update velocity text if (shipAlive && !gameOver) { - auto velocityTv = uiManager.findTextView("velocityText"); + auto velocityTv = menuManager.uiManager.findTextView("velocityText"); if (velocityTv) { std::string velocityStr = "Velocity: " + std::to_string(static_cast(Environment::shipState.velocity)); - uiManager.setText("velocityText", velocityStr); + menuManager.uiManager.setText("velocityText", velocityStr); } } - uiManager.update(static_cast(delta)); + menuManager.uiManager.update(static_cast(delta)); lastTickCount = newTickCount; } } @@ -1763,14 +1578,14 @@ namespace ZL // Обработка ввода текста if (event.type == SDL_KEYDOWN) { if (event.key.keysym.sym == SDLK_BACKSPACE) { - uiManager.onKeyBackspace(); + menuManager.uiManager.onKeyBackspace(); } } if (event.type == SDL_TEXTINPUT) { // Пропускаем ctrl+c и другие команды if ((event.text.text[0] & 0x80) == 0) { // ASCII символы - uiManager.onKeyPress((unsigned char)event.text.text[0]); + menuManager.uiManager.onKeyPress((unsigned char)event.text.text[0]); } } @@ -1903,41 +1718,7 @@ namespace ZL shipAlive = false; gameOver = true; Environment::shipState.velocity = 0.0f; - - if (!uiGameOverShown) { - if (uiManager.pushMenuFromFile("resources/config/game_over.json", - this->renderer, CONST_ZIP_FILE)) { - uiManager.setButtonCallback("restartButton", [this](const std::string& name) { - std::cout << "Client: Restart button clicked, sending RESPAWN" << std::endl; - - std::string respawnMsg = "RESPAWN:" + std::to_string(networkClient->GetClientId()); - networkClient->Send(respawnMsg); - - this->shipAlive = true; - this->gameOver = false; - this->uiGameOverShown = false; - this->showExplosion = false; - this->explosionEmitter.setEmissionPoints(std::vector()); - this->deadRemotePlayers.clear(); - - Environment::shipState.position = Vector3f{ 0, 0, 45000.f }; - Environment::shipState.velocity = 0.0f; - Environment::shipState.rotation = Eigen::Matrix3f::Identity(); - Environment::inverseShipMatrix = Eigen::Matrix3f::Identity(); - Environment::zoom = DEFAULT_ZOOM; - Environment::tapDownHold = false; - - uiManager.popMenu(); - std::cerr << "Game restarted\n"; - }); - - uiManager.setButtonCallback("gameOverExitButton", [this](const std::string& name) { - Environment::exitGameLoop = true; - }); - - uiGameOverShown = true; - } - } + menuManager.showGameOver(); } else { deadRemotePlayers.insert(d.targetId); @@ -1998,22 +1779,22 @@ namespace ZL int uiX = mx; int uiY = Environment::height - my; - uiManager.onMouseDown(uiX, uiY); + menuManager.uiManager.onMouseDown(uiX, uiY); bool uiHandled = false; - for (const auto& button : uiManager.findButton("") ? std::vector>{} : std::vector>{}) { + for (const auto& button : menuManager.uiManager.findButton("") ? std::vector>{} : std::vector>{}) { (void)button; } auto pressedSlider = [&]() -> std::shared_ptr { - for (const auto& slider : uiManager.findSlider("") ? std::vector>{} : std::vector>{}) { + for (const auto& slider : menuManager.uiManager.findSlider("") ? std::vector>{} : std::vector>{}) { (void)slider; } return nullptr; }(); - if (!uiManager.isUiInteraction()) { + if (!menuManager.uiManager.isUiInteraction()) { Environment::tapDownHold = true; Environment::tapDownStartPos(0) = mx; @@ -2029,9 +1810,9 @@ namespace ZL int uiX = mx; int uiY = Environment::height - my; - uiManager.onMouseUp(uiX, uiY); + menuManager.uiManager.onMouseUp(uiX, uiY); - if (!uiManager.isUiInteraction()) { + if (!menuManager.uiManager.isUiInteraction()) { Environment::tapDownHold = false; } } @@ -2041,9 +1822,9 @@ namespace ZL int uiX = mx; int uiY = Environment::height - my; - uiManager.onMouseMove(uiX, uiY); + menuManager.uiManager.onMouseMove(uiX, uiY); - if (Environment::tapDownHold && !uiManager.isUiInteraction()) { + if (Environment::tapDownHold && !menuManager.uiManager.isUiInteraction()) { Environment::tapDownCurrentPos(0) = mx; Environment::tapDownCurrentPos(1) = my; } diff --git a/src/Game.h b/src/Game.h index b49280f..84e3701 100644 --- a/src/Game.h +++ b/src/Game.h @@ -14,19 +14,13 @@ #include #include #include +#include "MenuManager.h" +#include "Space.h" #include namespace ZL { - - struct BoxCoords - { - Vector3f pos; - Matrix3f m; - }; - - class Game { public: Game(); @@ -43,6 +37,8 @@ namespace ZL { MainThreadHandler mainThreadHandler; std::unique_ptr networkClient; + + private: int64_t getSyncTimeMs(); void processTickCount(); @@ -100,7 +96,8 @@ namespace ZL { SparkEmitter projectileEmitter; SparkEmitter explosionEmitter; PlanetObject planetObject; - UiManager uiManager; + + MenuManager menuManager; std::vector> projectiles; std::shared_ptr projectileTexture; @@ -115,12 +112,10 @@ namespace ZL { std::vector boxAlive; float shipCollisionRadius = 15.0f; float boxCollisionRadius = 2.0f; - bool uiGameOverShown = false; + //bool uiGameOverShown = false; bool showExplosion = false; uint64_t lastExplosionTime = 0; const uint64_t explosionDurationMs = 500; - bool firePressed = false; - bool serverBoxesApplied = false; diff --git a/src/MenuManager.cpp b/src/MenuManager.cpp new file mode 100644 index 0000000..af0bcfd --- /dev/null +++ b/src/MenuManager.cpp @@ -0,0 +1,196 @@ +#include "MenuManager.h" + + +namespace ZL { + + MenuManager::MenuManager(Renderer& iRenderer) : + renderer(iRenderer) + { + } + + void MenuManager::setupMenu() + { + + uiManager.loadFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE); + + uiSavedRoot = loadUiFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE); + + settingsSavedRoot = loadUiFromFile("resources/config/settings.json", renderer, CONST_ZIP_FILE); + + multiplayerSavedRoot = loadUiFromFile("resources/config/multiplayer_menu.json", renderer, CONST_ZIP_FILE); + + gameOverSavedRoot = loadUiFromFile("resources/config/game_over.json", renderer, CONST_ZIP_FILE); + + std::function loadGameplayUI; + loadGameplayUI = [this]() { + uiManager.replaceRoot(uiSavedRoot); + + auto velocityTv = uiManager.findTextView("velocityText"); + if (velocityTv) { + velocityTv->rect.x = 10.0f; + velocityTv->rect.y = static_cast(Environment::height) - velocityTv->rect.h - 10.0f; + } + else { + std::cerr << "Failed to find velocityText in UI" << std::endl; + } + + uiManager.startAnimationOnNode("backgroundNode", "bgScroll"); + static bool isExitButtonAnimating = false; + uiManager.setAnimationCallback("settingsButton", "buttonsExit", [this]() { + std::cerr << "Settings button animation finished -> переход в настройки" << std::endl; + if (uiManager.pushMenuFromSavedRoot(settingsSavedRoot)) { + uiManager.setButtonCallback("Opt1", [this](const std::string& n) { + std::cerr << "Opt1 pressed: " << n << std::endl; + }); + uiManager.setButtonCallback("Opt2", [this](const std::string& n) { + std::cerr << "Opt2 pressed: " << n << std::endl; + }); + uiManager.setButtonCallback("backButton", [this](const std::string& n) { + uiManager.stopAllAnimations(); + uiManager.popMenu(); + }); + } + else { + std::cerr << "Failed to open settings menu after animations" << std::endl; + } + }); + + uiManager.setAnimationCallback("exitButton", "bgScroll", []() { + std::cerr << "Exit button bgScroll animation finished" << std::endl; + g_exitBgAnimating = false; + }); + + uiManager.setButtonCallback("playButton", [this](const std::string& name) { + std::cerr << "Play button pressed: " << name << std::endl; + }); + + uiManager.setButtonCallback("settingsButton", [this](const std::string& name) { + std::cerr << "Settings button pressed: " << name << std::endl; + uiManager.startAnimationOnNode("playButton", "buttonsExit"); + uiManager.startAnimationOnNode("settingsButton", "buttonsExit"); + uiManager.startAnimationOnNode("exitButton", "buttonsExit"); + }); + + uiManager.setButtonCallback("exitButton", [this](const std::string& name) { + std::cerr << "Exit button pressed: " << name << std::endl; + + if (!g_exitBgAnimating) { + std::cerr << "start repeat anim bgScroll on exitButton" << std::endl; + g_exitBgAnimating = true; + uiManager.startAnimationOnNode("exitButton", "bgScroll"); + } + else { + std::cerr << "stop repeat anim bgScroll on exitButton" << std::endl; + g_exitBgAnimating = false; + uiManager.stopAnimationOnNode("exitButton", "bgScroll"); + + auto exitButton = uiManager.findButton("exitButton"); + if (exitButton) { + exitButton->animOffsetX = 0.0f; + exitButton->animOffsetY = 0.0f; + exitButton->animScaleX = 1.0f; + exitButton->animScaleY = 1.0f; + exitButton->buildMesh(); + } + } + }); + + uiManager.setButtonCallback("shootButton", [this](const std::string& name) { + firePressed = true; + }); + uiManager.setButtonCallback("shootButton2", [this](const std::string& name) { + firePressed = true; + }); + uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) { + int newVel = roundf(value * 10); + /*if (newVel > 2) + { + newVel = 2; + }*/ + + if (newVel != Environment::shipState.selectedVelocity) { + onVelocityChanged(newVel); + } + }); + }; + + uiManager.setButtonCallback("singleButton", [loadGameplayUI](const std::string& name) { + std::cerr << "Single button pressed: " << name << " -> load gameplay UI\n"; + loadGameplayUI(); + }); + /*uiManager.setButtonCallback("multiplayerButton", [loadGameplayUI](const std::string& name) { + std::cerr << "Multiplayer button pressed: " << name << " -> load gameplay UI\n"; + loadGameplayUI(); + });*/ + + uiManager.setButtonCallback("multiplayerButton", [this](const std::string& name) { + std::cerr << "Multiplayer button pressed → opening multiplayer menu\n"; + + uiManager.startAnimationOnNode("playButton", "buttonsExit"); + uiManager.startAnimationOnNode("settingsButton", "buttonsExit"); + uiManager.startAnimationOnNode("multiplayerButton", "buttonsExit"); + uiManager.startAnimationOnNode("exitButton", "buttonsExit"); + + if (uiManager.pushMenuFromSavedRoot(multiplayerSavedRoot)) { + + // Callback для кнопки подключения + uiManager.setButtonCallback("connectButton", [this](const std::string& buttonName) { + std::string serverAddress = uiManager.getTextFieldValue("serverInputField"); + + if (serverAddress.empty()) { + uiManager.setText("statusText", "Please enter server address"); + return; + } + + uiManager.setText("statusText", "Connecting to " + serverAddress + "..."); + std::cerr << "Connecting to server: " << serverAddress << std::endl; + + // Здесь добавить вашу логику подключения к серверу + // connectToServer(serverAddress); + }); + + // Callback для кнопки назад + uiManager.setButtonCallback("backButton", [this](const std::string& buttonName) { + uiManager.popMenu(); + }); + + // Callback для отслеживания ввода текста + uiManager.setTextFieldCallback("serverInputField", + [this](const std::string& fieldName, const std::string& newText) { + std::cout << "Server input field changed to: " << newText << std::endl; + }); + + std::cerr << "Multiplayer menu loaded successfully\n"; + } + else { + std::cerr << "Failed to load multiplayer menu\n"; + } + }); + uiManager.setButtonCallback("exitButton", [](const std::string& name) { + std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n"; + Environment::exitGameLoop = true; + }); + } + + void MenuManager::showGameOver() + { + if (!uiGameOverShown) { + if (uiManager.pushMenuFromSavedRoot(gameOverSavedRoot)) { + uiManager.setButtonCallback("restartButton", [this](const std::string& name) { + uiGameOverShown = false; + uiManager.popMenu(); + onRestartPressed(); + }); + + uiManager.setButtonCallback("gameOverExitButton", [this](const std::string& name) { + Environment::exitGameLoop = true; + }); + + uiGameOverShown = true; + } + else { + std::cerr << "Failed to load game_over.json\n"; + } + } + } +} diff --git a/src/MenuManager.h b/src/MenuManager.h new file mode 100644 index 0000000..9ec0296 --- /dev/null +++ b/src/MenuManager.h @@ -0,0 +1,38 @@ +#pragma once +#include "render/Renderer.h" +#include "Environment.h" +#include "render/TextureManager.h" +#include "UiManager.h" + +namespace ZL { + + extern const char* CONST_ZIP_FILE; + extern bool g_exitBgAnimating; + extern bool firePressed; + + class MenuManager + { + protected: + Renderer& renderer; + bool uiGameOverShown = false; + + std::shared_ptr uiSavedRoot; + std::shared_ptr gameOverSavedRoot; + std::shared_ptr settingsSavedRoot; + std::shared_ptr multiplayerSavedRoot; + + public: + UiManager uiManager; + + MenuManager(Renderer& iRenderer); + + void setupMenu(); + + void showGameOver(); + + std::function onRestartPressed; + std::function onVelocityChanged; + + }; +}; + diff --git a/src/Space.cpp b/src/Space.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/Space.h b/src/Space.h new file mode 100644 index 0000000..f0d56d1 --- /dev/null +++ b/src/Space.h @@ -0,0 +1,152 @@ +#pragma once + +#include "render/Renderer.h" +#include "Environment.h" +#include "render/TextureManager.h" +#include "SparkEmitter.h" +#include "planet/PlanetObject.h" +#include "UiManager.h" +#include "Projectile.h" +#include "utils/TaskManager.h" +#include "network/NetworkInterface.h" +#include +#include +#include +#include +#include +#include "MenuManager.h" + +#include + +namespace ZL { + + + struct BoxCoords + { + Vector3f pos; + Matrix3f m; + }; + + /* + class Space { + public: + Space(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler); + ~Space(); + + void setup(); + void update(); + void render(); + + bool shouldExit() const { return Environment::exitGameLoop; } + + Renderer& renderer; + TaskManager& taskManager; + MainThreadHandler& mainThreadHandler; + + private: + int64_t getSyncTimeMs(); + void processTickCount(); + void drawScene(); + void drawCubemap(float skyPercent); + void drawShip(); + void drawBoxes(); + void drawBoxesLabels(); + void drawUI(); + void drawRemoteShips(); + void drawRemoteShipsLabels(); + void fireProjectiles(); + + bool worldToScreen(const Vector3f& world, float& outX, float& outY, float& outDepth) const; + + void handleDown(int mx, int my); + void handleUp(int mx, int my); + void handleMotion(int mx, int my); + + SDL_Window* window; + SDL_GLContext glContext; + + + + int64_t newTickCount; + int64_t lastTickCount; + + std::vector boxCoordsArr; + std::vector boxRenderArr; + + std::vector boxLabels; + std::unique_ptr textRenderer; + + //std::unordered_map latestRemotePlayers; + std::unordered_map remotePlayerStates; + + float newShipVelocity = 0; + + static const size_t CONST_TIMER_INTERVAL = 10; + static const size_t CONST_MAX_TIME_INTERVAL = 1000; + + std::shared_ptr sparkTexture; + std::shared_ptr spaceshipTexture; + std::shared_ptr cubemapTexture; + VertexDataStruct spaceshipBase; + VertexRenderStruct spaceship; + + + VertexRenderStruct cubemap; + + std::shared_ptr boxTexture; + VertexDataStruct boxBase; + + SparkEmitter sparkEmitter; + SparkEmitter projectileEmitter; + SparkEmitter explosionEmitter; + PlanetObject planetObject; + + MenuManager menuManager; + + std::vector> projectiles; + std::shared_ptr projectileTexture; + float projectileCooldownMs = 500.0f; + int64_t lastProjectileFireTime = 0; + int maxProjectiles = 32; + std::vector shipLocalEmissionPoints; + + + bool shipAlive = true; + bool gameOver = false; + std::vector boxAlive; + float shipCollisionRadius = 15.0f; + float boxCollisionRadius = 2.0f; + //bool uiGameOverShown = false; + bool showExplosion = false; + uint64_t lastExplosionTime = 0; + const uint64_t explosionDurationMs = 500; + + bool serverBoxesApplied = false; + + static constexpr float MAX_DIST_SQ = 10000.f * 10000.f; + static constexpr float FADE_START = 6000.f; + static constexpr float FADE_RANGE = 4000.f; + static constexpr float BASE_SCALE = 140.f; + static constexpr float PERSPECTIVE_K = 0.05f; // Tune + static constexpr float MIN_SCALE = 0.4f; + static constexpr float MAX_SCALE = 0.8f; + static constexpr float CLOSE_DIST = 600.0f; + + std::unordered_set deadRemotePlayers; + + // --- Target HUD (brackets + offscreen arrow) --- + int trackedTargetId = -1; + bool targetWasVisible = false; + float targetAcquireAnim = 0.0f; // 0..1 схлопывание (0 = далеко, 1 = на месте) + + // временный меш для HUD (будем перезаливать VBO маленькими порциями) + VertexRenderStruct hudTempMesh; + + // helpers + bool projectToNDC(const Vector3f& world, float& ndcX, float& ndcY, float& ndcZ, float& clipW) const; + void drawTargetHud(); // рисует рамку или стрелку + int pickTargetId() const; // выбирает цель (пока: ближайший живой удаленный игрок) + };*/ + + +} // namespace ZL \ No newline at end of file diff --git a/src/UiManager.cpp b/src/UiManager.cpp index 126140a..d051f8c 100644 --- a/src/UiManager.cpp +++ b/src/UiManager.cpp @@ -182,60 +182,7 @@ namespace ZL { } } - void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) { - std::string content; - try { - if (zipFile.empty()) { - content = readTextFile(path); - } - else { - auto buf = readFileFromZIP(path, zipFile); - if (buf.empty()) { - std::cerr << "UiManager: failed to read " << path << " from zip " << zipFile << std::endl; - throw std::runtime_error("Failed to load UI file: " + path); - } - content.assign(buf.begin(), buf.end()); - } - } - catch (const std::exception& e) { - std::cerr << "UiManager: failed to open " << path << " : " << e.what() << std::endl; - throw std::runtime_error("Failed to load UI file: " + path); - } - - json j; - try { - j = json::parse(content); - } - catch (const std::exception& e) { - std::cerr << "UiManager: json parse error: " << e.what() << std::endl; - throw std::runtime_error("Failed to load UI file: " + path); - } - - if (!j.contains("root") || !j["root"].is_object()) { - std::cerr << "UiManager: root node missing or invalid" << std::endl; - throw std::runtime_error("Failed to load UI file: " + path); - } - - root = parseNode(j["root"], renderer, zipFile); - layoutNode(root); - buttons.clear(); - sliders.clear(); - textViews.clear(); - textFields.clear(); - collectButtonsAndSliders(root); - - nodeActiveAnims.clear(); - - for (auto& b : buttons) { - b->buildMesh(); - } - for (auto& s : sliders) { - s->buildTrackMesh(); - s->buildKnobMesh(); - } - } - - std::shared_ptr UiManager::parseNode(const json& j, Renderer& renderer, const std::string& zipFile) { + std::shared_ptr parseNode(const json& j, Renderer& renderer, const std::string& zipFile) { auto node = std::make_shared(); if (j.contains("type") && j["type"].is_string()) node->type = j["type"].get(); if (j.contains("name") && j["name"].is_string()) node->name = j["name"].get(); @@ -416,6 +363,76 @@ namespace ZL { return node; } + std::shared_ptr loadUiFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) + { + std::shared_ptr root; + + std::string content; + try { + if (zipFile.empty()) { + content = readTextFile(path); + } + else { + auto buf = readFileFromZIP(path, zipFile); + if (buf.empty()) { + std::cerr << "UiManager: failed to read " << path << " from zip " << zipFile << std::endl; + throw std::runtime_error("Failed to load UI file: " + path); + } + content.assign(buf.begin(), buf.end()); + } + } + catch (const std::exception& e) { + std::cerr << "UiManager: failed to open " << path << " : " << e.what() << std::endl; + throw std::runtime_error("Failed to load UI file: " + path); + } + + json j; + try { + j = json::parse(content); + } + catch (const std::exception& e) { + std::cerr << "UiManager: json parse error: " << e.what() << std::endl; + throw std::runtime_error("Failed to load UI file: " + path); + } + + if (!j.contains("root") || !j["root"].is_object()) { + std::cerr << "UiManager: root node missing or invalid" << std::endl; + throw std::runtime_error("Failed to load UI file: " + path); + } + + root = parseNode(j["root"], renderer, zipFile); + + return root; + } + + void UiManager::replaceRoot(std::shared_ptr newRoot) { + root = newRoot; + layoutNode(root); + buttons.clear(); + sliders.clear(); + textViews.clear(); + textFields.clear(); + collectButtonsAndSliders(root); + + nodeActiveAnims.clear(); + + for (auto& b : buttons) { + b->buildMesh(); + } + for (auto& s : sliders) { + s->buildTrackMesh(); + s->buildKnobMesh(); + } + } + + void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) { + + std::shared_ptr newRoot = loadUiFromFile(path, renderer, zipFile); + replaceRoot(newRoot); + } + + + void UiManager::layoutNode(const std::shared_ptr& node) { for (auto& child : node->children) { child->rect.x += node->rect.x; @@ -553,7 +570,8 @@ namespace ZL { return tf->text; } - bool UiManager::pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) { + bool UiManager::pushMenuFromSavedRoot(std::shared_ptr newRoot) + { MenuState prev; prev.root = root; prev.buttons = buttons; @@ -579,17 +597,22 @@ namespace ZL { } } - loadFromFile(path, renderer, zipFile); + replaceRoot(newRoot); menuStack.push_back(std::move(prev)); return true; } catch (const std::exception& e) { - std::cerr << "UiManager: pushMenuFromFile failed to load " << path << " : " << e.what() << std::endl; + std::cerr << "UiManager: pushMenuFromFile failed to load from root : " << e.what() << std::endl; animCallbacks = prev.animCallbacks; return false; } } + bool UiManager::pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) { + auto newRoot = loadUiFromFile(path, renderer, zipFile); + return pushMenuFromSavedRoot(newRoot); + } + bool UiManager::popMenu() { if (menuStack.empty()) { std::cerr << "UiManager: popMenu called but menu stack is empty" << std::endl; diff --git a/src/UiManager.h b/src/UiManager.h index 8fa6e71..05af33c 100644 --- a/src/UiManager.h +++ b/src/UiManager.h @@ -136,10 +136,15 @@ namespace ZL { std::map animations; }; + std::shared_ptr parseNode(const json& j, Renderer& renderer, const std::string& zipFile); + std::shared_ptr loadUiFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = ""); + + class UiManager { public: UiManager() = default; + void replaceRoot(std::shared_ptr newRoot); void loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = ""); void draw(Renderer& renderer); @@ -186,6 +191,7 @@ namespace ZL { std::string getTextFieldValue(const std::string& name); bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = ""); + bool pushMenuFromSavedRoot(std::shared_ptr newRoot); bool popMenu(); void clearMenuStack(); @@ -196,7 +202,6 @@ namespace ZL { bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function cb); private: - std::shared_ptr parseNode(const json& j, Renderer& renderer, const std::string& zipFile); void layoutNode(const std::shared_ptr& node); void collectButtonsAndSliders(const std::shared_ptr& node); diff --git a/src/network/LocalClient.cpp b/src/network/LocalClient.cpp index c0a219e..5336b43 100644 --- a/src/network/LocalClient.cpp +++ b/src/network/LocalClient.cpp @@ -239,7 +239,7 @@ namespace ZL { for (size_t bi = 0; bi < serverBoxes.size(); ++bi) { if (serverBoxes[bi].destroyed) continue; - Eigen::Vector3f boxWorld = serverBoxes[bi].position + Eigen::Vector3f(0.0f, 6.0f, 45000.0f); + Eigen::Vector3f boxWorld = serverBoxes[bi].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f); for (size_t pi = 0; pi < projectiles.size(); ++pi) { const auto& pr = projectiles[pi]; @@ -387,8 +387,8 @@ namespace ZL { } const std::vector localOffsets = { - Eigen::Vector3f(-1.5f, 0.9f, 5.0f), - Eigen::Vector3f(1.5f, 0.9f, 5.0f) + Eigen::Vector3f(-1.5f, 0.9f - 6.f, 5.0f), + Eigen::Vector3f(1.5f, 0.9f - 6.f, 5.0f) }; uint64_t now_ms = std::chrono::duration_cast(