Fixing UI bugs, updating web UI

This commit is contained in:
Vladislav Khorev 2026-06-12 20:18:43 +03:00
parent dee9615483
commit 7031a21922
28 changed files with 216 additions and 48 deletions

View File

@ -185,6 +185,8 @@ set(EMSCRIPTEN_LINK_FLAGS
"-sALLOW_MEMORY_GROWTH=1" "-sALLOW_MEMORY_GROWTH=1"
"-sFULL_ES3=1" "-sFULL_ES3=1"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loading.png@resources/loading.png" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loading.png@resources/loading.png"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loadingProgressBar.png@resources/loadingProgressBar.png"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loadingProgressBarFrame.png@resources/loadingProgressBarFrame.png"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/shaders@resources/shaders" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/shaders@resources/shaders"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_uni_interior.lua@resources/start_uni_interior.lua" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_uni_interior.lua@resources/start_uni_interior.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_uni_exterior.lua@resources/start_uni_exterior.lua" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_uni_exterior.lua@resources/start_uni_exterior.lua"

Binary file not shown.

BIN
resources/loading.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/loadingProgressBar.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/loadingProgressBarFrame.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/rotateYourDevice.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -26,6 +26,7 @@ function on_container_clicked()
game_api.start_dialogue("dialog_contaier003") game_api.start_dialogue("dialog_contaier003")
elseif player_container_aware == 1 then elseif player_container_aware == 1 then
game_api.start_dialogue("dialog_contaier002") game_api.start_dialogue("dialog_contaier002")
game_api.setIntValue("player_has_coursework", 1)
else else
game_api.start_dialogue("dialog_contaier001") game_api.start_dialogue("dialog_contaier001")
end end

BIN
resources/w/ui/img/Hint1.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/Hint2.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/Hint3.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/Hint4.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/Hint5.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/Hint6a.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/Hint6b.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/Hint_darklands001.png (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
resources/w/ui/img/Phone001_State=Tap.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/hint_arrow_down.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/hint_arrow_left.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/hint_arrow_right.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/ui/img/hint_arrow_up.png (Stored with Git LFS)

Binary file not shown.

View File

@ -4,7 +4,7 @@
"name": "hud_root", "name": "hud_root",
"width": "match_parent", "width": "match_parent",
"height": "match_parent", "height": "match_parent",
"vertical_align": "center", "vertical_align": "top",
"horizontal_align": "center", "horizontal_align": "center",
"children": [ "children": [
{ {
@ -12,19 +12,20 @@
"name": "background", "name": "background",
"width": 1266, "width": 1266,
"height": 585, "height": 585,
"y": 68,
"horizontal_gravity": "center", "horizontal_gravity": "center",
"vertical_gravity": "center", "vertical_gravity": "top",
"texture": "resources/w/ui/img/journal/QuestJournal003.png" "texture": "resources/w/ui/img/journal/QuestJournal003.png"
}, },
{ {
"type": "Button", "type": "Button",
"name": "journalExitButton", "name": "journalExitButton",
"horizontal_gravity": "top", "horizontal_gravity": "center",
"vertical_gravity": "center", "vertical_gravity": "top",
"width": 90, "width": 90,
"height": 90, "height": 90,
"x": 1170, "x": 580,
"y": 240, "y": 70,
"textures": { "textures": {
"normal": "resources/w/ui/img/Close001_State=Default.png", "normal": "resources/w/ui/img/Close001_State=Default.png",
"hover": "resources/w/ui/img/Close001_State=Selected.png", "hover": "resources/w/ui/img/Close001_State=Selected.png",

View File

@ -48,18 +48,24 @@ namespace ZL
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
Game* Game::s_instance = nullptr; Game* Game::s_instance = nullptr;
void Game::onResourcesZipLoaded(const char* /*filename*/) { void Game::onResourcesZipLoaded(unsigned /*handle*/, void* /*userData*/, const char* /*filename*/) {
std::cout << "Resources.zip loaded successfully" << std::endl; std::cout << "Resources.zip loaded successfully" << std::endl;
if (s_instance) { if (s_instance) {
s_instance->resourcesDownloadProgress = 1.0f;
s_instance->mainThreadHandler.EnqueueMainThreadTask([&]() { s_instance->mainThreadHandler.EnqueueMainThreadTask([&]() {
s_instance->setupPart2(); s_instance->setupPart2();
}); });
} }
} }
void Game::onResourcesZipError(const char* /*filename*/) { void Game::onResourcesZipError(unsigned /*handle*/, void* /*userData*/, int /*httpStatus*/) {
std::cout << "Failed to download resources.zip" << std::endl; std::cout << "Failed to download resources.zip" << std::endl;
} }
void Game::onResourcesZipProgress(unsigned /*handle*/, void* /*userData*/, int percentComplete) {
if (s_instance)
s_instance->resourcesDownloadProgress = percentComplete / 100.0f;
}
#endif #endif
Game::Game() Game::Game()
@ -98,6 +104,12 @@ namespace ZL
#endif #endif
loadingTexture = renderer.textureManager.LoadFromPng("resources/loading.png", ""); loadingTexture = renderer.textureManager.LoadFromPng("resources/loading.png", "");
#ifdef EMSCRIPTEN
loadingProgressBarFrameTexture = renderer.textureManager.LoadFromPng(
"resources/loadingProgressBarFrame.png", "", true);
loadingProgressBarTexture = renderer.textureManager.LoadFromPng(
"resources/loadingProgressBar.png", "", true);
#endif
float minDimension; float minDimension;
float width = Environment::projectionWidth; float width = Environment::projectionWidth;
float height = Environment::projectionHeight; float height = Environment::projectionHeight;
@ -119,7 +131,8 @@ namespace ZL
// The loading screen stays visible until the download finishes. // The loading screen stays visible until the download finishes.
s_instance = this; s_instance = this;
std::cout << "Load resurces step 1" << std::endl; std::cout << "Load resurces step 1" << std::endl;
emscripten_async_wget("resources.zip", "resources.zip", onResourcesZipLoaded, onResourcesZipError); emscripten_async_wget2("resources.zip", "resources.zip", "GET", nullptr, nullptr,
onResourcesZipLoaded, onResourcesZipError, onResourcesZipProgress);
#else #else
mainThreadHandler.EnqueueMainThreadTask([this]() { mainThreadHandler.EnqueueMainThreadTask([this]() {
std::cout << "Load resurces step 2" << std::endl; std::cout << "Load resurces step 2" << std::endl;
@ -451,6 +464,9 @@ namespace ZL
renderer.textureManager.LoadFromPng("resources/w/ui/img/toast/item_received001.png", CONST_ZIP_FILE, true); renderer.textureManager.LoadFromPng("resources/w/ui/img/toast/item_received001.png", CONST_ZIP_FILE, true);
renderer.textureManager.LoadFromPng("resources/w/ui/img/toast/item_removed001.png", CONST_ZIP_FILE, true); renderer.textureManager.LoadFromPng("resources/w/ui/img/toast/item_removed001.png", CONST_ZIP_FILE, true);
mobileRotateTexture = renderer.textureManager.LoadFromPng(
"resources/rotateYourDevice.png", CONST_ZIP_FILE, true);
// Wire inventory callbacks: tutorial tracking + toast notifications. // Wire inventory callbacks: tutorial tracking + toast notifications.
@ -608,6 +624,9 @@ namespace ZL
if (!loadingCompleted) { if (!loadingCompleted) {
drawLoading(); drawLoading();
} }
else if (Environment::width < Environment::height) {
drawMobilePortraitOverlay();
}
else else
{ {
if (currentLocation) if (currentLocation)
@ -643,6 +662,37 @@ namespace ZL
CheckGlError(__FILE__, __LINE__); CheckGlError(__FILE__, __LINE__);
} }
void Game::drawMobilePortraitOverlay()
{
if (!mobileRotateTexture) return;
const float W = Environment::projectionWidth;
const float H = Environment::projectionHeight;
if (W != mobileRotateMeshLastW || H != mobileRotateMeshLastH) {
mobileRotateMeshLastW = W;
mobileRotateMeshLastH = H;
const float minDim = min(W, H);
mobileRotateMesh.data = CreateRect2D({ 0.f, 0.f }, { minDim * 0.5f, minDim * 0.5f }, 3.f);
mobileRotateMesh.RefreshVBO();
}
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.RenderUniform1f("uAlpha", 1.0f);
renderer.PushProjectionMatrix(-W * 0.5f, W * 0.5f, -H * 0.5f, H * 0.5f, -10, 10);
renderer.PushMatrix();
renderer.LoadIdentity();
glBindTexture(GL_TEXTURE_2D, mobileRotateTexture->getTexID());
renderer.DrawVertexRenderStruct(mobileRotateMesh);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
CheckGlError(__FILE__, __LINE__);
}
void Game::drawLoading() void Game::drawLoading()
{ {
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
@ -669,6 +719,81 @@ namespace ZL
renderer.PopMatrix(); renderer.PopMatrix();
renderer.PopProjectionMatrix(); renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader(); renderer.shaderManager.PopShader();
#ifdef EMSCRIPTEN
if (loadingProgressBarFrameTexture && loadingProgressBarTexture) {
const float W = Environment::projectionWidth;
const float H = Environment::projectionHeight;
static constexpr float kBarW = 400.0f;
static constexpr float kBarH = 40.0f;
const float barCenterY = -(H * 0.4f - 60.0f);
const float barLeft = -kBarW * 0.5f;
const float barBottom = barCenterY - kBarH * 0.5f;
const float barTop = barCenterY + kBarH * 0.5f;
// Rebuild frame mesh when projection dimensions change
if (W != loadingBarLastW || H != loadingBarLastH) {
loadingBarLastW = W;
loadingBarLastH = H;
VertexDataStruct frameData;
frameData.PositionData = {
{barLeft, barBottom, 4.f}, {barLeft, barTop, 4.f}, {barLeft+kBarW, barTop, 4.f},
{barLeft+kBarW, barTop, 4.f}, {barLeft+kBarW, barBottom, 4.f}, {barLeft, barBottom, 4.f}
};
frameData.TexCoordData = {
{0.f,0.f},{0.f,1.f},{1.f,1.f},
{1.f,1.f},{1.f,0.f},{0.f,0.f}
};
loadingProgressBarFrameMesh.data = std::move(frameData);
loadingProgressBarFrameMesh.RefreshVBO();
loadingBarLastRenderedProgress = -1.0f; // force fill rebuild too
}
// Rebuild fill mesh when progress changes
const float p = std::min(resourcesDownloadProgress, 1.0f);
if (p != loadingBarLastRenderedProgress) {
loadingBarLastRenderedProgress = p;
if (p > 0.001f) {
const float fillRight = barLeft + kBarW * p;
VertexDataStruct fillData;
fillData.PositionData = {
{barLeft, barBottom, 4.f}, {barLeft, barTop, 4.f}, {fillRight, barTop, 4.f},
{fillRight, barTop, 4.f}, {fillRight, barBottom, 4.f}, {barLeft, barBottom, 4.f}
};
fillData.TexCoordData = {
{0.f,0.f},{0.f,1.f},{p,1.f},
{p,1.f},{p,0.f},{0.f,0.f}
};
loadingProgressBarFillMesh.data = std::move(fillData);
loadingProgressBarFillMesh.RefreshVBO();
}
}
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.RenderUniform1f("uAlpha", 1.0f);
renderer.PushProjectionMatrix(-W*0.5f, W*0.5f, -H*0.5f, H*0.5f, -10, 10);
renderer.PushMatrix();
renderer.LoadIdentity();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, loadingProgressBarFrameTexture->getTexID());
renderer.DrawVertexRenderStruct(loadingProgressBarFrameMesh);
if (p > 0.001f) {
glBindTexture(GL_TEXTURE_2D, loadingProgressBarTexture->getTexID());
renderer.DrawVertexRenderStruct(loadingProgressBarFillMesh);
}
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
}
#endif
CheckGlError(__FILE__, __LINE__); CheckGlError(__FILE__, __LINE__);
} }
@ -729,6 +854,7 @@ namespace ZL
void Game::update() { void Game::update() {
SDL_Event event; SDL_Event event;
const bool mobileIsPortrait = loadingCompleted && (Environment::width < Environment::height);
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) { if (event.type == SDL_QUIT) {
Environment::exitGameLoop = true; Environment::exitGameLoop = true;
@ -746,6 +872,9 @@ namespace ZL
//space.clearTextRendererCache(); //space.clearTextRendererCache();
} }
// Suppress all input while portrait overlay is shown
if (mobileIsPortrait) continue;
#ifdef __ANDROID__ #ifdef __ANDROID__
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) { if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
Environment::exitGameLoop = true; Environment::exitGameLoop = true;

View File

@ -115,8 +115,18 @@ namespace ZL {
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
static Game* s_instance; static Game* s_instance;
static void onResourcesZipLoaded(const char* filename); static void onResourcesZipLoaded(unsigned handle, void* userData, const char* filename);
static void onResourcesZipError(const char* filename); static void onResourcesZipError(unsigned handle, void* userData, int httpStatus);
static void onResourcesZipProgress(unsigned handle, void* userData, int percentComplete);
float resourcesDownloadProgress = 0.0f;
std::shared_ptr<Texture> loadingProgressBarTexture;
std::shared_ptr<Texture> loadingProgressBarFrameTexture;
VertexRenderStruct loadingProgressBarFrameMesh;
VertexRenderStruct loadingProgressBarFillMesh;
float loadingBarLastRenderedProgress = -1.0f;
float loadingBarLastW = -1.0f;
float loadingBarLastH = -1.0f;
#endif #endif
// Screen-flash transition state (shared by darklands and night transitions) // Screen-flash transition state (shared by darklands and night transitions)
@ -131,6 +141,14 @@ namespace ZL {
void updateDarklandsFlash(int64_t deltaMs); void updateDarklandsFlash(int64_t deltaMs);
void drawDarklandsFlash(); void drawDarklandsFlash();
// Portrait-mode overlay (mobile / portrait browsers)
std::shared_ptr<Texture> mobileRotateTexture;
VertexRenderStruct mobileRotateMesh;
float mobileRotateMeshLastW = -1.0f;
float mobileRotateMeshLastH = -1.0f;
void drawMobilePortraitOverlay();
int64_t newTickCount; int64_t newTickCount;
int64_t lastTickCount; int64_t lastTickCount;

View File

@ -218,6 +218,7 @@ namespace ZL {
uiManager.pushMenuFromSavedRoot(aboutScreenRoot); uiManager.pushMenuFromSavedRoot(aboutScreenRoot);
uiManager.setTextButtonCallback("aboutBackButton", [this](const std::string&) { uiManager.setTextButtonCallback("aboutBackButton", [this](const std::string&) {
uiManager.popMenu(); uiManager.popMenu();
uiManager.updateAllLayouts();
}); });
} }
@ -308,6 +309,7 @@ namespace ZL {
void MenuManager::closeInventory() { void MenuManager::closeInventory() {
state = GameState::Gameplay; state = GameState::Gameplay;
uiManager.popMenu(); uiManager.popMenu();
uiManager.updateAllLayouts();
} }
void MenuManager::openQuestJournal() { void MenuManager::openQuestJournal() {
@ -344,6 +346,7 @@ namespace ZL {
selectedQuestIndex = -1; selectedQuestIndex = -1;
visibleQuestIds.clear(); visibleQuestIds.clear();
uiManager.popMenu(); uiManager.popMenu();
uiManager.updateAllLayouts();
} }
void MenuManager::toggleQuestJournal() { void MenuManager::toggleQuestJournal() {
@ -480,6 +483,7 @@ namespace ZL {
}); });
uiManager.setButtonCallback("buttonBack", [this](const std::string&) { uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
uiManager.popMenu(); uiManager.popMenu();
uiManager.updateAllLayouts();
}); });
} }
@ -491,6 +495,7 @@ namespace ZL {
}); });
uiManager.setButtonCallback("buttonBack", [this](const std::string&) { uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
uiManager.popMenu(); uiManager.popMenu();
uiManager.updateAllLayouts();
}); });
uiManager.setButtonCallback("videoSkip", [this](const std::string&) { uiManager.setButtonCallback("videoSkip", [this](const std::string&) {
closePhoneEntirely(); closePhoneEntirely();
@ -533,6 +538,7 @@ namespace ZL {
}); });
uiManager.setButtonCallback("buttonBack", [this](const std::string&) { uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
uiManager.popMenu(); uiManager.popMenu();
uiManager.updateAllLayouts();
}); });
uiManager.setButtonCallback("mapGo", [this](const std::string&) { uiManager.setButtonCallback("mapGo", [this](const std::string&) {
tutorialNeedOpenTaxiScreen = false; tutorialNeedOpenTaxiScreen = false;
@ -572,6 +578,7 @@ namespace ZL {
activeChatIndex_ = -1; activeChatIndex_ = -1;
phoneChatVisibleBubbles_.clear(); phoneChatVisibleBubbles_.clear();
uiManager.popMenu(); uiManager.popMenu();
uiManager.updateAllLayouts();
if (tutorialPhoneChatScreenOpened) if (tutorialPhoneChatScreenOpened)
{ {
uiManager.setNodeVisible("hint_m002", false); uiManager.setNodeVisible("hint_m002", false);
@ -590,6 +597,7 @@ namespace ZL {
phoneChatVisibleBubbles_.clear(); phoneChatVisibleBubbles_.clear();
const int depth = uiManager.menuStackSize(); const int depth = uiManager.menuStackSize();
for (int i = 0; i < depth; ++i) uiManager.popMenu(); for (int i = 0; i < depth; ++i) uiManager.popMenu();
uiManager.updateAllLayouts();
if (tutorialNeedOpenTaxiScreen) if (tutorialNeedOpenTaxiScreen)
{ {