Adapting music to web, fixing scripts, fixing bug in initial state
This commit is contained in:
1 parent
c3f449b999
commit
5ecf5fb9d7
43 files changed
+875
-388
No files matched your search
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -290,7 +290,7 @@ endif()
|
||||
# ===========================================
|
||||
set(RUNTIME_RESOURCE_DIRS
|
||||
"resources"
|
||||
"audio"
|
||||
"music"
|
||||
)
|
||||
|
||||
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
|
||||
|
||||
+31
-14
@@ -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..."
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<!doctypehtml><html lang=en-us><head><meta charset=utf-8><meta content="text/html; charset=utf-8"http-equiv=Content-Type><title>Emscripten-Generated Code</title><style>body{font-family:arial;margin:0;padding:none}.emscripten{padding-right:0;margin-left:auto;margin-right:auto;display:block}div.emscripten{text-align:center}div.emscripten_border{border:1px solid #000}canvas.emscripten{border:0 none;background-color:#000}#emscripten_logo{display:inline-block;margin:0;padding:6px;width:265px}.spinner{height:30px;width:30px;margin:0;margin-top:20px;margin-left:20px;display:inline-block;vertical-align:top;-webkit-animation:rotation .8s linear infinite;-moz-animation:rotation .8s linear infinite;-o-animation:rotation .8s linear infinite;animation:rotation .8s linear infinite;border-left:5px solid #ebebeb;border-right:5px solid #ebebeb;border-bottom:5px solid #ebebeb;border-top:5px solid #787878;border-radius:100%;background-color:#bdd72e}@-webkit-keyframes rotation{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@-moz-keyframes rotation{from{-moz-transform:rotate(0)}to{-moz-transform:rotate(360deg)}}@-o-keyframes rotation{from{-o-transform:rotate(0)}to{-o-transform:rotate(360deg)}}@keyframes rotation{from{transform:rotate(0)}to{transform:rotate(360deg)}}#status{display:inline-block;vertical-align:top;margin-top:30px;margin-left:20px;font-weight:700;color:#787878}#progress{height:20px;width:300px}#controls{display:inline-block;float:right;vertical-align:top;margin-top:30px;margin-right:20px}#output{width:100%;height:200px;margin:0 auto;margin-top:10px;border-left:0;border-right:0px;padding-left:0;padding-right:0;display:block;background-color:#000;color:#fff;font-family:'Lucida Console',Monaco,monospace;outline:0}</style></head><body>
|
||||
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
|
||||
<script>eruda.init();</script>
|
||||
|
||||
<a href=http://emscripten.org><img id=emscripten_logo src=data:image/png;base64,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 truncated
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
<!doctypehtml><html lang=en-us><head><meta charset=utf-8><meta content="text/html; charset=utf-8"http-equiv=Content-Type><title>Emscripten-Generated Code</title><style>body{font-family:arial;margin:0;padding:none}.emscripten{padding-right:0;margin-left:auto;margin-right:auto;display:block}div.emscripten{text-align:center}div.emscripten_border{border:1px solid #000}canvas.emscripten{border:0 none;background-color:#000}#emscripten_logo{display:inline-block;margin:0;padding:6px;width:265px}.spinner{height:30px;width:30px;margin:0;margin-top:20px;margin-left:20px;display:inline-block;vertical-align:top;-webkit-animation:rotation .8s linear infinite;-moz-animation:rotation .8s linear infinite;-o-animation:rotation .8s linear infinite;animation:rotation .8s linear infinite;border-left:5px solid #ebebeb;border-right:5px solid #ebebeb;border-bottom:5px solid #ebebeb;border-top:5px solid #787878;border-radius:100%;background-color:#bdd72e}@-webkit-keyframes rotation{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@-moz-keyframes rotation{from{-moz-transform:rotate(0)}to{-moz-transform:rotate(360deg)}}@-o-keyframes rotation{from{-o-transform:rotate(0)}to{-o-transform:rotate(360deg)}}@keyframes rotation{from{transform:rotate(0)}to{transform:rotate(360deg)}}#status{display:inline-block;vertical-align:top;margin-top:30px;margin-left:20px;font-weight:700;color:#787878}#progress{height:20px;width:300px}#controls{display:inline-block;float:right;vertical-align:top;margin-top:30px;margin-right:20px}#output{width:100%;height:200px;margin:0 auto;margin-top:10px;border-left:0;border-right:0px;padding-left:0;padding-right:0;display:block;background-color:#000;color:#fff;font-family:'Lucida Console',Monaco,monospace;outline:0}</style></head><body><script src="https://cdn.jsdelivr.net/npm/eruda"></script>
|
||||
<script>eruda.init();</script><a href=http://emscripten.org><img id=emscripten_logo src=""></a><div class=spinner id=spinner></div><div class=emscripten id=status>Downloading...</div><span id=controls><span><input type=checkbox id=resize>Resize canvas</span> <span><input type=checkbox id=pointerLock checked>Lock/hide mouse pointer </span><span><input type=button onclick='Module.requestFullscreen(document.getElementById("pointerLock").checked,document.getElementById("resize").checked)'value=Fullscreen></span></span><div class=emscripten><progress hidden id=progress max=100 value=0></progress></div><div class=emscripten_border><canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas></div><textarea id=output rows=8></textarea><script>var statusElement=document.getElementById("status"),progressElement=document.getElementById("progress"),spinnerElement=document.getElementById("spinner"),canvasElement=document.getElementById("canvas"),outputElement=document.getElementById("output");outputElement&&(outputElement.value=""),canvasElement.addEventListener("webglcontextlost",(e=>{alert("WebGL context lost. You will need to reload the page."),e.preventDefault()}),!1);var Module={print(...e){if(console.log(...e),outputElement){var t=e.join(" ");outputElement.value+=t+"\n",outputElement.scrollTop=outputElement.scrollHeight}},canvas:canvasElement,setStatus(e){if(Module.setStatus.last||(Module.setStatus.last={time:Date.now(),text:""}),e!==Module.setStatus.last.text){var t=e.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/),n=Date.now();t&&n-Module.setStatus.last.time<30||(Module.setStatus.last.time=n,Module.setStatus.last.text=e,t?(e=t[1],progressElement.value=100*parseInt(t[2]),progressElement.max=100*parseInt(t[4]),progressElement.hidden=!1,spinnerElement.hidden=!1):(progressElement.value=null,progressElement.max=null,progressElement.hidden=!0,e||(spinnerElement.style.display="none")),statusElement.innerHTML=e)}},totalDependencies:0,monitorRunDependencies(e){this.totalDependencies=Math.max(this.totalDependencies,e),Module.setStatus(e?"Preparing... ("+(this.totalDependencies-e)+"/"+this.totalDependencies+")":"All downloads complete.")}};Module.setStatus("Downloading..."),window.onerror=e=>{Module.setStatus("Exception thrown, see JavaScript console"),spinnerElement.style.display="none",Module.setStatus=e=>{e&&console.error("[post-exception status] "+e)}}</script><script async src="space-game001.js" crossorigin="anonymous"></script></body></html>
|
||||
@@ -1,200 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<title>Sky Trek Tales</title>
|
||||
<style>
|
||||
body, html {
|
||||
margin: 0; padding: 0; width: 100%; height: 100%;
|
||||
overflow: hidden; background-color: #000;
|
||||
position: fixed;
|
||||
}
|
||||
#canvas {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100vw; height: 100vh;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#fs-button {
|
||||
position: absolute;
|
||||
top: 10px; right: 10px;
|
||||
padding: 10px;
|
||||
z-index: 10;
|
||||
background: rgba(255,255,255,0.3);
|
||||
color: white; border: 1px solid white;
|
||||
cursor: pointer;
|
||||
font-family: sans-serif;
|
||||
border-radius: 5px;
|
||||
}
|
||||
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
|
||||
|
||||
/* Nick modal */
|
||||
#nickOverlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0,0,0,0.85);
|
||||
z-index: 9999;
|
||||
}
|
||||
#nickBox {
|
||||
background: #111;
|
||||
border: 1px solid #444;
|
||||
padding: 24px;
|
||||
width: 320px;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.6);
|
||||
text-align: center;
|
||||
}
|
||||
#nickBox h2 { margin: 0 0 12px 0; font-size: 18px; color: #eee; }
|
||||
#nickBox input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid #333;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
}
|
||||
#nickBox button {
|
||||
padding: 10px 16px;
|
||||
font-size: 16px;
|
||||
background: #2a9fd6;
|
||||
border: none;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button id="fs-button">Fullscreen</button>
|
||||
<div id="status">Downloading...</div>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
|
||||
<div id="nickOverlay" style="display:none;">
|
||||
<div id="nickBox">
|
||||
<h2>Enter your nickname</h2>
|
||||
<input id="nickInput" type="text" maxlength="32" placeholder="Player" />
|
||||
<div>
|
||||
<button id="nickSubmit">Start</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Utility: подготовить глобальный Module до загрузки Emscripten-скрипта
|
||||
function prepareModuleEnvironment() {
|
||||
window.Module = window.Module || {};
|
||||
var canvasEl = document.getElementById('canvas');
|
||||
// Устанавливаем canvas для Emscripten, чтобы createContext не падал
|
||||
window.Module.canvas = canvasEl;
|
||||
// Подготовим заглушку setStatus, если ещё нет
|
||||
window.Module.setStatus = window.Module.setStatus || function (text) {
|
||||
var statusElement = document.getElementById("status");
|
||||
statusElement.innerHTML = text;
|
||||
statusElement.style.display = text ? 'block' : 'none';
|
||||
};
|
||||
}
|
||||
|
||||
// Show overlay only if no nickname saved.
|
||||
function loadGameScript() {
|
||||
var s = document.createElement('script');
|
||||
s.src = 'space-game001.js';
|
||||
s.async = true;
|
||||
document.body.appendChild(s);
|
||||
}
|
||||
|
||||
function showNickOverlay() {
|
||||
var overlay = document.getElementById('nickOverlay');
|
||||
overlay.style.display = 'flex';
|
||||
var input = document.getElementById('nickInput');
|
||||
input.focus();
|
||||
}
|
||||
|
||||
function hideNickOverlay() {
|
||||
var overlay = document.getElementById('nickOverlay');
|
||||
overlay.style.display = 'none';
|
||||
}
|
||||
|
||||
function saveNickAndStart(nick) {
|
||||
try {
|
||||
if (!nick || nick.trim() === '') nick = 'Player';
|
||||
localStorage.setItem('spacegame_nick', nick);
|
||||
} catch (e) {
|
||||
console.warn('localStorage not available', e);
|
||||
}
|
||||
hideNickOverlay();
|
||||
// перед загрузкой скрипта гарантируем, что Module.canvas задан
|
||||
prepareModuleEnvironment();
|
||||
loadGameScript();
|
||||
}
|
||||
|
||||
document.getElementById('fs-button').addEventListener('click', function() {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen().catch(function(e) {
|
||||
console.error('Fullscreen error: ' + e.message);
|
||||
});
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Готовим Module сразу — даже если откроется модалка, поле canvas будет доступно для скрипта (если он загружается позже)
|
||||
prepareModuleEnvironment();
|
||||
|
||||
var stored = null;
|
||||
try {
|
||||
stored = localStorage.getItem('spacegame_nick');
|
||||
} catch (e) {
|
||||
console.warn('localStorage not available', e);
|
||||
}
|
||||
|
||||
if (stored && stored.trim() !== '') {
|
||||
// Nick is present — start immediately
|
||||
loadGameScript();
|
||||
} else {
|
||||
// Show modal to request nickname before loading WASM
|
||||
showNickOverlay();
|
||||
var submit = document.getElementById('nickSubmit');
|
||||
var input = document.getElementById('nickInput');
|
||||
|
||||
submit.addEventListener('click', function() {
|
||||
saveNickAndStart(input.value);
|
||||
});
|
||||
|
||||
input.addEventListener('input', function() {
|
||||
// Strip any character that is not a-z, A-Z, 0-9 or space
|
||||
var pos = this.selectionStart;
|
||||
var cleaned = this.value.replace(/[^a-zA-Z0-9 ]/g, '');
|
||||
if (cleaned !== this.value) {
|
||||
this.value = cleaned;
|
||||
this.setSelectionRange(pos - 1, pos - 1);
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
saveNickAndStart(input.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("orientationchange", function() {
|
||||
// Chrome на Android обновляет innerWidth/Height не мгновенно.
|
||||
// Ждем завершения анимации поворота.
|
||||
setTimeout(() => {
|
||||
// В Emscripten это вызовет ваш onWindowResized в C++
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}, 200);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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..."
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+46
-15
@@ -6,6 +6,13 @@
|
||||
#include <android/log.h>
|
||||
#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<char> buffer = FRG::readFile(filePath);
|
||||
//Sounds are loaded from regular resource file
|
||||
std::vector<char> 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;
|
||||
|
||||
+2
-2
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
|
||||
+103
-27
@@ -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<char> 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<char> 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -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);
|
||||
|
||||
@@ -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<int>();
|
||||
}
|
||||
//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<float>();
|
||||
}
|
||||
|
||||
//logger() << "GameState::load step 5" << std::endl;
|
||||
|
||||
// --- Tutorial ---
|
||||
tutorialStep = static_cast<TutorialStep>(in.value("tutorialStep", static_cast<int>(tutorialStep)));
|
||||
uniIntTutorialState = static_cast<UniIntTutorialState>(in.value("uniIntTutorialState", static_cast<int>(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<int>(in["chatUnread"].size()); ++i)
|
||||
chatUnread[i] = in["chatUnread"][i].get<bool>();
|
||||
}
|
||||
//logger() << "GameState::load step 7" << std::endl;
|
||||
|
||||
if (in.contains("chatPreviewMsg") && in["chatPreviewMsg"].is_array()) {
|
||||
for (int i = 0; i < 3 && i < static_cast<int>(in["chatPreviewMsg"].size()); ++i)
|
||||
chatPreviewMsg[i] = in["chatPreviewMsg"][i].get<std::string>();
|
||||
}
|
||||
//logger() << "GameState::load step 8" << std::endl;
|
||||
|
||||
if (in.contains("chatHistory") && in["chatHistory"].is_array()) {
|
||||
for (int i = 0; i < 3 && i < static_cast<int>(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
|
||||
@@ -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
|
||||
+23
-13
@@ -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<NpcCar::Mode>(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<int>(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<NpcCar::Mode>(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<int>(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<int>(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
|
||||
+1
-1
@@ -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<Vector3f> taxiCarWaypoints;
|
||||
|
||||
// (trigger/teleport zone runtime flags are serialised by id directly in Location::save/load)
|
||||
|
||||
+324
-89
@@ -10,8 +10,16 @@
|
||||
#include <string>
|
||||
#include <SDL.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#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<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
||||
: std::array<float, 4>{ 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<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
||||
: std::array<float, 4>{ 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<int>(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<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
||||
: std::array<float, 4>{ 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<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
||||
: std::array<float, 4>{ 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<UiNode> 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)
|
||||
|
||||
@@ -130,7 +130,20 @@ namespace FRG {
|
||||
std::shared_ptr<Texture> 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<UiNode> creditsScreenRoot;
|
||||
std::shared_ptr<UiNode> portraitCreditsScreenRoot;
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
std::shared_ptr<UiNode> settingsScreenNoMusicWebRoot;
|
||||
#endif
|
||||
std::shared_ptr<UiNode> settingsScreenRoot;
|
||||
std::shared_ptr<UiNode> settingsGraphicsScreenRoot;
|
||||
std::shared_ptr<UiNode> languageScreenRoot;
|
||||
@@ -343,6 +363,7 @@ namespace FRG {
|
||||
std::string fullScreenToggleOff;
|
||||
std::string shadowToggleOn;
|
||||
std::string shadowToggleOff;
|
||||
std::string musicDownloadProgressPrefix;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+73
-10
@@ -105,12 +105,20 @@ namespace FRG
|
||||
}
|
||||
|
||||
std::vector<char> 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
|
||||
};
|
||||
@@ -23,6 +23,8 @@ namespace FRG
|
||||
|
||||
std::vector<char> 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
|
||||
}
|
||||
Reference in new issue
Block a user