This commit is contained in:
Vlad committed 2026-04-30 15:15:11 +06:00
commit 4de8f78eca
29 files changed
+2052 -111

No files matched your search

+172 -18
View File
@@ -53,7 +53,7 @@ namespace ZL
if (s_instance) {
s_instance->mainThreadHandler.EnqueueMainThreadTask([&]() {
s_instance->setupPart2();
});
});
}
}
@@ -86,7 +86,7 @@ namespace ZL
ZL::BindOpenGlFunctions();
ZL::CheckGlError(__FILE__, __LINE__);
renderer.InitOpenGL();
#ifdef EMSCRIPTEN
// These shaders and loading.png are preloaded separately (not from zip),
// so they are available immediately without waiting for resources.zip.
@@ -111,9 +111,9 @@ namespace ZL
minDimension = width;
}
loadingMesh.data = CreateRect2D({ 0.0f, 0.0f }, { minDimension*0.5f, minDimension*0.5f }, 3);
loadingMesh.data = CreateRect2D({ 0.0f, 0.0f }, { minDimension * 0.5f, minDimension * 0.5f }, 3);
loadingMesh.RefreshVBO();
#ifdef EMSCRIPTEN
// Asynchronously download resources.zip; setupPart2() is called on completion.
// The loading screen stays visible until the download finishes.
@@ -125,7 +125,7 @@ namespace ZL
std::cout << "Load resurces step 2" << std::endl;
this->setupPart2();
std::cout << "Load resurces step 3" << std::endl;
});
});
#endif
}
@@ -201,7 +201,7 @@ namespace ZL
currentLocation->player->setTarget(currentLocation->teleportPosition);
}
currentLocation->playerInTeleportZone = true;
};
};
location2->onTeleport = [this]() {
std::cout << "[TELEPORT] location 2 -> location 1" << std::endl;
currentLocation = location1;
@@ -210,12 +210,12 @@ namespace ZL
currentLocation->player->setTarget(currentLocation->teleportPosition);
}
currentLocation->playerInTeleportZone = true;
};
};
currentLocation = location2;
std::cout << "Load resurces step 5" << std::endl;
std::cout << "Load resurces step 12" << std::endl;
// Shadow mapping shaders
@@ -240,6 +240,10 @@ namespace ZL
// 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();
std::cout << "UI loaded successfully" << std::endl;
menuManager.uiManager.setNodeVisible("inventory_items_panel", false);
@@ -247,6 +251,9 @@ namespace ZL
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;
@@ -266,7 +273,7 @@ namespace ZL
}
this->menuManager.uiManager.setText("inventory_items_text", itemText);
});
});
menuManager.uiManager.setTextButtonCallback("close_inventory_button", [this](const std::string& name) {
@@ -274,7 +281,7 @@ namespace ZL
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;
@@ -292,7 +299,7 @@ namespace ZL
}
}
void Game::drawUI()
{
glClear(GL_DEPTH_BUFFER_BIT);
@@ -318,7 +325,150 @@ namespace ZL
CheckGlError(__FILE__, __LINE__);
}
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) {
@@ -360,7 +510,7 @@ namespace ZL
float height = Environment::projectionHeight;
renderer.PushProjectionMatrix(
-width * 0.5f, width*0.5f,
-width * 0.5f, width * 0.5f,
-height * 0.5f, height * 0.5f,
-10, 10);
@@ -376,13 +526,13 @@ namespace ZL
CheckGlError(__FILE__, __LINE__);
}
int64_t Game::getSyncTimeMs() {
int64_t localNow = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
return localNow;
}
void Game::processTickCount() {
@@ -431,7 +581,7 @@ namespace ZL
Environment::exitGameLoop = true;
}
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
// Обновляем размеры и сбрасываем кеш текстов, т.к. меши хранятся в пикселях
Environment::width = event.window.data1;
@@ -552,6 +702,10 @@ namespace ZL
activateSlowMoEffect();
break;
case SDLK_j:
toggleQuestJournal();
break;
case SDLK_RETURN:
default:
@@ -584,7 +738,7 @@ namespace ZL
}
}
render();
mainThreadHandler.processMainThreadTasks();
}
@@ -801,4 +955,4 @@ namespace ZL
}
}
} // namespace ZL
} // namespace ZL