diff --git a/audio/bishkek fight calm.ogg b/music/bishkek fight calm.ogg
similarity index 100%
rename from audio/bishkek fight calm.ogg
rename to music/bishkek fight calm.ogg
diff --git a/audio/bishkek fight.ogg b/music/bishkek fight.ogg
similarity index 100%
rename from audio/bishkek fight.ogg
rename to music/bishkek fight.ogg
diff --git a/audio/bishkek night.ogg b/music/bishkek night.ogg
similarity index 100%
rename from audio/bishkek night.ogg
rename to music/bishkek night.ogg
diff --git a/audio/bishkek univer day.ogg b/music/bishkek univer day.ogg
similarity index 100%
rename from audio/bishkek univer day.ogg
rename to music/bishkek univer day.ogg
diff --git a/audio/main menu final.ogg b/music/main menu final.ogg
similarity index 100%
rename from audio/main menu final.ogg
rename to music/main menu final.ogg
diff --git a/audio/obshaga.ogg b/music/obshaga.ogg
similarity index 100%
rename from audio/obshaga.ogg
rename to music/obshaga.ogg
diff --git a/proj-linux/CMakeLists.txt b/proj-linux/CMakeLists.txt
index 1b10e8d..61bca5c 100644
--- a/proj-linux/CMakeLists.txt
+++ b/proj-linux/CMakeLists.txt
@@ -290,7 +290,7 @@ endif()
# ===========================================
set(RUNTIME_RESOURCE_DIRS
"resources"
- "audio"
+ "music"
)
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
diff --git a/proj-web/CMakeLists.txt b/proj-web/CMakeLists.txt
index 64c3c73..f17b45d 100644
--- a/proj-web/CMakeLists.txt
+++ b/proj-web/CMakeLists.txt
@@ -8,7 +8,7 @@ if(NOT CMAKE_MAKE_PROGRAM AND WIN32)
endif()
endif()
-project(bishkek-witcher LANGUAGES C CXX)
+project(ShadowOverBishkekDemo LANGUAGES C CXX)
option(OPTIMIZE_WEB_RESOURCES "Resize PNG images for web build (requires Python3 + Pillow)" ON)
@@ -152,10 +152,10 @@ set(SOURCES
../src/render/UiQuad.h
)
-add_executable(bishkek-witcher ${SOURCES})
+add_executable(ShadowOverBishkekDemo ${SOURCES})
# Настройка путей к инклудам (используем скачанные исходники)
-target_include_directories(bishkek-witcher PRIVATE
+target_include_directories(ShadowOverBishkekDemo PRIVATE
../src
../thirdparty/eigen-5.0.0
../thirdparty/boost_1_90_0
@@ -170,7 +170,7 @@ set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE)
add_subdirectory("../thirdparty/libzip-1.11.4" libzip-build)
-target_link_libraries(bishkek-witcher PRIVATE zip z lua_static websocket.js)
+target_link_libraries(ShadowOverBishkekDemo PRIVATE zip z lua_static websocket.js)
# Эмскриптен-флаги
set(EMSCRIPTEN_FLAGS
@@ -187,7 +187,7 @@ set(EMSCRIPTEN_FLAGS
"-DNETWORK"
)
-target_compile_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_FLAGS} "-O2")
+target_compile_options(ShadowOverBishkekDemo PRIVATE ${EMSCRIPTEN_FLAGS} "-O2")
# Only loading.png and the shaders used before resources.zip is ready are preloaded.
# resources.zip is downloaded asynchronously at runtime and served as a separate file.
@@ -205,15 +205,14 @@ set(EMSCRIPTEN_LINK_FLAGS
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_uni_interior.lua@resources/start_uni_interior.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_uni_exterior.lua@resources/start_uni_exterior.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_dorm.lua@resources/start_dorm.lua"
- "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../audio@audio"
)
# Применяем настройки линковки
-target_link_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
+target_link_options(ShadowOverBishkekDemo PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
# Для совместимости со старыми версиями CMake, если target_link_options недостаточно
string(REPLACE ";" " " EMSCRIPTEN_LINK_FLAGS_STR "${EMSCRIPTEN_LINK_FLAGS}")
-set_target_properties(bishkek-witcher PROPERTIES
+set_target_properties(ShadowOverBishkekDemo PROPERTIES
LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS_STR}"
SUFFIX ".html"
)
@@ -257,23 +256,40 @@ else()
)
endif()
+
+# music resources
+set(MUSIC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../music")
+set(MUSIC_ZIP "${CMAKE_CURRENT_BINARY_DIR}/music.zip")
+get_filename_component(MUSIC_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
+
+add_custom_command(
+ OUTPUT "${MUSIC_ZIP}"
+ COMMAND ${CMAKE_COMMAND} -E tar "cf" "${MUSIC_ZIP}" --format=zip "music"
+ WORKING_DIRECTORY "${MUSIC_PARENT_DIR}"
+ DEPENDS "${MUSIC_PARENT_DIR}/music"
+)
+
+
add_custom_target(pack_resources DEPENDS "${RESOURCES_ZIP}")
-add_dependencies(bishkek-witcher pack_resources)
+add_dependencies(ShadowOverBishkekDemo pack_resources)
+
+add_custom_target(pack_music DEPENDS "${MUSIC_ZIP}")
+add_dependencies(ShadowOverBishkekDemo pack_music)
# Определяем путь к директории установки (относительно папки билда)
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/public")
# 1. Устанавливаем основной HTML файл
-install(TARGETS bishkek-witcher
+install(TARGETS ShadowOverBishkekDemo
RUNTIME DESTINATION .
)
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
install(FILES
- "${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.js"
- "${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.wasm"
- "${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.data"
+ "${CMAKE_CURRENT_BINARY_DIR}/ShadowOverBishkekDemo.js"
+ "${CMAKE_CURRENT_BINARY_DIR}/ShadowOverBishkekDemo.wasm"
+ "${CMAKE_CURRENT_BINARY_DIR}/ShadowOverBishkekDemo.data"
DESTINATION .
)
@@ -283,8 +299,9 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/index.html"
# resources.zip is served separately and downloaded asynchronously at runtime
install(FILES "${RESOURCES_ZIP}" DESTINATION .)
+install(FILES "${MUSIC_ZIP}" DESTINATION .)
-add_custom_command(TARGET bishkek-witcher POST_BUILD
+add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
COMMAND ${CMAKE_COMMAND} --install .
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
COMMENT "Automatically deploying to public directory..."
diff --git a/proj-web/bishkek-witcher.html b/proj-web/bishkek-witcher.html
deleted file mode 100644
index 7d859d4..0000000
--- a/proj-web/bishkek-witcher.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
Emscripten-Generated Code
-
-
-
-
Downloading...
Resize canvas Lock/hide mouse pointer
\ No newline at end of file
diff --git a/proj-web/index.html b/proj-web/index.html
index 5555a9f..2d21fdd 100644
--- a/proj-web/index.html
+++ b/proj-web/index.html
@@ -39,7 +39,7 @@
function loadGameScript() {
var s = document.createElement('script');
- s.src = 'bishkek-witcher.js';
+ s.src = 'ShadowOverBishkekDemo.js';
s.async = true;
document.body.appendChild(s);
}
diff --git a/proj-web/space-game001.html b/proj-web/space-game001.html
deleted file mode 100644
index 710f6f4..0000000
--- a/proj-web/space-game001.html
+++ /dev/null
@@ -1,2 +0,0 @@
-Emscripten-Generated Code
-![]()
Downloading...
Resize canvas Lock/hide mouse pointer
\ No newline at end of file
diff --git a/proj-web/space-game001plain.html b/proj-web/space-game001plain.html
deleted file mode 100644
index e4acfc9..0000000
--- a/proj-web/space-game001plain.html
+++ /dev/null
@@ -1,200 +0,0 @@
-
-
-
-
-
- Sky Trek Tales
-
-
-
-
- Downloading...
-
-
-
-
Enter your nickname
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/proj-windows/CMakeLists.txt b/proj-windows/CMakeLists.txt
index 2ef30fa..6bbd50b 100644
--- a/proj-windows/CMakeLists.txt
+++ b/proj-windows/CMakeLists.txt
@@ -232,7 +232,7 @@ if (WIN32)
endif()
# Копирование ресурсов локально
-set(RUNTIME_RESOURCE_DIRS "resources" "audio")
+set(RUNTIME_RESOURCE_DIRS "resources" "music")
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
diff --git a/resources/audio/13925__adcbicycle__21.ogg b/resources/audio/13925__adcbicycle__21.ogg
new file mode 100644
index 0000000..5b76eaa
--- /dev/null
+++ b/resources/audio/13925__adcbicycle__21.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b9d30306535105c1070730f40aba8894d7c4b19d702bb95ef5ff2c78471b3cd5
+size 7662
diff --git a/resources/audio/255571__stradie__flipping-paper.ogg b/resources/audio/255571__stradie__flipping-paper.ogg
new file mode 100644
index 0000000..cae74b7
--- /dev/null
+++ b/resources/audio/255571__stradie__flipping-paper.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9442ccf84f7f2e856c16519b0cb060e1b36f4096db7c0a07889c94dd826a77bc
+size 45403
diff --git a/resources/audio/398751__anthousai__door-lock-01.ogg b/resources/audio/398751__anthousai__door-lock-01.ogg
new file mode 100644
index 0000000..f79dd21
--- /dev/null
+++ b/resources/audio/398751__anthousai__door-lock-01.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:659d78850496656075215ccd50ae62a64b4de7e4dc13c3c0191a964822481255
+size 10364
diff --git a/resources/audio/459618__n-lerche__blow.ogg b/resources/audio/459618__n-lerche__blow.ogg
new file mode 100644
index 0000000..957b415
--- /dev/null
+++ b/resources/audio/459618__n-lerche__blow.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4a55d5a4968a28ae948b72d0092b6a64beeb2d14bda4c2010a12890a0a4cec03
+size 42794
diff --git a/resources/audio/537061__imafoley__message-pop-sound.ogg b/resources/audio/537061__imafoley__message-pop-sound.ogg
new file mode 100644
index 0000000..c431da3
--- /dev/null
+++ b/resources/audio/537061__imafoley__message-pop-sound.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:624b696ed4ffa4e72cbcbc2a7005c7f282b4baaec921bb2be1f6853c3e15b097
+size 7916
diff --git a/resources/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg b/resources/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg
new file mode 100644
index 0000000..f1be8e0
--- /dev/null
+++ b/resources/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fd6b8506121e1ac9807a8fb2689f7c0cfca48777c08810b613ba761fc33d76c4
+size 19353
diff --git a/resources/audio/587036__antoineromo__bed-sheets.ogg b/resources/audio/587036__antoineromo__bed-sheets.ogg
new file mode 100644
index 0000000..ed0d4cb
--- /dev/null
+++ b/resources/audio/587036__antoineromo__bed-sheets.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:12d04399abfe2441f51dc8b5e268aebc734ba707de161239aa90b808ade630da
+size 35946
diff --git a/resources/audio/656126__itsthegoodstuff__paging-through-book.ogg b/resources/audio/656126__itsthegoodstuff__paging-through-book.ogg
new file mode 100644
index 0000000..0f64f91
--- /dev/null
+++ b/resources/audio/656126__itsthegoodstuff__paging-through-book.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ad3034bc469fba569d8569881676c27f5bbec40533d5255291bef6d379bfe4c9
+size 21567
diff --git a/resources/audio/702171__foxfire__door-opening.ogg b/resources/audio/702171__foxfire__door-opening.ogg
new file mode 100644
index 0000000..2c7c861
--- /dev/null
+++ b/resources/audio/702171__foxfire__door-opening.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9e725a730b1387a9df7bb0125f3dd7ccba1ca27e8422ee3c55b3bde4d8ce4124
+size 17136
diff --git a/resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg b/resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg
new file mode 100644
index 0000000..2ca58ce
--- /dev/null
+++ b/resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6a548df89a87919e3fd4bde3fa8eb2da883c402b7feeae7c0603d7d98d35399a
+size 5105
diff --git a/resources/audio/78564__joedeshon__alarm_clock_ticking_02.ogg b/resources/audio/78564__joedeshon__alarm_clock_ticking_02.ogg
new file mode 100644
index 0000000..4d0cee4
--- /dev/null
+++ b/resources/audio/78564__joedeshon__alarm_clock_ticking_02.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c1756c0ece8080c799cb6ca4911a2c610800089ec6a65c45ba69e990942b7af9
+size 104191
diff --git a/resources/audio/831642__ienba__generic-game-pick-up.ogg b/resources/audio/831642__ienba__generic-game-pick-up.ogg
new file mode 100644
index 0000000..44d62da
--- /dev/null
+++ b/resources/audio/831642__ienba__generic-game-pick-up.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c79391d1f26978900194f9d5af1a0dc59eb62a1572f4f9011d1538a6bf09ffab
+size 16067
diff --git a/resources/audio/sound_bag001.ogg b/resources/audio/sound_bag001.ogg
new file mode 100644
index 0000000..be0d04b
--- /dev/null
+++ b/resources/audio/sound_bag001.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e64ea00f0355371d2e1b93818b0c686da6e699e1cdb4de26a1c9dac5640bd0fb
+size 16054
diff --git a/resources/audio/sound_bag_close.ogg b/resources/audio/sound_bag_close.ogg
new file mode 100644
index 0000000..217533a
--- /dev/null
+++ b/resources/audio/sound_bag_close.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bcb99f3aa14d4f53ae636fab8fadb0ed19a4be17e994088e7e41e4872002c0be
+size 18569
diff --git a/resources/audio/sound_bag_open.ogg b/resources/audio/sound_bag_open.ogg
new file mode 100644
index 0000000..1080147
--- /dev/null
+++ b/resources/audio/sound_bag_open.ogg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e17ae2d18d36892cbc634cca6b5523293c2737e97e0a94b577a8709823a5db04
+size 17077
diff --git a/resources/config/start_state.json b/resources/config/start_state.json
index bbd0860..66288b5 100644
--- a/resources/config/start_state.json
+++ b/resources/config/start_state.json
@@ -1122,9 +1122,9 @@
"waypointReachRadius": 3.0,
"waypoints": []
},
- "taxiCarTriggerPositionX": -1.7014118346046923e+38,
- "taxiCarTriggerPositionY": -1.7014118346046923e+38,
- "taxiCarTriggerPositionZ": -1.7014118346046923e+38,
+ "taxiCarTriggerPositionX": 0.0,
+ "taxiCarTriggerPositionY": 0.0,
+ "taxiCarTriggerPositionZ": 0.0,
"taxiDefaultWaypoints": []
},
"tutorialInteractiveObjectsLocked": false
@@ -1476,7 +1476,6 @@
"morning_can_open_door_index": 0,
"morning_did_open_door_index": 0,
"player_hold_book": false,
- "player_hold_knife": false,
"teacher_arrived_to_library": false,
"teacher_door_opened": false,
"teacher_told_about_book": false,
@@ -1652,7 +1651,7 @@
}
]
},
- "savedAt": "2026-07-14 14:00",
+ "savedAt": "2026-07-24 23:40",
"taxiIsCalled": false,
"tutorialJournalPickedUp": false,
"tutorialJournalScreenOpened": false,
diff --git a/resources/w/ui/screen_settings_nomusic_web.json b/resources/w/ui/screen_settings_nomusic_web.json
new file mode 100644
index 0000000..639bf69
--- /dev/null
+++ b/resources/w/ui/screen_settings_nomusic_web.json
@@ -0,0 +1,131 @@
+{
+ "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": "Настройки",
+ "fontSize": 40,
+ "textCentered": true,
+ "color": [1.0, 1.0, 1.0, 1.0]
+ },
+ {
+ "type": "TextView",
+ "name": "noMusicText1",
+ "width": 500,
+ "height": 40,
+ "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]
+ },
+ {
+ "type": "TextButton",
+ "name": "musicDownloadButton",
+ "width": 500,
+ "height": 40,
+ "text": "Нажмите сюда, чтобы скачать и установить музыку (30 MB).",
+ "fontSize": 32,
+ "textCentered": true,
+ "color": [1.0, 1.0, 1.0, 1.0]
+ },
+ {
+ "type": "TextView",
+ "name": "soundVolumeText",
+ "width": 500,
+ "height": 40,
+ "text": "Громкость звука: 80",
+ "fontSize": 32,
+ "textCentered": false,
+ "topAligned": true,
+ "paddingX": 10,
+ "color": [1.0, 1.0, 1.0, 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": "Настройки графики",
+ "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": "Назад",
+ "fontSize": 40,
+ "textCentered": true
+ }
+ ]
+ }
+}
diff --git a/src/AudioPlayerAsync.cpp b/src/AudioPlayerAsync.cpp
index 2f1b1bc..5feacb0 100644
--- a/src/AudioPlayerAsync.cpp
+++ b/src/AudioPlayerAsync.cpp
@@ -6,6 +6,13 @@
#include
#endif
+namespace FRG
+{
+ extern const char* CONST_MUSIC_ZIP_FILE;
+ extern const char* CONST_ZIP_FILE;
+
+}
+
#ifdef __EMSCRIPTEN__
AudioPlayerAsync::AudioPlayerAsync() {}
@@ -92,9 +99,10 @@ void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, in
}
if (!sound) {
-#ifdef __ANDROID__
+//#ifdef __ANDROID__
- std::vector buffer = FRG::readFile(filePath);
+ //Sounds are loaded from regular resource file
+ std::vector buffer = FRG::readFileFromZIP(filePath, FRG::CONST_ZIP_FILE);
if (buffer.empty()) {
FRG::logger() << "Failed to read sound file into buffer: " << filePath << std::endl;
@@ -108,9 +116,9 @@ void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, in
sound = Mix_LoadWAV_RW(rw, 1); // 1 means it will free rw automatically
//FRG::logger() << "sound = Mix_LoadWAV_RW(rw, 1); just fired " << std::endl;
-#else
- sound = Mix_LoadWAV(filePath.c_str());
-#endif
+//#else
+// sound = Mix_LoadWAV(filePath.c_str());
+//#endif
if (!sound) {
FRG::logger() << "Failed to load sound " << filePath << ": " << Mix_GetError() << std::endl;
return;
@@ -144,6 +152,10 @@ void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
currentTrack = filePath;
if (!musicEnabled_) return;
+ if ((strlen(FRG::CONST_MUSIC_ZIP_FILE)>0) && !FRG::zipFileExists(FRG::CONST_MUSIC_ZIP_FILE)) {
+ return;
+ }
+
auto task = [this, filePath, loops]() {
Mix_HaltMusic();
if (currentMusic_) {
@@ -153,17 +165,17 @@ void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
currentMusicData_.clear();
Mix_Music* music = nullptr;
-#ifdef __ANDROID__
- currentMusicData_ = FRG::readFile(filePath);
+//#ifdef __ANDROID__
+ currentMusicData_ = FRG::readFileFromZIP(filePath, FRG::CONST_MUSIC_ZIP_FILE);
if (!currentMusicData_.empty()) {
SDL_RWops* rw = SDL_RWFromMem(currentMusicData_.data(), currentMusicData_.size());
if (rw) {
music = Mix_LoadMUS_RW(rw, 1);
}
}
-#else
- music = Mix_LoadMUS(filePath.c_str());
-#endif
+//#else
+// music = Mix_LoadMUS(filePath.c_str());
+//#endif
if (!music) {
FRG::logger() << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
@@ -268,11 +280,30 @@ void AudioPlayerAsync::setSoundVolume(int volume) {
}
void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fadeDurationMs, int loops) {
+ FRG::logger() << "crossFadeMusicAsync called step 1" << std::endl;
+
if (!initialized) return;
+ FRG::logger() << "crossFadeMusicAsync called step 1.1" << std::endl;
+
+ if ((strlen(FRG::CONST_MUSIC_ZIP_FILE) > 0) && !FRG::zipFileExists(FRG::CONST_MUSIC_ZIP_FILE)) {
+ FRG::logger() << "crossFadeMusicAsync called step 1.2" << std::endl;
+ return;
+ }
+ FRG::logger() << "crossFadeMusicAsync called step 1.3" << std::endl;
+
if (filePath == currentTrack) return;
+ FRG::logger() << "crossFadeMusicAsync called step 1.4" << std::endl;
+
currentTrack = filePath;
if (!musicEnabled_) return;
+ FRG::logger() << "crossFadeMusicAsync called step 2" << std::endl;
+
+
+
+
+ FRG::logger() << "crossFadeMusicAsync called step 3" << std::endl;
+
auto task = [this, filePath, fadeDurationMs, loops]() {
#ifdef __EMSCRIPTEN__
Mix_HaltMusic();
@@ -287,17 +318,17 @@ void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fade
currentMusicData_.clear();
#endif
Mix_Music* music = nullptr;
-#ifdef __ANDROID__
- currentMusicData_ = FRG::readFile(filePath);
+//#ifdef __ANDROID__
+ currentMusicData_ = FRG::readFileFromZIP(filePath, FRG::CONST_MUSIC_ZIP_FILE);
if (!currentMusicData_.empty()) {
SDL_RWops* rw = SDL_RWFromMem(currentMusicData_.data(), currentMusicData_.size());
if (rw) {
music = Mix_LoadMUS_RW(rw, 1);
}
}
-#else
- music = Mix_LoadMUS(filePath.c_str());
-#endif
+//#else
+// music = Mix_LoadMUS(filePath.c_str());
+//#endif
if (!music) {
FRG::logger() << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
return;
diff --git a/src/Character.cpp b/src/Character.cpp
index 49c4ee3..dffe04e 100644
--- a/src/Character.cpp
+++ b/src/Character.cpp
@@ -438,11 +438,11 @@ void Character::update(int64_t deltaMs, const CharacterResolver& resolver) {
if (audioPlayer) {
if (state.isPlayer)
{
- audioPlayer->playSoundAsync("audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg");
+ audioPlayer->playSoundAsync("resources/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg");
}
else
{
- audioPlayer->playSoundAsync("audio/13925__adcbicycle__21.ogg");
+ audioPlayer->playSoundAsync("resources/audio/13925__adcbicycle__21.ogg");
}
}
}
diff --git a/src/Environment.cpp b/src/Environment.cpp
index 4bdf8e1..158d72f 100644
--- a/src/Environment.cpp
+++ b/src/Environment.cpp
@@ -23,7 +23,7 @@ int Environment::height = CONST_DEFAULT_HEIGHT;
float Environment::zoom = DEFAULT_ZOOM;
-bool Environment::enableLogging = false;
+bool Environment::enableLogging = true;
SDL_Window* Environment::window = nullptr;
diff --git a/src/Game.cpp b/src/Game.cpp
index 2d22473..99f38ad 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -36,8 +36,10 @@ namespace FRG
#ifdef EMSCRIPTEN
const char* CONST_ZIP_FILE = "resources.zip";
+ const char* CONST_MUSIC_ZIP_FILE = "music.zip";
#else
const char* CONST_ZIP_FILE = "";
+ const char* CONST_MUSIC_ZIP_FILE = "";
#endif
float x = 0;
@@ -47,25 +49,29 @@ namespace FRG
#ifdef EMSCRIPTEN
Game* Game::s_instance = nullptr;
+ // Коллбэк успешной загрузки resources.zip
void Game::onResourcesZipLoaded(unsigned /*handle*/, void* /*userData*/, const char* /*filename*/) {
- logger() << "Resources.zip loaded successfully--0" << std::endl;
+ logger() << "Resources.zip downloaded successfully to VFS" << std::endl;
+
+ // Синхронизируем MemFS -> IndexedDB, чтобы файл сохранился между сессиями браузера
+ FRG::emscriptenSyncFs();
+
if (s_instance) {
s_instance->resourcesDownloadProgress = 1.0f;
- // 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();
}
}
- void Game::onResourcesZipError(unsigned /*handle*/, void* /*userData*/, int /*httpStatus*/) {
- logger() << "Failed to download resources.zip" << std::endl;
+ void Game::onResourcesZipError(unsigned /*handle*/, void* /*userData*/, int httpStatus) {
+ logger() << "Failed to download resources.zip, HTTP status: " << httpStatus << std::endl;
+ // Можно обработать ошибку или предложить пользователю перезагрузить страницу
}
void Game::onResourcesZipProgress(unsigned /*handle*/, void* /*userData*/, int percentComplete) {
if (s_instance)
s_instance->resourcesDownloadProgress = percentComplete / 100.0f;
}
+
#endif
Location* Game::currentLocation() const {
@@ -133,12 +139,53 @@ namespace FRG
loadingMesh.RefreshVBO();
#ifdef EMSCRIPTEN
- // Asynchronously download resources.zip; setupPart2() is called on completion.
- // The loading screen stays visible until the download finishes.
s_instance = this;
- logger() << "Load resurces step 1-" << std::endl;
- emscripten_async_wget2("resources.zip", "resources.zip", "GET", nullptr, nullptr,
- onResourcesZipLoaded, onResourcesZipError, onResourcesZipProgress);
+
+ std::string resPath = FRG::getPersistentResourcePath("resources.zip");
+ std::string musicPath = FRG::getPersistentResourcePath("music.zip");
+
+ // 1. Проверяем ресурсы (resources.zip)
+ if (FRG::isZipValid(resPath)) {
+ logger() << "[Load] Valid resources.zip found in IndexedDB cache: " << resPath << std::endl;
+ resourcesDownloadProgress = 1.0f;
+
+ // Архив уже есть и цел — переходим сразу к разбору/загрузке
+ setupPart2();
+ }
+ else {
+ logger() << "[Load] Resources.zip missing or corrupt. Downloading to: " << resPath << std::endl;
+
+ // Скачиваем сразу в постоянный путь /offline/resources.zip
+ emscripten_async_wget2(
+ "resources.zip", // URL на сервере
+ resPath.c_str(), // Назначение в VFS Emscripten
+ "GET",
+ nullptr,
+ nullptr,
+ onResourcesZipLoaded,
+ onResourcesZipError,
+ onResourcesZipProgress
+ );
+ }
+ /*
+ // 2. Проверяем музыку (music.zip)
+ if (FRG::isZipValid(musicPath)) {
+ logger() << "[Load] Valid music.zip found in IndexedDB cache." << std::endl;
+ }
+ else {
+ logger() << "[Load] Music.zip missing or corrupt. Downloading to: " << musicPath << std::endl;
+
+ emscripten_async_wget2(
+ "music.zip",
+ musicPath.c_str(),
+ "GET",
+ nullptr,
+ nullptr,
+ onMusicZipLoaded,
+ onMusicZipError,
+ onMusicZipProgress
+ );
+ }*/
#else
// Builds the loadSteps queue (cheap, no GL work); the actual loading
// happens one step per frame in Game::update() so the window keeps
@@ -299,12 +346,12 @@ namespace FRG
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");
+ audioPlayer->playSoundAsync("resources/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");
+ audioPlayer->playSoundAsync("resources/audio/831642__ienba__generic-game-pick-up.ogg");
};
// Wire phone dialogue start function so MenuManager can trigger dialogues.
@@ -344,7 +391,8 @@ namespace FRG
performResetToInitialState();
gameState.currentLocationName = "location_dorm";
currentLocation()->scriptEngine.callLocationEnterCallback();
- this->audioPlayer->crossFadeMusicAsync("audio/obshaga.ogg");
+ this->audioPlayer->crossFadeMusicAsync("music/obshaga.ogg");
+ menuManager.lastMusicTrack = "music/obshaga.ogg";
});
};
@@ -392,7 +440,8 @@ namespace FRG
loadSteps.push([this]() {
menuManager.loadSettings();
- audioPlayer->crossFadeMusicAsync("audio/main menu final.ogg");
+ audioPlayer->crossFadeMusicAsync("music/main menu final.ogg");
+ menuManager.lastMusicTrack = "music/main menu final.ogg";
});
@@ -465,7 +514,21 @@ namespace FRG
void Game::performResetToInitialState() {
const std::string path = "resources/config/start_state.json";
- const std::string content = FRG::readTextFile(path);
+
+ std::vector fileData;
+ if (strlen(CONST_ZIP_FILE) > 0)
+ {
+ fileData = readFileFromZIP(path, CONST_ZIP_FILE);
+ }
+ else
+ {
+ fileData = readFile(path);
+ }
+
+ std::string content(fileData.begin(), fileData.end());
+
+ //const std::vector contentVector = FRG::readFileFromZIP();
+ //const std::string content = FRG::readTextFile(path);
if (!content.empty()) {
try {
nlohmann::json root = nlohmann::json::parse(content);
@@ -485,10 +548,14 @@ namespace FRG
rearmDormOneShotCallbacks();
return;
} catch (const std::exception& e) {
- std::cerr << "[reset] Failed to parse " << path << ": " << e.what()
+ logger() << "[reset] Failed to parse " << path << ": " << e.what()
<< " -- falling back to full rebuild" << std::endl;
}
}
+ else
+ {
+ logger() << "File " << path << " is empty -- falling back to full rebuild" << std::endl;
+ }
performFullReset();
}
@@ -616,10 +683,12 @@ namespace FRG
}
gameState.locations["uni_interior"]->requestDarklandsPlayBattleMusic = [this]() {
- audioPlayer->crossFadeMusicFromPositionAsync("audio/bishkek fight.ogg");
+ audioPlayer->crossFadeMusicFromPositionAsync("music/bishkek fight.ogg");
+ menuManager.lastMusicTrack = "music/bishkek fight.ogg";
};
gameState.locations["uni_interior"]->requestDarklandsPlayNormalMusic = [this]() {
- audioPlayer->crossFadeMusicFromPositionAsync("audio/bishkek fight calm.ogg");
+ audioPlayer->crossFadeMusicFromPositionAsync("music/bishkek fight calm.ogg");
+ menuManager.lastMusicTrack = "music/bishkek fight calm.ogg";
};
}
@@ -1679,7 +1748,7 @@ namespace FRG
return false;
}
- audioPlayer->playSoundAsync("audio/459618__n-lerche__blow.ogg");
+ audioPlayer->playSoundAsync("resources/audio/459618__n-lerche__blow.ogg");
if (darklandsFlashActive) return false;
darklandsFlashActive = true;
darklandsFlashFadingIn = true;
@@ -1715,7 +1784,8 @@ namespace FRG
currentLocation()->scriptEngine.callTriggerNightEnterCallback();
}
- audioPlayer->crossFadeMusicAsync("audio/bishkek night.ogg");
+ audioPlayer->crossFadeMusicAsync("music/bishkek night.ogg");
+ menuManager.lastMusicTrack = "music/bishkek night.ogg";
} else {
gameState.isDarklands = !gameState.isDarklands;
@@ -1725,11 +1795,13 @@ namespace FRG
if (gameState.isDarklands)
{
- audioPlayer->crossFadeMusicAsync("audio/bishkek fight calm.ogg");
+ audioPlayer->crossFadeMusicAsync("music/bishkek fight calm.ogg");
+ menuManager.lastMusicTrack = "music/bishkek fight calm.ogg";
}
else
{
- audioPlayer->crossFadeMusicAsync("audio/bishkek night.ogg");
+ audioPlayer->crossFadeMusicAsync("music/bishkek night.ogg");
+ menuManager.lastMusicTrack = "music/bishkek night.ogg";
}
if (currentLocation()) {
@@ -1845,20 +1917,24 @@ namespace FRG
if (gameState.isDarklands)
{
- audioPlayer->crossFadeMusicAsync("audio/bishkek fight calm.ogg");
+ audioPlayer->crossFadeMusicAsync("music/bishkek fight calm.ogg");
+ menuManager.lastMusicTrack = "music/bishkek fight calm.ogg";
}
else if (gameState.isNight)
{
- audioPlayer->crossFadeMusicAsync("audio/bishkek night.ogg");
+ audioPlayer->crossFadeMusicAsync("music/bishkek night.ogg");
+ menuManager.lastMusicTrack = "music/bishkek night.ogg";
}
else
{
if (gameState.currentLocationName == "location_dorm") {
- audioPlayer->crossFadeMusicAsync("audio/obshaga.ogg");
+ audioPlayer->crossFadeMusicAsync("music/obshaga.ogg");
+ menuManager.lastMusicTrack = "music/obshaga.ogg";
}
else
{
- audioPlayer->crossFadeMusicAsync("audio/bishkek univer day.ogg");
+ audioPlayer->crossFadeMusicAsync("music/bishkek univer day.ogg");
+ menuManager.lastMusicTrack = "music/bishkek univer day.ogg";
}
}
}
diff --git a/src/Game.h b/src/Game.h
index d45089e..b95cd43 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -82,6 +82,10 @@ namespace FRG {
MenuManager menuManager;
void activateSlowMoEffect();
+
+#ifdef EMSCRIPTEN
+ static Game* s_instance;
+#endif
private:
// Unified pointer handling: mouse-left and a single touch share one path.
@@ -160,7 +164,6 @@ namespace FRG {
int countNonUiPointers() const;
#ifdef EMSCRIPTEN
- static Game* s_instance;
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);
diff --git a/src/GameState.cpp b/src/GameState.cpp
index 409379c..13de307 100644
--- a/src/GameState.cpp
+++ b/src/GameState.cpp
@@ -83,29 +83,40 @@ void GameState::save(nlohmann::json& out) const
void GameState::load(const nlohmann::json& in)
{
+ //logger() << "GameState::load step 1" << std::endl;
+
// --- World ---
currentLocationName = in.value("currentLocationName", currentLocationName);
isDarklands = in.value("isDarklands", isDarklands);
isNight = in.value("isNight", isNight);
isDawn = in.value("isDawn", isDawn);
+ //() << "GameState::load step 2" << std::endl;
+
+
// --- Player resources ---
playerHp = in.value("playerHp", playerHp);
playerMaxHp = in.value("playerMaxHp", playerMaxHp);
money = in.value("money", money);
+ //logger() << "GameState::load step 3" << std::endl;
+
// --- Script globals ---
globalInts.clear();
if (in.contains("globalInts") && in["globalInts"].is_object()) {
for (const auto& [k, v] : in["globalInts"].items())
globalInts[k] = v.get();
}
+ //logger() << "GameState::load step 4" << std::endl;
+
globalFloats.clear();
if (in.contains("globalFloats") && in["globalFloats"].is_object()) {
for (const auto& [k, v] : in["globalFloats"].items())
globalFloats[k] = v.get();
}
+ //logger() << "GameState::load step 5" << std::endl;
+
// --- Tutorial ---
tutorialStep = static_cast(in.value("tutorialStep", static_cast(tutorialStep)));
uniIntTutorialState = static_cast(in.value("uniIntTutorialState", static_cast(uniIntTutorialState)));
@@ -115,15 +126,21 @@ void GameState::load(const nlohmann::json& in)
tutorialJournalScreenOpened = in.value("tutorialJournalScreenOpened", tutorialJournalScreenOpened);
tutorialNeedOpenTaxiScreen = in.value("tutorialNeedOpenTaxiScreen", tutorialNeedOpenTaxiScreen);
+ //logger() << "GameState::load step 6" << std::endl;
+
// --- Phone / chat ---
if (in.contains("chatUnread") && in["chatUnread"].is_array()) {
for (int i = 0; i < 3 && i < static_cast(in["chatUnread"].size()); ++i)
chatUnread[i] = in["chatUnread"][i].get();
}
+ //logger() << "GameState::load step 7" << std::endl;
+
if (in.contains("chatPreviewMsg") && in["chatPreviewMsg"].is_array()) {
for (int i = 0; i < 3 && i < static_cast(in["chatPreviewMsg"].size()); ++i)
chatPreviewMsg[i] = in["chatPreviewMsg"][i].get();
}
+ //logger() << "GameState::load step 8" << std::endl;
+
if (in.contains("chatHistory") && in["chatHistory"].is_array()) {
for (int i = 0; i < 3 && i < static_cast(in["chatHistory"].size()); ++i) {
chatHistory[i].clear();
@@ -136,26 +153,38 @@ void GameState::load(const nlohmann::json& in)
}
}
}
+
}
+ //logger() << "GameState::load step 8" << std::endl;
// --- Inventory ---
if (in.contains("inventory")) inventory.load(in["inventory"]);
+ //logger() << "GameState::load step 9" << std::endl;
+
// --- Quest journal ---
if (in.contains("questJournal")) questJournal.load(in["questJournal"]);
+
+ logger() << "GameState::load step 10" << std::endl;
// --- Locations ---
if (in.contains("locations") && in["locations"].is_object()) {
for (const auto& [name, locJson] : in["locations"].items()) {
+ //logger() << "GameState::load step 10.1: " << name << std::endl;
+
auto it = locations.find(name);
if (it != locations.end() && it->second) {
it->second->load(locJson);
}
}
}
+ //logger() << "GameState::load step 11" << std::endl;
+
// --- Taxi ---
taxiIsCalled = in.value("taxiIsCalled", taxiIsCalled);
+ //logger() << "GameState::load step 12" << std::endl;
+
}
} // namespace FRG
diff --git a/src/Location.cpp b/src/Location.cpp
index fe47f60..de5812f 100644
--- a/src/Location.cpp
+++ b/src/Location.cpp
@@ -2147,10 +2147,13 @@ namespace FRG
void Location::load(const nlohmann::json& in)
{
+ //logger() << "Location::load step 1" << std::endl;
+
// Location-level state
if (in.contains("locationState")) {
state.load(in["locationState"]);
}
+ //logger() << "Location::load step 2" << std::endl;
// Re-sync navigation pointer from restored index
if (state.activeNavigationIndex >= 0 &&
@@ -2159,6 +2162,8 @@ namespace FRG
navigation = &navigationMaps[state.activeNavigationIndex];
}
+ //logger() << "Location::load step 3" << std::endl;
+
// Restore trigger zone runtime state (by id)
if (in.contains("triggerZoneState") && in["triggerZoneState"].is_object()) {
const auto& tzs = in["triggerZoneState"];
@@ -2169,6 +2174,7 @@ namespace FRG
}
}
}
+ //logger() << "Location::load step 4" << std::endl;
// Restore teleport zone runtime state (by id)
if (in.contains("teleportZoneState") && in["teleportZoneState"].is_object()) {
@@ -2180,11 +2186,15 @@ namespace FRG
}
}
+ //logger() << "Location::load step 5" << std::endl;
+
// Player character state
if (player && in.contains("playerState")) {
player->state.load(in["playerState"]);
}
+ //logger() << "Location::load step 6" << std::endl;
+
// NPC states
if (in.contains("npcStates") && in["npcStates"].is_array()) {
const auto& arr = in["npcStates"];
@@ -2195,6 +2205,8 @@ namespace FRG
}
}
+ //logger() << "Location::load step 7" << std::endl;
+
// Interactive object states
if (in.contains("interactiveObjectStates") && in["interactiveObjectStates"].is_array()) {
const auto& arr = in["interactiveObjectStates"];
@@ -2202,17 +2214,23 @@ namespace FRG
interactiveObjects[i].state.load(arr[i]);
}
}
+ //logger() << "Location::load step 8" << std::endl;
// Dialogue runtime state
if (in.contains("dialogueState")) {
dialogueSystem.loadDialogueState(in["dialogueState"]);
}
+ //logger() << "Location::load step 9" << std::endl;
+
// Lua globals
if (in.contains("scriptGlobals")) {
scriptEngine.loadScriptGlobals(in["scriptGlobals"]);
}
+ //logger() << "Location::load step 10" << std::endl;
+
+
// Re-wire in-flight, one-shot Lua callbacks (walk-arrival / animation-complete)
// that were serialized by name. Must run after scriptEngine has already run the
// location's script (guaranteed: Location::setup() always runs before load())
@@ -2220,16 +2238,24 @@ namespace FRG
if (player && !player->state.onArrivedCallbackName.empty()) {
scriptEngine.rehydrateNamedCallback(player->state.onArrivedCallbackName, player->state.onArrivedCallback);
}
+
+ //logger() << "Location::load step 11" << std::endl;
+
for (auto& npc : npcs) {
if (npc && !npc->state.onArrivedCallbackName.empty()) {
scriptEngine.rehydrateNamedCallback(npc->state.onArrivedCallbackName, npc->state.onArrivedCallback);
}
}
+ //logger() << "Location::load step 12" << std::endl;
+
for (auto& intObj : interactiveObjects) {
if (intObj.state.isAnimating && intObj.state.animTask && !intObj.state.animTask->onCompleteName.empty()) {
scriptEngine.rehydrateNamedCallback(intObj.state.animTask->onCompleteName, intObj.state.animTask->onComplete);
}
}
+
+ //logger() << "Location::load step 13" << std::endl;
+
}
} // namespace FRG
diff --git a/src/LocationState.cpp b/src/LocationState.cpp
index 3de601a..d177a19 100644
--- a/src/LocationState.cpp
+++ b/src/LocationState.cpp
@@ -107,6 +107,8 @@ void LocationState::save(nlohmann::json& out) const
void LocationState::load(const nlohmann::json& in)
{
+ //logger() << "LocationState::load step 1" << std::endl;
+
cameraAzimuth = in.value("cameraAzimuth", cameraAzimuth);
cameraInclination = in.value("cameraInclination", cameraInclination);
@@ -122,6 +124,8 @@ void LocationState::load(const nlohmann::json& in)
tutorialInteractiveObjectsLocked = in.value("tutorialInteractiveObjectsLocked", false);
+ //logger() << "LocationState::load step 2" << std::endl;
+
nlohmann::json taxiJson = in["taxi"];
taxiArrivedCallbackCalled = taxiJson.value("taxiArrivedCallbackCalled", false);
@@ -136,20 +140,18 @@ void LocationState::load(const nlohmann::json& in)
taxiCarJson.value("positionZ", 0.f)
);
+ //logger() << "LocationState::load step 3" << std::endl;
+
taxiCar.rotation = taxiCarJson.value("rotation", 0.f);
taxiCar.velocity = taxiCarJson.value("velocity", 0.f);
taxiCar.steeringAngle = taxiCarJson.value("steeringAngle", 0.f);
- /*taxiCar.acceleration = taxiCarJson.value("acceleration", 20.f);
- taxiCar.friction = taxiCarJson.value("friction", 6.f);
- taxiCar.maxSpeed = taxiCarJson.value("maxSpeed", 24.f);
- taxiCar.maxReverseSpeed = taxiCarJson.value("maxReverseSpeed", 8.f);
- taxiCar.turnRate = taxiCarJson.value("turnRate", 1.8f);
- taxiCar.maxSteerAngle = taxiCarJson.value("maxSteerAngle", 0.6f);*/
taxiCar.mode = static_cast(taxiCarJson.value("mode", 0));
+ //logger() << "LocationState::load step 4" << std::endl;
+
taxiCar.waypoints.clear();
if (taxiCarJson.contains("waypoints") && taxiCarJson["waypoints"].is_array()) {
for (int i = 0; i < static_cast(taxiCarJson["waypoints"].size()); ++i)
@@ -160,6 +162,8 @@ void LocationState::load(const nlohmann::json& in)
));
}
+ //logger() << "LocationState::load step 5" << std::endl;
+
taxiCar.currentWaypoint = taxiCarJson.value("currentWaypoint", 0);
taxiCar.waypointReachRadius = taxiCarJson.value("waypointReachRadius", 3.f);
taxiCar.wheelAngle = taxiCarJson.value("wheelAngle", 0.f);
@@ -167,6 +171,8 @@ void LocationState::load(const nlohmann::json& in)
//--------- Taxi initial state -----
+ //logger() << "LocationState::load step 6" << std::endl;
+
nlohmann::json taxiCarInitialStateJson = taxiJson["taxiCarInitialState"];
taxiCarInitialState.position = Eigen::Vector3f(
@@ -180,15 +186,12 @@ void LocationState::load(const nlohmann::json& in)
taxiCarInitialState.velocity = taxiCarInitialStateJson.value("velocity", 0.f);
taxiCarInitialState.steeringAngle = taxiCarInitialStateJson.value("steeringAngle", 0.f);
- /*
- taxiCarInitialState.acceleration = taxiCarInitialStateJson.value("acceleration", 20.f);
- taxiCarInitialState.friction = taxiCarInitialStateJson.value("friction", 6.f);
- taxiCarInitialState.maxSpeed = taxiCarInitialStateJson.value("maxSpeed", 24.f);
- taxiCarInitialState.maxReverseSpeed = taxiCarInitialStateJson.value("maxReverseSpeed", 8.f);
- taxiCarInitialState.turnRate = taxiCarInitialStateJson.value("turnRate", 1.8f);
- taxiCarInitialState.maxSteerAngle = taxiCarInitialStateJson.value("maxSteerAngle", 0.6f);*/
+
taxiCarInitialState.mode = static_cast(taxiCarInitialStateJson.value("mode", 0));
+ //logger() << "LocationState::load step 7" << std::endl;
+
+
taxiCarInitialState.waypoints.clear();
if (taxiCarInitialStateJson.contains("waypoints") && taxiCarInitialStateJson["waypoints"].is_array()) {
for (int i = 0; i < static_cast(taxiCarInitialStateJson["waypoints"].size()); ++i)
@@ -199,11 +202,14 @@ void LocationState::load(const nlohmann::json& in)
));
}
+ //logger() << "LocationState::load step 8" << std::endl;
+
taxiCarInitialState.currentWaypoint = taxiCarInitialStateJson.value("currentWaypoint", 0);
taxiCarInitialState.waypointReachRadius = taxiCarInitialStateJson.value("waypointReachRadius", 3.f);
taxiCarInitialState.wheelAngle = taxiCarInitialStateJson.value("wheelAngle", 0.f);
+
taxiCarWaypoints.clear();
if (taxiJson.contains("taxiDefaultWaypoints") && taxiJson["taxiDefaultWaypoints"].is_array()) {
for (int i = 0; i < static_cast(taxiJson["taxiDefaultWaypoints"].size()); ++i)
@@ -214,6 +220,7 @@ void LocationState::load(const nlohmann::json& in)
));
}
+ //logger() << "LocationState::load step 9" << std::endl;
taxiCarTriggerPosition = Eigen::Vector3f(
taxiJson.value("taxiCarTriggerPositionX", 0.f),
@@ -221,6 +228,9 @@ void LocationState::load(const nlohmann::json& in)
taxiJson.value("taxiCarTriggerPositionZ", 0.f)
);
+ //logger() << "LocationState::load step 10" << std::endl;
+
+
}
} // namespace FRG
diff --git a/src/LocationState.h b/src/LocationState.h
index e81ba1f..7f680a0 100644
--- a/src/LocationState.h
+++ b/src/LocationState.h
@@ -31,7 +31,7 @@ struct LocationState : public ISaveable {
NpcCar taxiCarInitialState;
NpcCar taxiCar;
bool taxiArrivedCallbackCalled = false;
- Vector3f taxiCarTriggerPosition;
+ Vector3f taxiCarTriggerPosition = Vector3f(0,0,0);
std::vector taxiCarWaypoints;
// (trigger/teleport zone runtime flags are serialised by id directly in Location::save/load)
diff --git a/src/MenuManager.cpp b/src/MenuManager.cpp
index d4cd7c1..976198b 100644
--- a/src/MenuManager.cpp
+++ b/src/MenuManager.cpp
@@ -10,8 +10,16 @@
#include
#include
+#ifdef __EMSCRIPTEN__
+#include
+#endif
+
namespace FRG {
+#ifdef __EMSCRIPTEN__
+ //extern Game* Game::s_instance;
+#endif
+
// Localization
static const std::string EMPTY_LANGUAGE_RU = u8"(пусто)";
static const std::string EMPTY_LANGUAGE_EN = "(empty)";
@@ -19,6 +27,11 @@ namespace FRG {
static const std::string BACK_TO_MAIN_MENU_RU = u8"Вернуться в меню";
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_PROGRESS_PREFIX_RU = u8"Загрузка музыки: ";
+ //static const std::string MUSIC_DOWNLOAD_COMPLETED_RU = u8"Музыка успешно загружена.";
+
static int questStatusPriority(Quest::QuestStatus status) {
switch (status) {
@@ -112,6 +125,78 @@ namespace FRG {
}
}
+#ifdef __EMSCRIPTEN__
+ // Коллбэк успешной загрузки music.zip
+ void MenuManager::onMusicZipLoaded(unsigned /*handle*/, void* /*userData*/, const char* /*filename*/) {
+ logger() << "File music.zip downloaded successfully to VFS" << std::endl;
+ FRG::emscriptenSyncFs();
+ if (Game::s_instance != nullptr)
+ {
+ Game::s_instance->menuManager.musicDownloadProgress = 1.f;
+ Game::s_instance->menuManager.musicDownloadStarted = false;
+ Game::s_instance->menuManager.musicDownloadError = false;
+ logger() << "onMusicZipLoaded step 2: " << Game::s_instance->menuManager.lastMusicTrack << std::endl;
+ if (Game::s_instance->menuManager.lastMusicTrack != "")
+ {
+ logger() << "onMusicZipLoaded step 3" << std::endl;
+
+ Game::s_instance->menuManager.audioPlayer_.crossFadeMusicAsync(Game::s_instance->menuManager.lastMusicTrack);
+ }
+ logger() << "onMusicZipLoaded step 4" << std::endl;
+
+ if (Game::s_instance->menuManager.playerInSettingsNoMusic)
+ {
+ if (Game::s_instance->menuManager.playerInSettingsNoMusicGraphics)
+ {
+ Game::s_instance->menuManager.uiManager.popMenu();
+ Game::s_instance->menuManager.uiManager.popMenu();
+ Game::s_instance->menuManager.showSettingsScreen();
+ Game::s_instance->menuManager.showSettingsGraphicsScreen();
+ Game::s_instance->menuManager.uiManager.updateAllLayouts();
+ Game::s_instance->menuManager.topUiManager.updateAllLayouts();
+ }
+ else
+ {
+ Game::s_instance->menuManager.uiManager.popMenu();
+ Game::s_instance->menuManager.showSettingsScreen();
+ Game::s_instance->menuManager.uiManager.updateAllLayouts();
+ Game::s_instance->menuManager.topUiManager.updateAllLayouts();
+ }
+ }
+ Game::s_instance->menuManager.playerInSettingsNoMusic = false;
+ Game::s_instance->menuManager.playerInSettingsNoMusicGraphics = false;
+ //Game::s_instance->menuManager.uiManager.setText("musicDownloadButton", musicDownloadCompleted);
+ }
+ }
+
+ void MenuManager::onMusicZipError(unsigned /*handle*/, void* /*userData*/, int httpStatus) {
+ logger() << "Failed to download music.zip, HTTP status: " << httpStatus << std::endl;
+ if (Game::s_instance != nullptr)
+ {
+ std::string musicDownloadError = MUSIC_DOWNLOAD_ERROR_RU;
+
+ Game::s_instance->menuManager.musicDownloadStarted = false;
+ Game::s_instance->menuManager.musicDownloadError = true;
+ Game::s_instance->menuManager.uiManager.setText("musicDownloadButton",
+ musicDownloadError);
+
+ }
+ }
+
+ void MenuManager::onMusicZipProgress(unsigned /*handle*/, void* /*userData*/, int percentComplete) {
+ // Прогресс для музыки при необходимости
+ if (Game::s_instance != nullptr)
+ {
+ std::string musicDownloadProgressPrefix = MUSIC_DOWNLOAD_PROGRESS_PREFIX_RU;
+
+ Game::s_instance->menuManager.musicDownloadProgress = percentComplete / 100.0f;
+ Game::s_instance->menuManager.uiManager.setText("musicDownloadButton",
+ musicDownloadProgressPrefix + std::to_string(percentComplete) + "%");
+
+ }
+ }
+#endif
+
MenuManager::MenuManager(Renderer& iRenderer, GameState& gameState, AudioPlayerAsync& audioPlayer) :
renderer(iRenderer),
gameState_(gameState),
@@ -254,7 +339,13 @@ namespace FRG {
creditsScreenRoot = loadLocalizedUi("resources/w/ui/screen_credits.json", zipFile);
portraitCreditsScreenRoot = loadLocalizedUi("resources/w/ui/portrait_screen_credits.json", zipFile);
+
+#ifdef __EMSCRIPTEN__
+ settingsScreenNoMusicWebRoot = loadLocalizedUi("resources/w/ui/screen_settings_nomusic_web.json", zipFile);
+#endif
settingsScreenRoot = loadLocalizedUi("resources/w/ui/screen_settings.json", zipFile);
+
+
settingsGraphicsScreenRoot = loadLocalizedUi("resources/w/ui/screen_settings_graphics.json", zipFile);
languageScreenRoot = loadLocalizedUi("resources/w/ui/screen_language.json", zipFile);
loadSavedGameScreenRoot = loadLocalizedUi("resources/w/ui/screen_load.json", zipFile);
@@ -375,7 +466,8 @@ namespace FRG {
void MenuManager::showMainMenu() {
gameState_.currentLocationName.clear();
- audioPlayer_.crossFadeMusicAsync("audio/main menu final.ogg");
+ audioPlayer_.crossFadeMusicAsync("music/main menu final.ogg");
+ lastMusicTrack = "music/main menu final.ogg";
uiState_ = GameUiState::MainMenu;
uiManager.clearMenuStack();
@@ -386,36 +478,46 @@ namespace FRG {
uiManager.setTextButtonCallback("menuStartButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
enterGameplay();
});
uiManager.setTextButtonCallback("menuAboutButton", [this](const std::string&) {
showAboutScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("menuCreditsButton", [this](const std::string&) {
showCreditsScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("menuSettingsButton", [this](const std::string&) {
+#ifdef __EMSCRIPTEN__
+ std::string musicPath = FRG::getPersistentResourcePath("music.zip");
+ if (FRG::isZipValid(musicPath)) {
+ showSettingsScreen();
+ }
+ else {
+ showSettingsNoMusicWebScreen();
+ }
+#else
showSettingsScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+#endif
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("menuLanguageButton", [this](const std::string&) {
showLanguageScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("menuLoadButton", [this](const std::string&) {
showLoadGameScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
#if !defined(EMSCRIPTEN) && !defined(__ANDROID__)
uiManager.setTextButtonCallback("menuExitButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
Environment::exitGameLoop = true;
});
#else
@@ -426,7 +528,8 @@ namespace FRG {
void MenuManager::showMenuAfterGameCompleted() {
//gameState_.currentLocationName.clear();
- audioPlayer_.crossFadeMusicAsync("audio/main menu final.ogg");
+ audioPlayer_.crossFadeMusicAsync("music/main menu final.ogg");
+ lastMusicTrack = "music/main menu final.ogg";
uiState_ = GameUiState::MainMenu;
uiManager.clearMenuStack();
@@ -437,36 +540,36 @@ namespace FRG {
uiManager.setTextButtonCallback("menuStartButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
enterGameplay();
});
uiManager.setTextButtonCallback("menuAboutButton", [this](const std::string&) {
showAboutScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("menuCreditsButton", [this](const std::string&) {
showCreditsScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("menuSettingsButton", [this](const std::string&) {
showSettingsScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("menuLanguageButton", [this](const std::string&) {
showLanguageScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("menuLoadButton", [this](const std::string&) {
showLoadGameScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
#ifndef EMSCRIPTEN
uiManager.setTextButtonCallback("menuExitButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
Environment::exitGameLoop = true;
});
#else
@@ -479,21 +582,21 @@ namespace FRG {
uiManager.setTextButtonCallback("aboutBackButton", [this](const std::string&) {
uiManager.popMenu();
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("steamButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
SDL_OpenURL("https://store.steampowered.com/app/4801010/");
});
uiManager.setTextButtonCallback("telegramButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
SDL_OpenURL("https://telegram.me/fishrungames");
});
uiManager.setTextButtonCallback("discordButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
SDL_OpenURL("https://discord.gg/S8jgGHXjw");
});
}
@@ -503,29 +606,29 @@ namespace FRG {
uiManager.setTextButtonCallback("aboutBackButton", [this](const std::string&) {
uiManager.popMenu();
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("steamButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
SDL_OpenURL("https://store.steampowered.com/app/4801010/");
});
#ifdef __ANDROID__
uiManager.setTextButtonCallback("websiteButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
SDL_OpenURL("https://shadowoverbishkek.com/");
});
#endif
uiManager.setTextButtonCallback("telegramButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
SDL_OpenURL("https://telegram.me/fishrungames");
});
uiManager.setTextButtonCallback("discordButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
SDL_OpenURL("https://discord.gg/S8jgGHXjw");
});
}
@@ -561,7 +664,7 @@ namespace FRG {
isPlayerInCreditsMenu = false;
uiManager.popMenu();
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.updateAllLayouts();
@@ -577,12 +680,12 @@ namespace FRG {
saveSettings();
uiManager.popMenu();
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("graphicsButton", [this](const std::string&) {
showSettingsGraphicsScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
// Set initial slider positions before registering callbacks so
@@ -658,7 +761,7 @@ namespace FRG {
/*uiManager.setTextButtonColor("musicToggleButton", on
? std::array{ 0.2f, 0.9f, 0.2f, 1.0f }
: std::array{ 0.9f, 0.2f, 0.2f, 1.0f });*/
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("soundToggleButton", [this](const std::string&) {
audioPlayer_.setSoundEnabled(!audioPlayer_.isSoundEnabled());
@@ -667,7 +770,7 @@ namespace FRG {
/*uiManager.setTextButtonColor("soundToggleButton", on
? std::array{ 0.2f, 0.9f, 0.2f, 1.0f }
: std::array{ 0.9f, 0.2f, 0.2f, 1.0f });*/
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
}
@@ -676,9 +779,12 @@ namespace FRG {
uiManager.setTextButtonCallback("settingsBackButton", [this](const std::string&) {
saveSettings();
uiManager.popMenu();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
uiManager.updateAllLayouts();
topUiManager.updateAllLayouts();
+#ifdef __EMSCRIPTEN__
+ playerInSettingsNoMusicGraphics = false;
+#endif
});
@@ -718,7 +824,7 @@ namespace FRG {
uiManager.setTextButtonCallback("fullScreenToggleButton", [this, refreshFullscreenToggle](const std::string&) {
Environment::setFullscreen(!Environment::isFullscreen);
refreshFullscreenToggle();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
uiManager.updateAllLayouts();
topUiManager.updateAllLayouts();
});
@@ -728,27 +834,148 @@ namespace FRG {
bool newValue = !this->shadowsEnabled;
shadowMapSettingsChangedFunc(newValue);
refreshShadowToggle();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
}
+
+#ifdef __EMSCRIPTEN__
+ void MenuManager::showSettingsNoMusicWebScreen()
+ {
+ playerInSettingsNoMusic = true;
+ uiManager.pushMenuFromSavedRoot(settingsScreenNoMusicWebRoot);
+ uiManager.setTextButtonCallback("settingsBackButton", [this](const std::string&) {
+ saveSettings();
+ uiManager.popMenu();
+ uiManager.updateAllLayouts();
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ playerInSettingsNoMusic = false;
+ });
+
+ uiManager.setTextButtonCallback("graphicsButton", [this](const std::string&) {
+ playerInSettingsNoMusicGraphics = true;
+ showSettingsGraphicsScreen();
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ });
+
+ // Set initial slider positions before registering callbacks so
+ // setSliderValue does not fire an unregistered callback.
+ const float soundFrac = audioPlayer_.getSoundVolume() / 128.0f;
+ uiManager.setSliderValue("soundVolumeSlider", soundFrac);
+
+ // Localization
+ if (g_currentLanguage == Language::English)
+ {
+ soundVolumePrefix = "Sound volume: ";
+ soundToggleOn = "Sound: ON";
+ soundToggleOff = "Sound: OFF";
+ }
+ else if (g_currentLanguage == Language::Russian)
+ {
+ soundVolumePrefix = u8"Громкость звука: ";
+ soundToggleOn = u8"Звук: ВКЛ";
+ soundToggleOff = u8"Звук: ВЫКЛ";
+ }
+
+ static std::string musicDownloadErrorString = u8"Ошибка скачивания музыки";
+ static std::string musicDownloadNotStartedString = u8"Нажмите сюда, чтобы скачать\nи установить музыку (27 MB).";
+ static std::string musicDownloadProgressPrefix = u8"Загрузка музыки: ";
+
+ // musicDownloadStarted = false;
+ //musicDownloadError = false;
+
+ if (musicDownloadError)
+ {
+ uiManager.setText("musicDownloadButton", musicDownloadErrorString);
+
+ }
+ else
+ {
+ if (musicDownloadStarted == false)
+ {
+ uiManager.setText("musicDownloadButton", musicDownloadNotStartedString);
+ }
+ else
+ {
+ //uiManager.setText("musicDownloadButton", musicDownloadProgressString);
+ uiManager.setText("musicDownloadButton",
+ musicDownloadProgressPrefix + std::to_string(round(musicDownloadProgress*100)) + "%");
+ }
+ }
+
+ uiManager.setText("soundVolumeText",
+ soundVolumePrefix + std::to_string(audioPlayer_.getSoundVolume()));
+
+ uiManager.setSliderCallback("soundVolumeSlider",
+ [this](const std::string&, float value) {
+ const int vol = static_cast(value * 128.0f + 0.5f);
+ audioPlayer_.setSoundVolume(vol);
+ uiManager.setText("soundVolumeText",
+ soundVolumePrefix + std::to_string(vol));
+ });
+
+ // Music on/off toggle — helper to sync button text and color from current state
+ auto refreshSoundToggle = [this]() {
+ const bool on = audioPlayer_.isSoundEnabled();
+ uiManager.setTextButtonText("soundToggleButton", on ? soundToggleOn : soundToggleOff);
+ /*uiManager.setTextButtonColor("soundToggleButton", on
+ ? std::array{ 0.2f, 0.9f, 0.2f, 1.0f }
+ : std::array{ 0.9f, 0.2f, 0.2f, 1.0f });*/
+ };
+
+ refreshSoundToggle();
+
+
+ uiManager.setTextButtonCallback("soundToggleButton", [this](const std::string&) {
+ audioPlayer_.setSoundEnabled(!audioPlayer_.isSoundEnabled());
+ const bool on = audioPlayer_.isSoundEnabled();
+ uiManager.setTextButtonText("soundToggleButton", on ? soundToggleOn : soundToggleOff);
+ /*uiManager.setTextButtonColor("soundToggleButton", on
+ ? std::array{ 0.2f, 0.9f, 0.2f, 1.0f }
+ : std::array{ 0.9f, 0.2f, 0.2f, 1.0f });*/
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ });
+
+ uiManager.setTextButtonCallback("musicDownloadButton", [this](const std::string&) {
+ if (musicDownloadStarted) return; // Already downloading, ignore further clicks
+
+ musicDownloadStarted = true;
+ musicDownloadError = false;
+
+ std::string musicPath = FRG::getPersistentResourcePath("music.zip");
+
+ emscripten_async_wget2(
+ "music.zip",
+ musicPath.c_str(),
+ "GET",
+ nullptr,
+ nullptr,
+ onMusicZipLoaded,
+ onMusicZipError,
+ onMusicZipProgress
+ );
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ });
+ }
+#endif
+
void MenuManager::showLanguageScreen() {
uiManager.pushMenuFromSavedRoot(languageScreenRoot);
uiManager.setTextButtonCallback("languageBackButton", [this](const std::string&) {
saveSettings();
uiManager.popMenu();
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("languageRussianButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
FRG::g_currentLanguage = FRG::Language::Russian;
reloadLocalizedGameContent();
saveSettings();
});
uiManager.setTextButtonCallback("languageEnglishButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
FRG::g_currentLanguage = FRG::Language::English;
reloadLocalizedGameContent();
saveSettings();
@@ -810,7 +1037,7 @@ namespace FRG {
uiManager.setTextButtonCallback("loadBackButton", [this](const std::string&) {
uiManager.popMenu();
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
static const char* kSlotButtons[4] = {
@@ -837,7 +1064,7 @@ namespace FRG {
uiManager.setTextButtonText(kSlotButtons[i], label);
}
uiManager.setTextButtonCallback(kSlotButtons[i], [this, slot](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
if (onLoadGame) onLoadGame(slot);
enterGameplay(true);
});
@@ -850,7 +1077,7 @@ namespace FRG {
uiManager.setTextButtonCallback("saveBackButton", [this](const std::string&) {
uiManager.popMenu();
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
static const char* kSlotButtons[4] = {
@@ -896,7 +1123,7 @@ namespace FRG {
? emptyText
: localizedLocationName[info.locationName][FRG::g_currentLanguage] + " " + info.savedAt;
uiManager.setTextButtonText(buttonName, label);
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
}
});
}
@@ -941,7 +1168,7 @@ namespace FRG {
selectInventoryItem(i);
//logger() << "Callback called for button number " << i << " END" << std::endl;
- audioPlayer_.playSoundAsync("audio/sound_bag001.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/sound_bag001.ogg");
});
} else {
uiManager.setNodeVisible(btnName, false);
@@ -953,7 +1180,7 @@ namespace FRG {
selectInventoryItem(0);
}
- audioPlayer_.playSoundAsync("audio/sound_bag_open.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/sound_bag_open.ogg");
}
void MenuManager::selectInventoryItem(int index) {
@@ -1005,7 +1232,7 @@ namespace FRG {
for (int i = 0; i < inventoryMenuStackDepth_; ++i) uiManager.popMenu();
inventoryMenuStackDepth_ = 0;
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/sound_bag_close.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/sound_bag_close.ogg");
}
void MenuManager::openQuestJournal() {
@@ -1021,12 +1248,12 @@ namespace FRG {
uiManager.setButtonCallback("journalExitButton", [this](const std::string&) {
closeQuestJournal();
- audioPlayer_.playSoundAsync("audio/656126__itsthegoodstuff__paging-through-book.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/656126__itsthegoodstuff__paging-through-book.ogg");
});
uiManager.setButtonCallback("journalExitButton2", [this](const std::string&) {
closeQuestJournal();
- audioPlayer_.playSoundAsync("audio/656126__itsthegoodstuff__paging-through-book.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/656126__itsthegoodstuff__paging-through-book.ogg");
});
@@ -1041,7 +1268,7 @@ namespace FRG {
for (int i = 0; i < 9; ++i) {
uiManager.setTextButtonCallback(kItemNames[i], [this, i](const std::string&) {
selectQuestByIndex(i);
- audioPlayer_.playSoundAsync("audio/255571__stradie__flipping-paper.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/255571__stradie__flipping-paper.ogg");
});
}
@@ -1050,7 +1277,7 @@ namespace FRG {
selectQuestByIndex(0);
}
- audioPlayer_.playSoundAsync("audio/255571__stradie__flipping-paper.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/255571__stradie__flipping-paper.ogg");
}
void MenuManager::closeQuestJournal() {
@@ -1101,24 +1328,24 @@ namespace FRG {
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
closePhoneEntirely();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
uiManager.setButtonCallback("phoneMessenger", [this](const std::string&) {
openPhoneMessenger();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("phoneBank", [this](const std::string&) {
openPhoneBank();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("phoneVideo", [this](const std::string&) {
openPhoneVideo();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("phoneTaxi", [this](const std::string&) {
openPhoneTaxi();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
if (gameState_.isNight)
@@ -1143,7 +1370,7 @@ namespace FRG {
uiManager.setNodeVisible("phoneTimeDawn", false);
}
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
}
void MenuManager::openPhoneMessenger() {
@@ -1161,23 +1388,23 @@ namespace FRG {
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
closePhoneEntirely();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
uiManager.setTextButtonCallback("chat1button", [this](const std::string&) {
gameState_.chatUnread[0] = false;
openPhoneChatFromList(0, isPortraitMode() ? verticalPhoneChat1Root : phoneChat1Root);
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setTextButtonCallback("chat2button", [this](const std::string&) {
gameState_.chatUnread[1] = false;
openPhoneChatFromList(1, isPortraitMode() ? verticalPhoneChat2Root : phoneChat2Root);
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setTextButtonCallback("chat3button", [this](const std::string&) {
gameState_.chatUnread[2] = false;
openPhoneChatFromList(2, isPortraitMode() ? verticalPhoneChat3Root : phoneChat3Root);
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
}
@@ -1213,13 +1440,13 @@ namespace FRG {
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
closePhoneEntirely();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
uiManager.popMenu();
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
}
@@ -1229,13 +1456,13 @@ namespace FRG {
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
closePhoneEntirely();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
uiManager.popMenu();
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("videoSkip", [this](const std::string&) {
closePhoneEntirely();
@@ -1252,7 +1479,7 @@ namespace FRG {
}
else
{
- audioPlayer_.playSoundAsync("audio/78564__joedeshon__alarm_clock_ticking_02.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/78564__joedeshon__alarm_clock_ticking_02.ogg");
startNightTransitionFunc();
}
}
@@ -1277,13 +1504,13 @@ namespace FRG {
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
closePhoneEntirely();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
uiManager.popMenu();
uiManager.updateAllLayouts();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("mapGo", [this](const std::string&) {
gameState_.tutorialNeedOpenTaxiScreen = false;
@@ -1298,17 +1525,17 @@ namespace FRG {
gameState_.money -= 500;
closePhoneEntirely();
if (startDialogueFunc) startDialogueFunc("dialog_taxi002");
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
}
else
{
closePhoneEntirely();
if (startDialogueFunc) startDialogueFunc("dialog_taxi004");
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
}
});
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
}
void MenuManager::openPhoneChatFromList(int chatIndex, std::shared_ptr chatRoot) {
@@ -1348,12 +1575,12 @@ namespace FRG {
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
closePhoneScreenFromChat();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
uiManager.setTextButtonCallback("chatTitleButton", [this](const std::string&) {
returnToPhoneChatList();
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
if (chatOpenCallback) {
@@ -1437,37 +1664,37 @@ namespace FRG {
}
void MenuManager::showModalMenuScreen() {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
uiManager.pushMenuFromSavedRoot(modalMenuRoot);
uiManager.setButtonCallback("modalExitButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
uiManager.popMenu();
uiManager.updateAllLayouts();
});
uiManager.setButtonCallback("menuCloseButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
uiManager.popMenu();
uiManager.updateAllLayouts();
});
uiManager.setButtonCallback("menuSaveButton", [this](const std::string&) {
showSaveGameScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setButtonCallback("menuLoadButton", [this](const std::string&) {
showLoadGameScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setButtonCallback("menuSettingsButton", [this](const std::string&) {
showSettingsScreen();
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setButtonCallback("menuExitButton", [this](const std::string&) {
- audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
uiManager.popMenu();
showMainMenu();
});
@@ -1579,29 +1806,35 @@ namespace FRG {
setupGameplayHudCallbacks();
if (gameState_.isDarklands)
{
- audioPlayer_.crossFadeMusicAsync("audio/bishkek fight calm.ogg");
+ audioPlayer_.crossFadeMusicAsync("music/bishkek fight calm.ogg");
+ lastMusicTrack = "music/bishkek fight calm.ogg";
}
else if (gameState_.isNight)
{
- audioPlayer_.crossFadeMusicAsync("audio/bishkek night.ogg");
+ audioPlayer_.crossFadeMusicAsync("music/bishkek night.ogg");
+ lastMusicTrack = "music/bishkek night.ogg";
}
else
{
- audioPlayer_.crossFadeMusicAsync("audio/bishkek univer day.ogg");
+ audioPlayer_.crossFadeMusicAsync("music/bishkek univer day.ogg");
+ lastMusicTrack = "music/bishkek univer day.ogg";
}
} else if (locationName == "uni_interior") {
applyUniIntHud();
if (gameState_.isDarklands)
{
- audioPlayer_.crossFadeMusicAsync("audio/bishkek fight calm.ogg");
+ audioPlayer_.crossFadeMusicAsync("music/bishkek fight calm.ogg");
+ lastMusicTrack = "music/bishkek fight calm.ogg";
}
else if (gameState_.isNight)
{
- audioPlayer_.crossFadeMusicAsync("audio/bishkek night.ogg");
+ audioPlayer_.crossFadeMusicAsync("music/bishkek night.ogg");
+ lastMusicTrack = "music/bishkek night.ogg";
}
else
{
- audioPlayer_.crossFadeMusicAsync("audio/bishkek univer day.ogg");
+ audioPlayer_.crossFadeMusicAsync("music/bishkek univer day.ogg");
+ lastMusicTrack = "music/bishkek univer day.ogg";
}
} else {
// Returning to dorm: reuse step5ab, suppress already-completed hints
@@ -1611,11 +1844,13 @@ namespace FRG {
if (gameState_.isNight)
{
- audioPlayer_.crossFadeMusicAsync("audio/bishkek night.ogg");
+ audioPlayer_.crossFadeMusicAsync("music/bishkek night.ogg");
+ lastMusicTrack = "music/bishkek night.ogg";
}
else
{
- audioPlayer_.crossFadeMusicAsync("audio/obshaga.ogg");
+ audioPlayer_.crossFadeMusicAsync("music/obshaga.ogg");
+ lastMusicTrack = "music/obshaga.ogg";
}
}
}
@@ -1844,12 +2079,12 @@ namespace FRG {
uiManager.setButtonCallback("inventoryExitButton", [this](const std::string&) {
closePortraitInventoryItem();
- audioPlayer_.playSoundAsync("audio/sound_bag001.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/sound_bag001.ogg");
});
uiManager.setButtonCallback("inventoryExitButton2", [this](const std::string&) {
closePortraitInventoryItem();
- audioPlayer_.playSoundAsync("audio/sound_bag001.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/sound_bag001.ogg");
});
uiManager.setButtonCallback("inventoryMain", [this](const std::string&) {});
@@ -1880,12 +2115,12 @@ namespace FRG {
uiManager.setButtonCallback("journalItemExitButton", [this](const std::string&) {
closePortraitJournalItem();
- audioPlayer_.playSoundAsync("audio/255571__stradie__flipping-paper.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/255571__stradie__flipping-paper.ogg");
});
uiManager.setButtonCallback("journalItemExitButton2", [this](const std::string&) {
closeQuestJournal();
- audioPlayer_.playSoundAsync("audio/656126__itsthegoodstuff__paging-through-book.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/656126__itsthegoodstuff__paging-through-book.ogg");
});
@@ -2085,7 +2320,7 @@ namespace FRG {
recomputePhoneChatPositions();
}
chatUiManager.startPopIn(nodeName, 300.0f);
- audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
+ audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
}
void MenuManager::setDarklandsMode(bool enabled)
diff --git a/src/MenuManager.h b/src/MenuManager.h
index f327ef1..b78a503 100644
--- a/src/MenuManager.h
+++ b/src/MenuManager.h
@@ -130,7 +130,20 @@ namespace FRG {
std::shared_ptr languageLoadingEn;
bool shadowsEnabled = true;
+ std::string lastMusicTrack;
+#ifdef __EMSCRIPTEN__
+ static void onMusicZipLoaded(unsigned /*handle*/, void* /*userData*/, const char* /*filename*/);
+ static void onMusicZipError(unsigned /*handle*/, void* /*userData*/, int httpStatus);
+ static void onMusicZipProgress(unsigned /*handle*/, void* /*userData*/, int percentComplete);
+
+ float musicDownloadProgress = 0.0f;
+
+ bool musicDownloadStarted = false;
+ bool musicDownloadError = false;
+ bool playerInSettingsNoMusic = false;
+ bool playerInSettingsNoMusicGraphics = false;
+#endif
protected:
Renderer& renderer;
@@ -176,6 +189,10 @@ namespace FRG {
void showCreditsScreen();
void showSettingsScreen();
void showSettingsGraphicsScreen();
+
+#ifdef __EMSCRIPTEN__
+ void showSettingsNoMusicWebScreen();
+#endif
void showLanguageScreen();
void showLoadGameScreen();
void showSaveGameScreen();
@@ -268,6 +285,9 @@ namespace FRG {
std::shared_ptr creditsScreenRoot;
std::shared_ptr portraitCreditsScreenRoot;
+#ifdef __EMSCRIPTEN__
+ std::shared_ptr settingsScreenNoMusicWebRoot;
+#endif
std::shared_ptr settingsScreenRoot;
std::shared_ptr settingsGraphicsScreenRoot;
std::shared_ptr languageScreenRoot;
@@ -343,6 +363,7 @@ namespace FRG {
std::string fullScreenToggleOff;
std::string shadowToggleOn;
std::string shadowToggleOff;
+ std::string musicDownloadProgressPrefix;
};
diff --git a/src/utils/Utils.cpp b/src/utils/Utils.cpp
index f2d550a..dadefb4 100644
--- a/src/utils/Utils.cpp
+++ b/src/utils/Utils.cpp
@@ -105,12 +105,20 @@ namespace FRG
}
std::vector readFileFromZIP(const std::string& filename, const std::string& zipfilename) {
+ if (zipfilename.empty()) {
+ logger() << "readFileFromZIP: zipfilename empty, reading from file directly..." << std::endl;
+ return readFile(filename);
+ }
+
#ifdef EMSCRIPTEN
+
+ std::string webZipFilename = getPersistentResourcePath(zipfilename);
+
int zipErr = 0;
- zip_t* archive = zip_open(zipfilename.c_str(), ZIP_RDONLY, &zipErr);
+ zip_t* archive = zip_open(webZipFilename.c_str(), ZIP_RDONLY, &zipErr);
if (!archive) {
- logger() << "Failed to open ZIP: " << zipfilename << " (error code " << zipErr << ")" << std::endl;
- throw std::runtime_error("Failed to open ZIP: " + zipfilename);
+ logger() << "Failed to open ZIP: " << webZipFilename << " (error code " << zipErr << ")" << std::endl;
+ throw std::runtime_error("Failed to open ZIP: " + webZipFilename);
}
std::string cleanFilename = filename;
@@ -120,14 +128,14 @@ namespace FRG
zip_stat_init(&fileStat);
if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0 || !(fileStat.valid & ZIP_STAT_SIZE)) {
zip_close(archive);
- logger() << "Failed to stat file in ZIP: " << cleanFilename << " in " << zipfilename << std::endl;
+ logger() << "Failed to stat file in ZIP: " << cleanFilename << " in " << webZipFilename << std::endl;
throw std::runtime_error("Can't stat file in ZIP: " + cleanFilename);
}
zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
if (!zipFile) {
zip_close(archive);
- logger() << "Failed to open file in ZIP: " << cleanFilename << std::endl;
+ logger() << "Failed to open file in ZIP: " << cleanFilename << " in " << webZipFilename << std::endl;
throw std::runtime_error("Can't open file in ZIP: " + cleanFilename);
}
@@ -146,11 +154,6 @@ namespace FRG
return fileData;
#else
- if (zipfilename.empty()) {
- std::cerr << "readFileFromZIP: zipfilename empty" << std::endl;
- return {};
- }
-
int zipErr = 0;
zip_t* archive = zip_open(zipfilename.c_str(), ZIP_RDONLY, &zipErr);
if (!archive) {
@@ -197,6 +200,39 @@ namespace FRG
#endif
}
+ bool zipFileExists(const std::string& zipfilename)
+ {
+ if (zipfilename.empty()) {
+ return false;
+ }
+
+#ifdef EMSCRIPTEN
+ std::string webZipFilename = getPersistentResourcePath(zipfilename);
+
+ int zipErr = 0;
+ zip_t* archive = zip_open(webZipFilename.c_str(), ZIP_RDONLY, &zipErr);
+ if (!archive) {
+ return false;
+ }
+
+ zip_close(archive);
+ return true;
+#else
+
+ int zipErr = 0;
+ zip_t* archive = zip_open(zipfilename.c_str(), ZIP_RDONLY, &zipErr);
+ if (!archive) {
+ std::cerr << "readFileFromZIP: zip_open failed for: " << zipfilename
+ << " (error code " << zipErr << ")" << std::endl;
+ return false;
+ }
+
+ zip_close(archive);
+
+ return true;
+#endif
+ }
+
bool findString(const char* in, char* list)
{
size_t thisLength = strlen(in);
@@ -481,4 +517,31 @@ namespace FRG
// Если логирование отключено, возвращаем заглушку.
return g_nullStream;
}
+
+
+#ifdef __EMSCRIPTEN__
+ // Получение абсолютного пути к ресурсам в хранилище сохранений (/offline/resources.zip)
+ std::string getPersistentResourcePath(const std::string& filename) {
+ namespace fs = std::filesystem;
+ return (getSaveDirectory() / filename).string();
+ }
+
+ // Проверка целостности zip-файла по указанному пути
+ bool isZipValid(const std::string& fullPath) {
+ namespace fs = std::filesystem;
+ if (!fs::exists(fullPath)) {
+ return false;
+ }
+
+ int zipErr = 0;
+ zip_t* archive = zip_open(fullPath.c_str(), ZIP_RDONLY, &zipErr);
+ if (!archive) {
+ logger() << "[VFS] ZIP file invalid or corrupted: " << fullPath
+ << " (code " << zipErr << ")" << std::endl;
+ return false;
+ }
+ zip_close(archive);
+ return true;
+ }
+#endif
};
\ No newline at end of file
diff --git a/src/utils/Utils.h b/src/utils/Utils.h
index be698cd..7c9f512 100644
--- a/src/utils/Utils.h
+++ b/src/utils/Utils.h
@@ -23,6 +23,8 @@ namespace FRG
std::vector readFileFromZIP(const std::string& filename, const std::string& zipfilename);
+ bool zipFileExists(const std::string& zipfilename);
+
bool findString(const char* in, char* list);
std::filesystem::path getSaveDirectory();
@@ -38,4 +40,10 @@ namespace FRG
void initLogger();
std::ostream& logger();
+
+#ifdef __EMSCRIPTEN__
+ std::string getPersistentResourcePath(const std::string& filename);
+
+ bool isZipValid(const std::string& fullPath);
+#endif
}
\ No newline at end of file