Fixing bugs, added girl

This commit is contained in:
Vladislav Khorev committed 2026-05-02 20:34:43 +03:00
1 parent d24748df1e
commit 6149182f02
21 files changed
+1255815 -654

No files matched your search

-1
View File
@@ -341,7 +341,6 @@ void Character::update(int64_t deltaMs) {
facingAngle += (angleDiff > 0.f ? rotStep : -rotStep);
}
if (hitSparkEmitter.isConfigured()) {
hitSparkEmitter.update(static_cast<float>(deltaMs));
+2 -185
View File
@@ -236,51 +236,9 @@ namespace ZL
std::cout << "Load resurces step 13" << std::endl;
// Load UI with inventory button
try {
menuManager.uiManager.loadFromFile("resources/config2/ui_inventory.json", renderer, CONST_ZIP_FILE);
menuManager.uiManager.appendFromFile("resources/config2/ui_quest_journal.json", renderer, CONST_ZIP_FILE);
questJournal.loadFromFile("resources/quests/quests.json", CONST_ZIP_FILE);
setupQuestJournalUi();
menuManager.setup(inventory, CONST_ZIP_FILE);
std::cout << "UI loaded successfully" << std::endl;
menuManager.uiManager.setNodeVisible("inventory_items_panel", false);
menuManager.uiManager.setNodeVisible("close_inventory_button", false);
menuManager.uiManager.setTextButtonCallback("inventory_button", [this](const std::string& name) {
std::cout << "[UI] Inventory button clicked" << std::endl;
if (this->questJournalOpen) {
this->toggleQuestJournal();
}
this->menuManager.uiManager.setNodeVisible("inventory_items_panel", true);
this->menuManager.uiManager.setNodeVisible("close_inventory_button", true);
this->inventoryOpen = true;
// Update UI with current items
const auto& items = this->inventory.getItems();
std::string itemText;
if (items.empty()) {
itemText = "Inventory (Empty)";
}
else {
itemText = "Inventory (" + std::to_string(items.size()) + " items)\n\n";
for (size_t i = 0; i < items.size(); ++i) {
itemText += std::to_string(i + 1) + ". " + items[i].name + "\n";
}
}
this->menuManager.uiManager.setText("inventory_items_text", itemText);
});
menuManager.uiManager.setTextButtonCallback("close_inventory_button", [this](const std::string& name) {
std::cout << "[UI] Close button clicked" << std::endl;
menuManager.uiManager.setNodeVisible("inventory_items_panel", false);
menuManager.uiManager.setNodeVisible("close_inventory_button", false);
inventoryOpen = false;
});
}
catch (const std::exception& e) {
std::cerr << "Failed to load UI: " << e.what() << std::endl;
@@ -327,147 +285,6 @@ namespace ZL
static int questStatusPriority(Quest::QuestStatus status) {
switch (status) {
case Quest::QuestStatus::Active: return 0;
case Quest::QuestStatus::Available: return 1;
case Quest::QuestStatus::Completed: return 2;
case Quest::QuestStatus::Failed: return 3;
default: return 4;
}
}
static std::array<float, 4> questStatusColor(Quest::QuestStatus status) {
switch (status) {
case Quest::QuestStatus::Completed: return { 0.25f, 0.95f, 0.35f, 1.0f };
case Quest::QuestStatus::Failed: return { 1.0f, 0.25f, 0.25f, 1.0f };
case Quest::QuestStatus::Active: return { 1.0f, 1.0f, 1.0f, 1.0f };
case Quest::QuestStatus::Available: return { 0.86f, 0.86f, 0.86f, 1.0f };
default: return { 0.45f, 0.45f, 0.45f, 1.0f };
}
}
void Game::setupQuestJournalUi() {
questJournalOpen = false;
selectedQuestIndex = -1;
visibleQuestIds.clear();
menuManager.uiManager.setNodeVisible("quest_journal_panel", false);
menuManager.uiManager.setNodeVisible("quest_close_button", false);
menuManager.uiManager.setTextButtonCallback("quest_journal_button", [this](const std::string&) {
toggleQuestJournal();
});
menuManager.uiManager.setTextButtonCallback("quest_close_button", [this](const std::string&) {
if (questJournalOpen) {
toggleQuestJournal();
}
});
for (int i = 0; i < 9; ++i) {
const std::string slotName = "quest_slot_" + std::to_string(i);
menuManager.uiManager.setTextButtonCallback(slotName, [this, i](const std::string&) {
selectQuestByIndex(i);
});
menuManager.uiManager.setNodeVisible(slotName, false);
}
}
void Game::toggleQuestJournal() {
questJournalOpen = !questJournalOpen;
std::cout << "[quest] toggleQuestJournal: " << (questJournalOpen ? "open" : "closed") << std::endl;
if (questJournalOpen) {
if (inventoryOpen) {
menuManager.uiManager.setNodeVisible("inventory_items_panel", false);
menuManager.uiManager.setNodeVisible("close_inventory_button", false);
inventoryOpen = false;
}
}
menuManager.uiManager.setNodeVisible("quest_journal_panel", questJournalOpen);
menuManager.uiManager.setNodeVisible("quest_close_button", questJournalOpen);
if (questJournalOpen) {
refreshQuestJournalUi();
if (!visibleQuestIds.empty()) {
selectQuestByIndex(0);
}
}
}
void Game::refreshQuestJournalUi() {
visibleQuestIds.clear();
auto quests = questJournal.getVisibleQuests();
std::sort(quests.begin(), quests.end(), [](const Quest::QuestState* a, const Quest::QuestState* b) {
const int pa = questStatusPriority(a->status);
const int pb = questStatusPriority(b->status);
if (pa != pb) return pa < pb;
// Newer quests are shown above older quests inside the same status bucket.
return a->orderIndex > b->orderIndex;
});
for (int i = 0; i < 9; ++i) {
const std::string slotName = "quest_slot_" + std::to_string(i);
if (i < static_cast<int>(quests.size())) {
const auto* quest = quests[i];
visibleQuestIds.push_back(quest->definition.id);
const bool selected = (i == selectedQuestIndex);
const std::string prefix = selected ? "> " : " ";
menuManager.uiManager.setTextButtonText(slotName, prefix + quest->definition.title);
menuManager.uiManager.setTextButtonColor(slotName, questStatusColor(quest->status));
menuManager.uiManager.setNodeVisible(slotName, true);
}
else {
menuManager.uiManager.setTextButtonText(slotName, "");
menuManager.uiManager.setNodeVisible(slotName, false);
}
}
}
void Game::selectQuestByIndex(int index) {
if (index < 0 || index >= static_cast<int>(visibleQuestIds.size())) {
return;
}
selectedQuestIndex = index;
Quest::QuestState* quest = questJournal.findQuest(visibleQuestIds[index]);
if (!quest) {
return;
}
const auto& def = quest->definition;
menuManager.uiManager.setText("quest_middle_title_text", def.title);
menuManager.uiManager.setTextColor("quest_middle_title_text", questStatusColor(quest->status));
const std::string meta = std::string("Category: ") + Quest::toString(def.category)
+ " | Status: " + Quest::toString(quest->status)
+ " | Level: " + std::to_string(def.recommendedLevel);
menuManager.uiManager.setText("quest_meta_text", meta);
std::string objectivesText;
for (size_t i = 0; i < def.objectives.size(); ++i) {
const auto& obj = def.objectives[i];
const bool isActive = static_cast<int>(i) == quest->activeObjectiveIndex;
const std::string mark = obj.completed ? "[x] " : (isActive ? "> [ ] " : "[ ] ");
objectivesText += mark + obj.text;
if (i + 1 < def.objectives.size()) {
objectivesText += "\n";
}
}
menuManager.uiManager.setText("quest_objectives_text", objectivesText);
menuManager.uiManager.setText("quest_lore_title_text", "Описание задания");
menuManager.uiManager.setText("quest_description_text", def.description);
refreshQuestJournalUi();
}
void Game::drawScene() {
glViewport(0, 0, Environment::width, Environment::height);
if (!loadingCompleted) {
@@ -702,7 +519,7 @@ namespace ZL
break;
case SDLK_j:
toggleQuestJournal();
menuManager.toggleQuestJournal();
break;
case SDLK_RETURN:
-14
View File
@@ -20,8 +20,6 @@
#include <unordered_set>
#include "Location.h"
#include "AudioPlayerAsync.h"
#include "quest/QuestJournal.h"
namespace ZL {
class Game {
@@ -51,13 +49,6 @@ namespace ZL {
Inventory inventory;
InteractiveObject* pickedUpObject = nullptr;
bool inventoryOpen = false;
ZL::Quest::QuestJournal questJournal;
bool questJournalOpen = false;
int selectedQuestIndex = -1;
std::vector<std::string> visibleQuestIds;
MenuManager menuManager;
void activateSlowMoEffect();
@@ -107,11 +98,6 @@ namespace ZL {
void endPinch();
int countNonUiPointers() const;
void setupQuestJournalUi();
void toggleQuestJournal();
void refreshQuestJournalUi();
void selectQuestByIndex(int index);
#ifdef EMSCRIPTEN
static Game* s_instance;
static void onResourcesZipLoaded(const char* filename);
+3 -2
View File
@@ -157,6 +157,7 @@ namespace ZL
teleportSparks->setUseWorldSpace(true);
teleportSparks->markConfigured();
// If the player happens to spawn already inside the zone, treat them
// as in-zone so they don't immediately teleport on the first update.
if (player && (player->position - teleportPosition).norm() <= teleportRadius) {
@@ -957,7 +958,7 @@ namespace ZL
// Check if we clicked on an NPC
Character* clickedNpc = raycastNpcs(camPos, rayDir);
if (clickedNpc && player) {
if (clickedNpc && player && clickedNpc->hp > 0) {
float distance = (player->position - clickedNpc->position).norm();
int npcIndex = -1;
for (size_t i = 0; i < npcs.size(); ++i) {
@@ -966,7 +967,7 @@ namespace ZL
break;
}
}
if (npcIndex != -1 && clickedNpc->hp > 0) {
if (npcIndex != -1) {
targetInteractiveObject = nullptr;
if (clickedNpc->canAttack) {
+158 -352
View File
@@ -1,392 +1,198 @@
#include "MenuManager.h"
#include <iostream>
#ifdef EMSCRIPTEN
#include <emscripten.h>
#include <cstdlib>
#endif
#include <algorithm>
#include <string>
namespace ZL {
extern bool inverseVertical;
static int questStatusPriority(Quest::QuestStatus status) {
switch (status) {
case Quest::QuestStatus::Active: return 0;
case Quest::QuestStatus::Available: return 1;
case Quest::QuestStatus::Completed: return 2;
case Quest::QuestStatus::Failed: return 3;
default: return 4;
}
}
static std::array<float, 4> questStatusColor(Quest::QuestStatus status) {
switch (status) {
case Quest::QuestStatus::Completed: return { 0.25f, 0.95f, 0.35f, 1.0f };
case Quest::QuestStatus::Failed: return { 1.0f, 0.25f, 0.25f, 1.0f };
case Quest::QuestStatus::Active: return { 1.0f, 1.0f, 1.0f, 1.0f };
case Quest::QuestStatus::Available: return { 0.86f, 0.86f, 0.86f, 1.0f };
default: return { 0.45f, 0.45f, 0.45f, 1.0f };
}
}
MenuManager::MenuManager(Renderer& iRenderer) :
renderer(iRenderer)
{
}
/*
void MenuManager::setupMenu()
{
mainMenuRoot = loadUiFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE);
aboutMenuRoot = loadUiFromFile("resources/config/about.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);
helpScreenRoot = loadUiFromFile("resources/config/ui_with_help.json", renderer, CONST_ZIP_FILE);
connectionLostRoot = loadUiFromFile("resources/config/connection_lost.json", renderer, CONST_ZIP_FILE);
void MenuManager::setup(Inventory& inv, const std::string& zipFile) {
inventory = &inv;
enterMainMenu();
ingameRoot = loadUiFromFile("resources/config2/ui_inventory.json", renderer, zipFile);
questJournalRoot = loadUiFromFile("resources/config2/ui_quest_journal.json", renderer, zipFile);
questJournal.loadFromFile("resources/quests/quests.json", zipFile);
enterGameplay();
}
bool MenuManager::shouldRenderSpace() const
{
return state == GameState::Gameplay
|| state == GameState::GameOver
|| state == GameState::HelpScreen
|| 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();
});
uiManager.setButtonCallback("aboutButton", [this](const std::string&) {
enterAboutMenu();
});
}
void MenuManager::enterAboutMenu()
{
state = GameState::AboutMenu;
uiManager.replaceRoot(aboutMenuRoot);
uiManager.setButtonCallback("aboutBackButton", [this](const std::string&) {
enterMainMenu();
});
}
// ── 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()
{
void MenuManager::enterGameplay() {
state = GameState::Gameplay;
uiManager.replaceRoot(gameplayRoot);
uiManager.replaceRoot(ingameRoot);
if (Environment::shipState.shipType == 1)
{
uiManager.findButton("shootButton")->state = ButtonState::Disabled;
uiManager.findButton("shootButton2")->state = ButtonState::Disabled;
}
else
{
if (Environment::shipState.velocity < 0.1)
{
uiManager.findButton("minusButton")->state = ButtonState::Disabled;
uiManager.findButton("plusButton")->state = ButtonState::Normal;
uiManager.findButton("shootButton")->state = ButtonState::Normal;
uiManager.findButton("shootButton2")->state = ButtonState::Normal;
uiManager.setNodeVisible("inventory_items_panel", false);
uiManager.setNodeVisible("close_inventory_button", false);
inventoryOpen = false;
uiManager.setTextButtonCallback("inventory_button", [this](const std::string&) {
std::cout << "[UI] Inventory button clicked" << std::endl;
uiManager.setNodeVisible("inventory_items_panel", true);
uiManager.setNodeVisible("close_inventory_button", true);
inventoryOpen = true;
const auto& items = inventory->getItems();
std::string itemText;
if (items.empty()) {
itemText = "Inventory (Empty)";
}
else if (Environment::shipState.velocity >= 0.1 && Environment::shipState.velocity <= 200)
{
uiManager.findButton("minusButton")->state = ButtonState::Normal;
uiManager.findButton("plusButton")->state = ButtonState::Normal;
uiManager.findButton("shootButton")->state = ButtonState::Normal;
uiManager.findButton("shootButton2")->state = ButtonState::Normal;
else {
itemText = "Inventory (" + std::to_string(items.size()) + " items)\n\n";
for (size_t i = 0; i < items.size(); ++i) {
itemText += std::to_string(i + 1) + ". " + items[i].name + "\n";
}
}
else if (Environment::shipState.velocity > 200 && Environment::shipState.velocity < 400 - 0.1)
{
uiManager.findButton("minusButton")->state = ButtonState::Normal;
uiManager.findButton("plusButton")->state = ButtonState::Normal;
uiManager.findButton("shootButton")->state = ButtonState::Disabled;
uiManager.findButton("shootButton2")->state = ButtonState::Disabled;
}
else if (Environment::shipState.velocity >= 400 - 0.1)
{
uiManager.findButton("minusButton")->state = ButtonState::Normal;
uiManager.findButton("plusButton")->state = ButtonState::Disabled;
uiManager.findButton("shootButton")->state = ButtonState::Disabled;
uiManager.findButton("shootButton2")->state = ButtonState::Disabled;
}
}
if (forceSetupSpaceUICallback)
{
forceSetupSpaceUICallback();
}
if (auto btn = uiManager.findButton("takeButton")) btn->state = ButtonState::Disabled;
if (auto btn = uiManager.findButton("showPlayersButton"))
{
btn->state = ButtonState::Disabled;
}
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.setText("inventory_items_text", itemText);
});
uiManager.setButtonPressCallback("takeButton", [this](const std::string&) {
if (onTakeButtonPressed) onTakeButtonPressed();
uiManager.setTextButtonCallback("close_inventory_button", [this](const std::string&) {
std::cout << "[UI] Close button clicked" << std::endl;
uiManager.setNodeVisible("inventory_items_panel", false);
uiManager.setNodeVisible("close_inventory_button", false);
inventoryOpen = false;
});
uiManager.setButtonCallback("showPlayersButton", [this](const std::string&) {
if (onShowPlayersPressed) onShowPlayersPressed();
});
uiManager.setButtonPressCallback("inverseMouseButton", [this](const std::string&) {
inverseVertical = !inverseVertical;
std::cout << "Inverse mouse: " << (inverseVertical ? "ON" : "OFF") << std::endl;
});
uiManager.setButtonCallback("infoButton", [this](const std::string&) {
//if (onShowPlayersPressed) onShowPlayersPressed();
enterHelp();
});
}
// ── 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();
uiManager.setTextButtonCallback("quest_journal_button", [this](const std::string&) {
openQuestJournal();
});
}
void MenuManager::enterHelp()
{
state = GameState::HelpScreen;
uiManager.replaceRoot(helpScreenRoot);
void MenuManager::openQuestJournal() {
if (inventoryOpen) {
uiManager.setNodeVisible("inventory_items_panel", false);
uiManager.setNodeVisible("close_inventory_button", false);
inventoryOpen = false;
}
uiManager.setButtonCallback("infoButtonUnderlying_help", [this](const std::string&) {
enterGameplay();
state = GameState::QuestJournal;
uiManager.pushMenuFromSavedRoot(questJournalRoot);
uiManager.setTextButtonCallback("quest_close_button", [this](const std::string&) {
closeQuestJournal();
});
for (int i = 0; i < 9; ++i) {
const std::string slotName = "quest_slot_" + std::to_string(i);
uiManager.setTextButtonCallback(slotName, [this, i](const std::string&) {
selectQuestByIndex(i);
});
}
refreshQuestJournalUi();
if (!visibleQuestIds.empty()) {
selectQuestByIndex(0);
}
}
// ── State: ConnectionLost ─────────────────────────────────────────────────
void MenuManager::closeQuestJournal() {
state = GameState::Gameplay;
selectedQuestIndex = -1;
visibleQuestIds.clear();
uiManager.popMenu();
}
void MenuManager::enterConnectionLost()
{
state = GameState::ConnectionLost;
uiManager.replaceRoot(connectionLostRoot);
void MenuManager::toggleQuestJournal() {
std::cout << "[quest] toggleQuestJournal: " << (isQuestJournalOpen() ? "closing" : "opening") << std::endl;
if (isQuestJournalOpen()) {
closeQuestJournal();
}
else {
openQuestJournal();
}
}
uiManager.setButtonCallback("reconnectButton", [this](const std::string&) {
enterConnecting();
if (onMultiplayerPressed) onMultiplayerPressed(pendingMultiNick, pendingMultiShipType);
void MenuManager::refreshQuestJournalUi() {
visibleQuestIds.clear();
auto quests = questJournal.getVisibleQuests();
std::sort(quests.begin(), quests.end(), [](const Quest::QuestState* a, const Quest::QuestState* b) {
const int pa = questStatusPriority(a->status);
const int pb = questStatusPriority(b->status);
if (pa != pb) return pa < pb;
return a->orderIndex > b->orderIndex;
});
uiManager.setButtonCallback("exitServerButton", [this](const std::string&) {
enterMainMenu();
});
}
// ── Public event API ──────────────────────────────────────────────────────
for (int i = 0; i < 9; ++i) {
const std::string slotName = "quest_slot_" + std::to_string(i);
void MenuManager::notifyConnected()
{
if (state == GameState::Connecting) {
enterGameplay();
if (i < static_cast<int>(quests.size())) {
const auto* quest = quests[i];
visibleQuestIds.push_back(quest->definition.id);
const bool selected = (i == selectedQuestIndex);
const std::string prefix = selected ? "> " : " ";
uiManager.setTextButtonText(slotName, prefix + quest->definition.title);
uiManager.setTextButtonColor(slotName, questStatusColor(quest->status));
uiManager.setNodeVisible(slotName, true);
}
else {
uiManager.setTextButtonText(slotName, "");
uiManager.setNodeVisible(slotName, false);
}
}
}
void MenuManager::notifyConnectionFailed()
{
if (state == GameState::Connecting) {
enterConnectionFailed();
void MenuManager::selectQuestByIndex(int index) {
if (index < 0 || index >= static_cast<int>(visibleQuestIds.size())) {
return;
}
selectedQuestIndex = index;
Quest::QuestState* quest = questJournal.findQuest(visibleQuestIds[index]);
if (!quest) {
return;
}
const auto& def = quest->definition;
uiManager.setText("quest_middle_title_text", def.title);
uiManager.setTextColor("quest_middle_title_text", questStatusColor(quest->status));
const std::string meta = std::string("Category: ") + Quest::toString(def.category)
+ " | Status: " + Quest::toString(quest->status)
+ " | Level: " + std::to_string(def.recommendedLevel);
uiManager.setText("quest_meta_text", meta);
std::string objectivesText;
for (size_t i = 0; i < def.objectives.size(); ++i) {
const auto& obj = def.objectives[i];
const bool isActive = static_cast<int>(i) == quest->activeObjectiveIndex;
const std::string mark = obj.completed ? "[x] " : (isActive ? "> [ ] " : "[ ] ");
objectivesText += mark + obj.text;
if (i + 1 < def.objectives.size()) {
objectivesText += "\n";
}
}
uiManager.setText("quest_objectives_text", objectivesText);
uiManager.setText("quest_lore_title_text", "Описание задания");
uiManager.setText("quest_description_text", def.description);
refreshQuestJournalUi();
}
void MenuManager::showGameOver(int score)
{
if (state == GameState::Gameplay) {
enterGameOver(score);
}
}
void MenuManager::showConnectionLost()
{
if (state == GameState::Gameplay) {
enterConnectionLost();
}
}*/
} // namespace ZL
} // namespace ZL
+27 -62
View File
@@ -3,86 +3,51 @@
#include "Environment.h"
#include "render/TextureManager.h"
#include "UiManager.h"
#include "items/Item.h"
#include "quest/QuestJournal.h"
#include <vector>
#include <string>
#include <memory>
namespace ZL {
extern const char* CONST_ZIP_FILE;
enum class GameState {
MainMenu,
AboutMenu,
ShipSelectionSingle,
ShipSelectionMulti,
Connecting,
ConnectionFailed,
Gameplay,
GameOver,
HelpScreen,
ConnectionLost
QuestJournal
};
class MenuManager {
protected:
Renderer& renderer;
/*
// Pre-loaded UI roots (loaded once in setupMenu)
std::shared_ptr<UiNode> mainMenuRoot;
std::shared_ptr<UiNode> aboutMenuRoot;
std::shared_ptr<UiNode> shipSelectionRoot;
std::shared_ptr<UiNode> connectingRoot;
std::shared_ptr<UiNode> connectionFailedRoot;
std::shared_ptr<UiNode> gameplayRoot;
std::shared_ptr<UiNode> gameOverRoot;
std::shared_ptr<UiNode> helpScreenRoot;
std::shared_ptr<UiNode> connectionLostRoot;
// Stored for multiplayer retry
std::string pendingMultiNick;
int pendingMultiShipType = 0;
GameState state = GameState::MainMenu;
// State transition methods
void enterMainMenu();
void enterAboutMenu();
void enterShipSelectionSingle();
void enterShipSelectionMulti();
void enterConnecting();
void enterConnectionFailed();
void enterGameplay();
void enterGameOver(int score);
void enterHelp();
void enterConnectionLost();
*/
public:
UiManager uiManager;
ZL::Quest::QuestJournal questJournal;
MenuManager(Renderer& iRenderer);
//void setupMenu();
void setup(Inventory& inv, const std::string& zipFile);
void openQuestJournal();
void closeQuestJournal();
void toggleQuestJournal();
bool isQuestJournalOpen() const { return state == GameState::QuestJournal; }
/*
// Returns true for states where Space should render and run (Gameplay, GameOver, ConnectionLost)
bool shouldRenderSpace() const;
GameState getState() const { return state; }
protected:
Renderer& renderer;
// Called by game events
void showGameOver(int score);
void showConnectionLost();
void notifyConnected();
void notifyConnectionFailed();
private:
void enterGameplay();
void refreshQuestJournalUi();
void selectQuestByIndex(int index);
// Callbacks set by Game/Space
std::function<void()> onMainMenuEntered;
std::function<void()> onRestartPressed;
std::function<void(float)> onVelocityChanged;
std::function<void()> onFirePressed;
std::function<void()> onTakeButtonPressed;
std::function<void(const std::string&, int)> onSingleplayerPressed;
std::function<void(const std::string&, int)> onMultiplayerPressed;
std::function<void()> onShowPlayersPressed;
GameState state = GameState::Gameplay;
Inventory* inventory = nullptr;
std::function<void()> forceSetupSpaceUICallback;*/
std::shared_ptr<UiNode> ingameRoot;
std::shared_ptr<UiNode> questJournalRoot;
bool inventoryOpen = false;
int selectedQuestIndex = -1;
std::vector<std::string> visibleQuestIds;
};
} // namespace ZL
} // namespace ZL