Fixing minor stuff, working on Web version

This commit is contained in:
Vladislav Khorev committed 2026-07-25 16:56:02 +03:00
1 parent 5ecf5fb9d7
commit 7df68febca
38 files changed
+502 -167

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+27 -1
View File
@@ -27,7 +27,18 @@ except ImportError:
# Paths relative to the resources directory — these files are never resized.
EXCEPTIONS = {
"loading.png",
"loading_land003_en.png",
"loading_land003_ru.png",
"black.png",
"transparent.png",
"w/blue.png",
"w/red.png",
"w/spark.png",
"w/star.png",
"w/star_red.png",
"w/white.png",
"w/ui/img/main/aboutPage003android.png",
"w/ui_en/img/main/aboutPage003android.png"
}
# Path prefixes (relative to resources root, forward-slash) that get 8x reduction.
@@ -35,13 +46,28 @@ HIGH_REDUCTION_PREFIXES = (
"w/ui/img",
)
LOW_REDUCTION_POSTFIXES = (
"texAtlas002.png",
"Building_work014.png",
"Building_Floors_tex004.png",
"Building_Walls_tex006.png",
"asphalt003small.png",
"Ext_Building004.png",
"Plane3_tex002.png",
"darklands_building002.png",
"Staircase001.png",
"Staircase_Obj002.png"
)
def _resize_factor(rel: str) -> int:
norm = rel.replace("\\", "/")
for prefix in HIGH_REDUCTION_PREFIXES:
if norm.startswith(prefix):
return 8
return 4
for prefix in LOW_REDUCTION_POSTFIXES:
if norm.endswith(prefix):
return 2
return 8
def _next_pot(n: int) -> int:
+10
View File
@@ -15,6 +15,12 @@ option(OPTIMIZE_WEB_RESOURCES "Resize PNG images for web build (requires Python3
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (CMAKE_BUILD_TYPE STREQUAL "Release")
# Максимальная оптимизация кода и LTO (Link-Time Optimization)
add_compile_options(-O3 -flto)
add_link_options(-O3 -flto)
endif()
# --- АВТО-ЗАГРУЗКА ЗАВИСИМОСТЕЙ ---
include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/FetchDependencies.cmake")
# Теперь гарантированно есть папка ../thirdparty со всеми исходниками
@@ -297,6 +303,10 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/index.html"
DESTINATION .
)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/favicon.ico"
DESTINATION .
)
# resources.zip is served separately and downloaded asynchronously at runtime
install(FILES "${RESOURCES_ZIP}" DESTINATION .)
install(FILES "${MUSIC_ZIP}" DESTINATION .)
+11
View File
@@ -37,3 +37,14 @@ Run:
```
emrun --no_browser --port 8080 public
```
# Release build
```
mkdir build_release
cd build_release
emcmake cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ..
cmake --build . --config Release
cmake --install . --prefix ../dist
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+3 -3
View File
@@ -2276,9 +2276,9 @@
"en": "And maybe she will even give me some difficult assignment."
},
{
"key": "Я даже уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"ru": "Я даже уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"en": "I'm even sure she's looking for me. She wants to give me some kind of assignment."
"key": "Я уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"ru": "Я уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"en": "I'm sure she's looking for me. She wants to give me some kind of assignment."
},
{
"key": "Пока ты не принесешь мне нож из учительской, никуда я тебя не выпущу. Ошо!",
@@ -293,14 +293,6 @@
"speaker": "Бекзат",
"portrait": "resources/dialogue/portrait_hero_neutral.png",
"text": "Почему?",
"next": "line_30"
},
{
"id": "line_30",
"type": "Line",
"speaker": "Айпери",
"portrait": "resources/dialogue/portrait_aiperi.png",
"text": "Она опять будет скафнить.",
"next": "line_25"
},
{
@@ -308,7 +300,7 @@
"type": "Line",
"speaker": "Айпери",
"portrait": "resources/dialogue/portrait_aiperi.png",
"text": "Я даже уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"text": "Я уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"next": "line_26"
},
{
+2 -2
View File
@@ -24,13 +24,13 @@
},
{
"type": "TextButton",
"name": "websiteButton",
"name": "reviewButton",
"horizontal_gravity": "center",
"vertical_gravity": "center",
"y": 200,
"width": 200,
"height": 60,
"text": "Сайт игры Тень над Бишкеком",
"text": "Оставьте отзыв в Google Play",
"fontSize": 36,
"color": [0.55, 0.9, 1.0, 1.0],
"textCentered": true
+80
View File
@@ -0,0 +1,80 @@
{
"root": {
"type": "FrameLayout",
"width": "match_parent",
"height": "match_parent",
"children": [
{
"type": "StaticImage",
"name": "aboutBkg",
"width": 2992,
"height": 1632,
"horizontal_gravity": "center",
"vertical_gravity": "center",
"texture": "resources/w/ui/img/main/MenuBkg001_v001.png"
},
{
"type": "StaticImage",
"name": "aboutPicture",
"width": 720,
"height": 720,
"horizontal_gravity": "center",
"vertical_gravity": "center",
"texture": "resources/w/ui/img/main/aboutPage003android.png"
},
{
"type": "TextButton",
"name": "websiteButton",
"horizontal_gravity": "center",
"vertical_gravity": "center",
"y": 200,
"width": 200,
"height": 60,
"text": "Сайт игры Тень над Бишкеком",
"fontSize": 36,
"color": [0.55, 0.9, 1.0, 1.0],
"textCentered": true
},
{
"type": "TextButton",
"name": "telegramButton",
"horizontal_gravity": "center",
"vertical_gravity": "center",
"y": -240,
"x": -150,
"width": 200,
"height": 60,
"text": "Telegram-канал",
"fontSize": 40,
"color": [0.55, 0.9, 1.0, 1.0],
"textCentered": true
},
{
"type": "TextButton",
"name": "discordButton",
"horizontal_gravity": "center",
"vertical_gravity": "center",
"y": -240,
"x": 150,
"width": 200,
"height": 60,
"text": "Наш Discord",
"fontSize": 40,
"color": [0.55, 0.9, 1.0, 1.0],
"textCentered": true
},
{
"type": "TextButton",
"name": "aboutBackButton",
"horizontal_gravity": "center",
"vertical_gravity": "bottom",
"y": 40,
"width": 200,
"height": 60,
"text": "Назад",
"fontSize": 40,
"textCentered": true
}
]
}
}
+2 -2
View File
@@ -43,7 +43,7 @@
"textCentered": false,
"topAligned": true,
"paddingX": 10,
"color": [1.0, 1.0, 1.0, 1.0]
"color": [0.7, 0.7, 0.7, 1.0]
},
{
"type": "Slider",
@@ -68,7 +68,7 @@
"textCentered": false,
"topAligned": true,
"paddingX": 10,
"color": [1.0, 1.0, 1.0, 1.0]
"color": [0.7, 0.7, 0.7, 1.0]
},
{
"type": "Slider",
@@ -38,31 +38,19 @@
"name": "noMusicText1",
"width": 500,
"height": 40,
"text": "Настройки музыки отсутствуют.",
"text": "Музыка отсутствует.",
"fontSize": 32,
"textCentered": false,
"topAligned": true,
"paddingX": 10,
"color": [1.0, 1.0, 1.0, 1.0]
},
{
"type": "TextView",
"name": "noMusicText2",
"width": 500,
"height": 40,
"text": "Пакет с музыкой не установлен.",
"fontSize": 32,
"textCentered": false,
"topAligned": true,
"paddingX": 10,
"color": [1.0, 1.0, 1.0, 1.0]
"color": [0.7, 0.7, 0.7, 1.0]
},
{
"type": "TextButton",
"name": "musicDownloadButton",
"width": 500,
"height": 40,
"text": "Нажмите сюда, чтобы скачать и установить музыку (30 MB).",
"text": "Скачать пакет с музыкой (27 MB).",
"fontSize": 32,
"textCentered": true,
"color": [1.0, 1.0, 1.0, 1.0]
@@ -77,7 +65,7 @@
"textCentered": false,
"topAligned": true,
"paddingX": 10,
"color": [1.0, 1.0, 1.0, 1.0]
"color": [0.7, 0.7, 0.7, 1.0]
},
{
"type": "Slider",
Binary file not shown.
+2 -2
View File
@@ -24,13 +24,13 @@
},
{
"type": "TextButton",
"name": "websiteButton",
"name": "reviewButton",
"horizontal_gravity": "center",
"vertical_gravity": "center",
"y": 200,
"width": 200,
"height": 60,
"text": "Shadow over Bishkek website",
"text": "Leave a review on Google Play",
"fontSize": 36,
"color": [0.55, 0.9, 1.0, 1.0],
"textCentered": true
+80
View File
@@ -0,0 +1,80 @@
{
"root": {
"type": "FrameLayout",
"width": "match_parent",
"height": "match_parent",
"children": [
{
"type": "StaticImage",
"name": "aboutBkg",
"width": 2992,
"height": 1632,
"horizontal_gravity": "center",
"vertical_gravity": "center",
"texture": "resources/w/ui/img/main/MenuBkg001_v001.png"
},
{
"type": "StaticImage",
"name": "aboutPicture",
"width": 720,
"height": 720,
"horizontal_gravity": "center",
"vertical_gravity": "center",
"texture": "resources/w/ui_en/img/main/aboutPage003android.png"
},
{
"type": "TextButton",
"name": "websiteButton",
"horizontal_gravity": "center",
"vertical_gravity": "center",
"y": 200,
"width": 200,
"height": 60,
"text": "Shadow over Bishkek website",
"fontSize": 36,
"color": [0.55, 0.9, 1.0, 1.0],
"textCentered": true
},
{
"type": "TextButton",
"name": "telegramButton",
"horizontal_gravity": "center",
"vertical_gravity": "center",
"y": -240,
"x": -150,
"width": 200,
"height": 60,
"text": "Telegram channel",
"fontSize": 40,
"color": [0.55, 0.9, 1.0, 1.0],
"textCentered": true
},
{
"type": "TextButton",
"name": "discordButton",
"horizontal_gravity": "center",
"vertical_gravity": "center",
"y": -240,
"x": 150,
"width": 200,
"height": 60,
"text": "Our Discord",
"fontSize": 40,
"color": [0.55, 0.9, 1.0, 1.0],
"textCentered": true
},
{
"type": "TextButton",
"name": "aboutBackButton",
"horizontal_gravity": "center",
"vertical_gravity": "bottom",
"y": 40,
"width": 200,
"height": 60,
"text": "Back",
"fontSize": 40,
"textCentered": true
}
]
}
}
-10
View File
@@ -43,16 +43,6 @@
"width": "match_parent",
"height": "match_parent"
},
{
"type": "StaticImage",
"name": "chatTopCover",
"width": 446.25,
"height": 70.82,
"x": 0,
"y": -50.82,
"horizontal_gravity": "center",
"texture": "resources/w/ui/img/phone/chat_top_cover001.png"
},
{
"type": "TextButton",
"name": "chatTitleButton",
+2 -2
View File
@@ -43,7 +43,7 @@
"textCentered": false,
"topAligned": true,
"paddingX": 10,
"color": [1.0, 1.0, 1.0, 1.0]
"color": [0.7, 0.7, 0.7, 1.0]
},
{
"type": "Slider",
@@ -68,7 +68,7 @@
"textCentered": false,
"topAligned": true,
"paddingX": 10,
"color": [1.0, 1.0, 1.0, 1.0]
"color": [0.7, 0.7, 0.7, 1.0]
},
{
"type": "Slider",
@@ -0,0 +1,119 @@
{
"root": {
"type": "FrameLayout",
"width": "match_parent",
"height": "match_parent",
"children": [
{
"type": "StaticImage",
"name": "settingsBkg",
"width": 2992,
"height": 1632,
"horizontal_gravity": "center",
"vertical_gravity": "center",
"texture": "resources/w/ui/img/main/MenuBkg001_v001.png"
},
{
"type": "LinearLayout",
"orientation": "vertical",
"horizontal_gravity": "center",
"vertical_gravity": "center",
"horizontal_align": "left",
"width": 500,
"height": 600,
"spacing": 20,
"children": [
{
"type": "TextView",
"name": "settingsTitleText",
"width": 500,
"height": 60,
"text": "Settings",
"fontSize": 40,
"textCentered": true,
"color": [1.0, 1.0, 1.0, 1.0]
},
{
"type": "TextView",
"name": "noMusicText1",
"width": 500,
"height": 40,
"text": "There is no music.",
"fontSize": 32,
"textCentered": false,
"topAligned": true,
"paddingX": 10,
"color": [0.7, 0.7, 0.7, 1.0]
},
{
"type": "TextButton",
"name": "musicDownloadButton",
"width": 500,
"height": 40,
"text": "Download music package (27 MB).",
"fontSize": 32,
"textCentered": true,
"color": [1.0, 1.0, 1.0, 1.0]
},
{
"type": "TextView",
"name": "soundVolumeText",
"width": 500,
"height": 40,
"text": "",
"fontSize": 32,
"textCentered": false,
"topAligned": true,
"paddingX": 10,
"color": [0.7, 0.7, 0.7, 1.0]
},
{
"type": "Slider",
"name": "soundVolumeSlider",
"width": 500,
"height": 40,
"orientation": "horizontal",
"value": 0.625,
"interactive": true,
"textures": {
"track": "resources/loadingProgressBarFrame.png",
"knob": "resources/loadingProgressBar.png"
}
},
{
"type": "TextButton",
"name": "soundToggleButton",
"width": 500,
"height": 40,
"text": "",
"fontSize": 32,
"textCentered": true,
"color": [1.0, 1.0, 1.0, 1.0]
},
{
"type": "TextButton",
"name": "graphicsButton",
"width": 500,
"height": 60,
"text": "Graphics settings",
"fontSize": 40,
"textCentered": true,
"color": [1.0, 1.0, 1.0, 1.0]
}
]
},
{
"type": "TextButton",
"name": "settingsBackButton",
"horizontal_gravity": "center",
"vertical_gravity": "bottom",
"y": 40,
"width": 200,
"height": 60,
"text": "Back",
"fontSize": 40,
"textCentered": true
}
]
}
}
-1
View File
@@ -22,7 +22,6 @@ int Environment::width = CONST_DEFAULT_WIDTH;
int Environment::height = CONST_DEFAULT_HEIGHT;
float Environment::zoom = DEFAULT_ZOOM;
bool Environment::enableLogging = true;
SDL_Window* Environment::window = nullptr;
+2
View File
@@ -1291,6 +1291,8 @@ namespace FRG
menuManager.onOrientationChanged();
}
menuManager.refreshFullScreenToggleButton();
logger() << "Window resized (Render Size): " << Environment::width << "x" << Environment::height << std::endl;
}
+57 -11
View File
@@ -28,9 +28,12 @@ namespace FRG {
static const std::string BACK_TO_MAIN_MENU_EN = "Back to Main Menu";
static const std::string MUSIC_DOWNLOAD_ERROR_RU = u8"Ошибка скачивания музыки";
static const std::string MUSIC_DOWNLOAD_NOT_STARTED_RU = u8"Нажмите сюда, чтобы скачать и установить музыку (27 MB).";
static const std::string MUSIC_DOWNLOAD_NOT_STARTED_RU = u8"Скачать пакет с музыкой (27 MB).";
static const std::string MUSIC_DOWNLOAD_PROGRESS_PREFIX_RU = u8"Загрузка музыки: ";
//static const std::string MUSIC_DOWNLOAD_COMPLETED_RU = u8"Музыка успешно загружена.";
static const std::string MUSIC_DOWNLOAD_ERROR_EN = u8"Music download error";
static const std::string MUSIC_DOWNLOAD_NOT_STARTED_EN = u8"Download music package (27 MB).";
static const std::string MUSIC_DOWNLOAD_PROGRESS_PREFIX_EN = u8"Music download: ";
static int questStatusPriority(Quest::QuestStatus status) {
@@ -246,7 +249,7 @@ namespace FRG {
void MenuManager::loadUiRoots(const std::string& zipFile) {
hudRoot = loadLocalizedUi("resources/w/ui/hud_step0.json", zipFile);
#ifdef __ANDROID__
#if defined(__ANDROID__)
hudStep1Root = loadLocalizedUi("resources/w/ui/hud_step1android.json", zipFile);
hudStep2Root = loadLocalizedUi("resources/w/ui/hud_step2android.json", zipFile);
#else
@@ -288,7 +291,10 @@ namespace FRG {
hudUniIntDarkFullRoot = loadLocalizedUi("resources/w/ui/hud_uni_int_dark_full.json", zipFile);
hudUniExtDarkRoot = loadLocalizedUi("resources/w/ui/hud_uni_ext_dark.json", zipFile);
hudCutsceneRoot_ = loadLocalizedUi("resources/w/ui/hud_cutscene.json", zipFile);
//hudCutsceneRoot_ = loadLocalizedUi("resources/w/ui/hud_cutscene.json", zipFile);
hudCutsceneRoot_ = loadUiFromFile("resources/w/ui/hud_cutscene.json", renderer, zipFile);
hudTopHintRoot_ = loadLocalizedUi("resources/w/ui/hud_top_hint01.json", zipFile);
phoneMainRoot = loadLocalizedUi("resources/w/ui/screen_phone.json", zipFile);
@@ -329,12 +335,18 @@ namespace FRG {
portraitQuestJournalListRoot = loadLocalizedUi("resources/w/ui/portrait_screen_journal.json", zipFile);
portraitQuestJournalItemRoot = loadLocalizedUi("resources/w/ui/portrait_screen_journal_item.json", zipFile);
#ifdef __ANDROID__
#if defined(__ANDROID__) || defined(__EMSCRIPTEN__)
mainMenuRoot = loadLocalizedUi("resources/w/ui/screen_main_menu_android.json", zipFile);
aboutScreenRoot = loadLocalizedUi("resources/w/ui/screen_about_android.json", zipFile);
#else
mainMenuRoot = loadLocalizedUi("resources/w/ui/screen_main_menu.json", zipFile);
aboutScreenRoot = loadLocalizedUi("resources/w/ui/screen_about.json", zipFile);
#endif
#if defined(__ANDROID__)
aboutScreenRoot = loadLocalizedUi("resources/w/ui/screen_about_android.json", zipFile);
#elif defined(__EMSCRIPTEN__)
aboutScreenRoot = loadLocalizedUi("resources/w/ui/screen_about_web.json", zipFile);
#else
aboutScreenRoot = loadLocalizedUi("resources/w/ui/screen_about.json", zipFile);
#endif
creditsScreenRoot = loadLocalizedUi("resources/w/ui/screen_credits.json", zipFile);
@@ -615,13 +627,19 @@ namespace FRG {
});
#ifdef __ANDROID__
#ifdef __EMSCRIPTEN__
uiManager.setTextButtonCallback("websiteButton", [this](const std::string&) {
audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
SDL_OpenURL("https://shadowoverbishkek.com/");
});
#endif
#ifdef __ANDROID__
uiManager.setTextButtonCallback("reviewButton", [this](const std::string&) {
audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
SDL_OpenURL("https://play.google.com/store/apps/details?id=fishrungames.shadowoverbishkek");
});
#endif
uiManager.setTextButtonCallback("telegramButton", [this](const std::string&) {
audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
SDL_OpenURL("https://telegram.me/fishrungames");
@@ -838,6 +856,23 @@ namespace FRG {
});
}
void MenuManager::refreshFullScreenToggleButton()
{
// Localization
if (g_currentLanguage == Language::English)
{
fullScreenToggleOn = "Full Screen: ON";
fullScreenToggleOff = "Full Screen: OFF";
}
else if (g_currentLanguage == Language::Russian)
{
fullScreenToggleOn = u8"Полноэкранный режим: ВКЛ";
fullScreenToggleOff = u8"Полноэкранный режим: ВЫКЛ";
}
const bool on = Environment::isFullscreen;
uiManager.setTextButtonText("fullScreenToggleButton", on ? fullScreenToggleOn : fullScreenToggleOff);
}
#ifdef __EMSCRIPTEN__
void MenuManager::showSettingsNoMusicWebScreen()
@@ -863,23 +898,30 @@ namespace FRG {
const float soundFrac = audioPlayer_.getSoundVolume() / 128.0f;
uiManager.setSliderValue("soundVolumeSlider", soundFrac);
static std::string musicDownloadErrorString;// = MUSIC_DOWNLOAD_ERROR_RU;
static std::string musicDownloadNotStartedString;// = MUSIC_DOWNLOAD_NOT_STARTED_RU;
static std::string musicDownloadProgressPrefix;// = MUSIC_DOWNLOAD_PROGRESS_PREFIX_RU;
// Localization
if (g_currentLanguage == Language::English)
{
soundVolumePrefix = "Sound volume: ";
soundToggleOn = "Sound: ON";
soundToggleOff = "Sound: OFF";
musicDownloadErrorString = MUSIC_DOWNLOAD_ERROR_EN;
musicDownloadNotStartedString = MUSIC_DOWNLOAD_NOT_STARTED_EN;
musicDownloadProgressPrefix = MUSIC_DOWNLOAD_PROGRESS_PREFIX_EN;
}
else if (g_currentLanguage == Language::Russian)
{
soundVolumePrefix = u8"Громкость звука: ";
soundToggleOn = u8"Звук: ВКЛ";
soundToggleOff = u8"Звук: ВЫКЛ";
musicDownloadErrorString = MUSIC_DOWNLOAD_ERROR_RU;
musicDownloadNotStartedString = MUSIC_DOWNLOAD_NOT_STARTED_RU;
musicDownloadProgressPrefix = MUSIC_DOWNLOAD_PROGRESS_PREFIX_RU;
}
static std::string musicDownloadErrorString = u8"Ошибка скачивания музыки";
static std::string musicDownloadNotStartedString = u8"Нажмите сюда, чтобы скачать\nи установить музыку (27 MB).";
static std::string musicDownloadProgressPrefix = u8"Загрузка музыки: ";
// musicDownloadStarted = false;
//musicDownloadError = false;
@@ -969,13 +1011,17 @@ namespace FRG {
});
uiManager.setTextButtonCallback("languageRussianButton", [this](const std::string&) {
#ifndef __EMSCRIPTEN__
audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
#endif
FRG::g_currentLanguage = FRG::Language::Russian;
reloadLocalizedGameContent();
saveSettings();
});
uiManager.setTextButtonCallback("languageEnglishButton", [this](const std::string&) {
#ifndef __EMSCRIPTEN__
audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
#endif
FRG::g_currentLanguage = FRG::Language::English;
reloadLocalizedGameContent();
saveSettings();
+8
View File
@@ -129,7 +129,14 @@ namespace FRG {
std::shared_ptr<Texture> languageLoadingRu;
std::shared_ptr<Texture> languageLoadingEn;
void refreshFullScreenToggleButton();
#ifdef __EMSCRIPTEN__
bool shadowsEnabled = false;
#else
bool shadowsEnabled = true;
#endif
std::string lastMusicTrack;
#ifdef __EMSCRIPTEN__
@@ -190,6 +197,7 @@ namespace FRG {
void showSettingsScreen();
void showSettingsGraphicsScreen();
#ifdef __EMSCRIPTEN__
void showSettingsNoMusicWebScreen();
#endif
+4 -4
View File
@@ -798,14 +798,14 @@ namespace FRG {
else {
auto buf = readFileFromZIP(path, zipFile);
if (buf.empty()) {
std::cerr << "UiManager: failed to read " << path << " from zip " << zipFile << std::endl;
logger() << "UiManager: failed to read " << path << " from zip " << zipFile << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
content.assign(buf.begin(), buf.end());
}
}
catch (const std::exception& e) {
std::cerr << "UiManager: failed to open " << path << " : " << e.what() << std::endl;
logger() << "UiManager: failed to open " << path << " : " << e.what() << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
@@ -814,12 +814,12 @@ namespace FRG {
j = json::parse(content);
}
catch (const std::exception& e) {
std::cerr << "UiManager: json parse error: " << e.what() << std::endl;
logger() << "UiManager: json parse error: " << e.what() << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
if (!j.contains("root") || !j["root"].is_object()) {
std::cerr << "UiManager: root node missing or invalid" << std::endl;
logger() << "UiManager: root node missing or invalid" << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
+80 -59
View File
@@ -20,15 +20,48 @@
FRG::Game* g_game = nullptr;
// Read the persisted fullscreen preference (if any) before creating the
// window, so it opens directly in the right mode instead of flashing
// windowed -> fullscreen once settings are loaded later during setupPart2().
void readSettings()
{
const std::string settingsContent = FRG::readSavedTextFile("settings.json");
if (!settingsContent.empty()) {
try {
const nlohmann::json settingsRoot = nlohmann::json::parse(settingsContent);
if (settingsRoot.contains("fullscreen")) {
FRG::Environment::isFullscreen = settingsRoot["fullscreen"].get<bool>();
}
if (settingsRoot.contains("isHighDPIEnabled")) {
FRG::Environment::isHighDPIEnabled = settingsRoot["isHighDPIEnabled"].get<bool>();
}
if (settingsRoot.contains("enableLogging")) {
FRG::Environment::enableLogging = settingsRoot["enableLogging"].get<bool>();
}
}
catch (const std::exception& e) {
std::cerr << "[settings] Failed to parse settings.json: " << e.what() << std::endl;
}
}
}
#if defined(EMSCRIPTEN)
static SDL_GLContext glContext_x;
static bool g_isFsLoaded = false; // Флаг готовности
static bool g_pendingFullscreenResize = false;
// Макрос EMSCRIPTEN_KEEPALIVE экспортирует функцию в глобальный объект Module
extern "C" void EMSCRIPTEN_KEEPALIVE onFileSystemLoaded() {
// 1. Файлы загружены. Теперь можно читать настройки и логи
// Если функции readSettings() нет для Emscripten, форсируем логгер:
FRG::Environment::enableLogging = true;
//FRG::Environment::enableLogging = true;
//readSettings();
FRG::initLogger();
FRG::logger() << "[boot] File system is ready. Setting up game..." << std::endl;
@@ -40,26 +73,7 @@ extern "C" void EMSCRIPTEN_KEEPALIVE onFileSystemLoaded() {
// 3. Даем отмашку главному циклу
g_isFsLoaded = true;
}
#endif
void MainLoop() {
#if defined(EMSCRIPTEN)
// Крутим цикл вхолостую, пока JS читает IndexedDB
if (!g_isFsLoaded) return;
#endif
g_game->update();
}
#ifdef EMSCRIPTEN
EM_BOOL onWebGLContextLost(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
return EM_TRUE;
}
EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
return EM_TRUE;
}
static void applyResize(int logicalW, int logicalH) {
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
@@ -92,22 +106,59 @@ static void applyResize(int logicalW, int logicalH) {
<< " (physical: " << physicalW << "x" << physicalH
<< ", DPR: " << dpr << ")" << std::endl;
}
#endif
void MainLoop() {
#if defined(EMSCRIPTEN)
// Крутим цикл вхолостую, пока JS читает IndexedDB
if (!g_isFsLoaded) return;
if (g_pendingFullscreenResize) {
g_pendingFullscreenResize = false;
// Теперь DOM перестроен, можно забирать актуальные размеры.
// Если canvas должен занимать все окно браузера:
int canvasW = EM_ASM_INT({ return window.innerWidth; });
int canvasH = EM_ASM_INT({ return window.innerHeight; });
applyResize(canvasW, canvasH);
}
#endif
g_game->update();
}
#ifdef EMSCRIPTEN
EM_BOOL onWebGLContextLost(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
return EM_TRUE;
}
EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
return EM_TRUE;
}
EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*userData*/) {
// Use the event's window dimensions — querying the canvas element would
// return its old fixed size (e.g. 1280x720) before it has been resized.
applyResize(e->windowInnerWidth, e->windowInnerHeight);
double w, h;
emscripten_get_element_css_size("window", &w, &h);
// Если "window" не поддерживается вашей версией SDK, используем данные из e:
// int logicalW = e->windowInnerWidth;
// int logicalH = e->windowInnerHeight;
applyResize(static_cast<int>(w), static_cast<int>(h));
return EM_FALSE;
}
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
FRG::Environment::isFullscreen = e->isFullscreen;
FRG::logger() << "onFullscreenChanged: isFullscreen = " << e->isFullscreen << std::endl;
// Браузер еще не обновил DOM. Ставим флаг, чтобы применить размеры в следующем кадре.
g_pendingFullscreenResize = true;
// Вместо window.innerWidth, попробуйте запросить размер целевого элемента
// так как после перехода в FS именно он растягивается на весь экран.
double clientW, clientH;
emscripten_get_element_css_size("#canvas", &clientW, &clientH);
applyResize(clientW, clientH);
return EM_FALSE;
}
@@ -128,7 +179,7 @@ int main(int argc, char* argv[]) {
emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored);
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_TRUE, onFullscreenChanged);
int canvasW = EM_ASM_INT({ return window.innerWidth; });
int canvasH = EM_ASM_INT({ return window.innerHeight; });
@@ -289,36 +340,6 @@ int main(int argc, char* argv[]) {
#ifndef __ANDROID__
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__linux__)
// Read the persisted fullscreen preference (if any) before creating the
// window, so it opens directly in the right mode instead of flashing
// windowed -> fullscreen once settings are loaded later during setupPart2().
void readSettings()
{
const std::string settingsContent = FRG::readSavedTextFile("settings.json");
if (!settingsContent.empty()) {
try {
const nlohmann::json settingsRoot = nlohmann::json::parse(settingsContent);
if (settingsRoot.contains("fullscreen")) {
FRG::Environment::isFullscreen = settingsRoot["fullscreen"].get<bool>();
}
if (settingsRoot.contains("isHighDPIEnabled")) {
FRG::Environment::isHighDPIEnabled = settingsRoot["isHighDPIEnabled"].get<bool>();
}
if (settingsRoot.contains("enableLogging")) {
FRG::Environment::enableLogging = settingsRoot["enableLogging"].get<bool>();
}
}
catch (const std::exception& e) {
std::cerr << "[settings] Failed to parse settings.json: " << e.what() << std::endl;
}
}
}
int main(int argc, char* argv[]) {
SDL_GLContext ctx = nullptr;
+5
View File
@@ -509,8 +509,13 @@ namespace FRG
}
#ifdef __ANDROID__
return g_androidStream;
#else
#ifdef __EMSCRIPTEN__
return g_nullStream; //For web, remove all logging to browser console
#else
return std::cout;
#endif
#endif
}