diff --git a/audio/13925__adcbicycle__21.ogg b/audio/13925__adcbicycle__21.ogg deleted file mode 100644 index 5b76eaa..0000000 --- a/audio/13925__adcbicycle__21.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b9d30306535105c1070730f40aba8894d7c4b19d702bb95ef5ff2c78471b3cd5 -size 7662 diff --git a/audio/255571__stradie__flipping-paper.ogg b/audio/255571__stradie__flipping-paper.ogg deleted file mode 100644 index cae74b7..0000000 --- a/audio/255571__stradie__flipping-paper.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9442ccf84f7f2e856c16519b0cb060e1b36f4096db7c0a07889c94dd826a77bc -size 45403 diff --git a/audio/398751__anthousai__door-lock-01.ogg b/audio/398751__anthousai__door-lock-01.ogg deleted file mode 100644 index f79dd21..0000000 --- a/audio/398751__anthousai__door-lock-01.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:659d78850496656075215ccd50ae62a64b4de7e4dc13c3c0191a964822481255 -size 10364 diff --git a/audio/459618__n-lerche__blow.ogg b/audio/459618__n-lerche__blow.ogg deleted file mode 100644 index 957b415..0000000 --- a/audio/459618__n-lerche__blow.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4a55d5a4968a28ae948b72d0092b6a64beeb2d14bda4c2010a12890a0a4cec03 -size 42794 diff --git a/audio/537061__imafoley__message-pop-sound.ogg b/audio/537061__imafoley__message-pop-sound.ogg deleted file mode 100644 index c431da3..0000000 --- a/audio/537061__imafoley__message-pop-sound.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:624b696ed4ffa4e72cbcbc2a7005c7f282b4baaec921bb2be1f6853c3e15b097 -size 7916 diff --git a/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg b/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg deleted file mode 100644 index f1be8e0..0000000 --- a/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fd6b8506121e1ac9807a8fb2689f7c0cfca48777c08810b613ba761fc33d76c4 -size 19353 diff --git a/audio/587036__antoineromo__bed-sheets.ogg b/audio/587036__antoineromo__bed-sheets.ogg deleted file mode 100644 index ed0d4cb..0000000 --- a/audio/587036__antoineromo__bed-sheets.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:12d04399abfe2441f51dc8b5e268aebc734ba707de161239aa90b808ade630da -size 35946 diff --git a/audio/656126__itsthegoodstuff__paging-through-book.ogg b/audio/656126__itsthegoodstuff__paging-through-book.ogg deleted file mode 100644 index 0f64f91..0000000 --- a/audio/656126__itsthegoodstuff__paging-through-book.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ad3034bc469fba569d8569881676c27f5bbec40533d5255291bef6d379bfe4c9 -size 21567 diff --git a/audio/702171__foxfire__door-opening.ogg b/audio/702171__foxfire__door-opening.ogg deleted file mode 100644 index 2c7c861..0000000 --- a/audio/702171__foxfire__door-opening.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9e725a730b1387a9df7bb0125f3dd7ccba1ca27e8422ee3c55b3bde4d8ce4124 -size 17136 diff --git a/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg b/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg deleted file mode 100644 index 2ca58ce..0000000 --- a/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6a548df89a87919e3fd4bde3fa8eb2da883c402b7feeae7c0603d7d98d35399a -size 5105 diff --git a/audio/78564__joedeshon__alarm_clock_ticking_02.ogg b/audio/78564__joedeshon__alarm_clock_ticking_02.ogg deleted file mode 100644 index 4d0cee4..0000000 --- a/audio/78564__joedeshon__alarm_clock_ticking_02.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c1756c0ece8080c799cb6ca4911a2c610800089ec6a65c45ba69e990942b7af9 -size 104191 diff --git a/audio/831642__ienba__generic-game-pick-up.ogg b/audio/831642__ienba__generic-game-pick-up.ogg deleted file mode 100644 index 44d62da..0000000 --- a/audio/831642__ienba__generic-game-pick-up.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c79391d1f26978900194f9d5af1a0dc59eb62a1572f4f9011d1538a6bf09ffab -size 16067 diff --git a/audio/sound_bag001.ogg b/audio/sound_bag001.ogg deleted file mode 100644 index be0d04b..0000000 --- a/audio/sound_bag001.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e64ea00f0355371d2e1b93818b0c686da6e699e1cdb4de26a1c9dac5640bd0fb -size 16054 diff --git a/audio/sound_bag_close.ogg b/audio/sound_bag_close.ogg deleted file mode 100644 index 217533a..0000000 --- a/audio/sound_bag_close.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bcb99f3aa14d4f53ae636fab8fadb0ed19a4be17e994088e7e41e4872002c0be -size 18569 diff --git a/audio/sound_bag_open.ogg b/audio/sound_bag_open.ogg deleted file mode 100644 index 1080147..0000000 --- a/audio/sound_bag_open.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e17ae2d18d36892cbc634cca6b5523293c2737e97e0a94b577a8709823a5db04 -size 17077 diff --git a/optimize_web_resources.py b/optimize_web_resources.py index 773c0da..531c13e 100644 --- a/optimize_web_resources.py +++ b/optimize_web_resources.py @@ -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: diff --git a/proj-web/CMakeLists.txt b/proj-web/CMakeLists.txt index f17b45d..14284cb 100644 --- a/proj-web/CMakeLists.txt +++ b/proj-web/CMakeLists.txt @@ -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 .) diff --git a/proj-web/README.md b/proj-web/README.md index d3706c8..3600be5 100644 --- a/proj-web/README.md +++ b/proj-web/README.md @@ -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 +``` diff --git a/proj-web/favicon.ico b/proj-web/favicon.ico new file mode 100644 index 0000000..69c6783 Binary files /dev/null and b/proj-web/favicon.ico differ diff --git a/resources/dialogue/translate_dialogues.json b/resources/dialogue/translate_dialogues.json index 22f7d5e..1453cdd 100644 --- a/resources/dialogue/translate_dialogues.json +++ b/resources/dialogue/translate_dialogues.json @@ -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": "Пока ты не принесешь мне нож из учительской, никуда я тебя не выпущу. Ошо!", diff --git a/resources/dialogue/uni_interior_dialogues_010.json b/resources/dialogue/uni_interior_dialogues_010.json index e0122e3..820a87f 100644 --- a/resources/dialogue/uni_interior_dialogues_010.json +++ b/resources/dialogue/uni_interior_dialogues_010.json @@ -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" }, { diff --git a/resources/w/ui/screen_about_android.json b/resources/w/ui/screen_about_android.json index 1234216..03acce8 100644 --- a/resources/w/ui/screen_about_android.json +++ b/resources/w/ui/screen_about_android.json @@ -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 diff --git a/resources/w/ui/screen_about_web.json b/resources/w/ui/screen_about_web.json new file mode 100644 index 0000000..1234216 --- /dev/null +++ b/resources/w/ui/screen_about_web.json @@ -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 + } + ] + } +} diff --git a/resources/w/ui/screen_settings.json b/resources/w/ui/screen_settings.json index 596eb7a..e1059d4 100644 --- a/resources/w/ui/screen_settings.json +++ b/resources/w/ui/screen_settings.json @@ -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", diff --git a/resources/w/ui/screen_settings_nomusic_web.json b/resources/w/ui/screen_settings_nomusic_web.json index 639bf69..32617b5 100644 --- a/resources/w/ui/screen_settings_nomusic_web.json +++ b/resources/w/ui/screen_settings_nomusic_web.json @@ -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", diff --git a/resources/w/ui_en/img/inv/Inventory001.png b/resources/w/ui_en/img/inv/Inventory001.png new file mode 100644 index 0000000..7d70dca --- /dev/null +++ b/resources/w/ui_en/img/inv/Inventory001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f063e0977b210010b530468b0210fe3407e26d104c6a45cd9493340c1df0ca44 +size 269501 diff --git a/resources/w/ui_en/screen_about_android.json b/resources/w/ui_en/screen_about_android.json index c929238..8409f41 100644 --- a/resources/w/ui_en/screen_about_android.json +++ b/resources/w/ui_en/screen_about_android.json @@ -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 diff --git a/resources/w/ui_en/screen_about_web.json b/resources/w/ui_en/screen_about_web.json new file mode 100644 index 0000000..c929238 --- /dev/null +++ b/resources/w/ui_en/screen_about_web.json @@ -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 + } + ] + } +} diff --git a/resources/w/ui_en/screen_phone_chat1.json b/resources/w/ui_en/screen_phone_chat1.json index dab6051..8729e40 100644 --- a/resources/w/ui_en/screen_phone_chat1.json +++ b/resources/w/ui_en/screen_phone_chat1.json @@ -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", diff --git a/resources/w/ui_en/screen_settings.json b/resources/w/ui_en/screen_settings.json index 21614e8..082a296 100644 --- a/resources/w/ui_en/screen_settings.json +++ b/resources/w/ui_en/screen_settings.json @@ -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", diff --git a/resources/w/ui_en/screen_settings_nomusic_web.json b/resources/w/ui_en/screen_settings_nomusic_web.json new file mode 100644 index 0000000..c2a91b8 --- /dev/null +++ b/resources/w/ui_en/screen_settings_nomusic_web.json @@ -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 + } + ] + } +} diff --git a/src/Environment.cpp b/src/Environment.cpp index 158d72f..6f39cc8 100644 --- a/src/Environment.cpp +++ b/src/Environment.cpp @@ -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; diff --git a/src/Game.cpp b/src/Game.cpp index 99f38ad..a4f8ade 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -1291,6 +1291,8 @@ namespace FRG menuManager.onOrientationChanged(); } + menuManager.refreshFullScreenToggleButton(); + logger() << "Window resized (Render Size): " << Environment::width << "x" << Environment::height << std::endl; } diff --git a/src/MenuManager.cpp b/src/MenuManager.cpp index 976198b..d2523a7 100644 --- a/src/MenuManager.cpp +++ b/src/MenuManager.cpp @@ -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(); diff --git a/src/MenuManager.h b/src/MenuManager.h index b78a503..a64f68c 100644 --- a/src/MenuManager.h +++ b/src/MenuManager.h @@ -129,7 +129,14 @@ namespace FRG { std::shared_ptr languageLoadingRu; std::shared_ptr languageLoadingEn; + + void refreshFullScreenToggleButton(); + +#ifdef __EMSCRIPTEN__ + bool shadowsEnabled = false; +#else bool shadowsEnabled = true; +#endif std::string lastMusicTrack; #ifdef __EMSCRIPTEN__ @@ -189,6 +196,7 @@ namespace FRG { void showCreditsScreen(); void showSettingsScreen(); void showSettingsGraphicsScreen(); + #ifdef __EMSCRIPTEN__ void showSettingsNoMusicWebScreen(); diff --git a/src/UiManager.cpp b/src/UiManager.cpp index d204f86..64283d0 100644 --- a/src/UiManager.cpp +++ b/src/UiManager.cpp @@ -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); } diff --git a/src/main.cpp b/src/main.cpp index 8ad905c..119d7e9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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(); + } + + if (settingsRoot.contains("isHighDPIEnabled")) { + FRG::Environment::isHighDPIEnabled = settingsRoot["isHighDPIEnabled"].get(); + } + + if (settingsRoot.contains("enableLogging")) { + FRG::Environment::enableLogging = settingsRoot["enableLogging"].get(); + } + + } + 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(w), static_cast(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(); - } - - if (settingsRoot.contains("isHighDPIEnabled")) { - FRG::Environment::isHighDPIEnabled = settingsRoot["isHighDPIEnabled"].get(); - } - - if (settingsRoot.contains("enableLogging")) { - FRG::Environment::enableLogging = settingsRoot["enableLogging"].get(); - } - - } - 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; diff --git a/src/utils/Utils.cpp b/src/utils/Utils.cpp index dadefb4..290d095 100644 --- a/src/utils/Utils.cpp +++ b/src/utils/Utils.cpp @@ -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 }