Working on loading screen

This commit is contained in:
Vladislav Khorev 2026-07-09 20:32:57 +03:00
parent 1c2c80f0ea
commit 44f5136f1d
8 changed files with 272 additions and 187 deletions

View File

@ -44,15 +44,11 @@ namespace ZL
std::cout << "Resources.zip loaded successfully--0" << std::endl;
if (s_instance) {
s_instance->resourcesDownloadProgress = 1.0f;
/*s_instance->mainThreadHandler.EnqueueMainThreadTask([&]() {
s_instance->setupPart2();
});*/
std::cout << "s_instance setup part 2" << std::endl;
// Builds the loadSteps queue (cheap, no GL work); the actual loading
// happens one step per frame in Game::update() so the browser tab
// keeps repainting/responding instead of freezing for the whole load.
s_instance->setupPart2();
}
std::cout << "s_instance end setup" << std::endl;
}
void Game::onResourcesZipError(unsigned /*handle*/, void* /*userData*/, int /*httpStatus*/) {
@ -107,12 +103,11 @@ namespace ZL
#endif
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 width = Environment::projectionWidth;
float height = Environment::projectionHeight;
@ -137,196 +132,200 @@ namespace ZL
emscripten_async_wget2("resources.zip", "resources.zip", "GET", nullptr, nullptr,
onResourcesZipLoaded, onResourcesZipError, onResourcesZipProgress);
#else
mainThreadHandler.EnqueueMainThreadTask([this]() {
std::cout << "Load resurces step 2" << std::endl;
this->setupPart2();
std::cout << "Load resurces step 3" << std::endl;
});
// Builds the loadSteps queue (cheap, no GL work); the actual loading
// happens one step per frame in Game::update() so the window keeps
// pumping events / repainting instead of freezing for the whole load.
setupPart2();
#endif
}
void Game::setupPart2()
{
std::cout << "Load resurces step 12x1" << std::endl;
// Builds an ordered queue of small closures instead of doing the work here.
// Game::update() drains one closure per frame, so the event loop / browser
// paint gets a turn between steps instead of the window freezing for the
// whole duration of loading. Building the queue itself is cheap (no GL calls).
// Must happen before createLocations() below: some location scripts
// Must happen before the location step below: some location scripts
// (e.g. resources/start_dorm.lua) start a dialogue as soon as they're
// loaded, so the language needs to be known before that first load.
ZL::loadLanguageSetting();
loadSteps.push([this]() {
ZL::loadLanguageSetting();
});
loadSteps.push([this]() {
#ifdef EMSCRIPTEN
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x2" << std::endl;
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x3" << std::endl;
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x4" << std::endl;
renderer.shaderManager.AddShaderFromFiles("planetStone", "resources/shaders/planet_stone.vertex", "resources/shaders/planet_stone_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x5" << std::endl;
renderer.shaderManager.AddShaderFromFiles("planetLand", "resources/shaders/planet_land.vertex", "resources/shaders/planet_land_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x6" << std::endl;
renderer.shaderManager.AddShaderFromFiles("spark", "resources/shaders/spark.vertex", "resources/shaders/spark_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x7" << std::endl;
renderer.shaderManager.AddShaderFromFiles("skinning", "resources/shaders/skinning.vertex", "resources/shaders/default_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x8" << std::endl;
renderer.shaderManager.AddShaderFromFiles("fog", "resources/shaders/fog.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x9" << std::endl;
renderer.shaderManager.AddShaderFromFiles("fog_skinning", "resources/shaders/fog_skinning.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x10" << std::endl;
renderer.shaderManager.AddShaderFromFiles("darklands_fog", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x11" << std::endl;
renderer.shaderManager.AddShaderFromFiles("darklands_fog_skinning", "resources/shaders/darklands_fog_skinning.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x12" << std::endl;
renderer.shaderManager.AddShaderFromFiles("darklands_flash", "resources/shaders/default.vertex", "resources/shaders/darklands_flash_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x13" << std::endl;
renderer.shaderManager.AddShaderFromFiles("cutsceneFade", "resources/shaders/default.vertex", "resources/shaders/cutscene_fade_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x14" << std::endl;
renderer.shaderManager.AddShaderFromFiles("cutsceneBlack", "resources/shaders/default.vertex", "resources/shaders/cutscene_black_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x15" << std::endl;
renderer.shaderManager.AddShaderFromFiles("shadow_depth", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x16" << std::endl;
renderer.shaderManager.AddShaderFromFiles("shadow_depth_skinning", "resources/shaders/shadow_depth_skinning.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x17" << std::endl;
renderer.shaderManager.AddShaderFromFiles("default_shadow", "resources/shaders/default_shadow.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x18" << std::endl;
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x19" << std::endl;
renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x20" << std::endl;
renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x21" << std::endl;
renderer.shaderManager.AddShaderFromFiles("night_fog", "resources/shaders/night_fog.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x22" << std::endl;
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x23" << std::endl;
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x24" << std::endl;
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
std::cout << "Load resurces step 12x25" << std::endl;
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetStone", "resources/shaders/planet_stone.vertex", "resources/shaders/planet_stone_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetLand", "resources/shaders/planet_land.vertex", "resources/shaders/planet_land_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("spark", "resources/shaders/spark.vertex", "resources/shaders/spark_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("skinning", "resources/shaders/skinning.vertex", "resources/shaders/default_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog", "resources/shaders/fog.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_skinning", "resources/shaders/fog_skinning.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_fog", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_fog_skinning", "resources/shaders/darklands_fog_skinning.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_flash", "resources/shaders/default.vertex", "resources/shaders/darklands_flash_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("cutsceneFade", "resources/shaders/default.vertex", "resources/shaders/cutscene_fade_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("cutsceneBlack", "resources/shaders/default.vertex", "resources/shaders/cutscene_black_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("shadow_depth", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("shadow_depth_skinning", "resources/shaders/shadow_depth_skinning.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("default_shadow", "resources/shaders/default_shadow.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog", "resources/shaders/night_fog.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
#else
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetStone", "resources/shaders/planet_stone.vertex", "resources/shaders/planet_stone_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetLand", "resources/shaders/planet_land.vertex", "resources/shaders/planet_land_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("spark", "resources/shaders/spark.vertex", "resources/shaders/spark_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("skinning", "resources/shaders/skinning.vertex", "resources/shaders/default_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog", "resources/shaders/fog.vertex", "resources/shaders/fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_skinning", "resources/shaders/fog_skinning.vertex", "resources/shaders/fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_fog", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_fog_skinning", "resources/shaders/darklands_fog_skinning.vertex", "resources/shaders/darklands_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_flash", "resources/shaders/default.vertex", "resources/shaders/darklands_flash_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("cutsceneFade", "resources/shaders/default.vertex", "resources/shaders/cutscene_fade_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("cutsceneBlack", "resources/shaders/default.vertex", "resources/shaders/cutscene_black_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("shadow_depth", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("shadow_depth_skinning", "resources/shaders/shadow_depth_skinning.vertex", "resources/shaders/shadow_depth_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("default_shadow", "resources/shaders/default_shadow.vertex", "resources/shaders/default_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow.vertex", "resources/shaders/fog_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog", "resources/shaders/night_fog.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetStone", "resources/shaders/planet_stone.vertex", "resources/shaders/planet_stone_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetLand", "resources/shaders/planet_land.vertex", "resources/shaders/planet_land_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("spark", "resources/shaders/spark.vertex", "resources/shaders/spark_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("skinning", "resources/shaders/skinning.vertex", "resources/shaders/default_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog", "resources/shaders/fog.vertex", "resources/shaders/fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_skinning", "resources/shaders/fog_skinning.vertex", "resources/shaders/fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_fog", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_fog_skinning", "resources/shaders/darklands_fog_skinning.vertex", "resources/shaders/darklands_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_flash", "resources/shaders/default.vertex", "resources/shaders/darklands_flash_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("cutsceneFade", "resources/shaders/default.vertex", "resources/shaders/cutscene_fade_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("cutsceneBlack", "resources/shaders/default.vertex", "resources/shaders/cutscene_black_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("shadow_depth", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("shadow_depth_skinning", "resources/shaders/shadow_depth_skinning.vertex", "resources/shaders/shadow_depth_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("default_shadow", "resources/shaders/default_shadow.vertex", "resources/shaders/default_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow.vertex", "resources/shaders/fog_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog", "resources/shaders/night_fog.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
#endif
});
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_aiperi.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_ghost.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_hero_neutral.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_hero_think.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_phone.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_student_boy.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_student_girl.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_teacher.png", CONST_ZIP_FILE);
loadSteps.push([this]() {
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_aiperi.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_ghost.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_hero_neutral.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_hero_think.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_phone.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_student_boy.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_student_girl.png", CONST_ZIP_FILE);
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_teacher.png", CONST_ZIP_FILE);
});
std::cout << "Load resurces step 4" << std::endl;
loadSteps.push([this]() {
ItemRegistry::instance().loadFromJson("resources/config/items.json", CONST_ZIP_FILE);
Character::nameTranslations.loadFromFile("resources/config/translate_names.json");
});
ItemRegistry::instance().loadFromJson("resources/config/items.json", CONST_ZIP_FILE);
// createLocations() is also used by the full-reset path (see performFullReset()),
// so it stays a single synchronous function rather than being split into
// per-location queue steps; it's queued here as one coarse step.
loadSteps.push([this]() {
createLocations();
});
Character::nameTranslations.loadFromFile("resources/config/translate_names.json");
loadSteps.push([this]() {
// Wire inventory callbacks: tutorial tracking + toast notifications.
gameState.inventory.onItemAdded = [this](const std::string& itemId) {
menuManager.onItemPickedUp(itemId);
const Item* item = ItemRegistry::instance().findById(itemId);
if (item) menuManager.showToast(menuManager.toastTextureItemAdded, item->name);
audioPlayer->playSoundAsync("audio/831642__ienba__generic-game-pick-up.ogg");
};
gameState.inventory.onItemRemoved = [this](const std::string& itemId) {
const Item* item = ItemRegistry::instance().findById(itemId);
if (item) menuManager.showToast(menuManager.toastTextureItemRemoved, item->name);
audioPlayer->playSoundAsync("audio/831642__ienba__generic-game-pick-up.ogg");
};
createLocations();
// Wire phone dialogue start function so MenuManager can trigger dialogues.
menuManager.startDialogueFunc = [this](const std::string& id) {
if (currentLocation()) currentLocation()->dialogueSystem.startDialogue(id);
};
// Wire inventory callbacks: tutorial tracking + toast notifications.
gameState.inventory.onItemAdded = [this](const std::string& itemId) {
menuManager.onItemPickedUp(itemId);
const Item* item = ItemRegistry::instance().findById(itemId);
if (item) menuManager.showToast(menuManager.toastTextureItemAdded, item->name);
audioPlayer->playSoundAsync("audio/831642__ienba__generic-game-pick-up.ogg");
};
gameState.inventory.onItemRemoved = [this](const std::string& itemId) {
const Item* item = ItemRegistry::instance().findById(itemId);
if (item) menuManager.showToast(menuManager.toastTextureItemRemoved, item->name);
audioPlayer->playSoundAsync("audio/831642__ienba__generic-game-pick-up.ogg");
};
menuManager.startDarklandsTransitionFunc = [this]() {
startDarklandsTransition();
};
// Wire phone dialogue start function so MenuManager can trigger dialogues.
menuManager.startDialogueFunc = [this](const std::string& id) {
if (currentLocation()) currentLocation()->dialogueSystem.startDialogue(id);
};
menuManager.startNightTransitionFunc = [this]() {
startNightTransition();
};
menuManager.startDarklandsTransitionFunc = [this]() {
startDarklandsTransition();
};
menuManager.chatOpenCallback = [this](int chatIndex) {
if (currentLocation())
currentLocation()->scriptEngine.callChatOpenCallback(chatIndex);
};
menuManager.startNightTransitionFunc = [this]() {
startNightTransition();
};
menuManager.skipCutsceneFunc = [this]() {
if (currentLocation()) currentLocation()->dialogueSystem.skipCutscene();
};
});
menuManager.chatOpenCallback = [this](int chatIndex) {
if (currentLocation())
currentLocation()->scriptEngine.callChatOpenCallback(chatIndex);
};
loadSteps.push([this]() {
try {
menuManager.setup(gameState.inventory, CONST_ZIP_FILE);
std::cout << "UI loaded successfully" << std::endl;
}
catch (const std::exception& e) {
std::cerr << "Failed to load UI: " << e.what() << std::endl;
}
menuManager.skipCutsceneFunc = [this]() {
if (currentLocation()) currentLocation()->dialogueSystem.skipCutscene();
};
menuManager.startGameFunc = [this]() {
mainThreadHandler.EnqueueMainThreadTask([this]() {
performResetToInitialState();
gameState.currentLocationName = "location_dorm";
currentLocation()->scriptEngine.callLocationEnterCallback();
this->audioPlayer->crossFadeMusicAsync("audio/obshaga.ogg");
});
};
std::cout << "Load resurces step 13" << std::endl;
menuManager.onSaveGame = [this](int slot) { saveGame(slot); };
menuManager.onLoadGame = [this](int slot) { loadGame(slot); };
menuManager.getSlotInfoFunc = [this](int slot) { return readSlotInfo(slot); };
try {
menuManager.setup(gameState.inventory, CONST_ZIP_FILE);
std::cout << "UI loaded successfully" << std::endl;
}
catch (const std::exception& e) {
// Lets MenuManager run a heavy operation (e.g. reloadLocalizedGameContent())
// as a chunked sequence instead of one blocking call: the closures are
// drained one per frame by update(), with the loading screen shown for
// the duration, exactly like the initial resource load.
menuManager.runAsLoadingSequence = [this](std::vector<std::function<void()>> steps) {
for (auto& step : steps) loadSteps.push(std::move(step));
loadSteps.push([this]() { loadingCompleted = true; });
loadStepsTotal = loadSteps.size();
loadStepsDone = 0;
showLoadingProgressBar = false;
loadingCompleted = false;
};
});
std::cerr << "Failed to load UI: " << e.what() << std::endl;
}
loadSteps.push([this]() {
if (audioPlayer->init()) {
audioPlayer->setMusicVolume(100);
audioPlayer->setSoundVolume(80);
std::cout << "Audio initialized successfully" << std::endl;
}
else {
std::cout << "Audio initialization failed" << std::endl;
}
});
menuManager.startGameFunc = [this]() {
mainThreadHandler.EnqueueMainThreadTask([this]() {
performResetToInitialState();
gameState.currentLocationName = "location_dorm";
currentLocation()->scriptEngine.callLocationEnterCallback();
this->audioPlayer->crossFadeMusicAsync("audio/obshaga.ogg");
});
};
menuManager.onSaveGame = [this](int slot) { saveGame(slot); };
menuManager.onLoadGame = [this](int slot) { loadGame(slot); };
menuManager.getSlotInfoFunc = [this](int slot) { return readSlotInfo(slot); };
if (audioPlayer->init()) {
audioPlayer->setMusicVolume(100);
audioPlayer->setSoundVolume(80);
std::cout << "Audio initialized successfully" << std::endl;
}
else {
std::cout << "Audio initialization failed" << std::endl;
}
menuManager.loadSettings();
audioPlayer->crossFadeMusicAsync("audio/main menu final.ogg");
loadingCompleted = true;
loadSteps.push([this]() {
menuManager.loadSettings();
audioPlayer->crossFadeMusicAsync("audio/main menu final.ogg");
});
loadSteps.push([this]() {
loadingCompleted = true;
});
loadStepsTotal = loadSteps.size();
loadStepsDone = 0;
}
void Game::rearmDormOneShotCallbacks() {
@ -860,8 +859,7 @@ namespace ZL
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
#ifdef EMSCRIPTEN
if (loadingProgressBarFrameTexture && loadingProgressBarTexture) {
if (showLoadingProgressBar && loadingProgressBarFrameTexture && loadingProgressBarTexture) {
const float W = Environment::projectionWidth;
const float H = Environment::projectionHeight;
static constexpr float kBarW = 400.0f;
@ -891,7 +889,7 @@ namespace ZL
}
// Rebuild fill mesh when progress changes
const float p = std::min(resourcesDownloadProgress, 1.0f);
const float p = (std::min)(currentLoadingProgress(), 1.0f);
if (p != loadingBarLastRenderedProgress) {
loadingBarLastRenderedProgress = p;
if (p > 0.001f) {
@ -932,7 +930,6 @@ namespace ZL
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
}
#endif
CheckGlError(__FILE__, __LINE__);
}
@ -1282,6 +1279,14 @@ namespace ZL
}
}
// Drain one loading step per frame so the window/tab keeps pumping events
// and repainting between steps instead of freezing for the whole load.
if (!loadingCompleted && !loadSteps.empty()) {
loadSteps.front()();
loadSteps.pop();
loadStepsDone++;
}
render();
mainThreadHandler.processMainThreadTasks();

View File

@ -10,6 +10,7 @@
#include "items/Item.h"
#include "items/InteractiveObject.h"
#include <queue>
#include <functional>
#include <vector>
#include <string>
#include <memory>
@ -49,6 +50,22 @@ namespace ZL {
VertexRenderStruct loadingMesh;
bool loadingCompleted = false;
// setupPart2() populates this queue with one closure per loading step instead of
// running the work itself; Game::update() drains one step per frame so the window
// keeps pumping events / repainting (and, on Emscripten, the browser keeps the tab
// responsive) instead of freezing for the whole duration of loading.
std::queue<std::function<void()>> loadSteps;
size_t loadStepsTotal = 0;
size_t loadStepsDone = 0;
float currentLoadingProgress() const {
return loadStepsTotal ? (float)loadStepsDone / (float)loadStepsTotal : resourcesDownloadProgress;
}
// The progress bar only reflects the initial resource load (its steps are
// pre-counted upfront). Later loadSteps runs, like MenuManager's localized
// content reload, don't have a meaningful denominator, so the bar is hidden.
bool showLoadingProgressBar = true;
GameState gameState;
EditorMode editorMode = EditorMode::None;
@ -138,8 +155,13 @@ namespace ZL {
static void onResourcesZipLoaded(unsigned handle, void* userData, const char* filename);
static void onResourcesZipError(unsigned handle, void* userData, int httpStatus);
static void onResourcesZipProgress(unsigned handle, void* userData, int percentComplete);
#endif
// Only meaningful during the Emscripten zip-download phase, before loadSteps
// exists (see currentLoadingProgress() above); harmless/unused on other platforms.
float resourcesDownloadProgress = 0.0f;
// Progress bar assets/meshes, loaded and drawn on every platform so setupPart2's
// step-by-step progress is visible during desktop/Android loading too.
std::shared_ptr<Texture> loadingProgressBarTexture;
std::shared_ptr<Texture> loadingProgressBarFrameTexture;
VertexRenderStruct loadingProgressBarFrameMesh;
@ -147,7 +169,6 @@ namespace ZL {
float loadingBarLastRenderedProgress = -1.0f;
float loadingBarLastW = -1.0f;
float loadingBarLastH = -1.0f;
#endif
// Screen-flash transition state (shared by darklands and night transitions)
float darklandsFlashAlpha = 0.0f;

View File

@ -88,9 +88,11 @@ namespace ZL
// Load static game objects
gameObjects = GameObjectLoader::loadAndCreateGameObjects(params.gameObjectsJsonPath, renderer, CONST_ZIP_FILE);
SDL_PumpEvents();
// Load interactive objects
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects(params.interactiveObjectsJsonPath, renderer, CONST_ZIP_FILE);
SDL_PumpEvents();
//auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
//auto playerTexture = renderer.textureManager.LoadFromPng("resources/w/gg/UniV_Grid_2K_Base_color.png", CONST_ZIP_FILE);
@ -140,6 +142,7 @@ namespace ZL
player->setupHitSparks(sparkTexture);
player->audioPlayer = audioPlayer;
std::cout << "Load resurces step 9" << std::endl;
SDL_PumpEvents();
// Load NPCs from JSON. Aggressive (canAttack) NPCs need a player target
// and hit sparks; peaceful NPCs don't take damage from the player so
@ -155,6 +158,8 @@ namespace ZL
}
}
SDL_PumpEvents();
loadTeleportZones(params.teleportsJsonPath, CONST_ZIP_FILE);
loadTriggerZones(params.triggerZonesJsonPath, CONST_ZIP_FILE);
loadPointLights(params.lightsJsonPath, CONST_ZIP_FILE);
@ -167,6 +172,7 @@ namespace ZL
//#endif
setupNavigation(params.navigationJsonPaths);
SDL_PumpEvents();
dialogueSystem.init(renderer, CONST_ZIP_FILE);
dialogueSystem.loadDatabase(params.dialoguesJsonPath);
@ -175,6 +181,8 @@ namespace ZL
dialogueSystem.loadCutsceneTranslations("resources/dialogue/translate_cutscenes.json");
dialogueSystem.setQuestJournal(journal);
SDL_PumpEvents();
npcNameText = std::make_unique<TextRenderer>();
if (!npcNameText->init(renderer, "resources/fonts/DroidSans.ttf", 24, CONST_ZIP_FILE)) {
std::cerr << "Failed to init NPC name TextRenderer" << std::endl;
@ -405,6 +413,9 @@ namespace ZL
navigationMaps.resize(paths.size());
for (size_t i = 0; i < paths.size(); ++i) {
// Service the OS message queue between builds so the window doesn't
// get flagged as "not responding" while loading many navigation grids.
SDL_PumpEvents();
navigationMaps[i].build(paths[i], CONST_ZIP_FILE);
}

View File

@ -8,6 +8,7 @@
#include <fstream>
#include <algorithm>
#include <string>
#include <SDL.h>
namespace ZL {
@ -119,6 +120,9 @@ namespace ZL {
}
std::shared_ptr<UiNode> MenuManager::loadLocalizedUi(const std::string& basePath, const std::string& zipFile) {
// Service the OS message queue between UI screens so the window doesn't
// get flagged as "not responding" while loading the ~50 UI layout files.
SDL_PumpEvents();
static const std::string marker = "resources/w/ui/";
if (basePath.rfind(marker, 0) == 0) {
const std::string localizedPath = "resources/w/ui_" + languageToCode(g_currentLanguage) + "/" + basePath.substr(marker.size());
@ -507,9 +511,6 @@ namespace ZL {
}
void MenuManager::reloadLocalizedGameContent() {
ItemRegistry::instance().loadFromJson("resources/config/items.json", CONST_ZIP_FILE);
gameState_.questJournal.reloadDefinitionsPreservingState("resources/config/quests.json", CONST_ZIP_FILE);
// Screens below the Language screen on the menu stack hold onto their old
// (pre-reload) UiNode instances, so simply reopening Language on top of
// them would leave Main Menu / Settings / the pause menu showing stale
@ -525,16 +526,37 @@ namespace ZL {
for (int i = 0; i < depth; ++i) uiManager.popMenu();
}
loadUiRoots(CONST_ZIP_FILE);
// Unloading/reloading ~50 UI screens and their textures is heavy enough to
// freeze the window for a noticeable moment, so run it the same way the
// initial resource load runs: a queue of closures drained one per frame
// with the loading screen shown, instead of one big blocking call here.
std::vector<std::function<void()>> steps;
toastTextureQuestNew = loadLocalizedTexture("resources/w/ui/img/toast/quest_new001.png", CONST_ZIP_FILE);
toastTextureQuestCompleted = loadLocalizedTexture("resources/w/ui/img/toast/quest_completed001.png", CONST_ZIP_FILE);
toastTextureQuestFailed = loadLocalizedTexture("resources/w/ui/img/toast/quest_failed001.png", CONST_ZIP_FILE);
toastTextureItemAdded = loadLocalizedTexture("resources/w/ui/img/toast/item_received001.png", CONST_ZIP_FILE);
toastTextureItemRemoved = loadLocalizedTexture("resources/w/ui/img/toast/item_removed001.png", CONST_ZIP_FILE);
steps.push_back([this]() {
ItemRegistry::instance().loadFromJson("resources/config/items.json", CONST_ZIP_FILE);
gameState_.questJournal.reloadDefinitionsPreservingState("resources/config/quests.json", CONST_ZIP_FILE);
});
steps.push_back([this]() {
renderer.textureManager.UnloadByPrefix("resources/w/ui");
loadUiRoots(CONST_ZIP_FILE);
showMainMenu();
toastTextureQuestNew = loadLocalizedTexture("resources/w/ui/img/toast/quest_new001.png", CONST_ZIP_FILE);
toastTextureQuestCompleted = loadLocalizedTexture("resources/w/ui/img/toast/quest_completed001.png", CONST_ZIP_FILE);
toastTextureQuestFailed = loadLocalizedTexture("resources/w/ui/img/toast/quest_failed001.png", CONST_ZIP_FILE);
toastTextureItemAdded = loadLocalizedTexture("resources/w/ui/img/toast/item_received001.png", CONST_ZIP_FILE);
toastTextureItemRemoved = loadLocalizedTexture("resources/w/ui/img/toast/item_removed001.png", CONST_ZIP_FILE);
});
steps.push_back([this]() {
showMainMenu();
});
if (runAsLoadingSequence) {
runAsLoadingSequence(std::move(steps));
} else {
for (auto& step : steps) step();
}
}
void MenuManager::showLoadGameScreen()

View File

@ -79,6 +79,13 @@ namespace ZL {
std::function<void(int)> onLoadGame;
std::function<SaveSlotInfo(int)> getSlotInfoFunc;
// Set by Game: runs the given closures one per frame with the loading
// screen shown (same mechanism as the initial resource load), so a heavy
// operation like reloadLocalizedGameContent() doesn't block the window
// for its whole duration. Game re-enables normal rendering once all the
// closures have run.
std::function<void(std::vector<std::function<void()>>)> runAsLoadingSequence;
// Called when a chat message bubble should be shown (text + direction)
void onChatBubbleReady(const std::string& text, bool incoming);

View File

@ -7,6 +7,7 @@
#include "utils/Utils.h"
#include <Eigen/Geometry>
#include "../Character.h"
#include <SDL.h>
namespace ZL {
@ -195,6 +196,9 @@ namespace ZL {
std::unordered_map<std::string, LoadedGameObject> gameObjects;
for (const auto& data : loadFromJson(jsonPath, zipPath)) {
// Service the OS message queue between loads so the window doesn't
// get flagged as "not responding" during long location loads.
SDL_PumpEvents();
std::cout << "Loading game object: " << data.name << std::endl;
try {
LoadedGameObject obj = buildLoadedObject(data, renderer, zipPath);
@ -225,6 +229,7 @@ namespace ZL {
return interactiveObjects;
for (const auto& data : loadInteractiveFromJson(jsonPath, zipPath)) {
SDL_PumpEvents();
std::cout << "Loading interactive object: " << data.base.name << std::endl;
try {
InteractiveObject intObj = InteractiveObject::createFromState(
@ -359,6 +364,7 @@ namespace ZL {
std::vector<std::unique_ptr<Character>> npcs;
for (const auto& npcData : loadNpcsFromJson(jsonPath, zipPath)) {
SDL_PumpEvents();
std::cout << "Loading NPC: " << npcData.name << std::endl;
try {
auto npc = Character::createFromState(npcData.toCharacterState(), renderer, zipPath.c_str());

View File

@ -528,8 +528,20 @@ namespace ZL
void TextureManager::Unload(const std::string& fileName, const std::string& zipFile)
{
//For now, we should keep all textures in memory, to make game load faster after restart
//textureMap.erase(MakeKey(fileName, zipFile));
textureMap.erase(MakeKey(fileName, zipFile));
}
void TextureManager::UnloadByPrefix(const std::string& prefix)
{
for (auto& it = textureMap.begin(); it != textureMap.end(); ) {
if (it->first.substr(0, prefix.size()) == prefix) {
std::cout << "Unloading texture with prefix: " << it->first << std::endl;
it = textureMap.erase(it);
}
else {
++it;
}
}
}
void TextureManager::UnloadAll()

View File

@ -76,6 +76,7 @@ namespace ZL
#endif
void Unload(const std::string& fileName, const std::string& zipFile = "");
void UnloadByPrefix(const std::string& prefix);
void UnloadAll();
private: