Adapting music to web, fixing scripts, fixing bug in initial state
This commit is contained in:
parent
c3f449b999
commit
5ecf5fb9d7
@ -290,7 +290,7 @@ endif()
|
|||||||
# ===========================================
|
# ===========================================
|
||||||
set(RUNTIME_RESOURCE_DIRS
|
set(RUNTIME_RESOURCE_DIRS
|
||||||
"resources"
|
"resources"
|
||||||
"audio"
|
"music"
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
|
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
|
||||||
|
|||||||
@ -8,7 +8,7 @@ if(NOT CMAKE_MAKE_PROGRAM AND WIN32)
|
|||||||
endif()
|
endif()
|
||||||
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)
|
option(OPTIMIZE_WEB_RESOURCES "Resize PNG images for web build (requires Python3 + Pillow)" ON)
|
||||||
|
|
||||||
@ -152,10 +152,10 @@ set(SOURCES
|
|||||||
../src/render/UiQuad.h
|
../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
|
../src
|
||||||
../thirdparty/eigen-5.0.0
|
../thirdparty/eigen-5.0.0
|
||||||
../thirdparty/boost_1_90_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)
|
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
|
set(EMSCRIPTEN_FLAGS
|
||||||
@ -187,7 +187,7 @@ set(EMSCRIPTEN_FLAGS
|
|||||||
"-DNETWORK"
|
"-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.
|
# 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.
|
# 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_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_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}/../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 недостаточно
|
# Для совместимости со старыми версиями CMake, если target_link_options недостаточно
|
||||||
string(REPLACE ";" " " EMSCRIPTEN_LINK_FLAGS_STR "${EMSCRIPTEN_LINK_FLAGS}")
|
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}"
|
LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS_STR}"
|
||||||
SUFFIX ".html"
|
SUFFIX ".html"
|
||||||
)
|
)
|
||||||
@ -257,23 +256,40 @@ else()
|
|||||||
)
|
)
|
||||||
endif()
|
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_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")
|
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/public")
|
||||||
|
|
||||||
# 1. Устанавливаем основной HTML файл
|
# 1. Устанавливаем основной HTML файл
|
||||||
install(TARGETS bishkek-witcher
|
install(TARGETS ShadowOverBishkekDemo
|
||||||
RUNTIME DESTINATION .
|
RUNTIME DESTINATION .
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
|
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
|
||||||
install(FILES
|
install(FILES
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.js"
|
"${CMAKE_CURRENT_BINARY_DIR}/ShadowOverBishkekDemo.js"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.wasm"
|
"${CMAKE_CURRENT_BINARY_DIR}/ShadowOverBishkekDemo.wasm"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.data"
|
"${CMAKE_CURRENT_BINARY_DIR}/ShadowOverBishkekDemo.data"
|
||||||
DESTINATION .
|
DESTINATION .
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -283,8 +299,9 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/index.html"
|
|||||||
|
|
||||||
# resources.zip is served separately and downloaded asynchronously at runtime
|
# resources.zip is served separately and downloaded asynchronously at runtime
|
||||||
install(FILES "${RESOURCES_ZIP}" DESTINATION .)
|
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 .
|
COMMAND ${CMAKE_COMMAND} --install .
|
||||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||||
COMMENT "Automatically deploying to public directory..."
|
COMMENT "Automatically deploying to public directory..."
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
function loadGameScript() {
|
function loadGameScript() {
|
||||||
var s = document.createElement('script');
|
var s = document.createElement('script');
|
||||||
s.src = 'bishkek-witcher.js';
|
s.src = 'ShadowOverBishkekDemo.js';
|
||||||
s.async = true;
|
s.async = true;
|
||||||
document.body.appendChild(s);
|
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()
|
endif()
|
||||||
|
|
||||||
# Копирование ресурсов локально
|
# Копирование ресурсов локально
|
||||||
set(RUNTIME_RESOURCE_DIRS "resources" "audio")
|
set(RUNTIME_RESOURCE_DIRS "resources" "music")
|
||||||
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
|
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
|
||||||
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
|
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
|
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
|
||||||
|
|||||||
BIN
resources/audio/13925__adcbicycle__21.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/13925__adcbicycle__21.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/255571__stradie__flipping-paper.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/255571__stradie__flipping-paper.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/398751__anthousai__door-lock-01.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/398751__anthousai__door-lock-01.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/459618__n-lerche__blow.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/459618__n-lerche__blow.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/537061__imafoley__message-pop-sound.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/537061__imafoley__message-pop-sound.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/547041__cogfirestudios__hit-swing-sword-small-3.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/587036__antoineromo__bed-sheets.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/587036__antoineromo__bed-sheets.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/656126__itsthegoodstuff__paging-through-book.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/656126__itsthegoodstuff__paging-through-book.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/702171__foxfire__door-opening.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/702171__foxfire__door-opening.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/78564__joedeshon__alarm_clock_ticking_02.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/78564__joedeshon__alarm_clock_ticking_02.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/831642__ienba__generic-game-pick-up.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/831642__ienba__generic-game-pick-up.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/sound_bag001.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/sound_bag001.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/sound_bag_close.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/sound_bag_close.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/audio/sound_bag_open.ogg
(Stored with Git LFS)
Normal file
BIN
resources/audio/sound_bag_open.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -1122,9 +1122,9 @@
|
|||||||
"waypointReachRadius": 3.0,
|
"waypointReachRadius": 3.0,
|
||||||
"waypoints": []
|
"waypoints": []
|
||||||
},
|
},
|
||||||
"taxiCarTriggerPositionX": -1.7014118346046923e+38,
|
"taxiCarTriggerPositionX": 0.0,
|
||||||
"taxiCarTriggerPositionY": -1.7014118346046923e+38,
|
"taxiCarTriggerPositionY": 0.0,
|
||||||
"taxiCarTriggerPositionZ": -1.7014118346046923e+38,
|
"taxiCarTriggerPositionZ": 0.0,
|
||||||
"taxiDefaultWaypoints": []
|
"taxiDefaultWaypoints": []
|
||||||
},
|
},
|
||||||
"tutorialInteractiveObjectsLocked": false
|
"tutorialInteractiveObjectsLocked": false
|
||||||
@ -1476,7 +1476,6 @@
|
|||||||
"morning_can_open_door_index": 0,
|
"morning_can_open_door_index": 0,
|
||||||
"morning_did_open_door_index": 0,
|
"morning_did_open_door_index": 0,
|
||||||
"player_hold_book": false,
|
"player_hold_book": false,
|
||||||
"player_hold_knife": false,
|
|
||||||
"teacher_arrived_to_library": false,
|
"teacher_arrived_to_library": false,
|
||||||
"teacher_door_opened": false,
|
"teacher_door_opened": false,
|
||||||
"teacher_told_about_book": false,
|
"teacher_told_about_book": false,
|
||||||
@ -1652,7 +1651,7 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"savedAt": "2026-07-14 14:00",
|
"savedAt": "2026-07-24 23:40",
|
||||||
"taxiIsCalled": false,
|
"taxiIsCalled": false,
|
||||||
"tutorialJournalPickedUp": false,
|
"tutorialJournalPickedUp": false,
|
||||||
"tutorialJournalScreenOpened": false,
|
"tutorialJournalScreenOpened": false,
|
||||||
|
|||||||
131
resources/w/ui/screen_settings_nomusic_web.json
Normal file
131
resources/w/ui/screen_settings_nomusic_web.json
Normal file
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,13 @@
|
|||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace FRG
|
||||||
|
{
|
||||||
|
extern const char* CONST_MUSIC_ZIP_FILE;
|
||||||
|
extern const char* CONST_ZIP_FILE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
AudioPlayerAsync::AudioPlayerAsync() {}
|
AudioPlayerAsync::AudioPlayerAsync() {}
|
||||||
@ -92,9 +99,10 @@ void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, in
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!sound) {
|
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()) {
|
if (buffer.empty()) {
|
||||||
|
|
||||||
FRG::logger() << "Failed to read sound file into buffer: " << filePath << std::endl;
|
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
|
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;
|
//FRG::logger() << "sound = Mix_LoadWAV_RW(rw, 1); just fired " << std::endl;
|
||||||
|
|
||||||
#else
|
//#else
|
||||||
sound = Mix_LoadWAV(filePath.c_str());
|
// sound = Mix_LoadWAV(filePath.c_str());
|
||||||
#endif
|
//#endif
|
||||||
if (!sound) {
|
if (!sound) {
|
||||||
FRG::logger() << "Failed to load sound " << filePath << ": " << Mix_GetError() << std::endl;
|
FRG::logger() << "Failed to load sound " << filePath << ": " << Mix_GetError() << std::endl;
|
||||||
return;
|
return;
|
||||||
@ -144,6 +152,10 @@ void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
|
|||||||
currentTrack = filePath;
|
currentTrack = filePath;
|
||||||
if (!musicEnabled_) return;
|
if (!musicEnabled_) return;
|
||||||
|
|
||||||
|
if ((strlen(FRG::CONST_MUSIC_ZIP_FILE)>0) && !FRG::zipFileExists(FRG::CONST_MUSIC_ZIP_FILE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto task = [this, filePath, loops]() {
|
auto task = [this, filePath, loops]() {
|
||||||
Mix_HaltMusic();
|
Mix_HaltMusic();
|
||||||
if (currentMusic_) {
|
if (currentMusic_) {
|
||||||
@ -153,17 +165,17 @@ void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
|
|||||||
currentMusicData_.clear();
|
currentMusicData_.clear();
|
||||||
|
|
||||||
Mix_Music* music = nullptr;
|
Mix_Music* music = nullptr;
|
||||||
#ifdef __ANDROID__
|
//#ifdef __ANDROID__
|
||||||
currentMusicData_ = FRG::readFile(filePath);
|
currentMusicData_ = FRG::readFileFromZIP(filePath, FRG::CONST_MUSIC_ZIP_FILE);
|
||||||
if (!currentMusicData_.empty()) {
|
if (!currentMusicData_.empty()) {
|
||||||
SDL_RWops* rw = SDL_RWFromMem(currentMusicData_.data(), currentMusicData_.size());
|
SDL_RWops* rw = SDL_RWFromMem(currentMusicData_.data(), currentMusicData_.size());
|
||||||
if (rw) {
|
if (rw) {
|
||||||
music = Mix_LoadMUS_RW(rw, 1);
|
music = Mix_LoadMUS_RW(rw, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
//#else
|
||||||
music = Mix_LoadMUS(filePath.c_str());
|
// music = Mix_LoadMUS(filePath.c_str());
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
if (!music) {
|
if (!music) {
|
||||||
FRG::logger() << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
|
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) {
|
void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fadeDurationMs, int loops) {
|
||||||
|
FRG::logger() << "crossFadeMusicAsync called step 1" << std::endl;
|
||||||
|
|
||||||
if (!initialized) return;
|
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;
|
if (filePath == currentTrack) return;
|
||||||
|
FRG::logger() << "crossFadeMusicAsync called step 1.4" << std::endl;
|
||||||
|
|
||||||
currentTrack = filePath;
|
currentTrack = filePath;
|
||||||
if (!musicEnabled_) return;
|
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]() {
|
auto task = [this, filePath, fadeDurationMs, loops]() {
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
Mix_HaltMusic();
|
Mix_HaltMusic();
|
||||||
@ -287,17 +318,17 @@ void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fade
|
|||||||
currentMusicData_.clear();
|
currentMusicData_.clear();
|
||||||
#endif
|
#endif
|
||||||
Mix_Music* music = nullptr;
|
Mix_Music* music = nullptr;
|
||||||
#ifdef __ANDROID__
|
//#ifdef __ANDROID__
|
||||||
currentMusicData_ = FRG::readFile(filePath);
|
currentMusicData_ = FRG::readFileFromZIP(filePath, FRG::CONST_MUSIC_ZIP_FILE);
|
||||||
if (!currentMusicData_.empty()) {
|
if (!currentMusicData_.empty()) {
|
||||||
SDL_RWops* rw = SDL_RWFromMem(currentMusicData_.data(), currentMusicData_.size());
|
SDL_RWops* rw = SDL_RWFromMem(currentMusicData_.data(), currentMusicData_.size());
|
||||||
if (rw) {
|
if (rw) {
|
||||||
music = Mix_LoadMUS_RW(rw, 1);
|
music = Mix_LoadMUS_RW(rw, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
//#else
|
||||||
music = Mix_LoadMUS(filePath.c_str());
|
// music = Mix_LoadMUS(filePath.c_str());
|
||||||
#endif
|
//#endif
|
||||||
if (!music) {
|
if (!music) {
|
||||||
FRG::logger() << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
|
FRG::logger() << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -438,11 +438,11 @@ void Character::update(int64_t deltaMs, const CharacterResolver& resolver) {
|
|||||||
if (audioPlayer) {
|
if (audioPlayer) {
|
||||||
if (state.isPlayer)
|
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
|
else
|
||||||
{
|
{
|
||||||
audioPlayer->playSoundAsync("audio/13925__adcbicycle__21.ogg");
|
audioPlayer->playSoundAsync("resources/audio/13925__adcbicycle__21.ogg");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ int Environment::height = CONST_DEFAULT_HEIGHT;
|
|||||||
float Environment::zoom = DEFAULT_ZOOM;
|
float Environment::zoom = DEFAULT_ZOOM;
|
||||||
|
|
||||||
|
|
||||||
bool Environment::enableLogging = false;
|
bool Environment::enableLogging = true;
|
||||||
|
|
||||||
SDL_Window* Environment::window = nullptr;
|
SDL_Window* Environment::window = nullptr;
|
||||||
|
|
||||||
|
|||||||
130
src/Game.cpp
130
src/Game.cpp
@ -36,8 +36,10 @@ namespace FRG
|
|||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
const char* CONST_ZIP_FILE = "resources.zip";
|
const char* CONST_ZIP_FILE = "resources.zip";
|
||||||
|
const char* CONST_MUSIC_ZIP_FILE = "music.zip";
|
||||||
#else
|
#else
|
||||||
const char* CONST_ZIP_FILE = "";
|
const char* CONST_ZIP_FILE = "";
|
||||||
|
const char* CONST_MUSIC_ZIP_FILE = "";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float x = 0;
|
float x = 0;
|
||||||
@ -47,25 +49,29 @@ namespace FRG
|
|||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
Game* Game::s_instance = nullptr;
|
Game* Game::s_instance = nullptr;
|
||||||
|
|
||||||
|
// Коллбэк успешной загрузки resources.zip
|
||||||
void Game::onResourcesZipLoaded(unsigned /*handle*/, void* /*userData*/, const char* /*filename*/) {
|
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) {
|
if (s_instance) {
|
||||||
s_instance->resourcesDownloadProgress = 1.0f;
|
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();
|
s_instance->setupPart2();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::onResourcesZipError(unsigned /*handle*/, void* /*userData*/, int /*httpStatus*/) {
|
void Game::onResourcesZipError(unsigned /*handle*/, void* /*userData*/, int httpStatus) {
|
||||||
logger() << "Failed to download resources.zip" << std::endl;
|
logger() << "Failed to download resources.zip, HTTP status: " << httpStatus << std::endl;
|
||||||
|
// Можно обработать ошибку или предложить пользователю перезагрузить страницу
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::onResourcesZipProgress(unsigned /*handle*/, void* /*userData*/, int percentComplete) {
|
void Game::onResourcesZipProgress(unsigned /*handle*/, void* /*userData*/, int percentComplete) {
|
||||||
if (s_instance)
|
if (s_instance)
|
||||||
s_instance->resourcesDownloadProgress = percentComplete / 100.0f;
|
s_instance->resourcesDownloadProgress = percentComplete / 100.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Location* Game::currentLocation() const {
|
Location* Game::currentLocation() const {
|
||||||
@ -133,12 +139,53 @@ namespace FRG
|
|||||||
loadingMesh.RefreshVBO();
|
loadingMesh.RefreshVBO();
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
// Asynchronously download resources.zip; setupPart2() is called on completion.
|
|
||||||
// The loading screen stays visible until the download finishes.
|
|
||||||
s_instance = this;
|
s_instance = this;
|
||||||
logger() << "Load resurces step 1-" << std::endl;
|
|
||||||
emscripten_async_wget2("resources.zip", "resources.zip", "GET", nullptr, nullptr,
|
std::string resPath = FRG::getPersistentResourcePath("resources.zip");
|
||||||
onResourcesZipLoaded, onResourcesZipError, onResourcesZipProgress);
|
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
|
#else
|
||||||
// Builds the loadSteps queue (cheap, no GL work); the actual loading
|
// Builds the loadSteps queue (cheap, no GL work); the actual loading
|
||||||
// happens one step per frame in Game::update() so the window keeps
|
// happens one step per frame in Game::update() so the window keeps
|
||||||
@ -299,12 +346,12 @@ namespace FRG
|
|||||||
menuManager.onItemPickedUp(itemId);
|
menuManager.onItemPickedUp(itemId);
|
||||||
const Item* item = ItemRegistry::instance().findById(itemId);
|
const Item* item = ItemRegistry::instance().findById(itemId);
|
||||||
if (item) menuManager.showToast(menuManager.toastTextureItemAdded, item->name);
|
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) {
|
gameState.inventory.onItemRemoved = [this](const std::string& itemId) {
|
||||||
const Item* item = ItemRegistry::instance().findById(itemId);
|
const Item* item = ItemRegistry::instance().findById(itemId);
|
||||||
if (item) menuManager.showToast(menuManager.toastTextureItemRemoved, item->name);
|
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.
|
// Wire phone dialogue start function so MenuManager can trigger dialogues.
|
||||||
@ -344,7 +391,8 @@ namespace FRG
|
|||||||
performResetToInitialState();
|
performResetToInitialState();
|
||||||
gameState.currentLocationName = "location_dorm";
|
gameState.currentLocationName = "location_dorm";
|
||||||
currentLocation()->scriptEngine.callLocationEnterCallback();
|
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]() {
|
loadSteps.push([this]() {
|
||||||
menuManager.loadSettings();
|
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() {
|
void Game::performResetToInitialState() {
|
||||||
const std::string path = "resources/config/start_state.json";
|
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()) {
|
if (!content.empty()) {
|
||||||
try {
|
try {
|
||||||
nlohmann::json root = nlohmann::json::parse(content);
|
nlohmann::json root = nlohmann::json::parse(content);
|
||||||
@ -485,10 +548,14 @@ namespace FRG
|
|||||||
rearmDormOneShotCallbacks();
|
rearmDormOneShotCallbacks();
|
||||||
return;
|
return;
|
||||||
} catch (const std::exception& e) {
|
} 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;
|
<< " -- falling back to full rebuild" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger() << "File " << path << " is empty -- falling back to full rebuild" << std::endl;
|
||||||
|
}
|
||||||
performFullReset();
|
performFullReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -616,10 +683,12 @@ namespace FRG
|
|||||||
}
|
}
|
||||||
|
|
||||||
gameState.locations["uni_interior"]->requestDarklandsPlayBattleMusic = [this]() {
|
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]() {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
audioPlayer->playSoundAsync("audio/459618__n-lerche__blow.ogg");
|
audioPlayer->playSoundAsync("resources/audio/459618__n-lerche__blow.ogg");
|
||||||
if (darklandsFlashActive) return false;
|
if (darklandsFlashActive) return false;
|
||||||
darklandsFlashActive = true;
|
darklandsFlashActive = true;
|
||||||
darklandsFlashFadingIn = true;
|
darklandsFlashFadingIn = true;
|
||||||
@ -1715,7 +1784,8 @@ namespace FRG
|
|||||||
currentLocation()->scriptEngine.callTriggerNightEnterCallback();
|
currentLocation()->scriptEngine.callTriggerNightEnterCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
audioPlayer->crossFadeMusicAsync("audio/bishkek night.ogg");
|
audioPlayer->crossFadeMusicAsync("music/bishkek night.ogg");
|
||||||
|
menuManager.lastMusicTrack = "music/bishkek night.ogg";
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
gameState.isDarklands = !gameState.isDarklands;
|
gameState.isDarklands = !gameState.isDarklands;
|
||||||
@ -1725,11 +1795,13 @@ namespace FRG
|
|||||||
|
|
||||||
if (gameState.isDarklands)
|
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
|
else
|
||||||
{
|
{
|
||||||
audioPlayer->crossFadeMusicAsync("audio/bishkek night.ogg");
|
audioPlayer->crossFadeMusicAsync("music/bishkek night.ogg");
|
||||||
|
menuManager.lastMusicTrack = "music/bishkek night.ogg";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentLocation()) {
|
if (currentLocation()) {
|
||||||
@ -1845,20 +1917,24 @@ namespace FRG
|
|||||||
|
|
||||||
if (gameState.isDarklands)
|
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)
|
else if (gameState.isNight)
|
||||||
{
|
{
|
||||||
audioPlayer->crossFadeMusicAsync("audio/bishkek night.ogg");
|
audioPlayer->crossFadeMusicAsync("music/bishkek night.ogg");
|
||||||
|
menuManager.lastMusicTrack = "music/bishkek night.ogg";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (gameState.currentLocationName == "location_dorm") {
|
if (gameState.currentLocationName == "location_dorm") {
|
||||||
audioPlayer->crossFadeMusicAsync("audio/obshaga.ogg");
|
audioPlayer->crossFadeMusicAsync("music/obshaga.ogg");
|
||||||
|
menuManager.lastMusicTrack = "music/obshaga.ogg";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
audioPlayer->crossFadeMusicAsync("audio/bishkek univer day.ogg");
|
audioPlayer->crossFadeMusicAsync("music/bishkek univer day.ogg");
|
||||||
|
menuManager.lastMusicTrack = "music/bishkek univer day.ogg";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,6 +82,10 @@ namespace FRG {
|
|||||||
MenuManager menuManager;
|
MenuManager menuManager;
|
||||||
|
|
||||||
void activateSlowMoEffect();
|
void activateSlowMoEffect();
|
||||||
|
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
static Game* s_instance;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Unified pointer handling: mouse-left and a single touch share one path.
|
// Unified pointer handling: mouse-left and a single touch share one path.
|
||||||
@ -160,7 +164,6 @@ namespace FRG {
|
|||||||
int countNonUiPointers() const;
|
int countNonUiPointers() const;
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
static Game* s_instance;
|
|
||||||
static void onResourcesZipLoaded(unsigned handle, void* userData, const char* filename);
|
static void onResourcesZipLoaded(unsigned handle, void* userData, const char* filename);
|
||||||
static void onResourcesZipError(unsigned handle, void* userData, int httpStatus);
|
static void onResourcesZipError(unsigned handle, void* userData, int httpStatus);
|
||||||
static void onResourcesZipProgress(unsigned handle, void* userData, int percentComplete);
|
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)
|
void GameState::load(const nlohmann::json& in)
|
||||||
{
|
{
|
||||||
|
//logger() << "GameState::load step 1" << std::endl;
|
||||||
|
|
||||||
// --- World ---
|
// --- World ---
|
||||||
currentLocationName = in.value("currentLocationName", currentLocationName);
|
currentLocationName = in.value("currentLocationName", currentLocationName);
|
||||||
isDarklands = in.value("isDarklands", isDarklands);
|
isDarklands = in.value("isDarklands", isDarklands);
|
||||||
isNight = in.value("isNight", isNight);
|
isNight = in.value("isNight", isNight);
|
||||||
isDawn = in.value("isDawn", isDawn);
|
isDawn = in.value("isDawn", isDawn);
|
||||||
|
|
||||||
|
//() << "GameState::load step 2" << std::endl;
|
||||||
|
|
||||||
|
|
||||||
// --- Player resources ---
|
// --- Player resources ---
|
||||||
playerHp = in.value("playerHp", playerHp);
|
playerHp = in.value("playerHp", playerHp);
|
||||||
playerMaxHp = in.value("playerMaxHp", playerMaxHp);
|
playerMaxHp = in.value("playerMaxHp", playerMaxHp);
|
||||||
money = in.value("money", money);
|
money = in.value("money", money);
|
||||||
|
|
||||||
|
//logger() << "GameState::load step 3" << std::endl;
|
||||||
|
|
||||||
// --- Script globals ---
|
// --- Script globals ---
|
||||||
globalInts.clear();
|
globalInts.clear();
|
||||||
if (in.contains("globalInts") && in["globalInts"].is_object()) {
|
if (in.contains("globalInts") && in["globalInts"].is_object()) {
|
||||||
for (const auto& [k, v] : in["globalInts"].items())
|
for (const auto& [k, v] : in["globalInts"].items())
|
||||||
globalInts[k] = v.get<int>();
|
globalInts[k] = v.get<int>();
|
||||||
}
|
}
|
||||||
|
//logger() << "GameState::load step 4" << std::endl;
|
||||||
|
|
||||||
globalFloats.clear();
|
globalFloats.clear();
|
||||||
if (in.contains("globalFloats") && in["globalFloats"].is_object()) {
|
if (in.contains("globalFloats") && in["globalFloats"].is_object()) {
|
||||||
for (const auto& [k, v] : in["globalFloats"].items())
|
for (const auto& [k, v] : in["globalFloats"].items())
|
||||||
globalFloats[k] = v.get<float>();
|
globalFloats[k] = v.get<float>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//logger() << "GameState::load step 5" << std::endl;
|
||||||
|
|
||||||
// --- Tutorial ---
|
// --- Tutorial ---
|
||||||
tutorialStep = static_cast<TutorialStep>(in.value("tutorialStep", static_cast<int>(tutorialStep)));
|
tutorialStep = static_cast<TutorialStep>(in.value("tutorialStep", static_cast<int>(tutorialStep)));
|
||||||
uniIntTutorialState = static_cast<UniIntTutorialState>(in.value("uniIntTutorialState", static_cast<int>(uniIntTutorialState)));
|
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);
|
tutorialJournalScreenOpened = in.value("tutorialJournalScreenOpened", tutorialJournalScreenOpened);
|
||||||
tutorialNeedOpenTaxiScreen = in.value("tutorialNeedOpenTaxiScreen", tutorialNeedOpenTaxiScreen);
|
tutorialNeedOpenTaxiScreen = in.value("tutorialNeedOpenTaxiScreen", tutorialNeedOpenTaxiScreen);
|
||||||
|
|
||||||
|
//logger() << "GameState::load step 6" << std::endl;
|
||||||
|
|
||||||
// --- Phone / chat ---
|
// --- Phone / chat ---
|
||||||
if (in.contains("chatUnread") && in["chatUnread"].is_array()) {
|
if (in.contains("chatUnread") && in["chatUnread"].is_array()) {
|
||||||
for (int i = 0; i < 3 && i < static_cast<int>(in["chatUnread"].size()); ++i)
|
for (int i = 0; i < 3 && i < static_cast<int>(in["chatUnread"].size()); ++i)
|
||||||
chatUnread[i] = in["chatUnread"][i].get<bool>();
|
chatUnread[i] = in["chatUnread"][i].get<bool>();
|
||||||
}
|
}
|
||||||
|
//logger() << "GameState::load step 7" << std::endl;
|
||||||
|
|
||||||
if (in.contains("chatPreviewMsg") && in["chatPreviewMsg"].is_array()) {
|
if (in.contains("chatPreviewMsg") && in["chatPreviewMsg"].is_array()) {
|
||||||
for (int i = 0; i < 3 && i < static_cast<int>(in["chatPreviewMsg"].size()); ++i)
|
for (int i = 0; i < 3 && i < static_cast<int>(in["chatPreviewMsg"].size()); ++i)
|
||||||
chatPreviewMsg[i] = in["chatPreviewMsg"][i].get<std::string>();
|
chatPreviewMsg[i] = in["chatPreviewMsg"][i].get<std::string>();
|
||||||
}
|
}
|
||||||
|
//logger() << "GameState::load step 8" << std::endl;
|
||||||
|
|
||||||
if (in.contains("chatHistory") && in["chatHistory"].is_array()) {
|
if (in.contains("chatHistory") && in["chatHistory"].is_array()) {
|
||||||
for (int i = 0; i < 3 && i < static_cast<int>(in["chatHistory"].size()); ++i) {
|
for (int i = 0; i < 3 && i < static_cast<int>(in["chatHistory"].size()); ++i) {
|
||||||
chatHistory[i].clear();
|
chatHistory[i].clear();
|
||||||
@ -136,26 +153,38 @@ void GameState::load(const nlohmann::json& in)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
//logger() << "GameState::load step 8" << std::endl;
|
||||||
|
|
||||||
// --- Inventory ---
|
// --- Inventory ---
|
||||||
if (in.contains("inventory")) inventory.load(in["inventory"]);
|
if (in.contains("inventory")) inventory.load(in["inventory"]);
|
||||||
|
|
||||||
|
//logger() << "GameState::load step 9" << std::endl;
|
||||||
|
|
||||||
// --- Quest journal ---
|
// --- Quest journal ---
|
||||||
if (in.contains("questJournal")) questJournal.load(in["questJournal"]);
|
if (in.contains("questJournal")) questJournal.load(in["questJournal"]);
|
||||||
|
|
||||||
|
logger() << "GameState::load step 10" << std::endl;
|
||||||
|
|
||||||
// --- Locations ---
|
// --- Locations ---
|
||||||
if (in.contains("locations") && in["locations"].is_object()) {
|
if (in.contains("locations") && in["locations"].is_object()) {
|
||||||
for (const auto& [name, locJson] : in["locations"].items()) {
|
for (const auto& [name, locJson] : in["locations"].items()) {
|
||||||
|
//logger() << "GameState::load step 10.1: " << name << std::endl;
|
||||||
|
|
||||||
auto it = locations.find(name);
|
auto it = locations.find(name);
|
||||||
if (it != locations.end() && it->second) {
|
if (it != locations.end() && it->second) {
|
||||||
it->second->load(locJson);
|
it->second->load(locJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//logger() << "GameState::load step 11" << std::endl;
|
||||||
|
|
||||||
|
|
||||||
// --- Taxi ---
|
// --- Taxi ---
|
||||||
taxiIsCalled = in.value("taxiIsCalled", taxiIsCalled);
|
taxiIsCalled = in.value("taxiIsCalled", taxiIsCalled);
|
||||||
|
//logger() << "GameState::load step 12" << std::endl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FRG
|
} // namespace FRG
|
||||||
|
|||||||
@ -2147,10 +2147,13 @@ namespace FRG
|
|||||||
|
|
||||||
void Location::load(const nlohmann::json& in)
|
void Location::load(const nlohmann::json& in)
|
||||||
{
|
{
|
||||||
|
//logger() << "Location::load step 1" << std::endl;
|
||||||
|
|
||||||
// Location-level state
|
// Location-level state
|
||||||
if (in.contains("locationState")) {
|
if (in.contains("locationState")) {
|
||||||
state.load(in["locationState"]);
|
state.load(in["locationState"]);
|
||||||
}
|
}
|
||||||
|
//logger() << "Location::load step 2" << std::endl;
|
||||||
|
|
||||||
// Re-sync navigation pointer from restored index
|
// Re-sync navigation pointer from restored index
|
||||||
if (state.activeNavigationIndex >= 0 &&
|
if (state.activeNavigationIndex >= 0 &&
|
||||||
@ -2159,6 +2162,8 @@ namespace FRG
|
|||||||
navigation = &navigationMaps[state.activeNavigationIndex];
|
navigation = &navigationMaps[state.activeNavigationIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//logger() << "Location::load step 3" << std::endl;
|
||||||
|
|
||||||
// Restore trigger zone runtime state (by id)
|
// Restore trigger zone runtime state (by id)
|
||||||
if (in.contains("triggerZoneState") && in["triggerZoneState"].is_object()) {
|
if (in.contains("triggerZoneState") && in["triggerZoneState"].is_object()) {
|
||||||
const auto& tzs = in["triggerZoneState"];
|
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)
|
// Restore teleport zone runtime state (by id)
|
||||||
if (in.contains("teleportZoneState") && in["teleportZoneState"].is_object()) {
|
if (in.contains("teleportZoneState") && in["teleportZoneState"].is_object()) {
|
||||||
@ -2180,11 +2186,15 @@ namespace FRG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//logger() << "Location::load step 5" << std::endl;
|
||||||
|
|
||||||
// Player character state
|
// Player character state
|
||||||
if (player && in.contains("playerState")) {
|
if (player && in.contains("playerState")) {
|
||||||
player->state.load(in["playerState"]);
|
player->state.load(in["playerState"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//logger() << "Location::load step 6" << std::endl;
|
||||||
|
|
||||||
// NPC states
|
// NPC states
|
||||||
if (in.contains("npcStates") && in["npcStates"].is_array()) {
|
if (in.contains("npcStates") && in["npcStates"].is_array()) {
|
||||||
const auto& arr = in["npcStates"];
|
const auto& arr = in["npcStates"];
|
||||||
@ -2195,6 +2205,8 @@ namespace FRG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//logger() << "Location::load step 7" << std::endl;
|
||||||
|
|
||||||
// Interactive object states
|
// Interactive object states
|
||||||
if (in.contains("interactiveObjectStates") && in["interactiveObjectStates"].is_array()) {
|
if (in.contains("interactiveObjectStates") && in["interactiveObjectStates"].is_array()) {
|
||||||
const auto& arr = in["interactiveObjectStates"];
|
const auto& arr = in["interactiveObjectStates"];
|
||||||
@ -2202,17 +2214,23 @@ namespace FRG
|
|||||||
interactiveObjects[i].state.load(arr[i]);
|
interactiveObjects[i].state.load(arr[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//logger() << "Location::load step 8" << std::endl;
|
||||||
|
|
||||||
// Dialogue runtime state
|
// Dialogue runtime state
|
||||||
if (in.contains("dialogueState")) {
|
if (in.contains("dialogueState")) {
|
||||||
dialogueSystem.loadDialogueState(in["dialogueState"]);
|
dialogueSystem.loadDialogueState(in["dialogueState"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//logger() << "Location::load step 9" << std::endl;
|
||||||
|
|
||||||
// Lua globals
|
// Lua globals
|
||||||
if (in.contains("scriptGlobals")) {
|
if (in.contains("scriptGlobals")) {
|
||||||
scriptEngine.loadScriptGlobals(in["scriptGlobals"]);
|
scriptEngine.loadScriptGlobals(in["scriptGlobals"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//logger() << "Location::load step 10" << std::endl;
|
||||||
|
|
||||||
|
|
||||||
// Re-wire in-flight, one-shot Lua callbacks (walk-arrival / animation-complete)
|
// 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
|
// that were serialized by name. Must run after scriptEngine has already run the
|
||||||
// location's script (guaranteed: Location::setup() always runs before load())
|
// location's script (guaranteed: Location::setup() always runs before load())
|
||||||
@ -2220,16 +2238,24 @@ namespace FRG
|
|||||||
if (player && !player->state.onArrivedCallbackName.empty()) {
|
if (player && !player->state.onArrivedCallbackName.empty()) {
|
||||||
scriptEngine.rehydrateNamedCallback(player->state.onArrivedCallbackName, player->state.onArrivedCallback);
|
scriptEngine.rehydrateNamedCallback(player->state.onArrivedCallbackName, player->state.onArrivedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//logger() << "Location::load step 11" << std::endl;
|
||||||
|
|
||||||
for (auto& npc : npcs) {
|
for (auto& npc : npcs) {
|
||||||
if (npc && !npc->state.onArrivedCallbackName.empty()) {
|
if (npc && !npc->state.onArrivedCallbackName.empty()) {
|
||||||
scriptEngine.rehydrateNamedCallback(npc->state.onArrivedCallbackName, npc->state.onArrivedCallback);
|
scriptEngine.rehydrateNamedCallback(npc->state.onArrivedCallbackName, npc->state.onArrivedCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//logger() << "Location::load step 12" << std::endl;
|
||||||
|
|
||||||
for (auto& intObj : interactiveObjects) {
|
for (auto& intObj : interactiveObjects) {
|
||||||
if (intObj.state.isAnimating && intObj.state.animTask && !intObj.state.animTask->onCompleteName.empty()) {
|
if (intObj.state.isAnimating && intObj.state.animTask && !intObj.state.animTask->onCompleteName.empty()) {
|
||||||
scriptEngine.rehydrateNamedCallback(intObj.state.animTask->onCompleteName, intObj.state.animTask->onComplete);
|
scriptEngine.rehydrateNamedCallback(intObj.state.animTask->onCompleteName, intObj.state.animTask->onComplete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//logger() << "Location::load step 13" << std::endl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FRG
|
} // namespace FRG
|
||||||
|
|||||||
@ -107,6 +107,8 @@ void LocationState::save(nlohmann::json& out) const
|
|||||||
|
|
||||||
void LocationState::load(const nlohmann::json& in)
|
void LocationState::load(const nlohmann::json& in)
|
||||||
{
|
{
|
||||||
|
//logger() << "LocationState::load step 1" << std::endl;
|
||||||
|
|
||||||
cameraAzimuth = in.value("cameraAzimuth", cameraAzimuth);
|
cameraAzimuth = in.value("cameraAzimuth", cameraAzimuth);
|
||||||
cameraInclination = in.value("cameraInclination", cameraInclination);
|
cameraInclination = in.value("cameraInclination", cameraInclination);
|
||||||
|
|
||||||
@ -122,6 +124,8 @@ void LocationState::load(const nlohmann::json& in)
|
|||||||
|
|
||||||
tutorialInteractiveObjectsLocked = in.value("tutorialInteractiveObjectsLocked", false);
|
tutorialInteractiveObjectsLocked = in.value("tutorialInteractiveObjectsLocked", false);
|
||||||
|
|
||||||
|
//logger() << "LocationState::load step 2" << std::endl;
|
||||||
|
|
||||||
nlohmann::json taxiJson = in["taxi"];
|
nlohmann::json taxiJson = in["taxi"];
|
||||||
|
|
||||||
taxiArrivedCallbackCalled = taxiJson.value("taxiArrivedCallbackCalled", false);
|
taxiArrivedCallbackCalled = taxiJson.value("taxiArrivedCallbackCalled", false);
|
||||||
@ -136,20 +140,18 @@ void LocationState::load(const nlohmann::json& in)
|
|||||||
taxiCarJson.value("positionZ", 0.f)
|
taxiCarJson.value("positionZ", 0.f)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//logger() << "LocationState::load step 3" << std::endl;
|
||||||
|
|
||||||
taxiCar.rotation = taxiCarJson.value("rotation", 0.f);
|
taxiCar.rotation = taxiCarJson.value("rotation", 0.f);
|
||||||
|
|
||||||
taxiCar.velocity = taxiCarJson.value("velocity", 0.f);
|
taxiCar.velocity = taxiCarJson.value("velocity", 0.f);
|
||||||
|
|
||||||
taxiCar.steeringAngle = taxiCarJson.value("steeringAngle", 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));
|
taxiCar.mode = static_cast<NpcCar::Mode>(taxiCarJson.value("mode", 0));
|
||||||
|
|
||||||
|
//logger() << "LocationState::load step 4" << std::endl;
|
||||||
|
|
||||||
taxiCar.waypoints.clear();
|
taxiCar.waypoints.clear();
|
||||||
if (taxiCarJson.contains("waypoints") && taxiCarJson["waypoints"].is_array()) {
|
if (taxiCarJson.contains("waypoints") && taxiCarJson["waypoints"].is_array()) {
|
||||||
for (int i = 0; i < static_cast<int>(taxiCarJson["waypoints"].size()); ++i)
|
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.currentWaypoint = taxiCarJson.value("currentWaypoint", 0);
|
||||||
taxiCar.waypointReachRadius = taxiCarJson.value("waypointReachRadius", 3.f);
|
taxiCar.waypointReachRadius = taxiCarJson.value("waypointReachRadius", 3.f);
|
||||||
taxiCar.wheelAngle = taxiCarJson.value("wheelAngle", 0.f);
|
taxiCar.wheelAngle = taxiCarJson.value("wheelAngle", 0.f);
|
||||||
@ -167,6 +171,8 @@ void LocationState::load(const nlohmann::json& in)
|
|||||||
|
|
||||||
//--------- Taxi initial state -----
|
//--------- Taxi initial state -----
|
||||||
|
|
||||||
|
//logger() << "LocationState::load step 6" << std::endl;
|
||||||
|
|
||||||
nlohmann::json taxiCarInitialStateJson = taxiJson["taxiCarInitialState"];
|
nlohmann::json taxiCarInitialStateJson = taxiJson["taxiCarInitialState"];
|
||||||
|
|
||||||
taxiCarInitialState.position = Eigen::Vector3f(
|
taxiCarInitialState.position = Eigen::Vector3f(
|
||||||
@ -180,15 +186,12 @@ void LocationState::load(const nlohmann::json& in)
|
|||||||
taxiCarInitialState.velocity = taxiCarInitialStateJson.value("velocity", 0.f);
|
taxiCarInitialState.velocity = taxiCarInitialStateJson.value("velocity", 0.f);
|
||||||
|
|
||||||
taxiCarInitialState.steeringAngle = taxiCarInitialStateJson.value("steeringAngle", 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));
|
taxiCarInitialState.mode = static_cast<NpcCar::Mode>(taxiCarInitialStateJson.value("mode", 0));
|
||||||
|
|
||||||
|
//logger() << "LocationState::load step 7" << std::endl;
|
||||||
|
|
||||||
|
|
||||||
taxiCarInitialState.waypoints.clear();
|
taxiCarInitialState.waypoints.clear();
|
||||||
if (taxiCarInitialStateJson.contains("waypoints") && taxiCarInitialStateJson["waypoints"].is_array()) {
|
if (taxiCarInitialStateJson.contains("waypoints") && taxiCarInitialStateJson["waypoints"].is_array()) {
|
||||||
for (int i = 0; i < static_cast<int>(taxiCarInitialStateJson["waypoints"].size()); ++i)
|
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.currentWaypoint = taxiCarInitialStateJson.value("currentWaypoint", 0);
|
||||||
taxiCarInitialState.waypointReachRadius = taxiCarInitialStateJson.value("waypointReachRadius", 3.f);
|
taxiCarInitialState.waypointReachRadius = taxiCarInitialStateJson.value("waypointReachRadius", 3.f);
|
||||||
taxiCarInitialState.wheelAngle = taxiCarInitialStateJson.value("wheelAngle", 0.f);
|
taxiCarInitialState.wheelAngle = taxiCarInitialStateJson.value("wheelAngle", 0.f);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
taxiCarWaypoints.clear();
|
taxiCarWaypoints.clear();
|
||||||
if (taxiJson.contains("taxiDefaultWaypoints") && taxiJson["taxiDefaultWaypoints"].is_array()) {
|
if (taxiJson.contains("taxiDefaultWaypoints") && taxiJson["taxiDefaultWaypoints"].is_array()) {
|
||||||
for (int i = 0; i < static_cast<int>(taxiJson["taxiDefaultWaypoints"].size()); ++i)
|
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(
|
taxiCarTriggerPosition = Eigen::Vector3f(
|
||||||
taxiJson.value("taxiCarTriggerPositionX", 0.f),
|
taxiJson.value("taxiCarTriggerPositionX", 0.f),
|
||||||
@ -221,6 +228,9 @@ void LocationState::load(const nlohmann::json& in)
|
|||||||
taxiJson.value("taxiCarTriggerPositionZ", 0.f)
|
taxiJson.value("taxiCarTriggerPositionZ", 0.f)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//logger() << "LocationState::load step 10" << std::endl;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FRG
|
} // namespace FRG
|
||||||
|
|||||||
@ -31,7 +31,7 @@ struct LocationState : public ISaveable {
|
|||||||
NpcCar taxiCarInitialState;
|
NpcCar taxiCarInitialState;
|
||||||
NpcCar taxiCar;
|
NpcCar taxiCar;
|
||||||
bool taxiArrivedCallbackCalled = false;
|
bool taxiArrivedCallbackCalled = false;
|
||||||
Vector3f taxiCarTriggerPosition;
|
Vector3f taxiCarTriggerPosition = Vector3f(0,0,0);
|
||||||
std::vector<Vector3f> taxiCarWaypoints;
|
std::vector<Vector3f> taxiCarWaypoints;
|
||||||
|
|
||||||
// (trigger/teleport zone runtime flags are serialised by id directly in Location::save/load)
|
// (trigger/teleport zone runtime flags are serialised by id directly in Location::save/load)
|
||||||
|
|||||||
@ -10,8 +10,16 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
#include <emscripten.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace FRG {
|
namespace FRG {
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
//extern Game* Game::s_instance;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Localization
|
// Localization
|
||||||
static const std::string EMPTY_LANGUAGE_RU = u8"(пусто)";
|
static const std::string EMPTY_LANGUAGE_RU = u8"(пусто)";
|
||||||
static const std::string EMPTY_LANGUAGE_EN = "(empty)";
|
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_RU = u8"Вернуться в меню";
|
||||||
static const std::string BACK_TO_MAIN_MENU_EN = "Back to Main Menu";
|
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) {
|
static int questStatusPriority(Quest::QuestStatus status) {
|
||||||
switch (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) :
|
MenuManager::MenuManager(Renderer& iRenderer, GameState& gameState, AudioPlayerAsync& audioPlayer) :
|
||||||
renderer(iRenderer),
|
renderer(iRenderer),
|
||||||
gameState_(gameState),
|
gameState_(gameState),
|
||||||
@ -254,7 +339,13 @@ namespace FRG {
|
|||||||
|
|
||||||
creditsScreenRoot = loadLocalizedUi("resources/w/ui/screen_credits.json", zipFile);
|
creditsScreenRoot = loadLocalizedUi("resources/w/ui/screen_credits.json", zipFile);
|
||||||
portraitCreditsScreenRoot = loadLocalizedUi("resources/w/ui/portrait_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);
|
settingsScreenRoot = loadLocalizedUi("resources/w/ui/screen_settings.json", zipFile);
|
||||||
|
|
||||||
|
|
||||||
settingsGraphicsScreenRoot = loadLocalizedUi("resources/w/ui/screen_settings_graphics.json", zipFile);
|
settingsGraphicsScreenRoot = loadLocalizedUi("resources/w/ui/screen_settings_graphics.json", zipFile);
|
||||||
languageScreenRoot = loadLocalizedUi("resources/w/ui/screen_language.json", zipFile);
|
languageScreenRoot = loadLocalizedUi("resources/w/ui/screen_language.json", zipFile);
|
||||||
loadSavedGameScreenRoot = loadLocalizedUi("resources/w/ui/screen_load.json", zipFile);
|
loadSavedGameScreenRoot = loadLocalizedUi("resources/w/ui/screen_load.json", zipFile);
|
||||||
@ -375,7 +466,8 @@ namespace FRG {
|
|||||||
void MenuManager::showMainMenu() {
|
void MenuManager::showMainMenu() {
|
||||||
gameState_.currentLocationName.clear();
|
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;
|
uiState_ = GameUiState::MainMenu;
|
||||||
uiManager.clearMenuStack();
|
uiManager.clearMenuStack();
|
||||||
@ -386,36 +478,46 @@ namespace FRG {
|
|||||||
|
|
||||||
|
|
||||||
uiManager.setTextButtonCallback("menuStartButton", [this](const std::string&) {
|
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();
|
enterGameplay();
|
||||||
|
|
||||||
});
|
});
|
||||||
uiManager.setTextButtonCallback("menuAboutButton", [this](const std::string&) {
|
uiManager.setTextButtonCallback("menuAboutButton", [this](const std::string&) {
|
||||||
showAboutScreen();
|
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&) {
|
uiManager.setTextButtonCallback("menuCreditsButton", [this](const std::string&) {
|
||||||
showCreditsScreen();
|
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&) {
|
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();
|
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&) {
|
uiManager.setTextButtonCallback("menuLanguageButton", [this](const std::string&) {
|
||||||
showLanguageScreen();
|
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&) {
|
uiManager.setTextButtonCallback("menuLoadButton", [this](const std::string&) {
|
||||||
showLoadGameScreen();
|
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__)
|
#if !defined(EMSCRIPTEN) && !defined(__ANDROID__)
|
||||||
uiManager.setTextButtonCallback("menuExitButton", [this](const std::string&) {
|
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;
|
Environment::exitGameLoop = true;
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
@ -426,7 +528,8 @@ namespace FRG {
|
|||||||
void MenuManager::showMenuAfterGameCompleted() {
|
void MenuManager::showMenuAfterGameCompleted() {
|
||||||
//gameState_.currentLocationName.clear();
|
//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;
|
uiState_ = GameUiState::MainMenu;
|
||||||
uiManager.clearMenuStack();
|
uiManager.clearMenuStack();
|
||||||
@ -437,36 +540,36 @@ namespace FRG {
|
|||||||
|
|
||||||
|
|
||||||
uiManager.setTextButtonCallback("menuStartButton", [this](const std::string&) {
|
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();
|
enterGameplay();
|
||||||
|
|
||||||
});
|
});
|
||||||
uiManager.setTextButtonCallback("menuAboutButton", [this](const std::string&) {
|
uiManager.setTextButtonCallback("menuAboutButton", [this](const std::string&) {
|
||||||
showAboutScreen();
|
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&) {
|
uiManager.setTextButtonCallback("menuCreditsButton", [this](const std::string&) {
|
||||||
showCreditsScreen();
|
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&) {
|
uiManager.setTextButtonCallback("menuSettingsButton", [this](const std::string&) {
|
||||||
showSettingsScreen();
|
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&) {
|
uiManager.setTextButtonCallback("menuLanguageButton", [this](const std::string&) {
|
||||||
showLanguageScreen();
|
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&) {
|
uiManager.setTextButtonCallback("menuLoadButton", [this](const std::string&) {
|
||||||
showLoadGameScreen();
|
showLoadGameScreen();
|
||||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
uiManager.setTextButtonCallback("menuExitButton", [this](const std::string&) {
|
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;
|
Environment::exitGameLoop = true;
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
@ -479,21 +582,21 @@ namespace FRG {
|
|||||||
uiManager.setTextButtonCallback("aboutBackButton", [this](const std::string&) {
|
uiManager.setTextButtonCallback("aboutBackButton", [this](const std::string&) {
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
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&) {
|
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/");
|
SDL_OpenURL("https://store.steampowered.com/app/4801010/");
|
||||||
});
|
});
|
||||||
|
|
||||||
uiManager.setTextButtonCallback("telegramButton", [this](const std::string&) {
|
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");
|
SDL_OpenURL("https://telegram.me/fishrungames");
|
||||||
});
|
});
|
||||||
|
|
||||||
uiManager.setTextButtonCallback("discordButton", [this](const std::string&) {
|
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");
|
SDL_OpenURL("https://discord.gg/S8jgGHXjw");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -503,29 +606,29 @@ namespace FRG {
|
|||||||
uiManager.setTextButtonCallback("aboutBackButton", [this](const std::string&) {
|
uiManager.setTextButtonCallback("aboutBackButton", [this](const std::string&) {
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
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&) {
|
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/");
|
SDL_OpenURL("https://store.steampowered.com/app/4801010/");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
uiManager.setTextButtonCallback("websiteButton", [this](const std::string&) {
|
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/");
|
SDL_OpenURL("https://shadowoverbishkek.com/");
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uiManager.setTextButtonCallback("telegramButton", [this](const std::string&) {
|
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");
|
SDL_OpenURL("https://telegram.me/fishrungames");
|
||||||
});
|
});
|
||||||
|
|
||||||
uiManager.setTextButtonCallback("discordButton", [this](const std::string&) {
|
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");
|
SDL_OpenURL("https://discord.gg/S8jgGHXjw");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -561,7 +664,7 @@ namespace FRG {
|
|||||||
isPlayerInCreditsMenu = false;
|
isPlayerInCreditsMenu = false;
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
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();
|
uiManager.updateAllLayouts();
|
||||||
@ -577,12 +680,12 @@ namespace FRG {
|
|||||||
saveSettings();
|
saveSettings();
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
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&) {
|
uiManager.setTextButtonCallback("graphicsButton", [this](const std::string&) {
|
||||||
showSettingsGraphicsScreen();
|
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
|
// Set initial slider positions before registering callbacks so
|
||||||
@ -658,7 +761,7 @@ namespace FRG {
|
|||||||
/*uiManager.setTextButtonColor("musicToggleButton", on
|
/*uiManager.setTextButtonColor("musicToggleButton", on
|
||||||
? std::array<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
? std::array<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
||||||
: std::array<float, 4>{ 0.9f, 0.2f, 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&) {
|
uiManager.setTextButtonCallback("soundToggleButton", [this](const std::string&) {
|
||||||
audioPlayer_.setSoundEnabled(!audioPlayer_.isSoundEnabled());
|
audioPlayer_.setSoundEnabled(!audioPlayer_.isSoundEnabled());
|
||||||
@ -667,7 +770,7 @@ namespace FRG {
|
|||||||
/*uiManager.setTextButtonColor("soundToggleButton", on
|
/*uiManager.setTextButtonColor("soundToggleButton", on
|
||||||
? std::array<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
? std::array<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
||||||
: std::array<float, 4>{ 0.9f, 0.2f, 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&) {
|
uiManager.setTextButtonCallback("settingsBackButton", [this](const std::string&) {
|
||||||
saveSettings();
|
saveSettings();
|
||||||
uiManager.popMenu();
|
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();
|
uiManager.updateAllLayouts();
|
||||||
topUiManager.updateAllLayouts();
|
topUiManager.updateAllLayouts();
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
playerInSettingsNoMusicGraphics = false;
|
||||||
|
#endif
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -718,7 +824,7 @@ namespace FRG {
|
|||||||
uiManager.setTextButtonCallback("fullScreenToggleButton", [this, refreshFullscreenToggle](const std::string&) {
|
uiManager.setTextButtonCallback("fullScreenToggleButton", [this, refreshFullscreenToggle](const std::string&) {
|
||||||
Environment::setFullscreen(!Environment::isFullscreen);
|
Environment::setFullscreen(!Environment::isFullscreen);
|
||||||
refreshFullscreenToggle();
|
refreshFullscreenToggle();
|
||||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||||
uiManager.updateAllLayouts();
|
uiManager.updateAllLayouts();
|
||||||
topUiManager.updateAllLayouts();
|
topUiManager.updateAllLayouts();
|
||||||
});
|
});
|
||||||
@ -728,27 +834,148 @@ namespace FRG {
|
|||||||
bool newValue = !this->shadowsEnabled;
|
bool newValue = !this->shadowsEnabled;
|
||||||
shadowMapSettingsChangedFunc(newValue);
|
shadowMapSettingsChangedFunc(newValue);
|
||||||
refreshShadowToggle();
|
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() {
|
void MenuManager::showLanguageScreen() {
|
||||||
uiManager.pushMenuFromSavedRoot(languageScreenRoot);
|
uiManager.pushMenuFromSavedRoot(languageScreenRoot);
|
||||||
uiManager.setTextButtonCallback("languageBackButton", [this](const std::string&) {
|
uiManager.setTextButtonCallback("languageBackButton", [this](const std::string&) {
|
||||||
saveSettings();
|
saveSettings();
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
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&) {
|
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;
|
FRG::g_currentLanguage = FRG::Language::Russian;
|
||||||
reloadLocalizedGameContent();
|
reloadLocalizedGameContent();
|
||||||
saveSettings();
|
saveSettings();
|
||||||
});
|
});
|
||||||
uiManager.setTextButtonCallback("languageEnglishButton", [this](const std::string&) {
|
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;
|
FRG::g_currentLanguage = FRG::Language::English;
|
||||||
reloadLocalizedGameContent();
|
reloadLocalizedGameContent();
|
||||||
saveSettings();
|
saveSettings();
|
||||||
@ -810,7 +1037,7 @@ namespace FRG {
|
|||||||
uiManager.setTextButtonCallback("loadBackButton", [this](const std::string&) {
|
uiManager.setTextButtonCallback("loadBackButton", [this](const std::string&) {
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
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] = {
|
static const char* kSlotButtons[4] = {
|
||||||
@ -837,7 +1064,7 @@ namespace FRG {
|
|||||||
uiManager.setTextButtonText(kSlotButtons[i], label);
|
uiManager.setTextButtonText(kSlotButtons[i], label);
|
||||||
}
|
}
|
||||||
uiManager.setTextButtonCallback(kSlotButtons[i], [this, slot](const std::string&) {
|
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);
|
if (onLoadGame) onLoadGame(slot);
|
||||||
enterGameplay(true);
|
enterGameplay(true);
|
||||||
});
|
});
|
||||||
@ -850,7 +1077,7 @@ namespace FRG {
|
|||||||
uiManager.setTextButtonCallback("saveBackButton", [this](const std::string&) {
|
uiManager.setTextButtonCallback("saveBackButton", [this](const std::string&) {
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
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] = {
|
static const char* kSlotButtons[4] = {
|
||||||
@ -896,7 +1123,7 @@ namespace FRG {
|
|||||||
? emptyText
|
? emptyText
|
||||||
: localizedLocationName[info.locationName][FRG::g_currentLanguage] + " " + info.savedAt;
|
: localizedLocationName[info.locationName][FRG::g_currentLanguage] + " " + info.savedAt;
|
||||||
uiManager.setTextButtonText(buttonName, label);
|
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);
|
selectInventoryItem(i);
|
||||||
//logger() << "Callback called for button number " << i << " END" << std::endl;
|
//logger() << "Callback called for button number " << i << " END" << std::endl;
|
||||||
|
|
||||||
audioPlayer_.playSoundAsync("audio/sound_bag001.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/sound_bag001.ogg");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
uiManager.setNodeVisible(btnName, false);
|
uiManager.setNodeVisible(btnName, false);
|
||||||
@ -953,7 +1180,7 @@ namespace FRG {
|
|||||||
selectInventoryItem(0);
|
selectInventoryItem(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
audioPlayer_.playSoundAsync("audio/sound_bag_open.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/sound_bag_open.ogg");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::selectInventoryItem(int index) {
|
void MenuManager::selectInventoryItem(int index) {
|
||||||
@ -1005,7 +1232,7 @@ namespace FRG {
|
|||||||
for (int i = 0; i < inventoryMenuStackDepth_; ++i) uiManager.popMenu();
|
for (int i = 0; i < inventoryMenuStackDepth_; ++i) uiManager.popMenu();
|
||||||
inventoryMenuStackDepth_ = 0;
|
inventoryMenuStackDepth_ = 0;
|
||||||
uiManager.updateAllLayouts();
|
uiManager.updateAllLayouts();
|
||||||
audioPlayer_.playSoundAsync("audio/sound_bag_close.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/sound_bag_close.ogg");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::openQuestJournal() {
|
void MenuManager::openQuestJournal() {
|
||||||
@ -1021,12 +1248,12 @@ namespace FRG {
|
|||||||
|
|
||||||
uiManager.setButtonCallback("journalExitButton", [this](const std::string&) {
|
uiManager.setButtonCallback("journalExitButton", [this](const std::string&) {
|
||||||
closeQuestJournal();
|
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&) {
|
uiManager.setButtonCallback("journalExitButton2", [this](const std::string&) {
|
||||||
closeQuestJournal();
|
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) {
|
for (int i = 0; i < 9; ++i) {
|
||||||
uiManager.setTextButtonCallback(kItemNames[i], [this, i](const std::string&) {
|
uiManager.setTextButtonCallback(kItemNames[i], [this, i](const std::string&) {
|
||||||
selectQuestByIndex(i);
|
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);
|
selectQuestByIndex(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
audioPlayer_.playSoundAsync("audio/255571__stradie__flipping-paper.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/255571__stradie__flipping-paper.ogg");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::closeQuestJournal() {
|
void MenuManager::closeQuestJournal() {
|
||||||
@ -1101,24 +1328,24 @@ namespace FRG {
|
|||||||
|
|
||||||
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
||||||
closePhoneEntirely();
|
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("phoneMain", [this](const std::string&) {});
|
||||||
uiManager.setButtonCallback("phoneMessenger", [this](const std::string&) {
|
uiManager.setButtonCallback("phoneMessenger", [this](const std::string&) {
|
||||||
openPhoneMessenger();
|
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&) {
|
uiManager.setButtonCallback("phoneBank", [this](const std::string&) {
|
||||||
openPhoneBank();
|
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&) {
|
uiManager.setButtonCallback("phoneVideo", [this](const std::string&) {
|
||||||
openPhoneVideo();
|
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&) {
|
uiManager.setButtonCallback("phoneTaxi", [this](const std::string&) {
|
||||||
openPhoneTaxi();
|
openPhoneTaxi();
|
||||||
audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
|
||||||
});
|
});
|
||||||
|
|
||||||
if (gameState_.isNight)
|
if (gameState_.isNight)
|
||||||
@ -1143,7 +1370,7 @@ namespace FRG {
|
|||||||
uiManager.setNodeVisible("phoneTimeDawn", false);
|
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() {
|
void MenuManager::openPhoneMessenger() {
|
||||||
@ -1161,23 +1388,23 @@ namespace FRG {
|
|||||||
|
|
||||||
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
||||||
closePhoneEntirely();
|
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("phoneMain", [this](const std::string&) {});
|
||||||
uiManager.setTextButtonCallback("chat1button", [this](const std::string&) {
|
uiManager.setTextButtonCallback("chat1button", [this](const std::string&) {
|
||||||
gameState_.chatUnread[0] = false;
|
gameState_.chatUnread[0] = false;
|
||||||
openPhoneChatFromList(0, isPortraitMode() ? verticalPhoneChat1Root : phoneChat1Root);
|
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&) {
|
uiManager.setTextButtonCallback("chat2button", [this](const std::string&) {
|
||||||
gameState_.chatUnread[1] = false;
|
gameState_.chatUnread[1] = false;
|
||||||
openPhoneChatFromList(1, isPortraitMode() ? verticalPhoneChat2Root : phoneChat2Root);
|
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&) {
|
uiManager.setTextButtonCallback("chat3button", [this](const std::string&) {
|
||||||
gameState_.chatUnread[2] = false;
|
gameState_.chatUnread[2] = false;
|
||||||
openPhoneChatFromList(2, isPortraitMode() ? verticalPhoneChat3Root : phoneChat3Root);
|
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&) {
|
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
||||||
closePhoneEntirely();
|
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("phoneMain", [this](const std::string&) {});
|
||||||
uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
|
uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
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&) {
|
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
||||||
closePhoneEntirely();
|
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("phoneMain", [this](const std::string&) {});
|
||||||
uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
|
uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
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&) {
|
uiManager.setButtonCallback("videoSkip", [this](const std::string&) {
|
||||||
closePhoneEntirely();
|
closePhoneEntirely();
|
||||||
@ -1252,7 +1479,7 @@ namespace FRG {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
audioPlayer_.playSoundAsync("audio/78564__joedeshon__alarm_clock_ticking_02.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/78564__joedeshon__alarm_clock_ticking_02.ogg");
|
||||||
startNightTransitionFunc();
|
startNightTransitionFunc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1277,13 +1504,13 @@ namespace FRG {
|
|||||||
|
|
||||||
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
||||||
closePhoneEntirely();
|
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("phoneMain", [this](const std::string&) {});
|
||||||
uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
|
uiManager.setButtonCallback("buttonBack", [this](const std::string&) {
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
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&) {
|
uiManager.setButtonCallback("mapGo", [this](const std::string&) {
|
||||||
gameState_.tutorialNeedOpenTaxiScreen = false;
|
gameState_.tutorialNeedOpenTaxiScreen = false;
|
||||||
@ -1298,17 +1525,17 @@ namespace FRG {
|
|||||||
gameState_.money -= 500;
|
gameState_.money -= 500;
|
||||||
closePhoneEntirely();
|
closePhoneEntirely();
|
||||||
if (startDialogueFunc) startDialogueFunc("dialog_taxi002");
|
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
|
else
|
||||||
{
|
{
|
||||||
closePhoneEntirely();
|
closePhoneEntirely();
|
||||||
if (startDialogueFunc) startDialogueFunc("dialog_taxi004");
|
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) {
|
void MenuManager::openPhoneChatFromList(int chatIndex, std::shared_ptr<UiNode> chatRoot) {
|
||||||
@ -1348,12 +1575,12 @@ namespace FRG {
|
|||||||
|
|
||||||
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
||||||
closePhoneScreenFromChat();
|
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.setButtonCallback("phoneMain", [this](const std::string&) {});
|
||||||
uiManager.setTextButtonCallback("chatTitleButton", [this](const std::string&) {
|
uiManager.setTextButtonCallback("chatTitleButton", [this](const std::string&) {
|
||||||
returnToPhoneChatList();
|
returnToPhoneChatList();
|
||||||
audioPlayer_.playSoundAsync("audio/537061__imafoley__message-pop-sound.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
|
||||||
});
|
});
|
||||||
|
|
||||||
if (chatOpenCallback) {
|
if (chatOpenCallback) {
|
||||||
@ -1437,37 +1664,37 @@ namespace FRG {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::showModalMenuScreen() {
|
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.pushMenuFromSavedRoot(modalMenuRoot);
|
||||||
uiManager.setButtonCallback("modalExitButton", [this](const std::string&) {
|
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.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
uiManager.updateAllLayouts();
|
||||||
});
|
});
|
||||||
|
|
||||||
uiManager.setButtonCallback("menuCloseButton", [this](const std::string&) {
|
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.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
uiManager.updateAllLayouts();
|
||||||
});
|
});
|
||||||
|
|
||||||
uiManager.setButtonCallback("menuSaveButton", [this](const std::string&) {
|
uiManager.setButtonCallback("menuSaveButton", [this](const std::string&) {
|
||||||
showSaveGameScreen();
|
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&) {
|
uiManager.setButtonCallback("menuLoadButton", [this](const std::string&) {
|
||||||
showLoadGameScreen();
|
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&) {
|
uiManager.setButtonCallback("menuSettingsButton", [this](const std::string&) {
|
||||||
showSettingsScreen();
|
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&) {
|
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();
|
uiManager.popMenu();
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
});
|
});
|
||||||
@ -1579,29 +1806,35 @@ namespace FRG {
|
|||||||
setupGameplayHudCallbacks();
|
setupGameplayHudCallbacks();
|
||||||
if (gameState_.isDarklands)
|
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)
|
else if (gameState_.isNight)
|
||||||
{
|
{
|
||||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek night.ogg");
|
audioPlayer_.crossFadeMusicAsync("music/bishkek night.ogg");
|
||||||
|
lastMusicTrack = "music/bishkek night.ogg";
|
||||||
}
|
}
|
||||||
else
|
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") {
|
} else if (locationName == "uni_interior") {
|
||||||
applyUniIntHud();
|
applyUniIntHud();
|
||||||
if (gameState_.isDarklands)
|
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)
|
else if (gameState_.isNight)
|
||||||
{
|
{
|
||||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek night.ogg");
|
audioPlayer_.crossFadeMusicAsync("music/bishkek night.ogg");
|
||||||
|
lastMusicTrack = "music/bishkek night.ogg";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek univer day.ogg");
|
audioPlayer_.crossFadeMusicAsync("music/bishkek univer day.ogg");
|
||||||
|
lastMusicTrack = "music/bishkek univer day.ogg";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Returning to dorm: reuse step5ab, suppress already-completed hints
|
// Returning to dorm: reuse step5ab, suppress already-completed hints
|
||||||
@ -1611,11 +1844,13 @@ namespace FRG {
|
|||||||
|
|
||||||
if (gameState_.isNight)
|
if (gameState_.isNight)
|
||||||
{
|
{
|
||||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek night.ogg");
|
audioPlayer_.crossFadeMusicAsync("music/bishkek night.ogg");
|
||||||
|
lastMusicTrack = "music/bishkek night.ogg";
|
||||||
}
|
}
|
||||||
else
|
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&) {
|
uiManager.setButtonCallback("inventoryExitButton", [this](const std::string&) {
|
||||||
closePortraitInventoryItem();
|
closePortraitInventoryItem();
|
||||||
audioPlayer_.playSoundAsync("audio/sound_bag001.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/sound_bag001.ogg");
|
||||||
});
|
});
|
||||||
|
|
||||||
uiManager.setButtonCallback("inventoryExitButton2", [this](const std::string&) {
|
uiManager.setButtonCallback("inventoryExitButton2", [this](const std::string&) {
|
||||||
closePortraitInventoryItem();
|
closePortraitInventoryItem();
|
||||||
audioPlayer_.playSoundAsync("audio/sound_bag001.ogg");
|
audioPlayer_.playSoundAsync("resources/audio/sound_bag001.ogg");
|
||||||
});
|
});
|
||||||
|
|
||||||
uiManager.setButtonCallback("inventoryMain", [this](const std::string&) {});
|
uiManager.setButtonCallback("inventoryMain", [this](const std::string&) {});
|
||||||
@ -1880,12 +2115,12 @@ namespace FRG {
|
|||||||
|
|
||||||
uiManager.setButtonCallback("journalItemExitButton", [this](const std::string&) {
|
uiManager.setButtonCallback("journalItemExitButton", [this](const std::string&) {
|
||||||
closePortraitJournalItem();
|
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&) {
|
uiManager.setButtonCallback("journalItemExitButton2", [this](const std::string&) {
|
||||||
closeQuestJournal();
|
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();
|
recomputePhoneChatPositions();
|
||||||
}
|
}
|
||||||
chatUiManager.startPopIn(nodeName, 300.0f);
|
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)
|
void MenuManager::setDarklandsMode(bool enabled)
|
||||||
|
|||||||
@ -130,7 +130,20 @@ namespace FRG {
|
|||||||
std::shared_ptr<Texture> languageLoadingEn;
|
std::shared_ptr<Texture> languageLoadingEn;
|
||||||
|
|
||||||
bool shadowsEnabled = true;
|
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:
|
protected:
|
||||||
Renderer& renderer;
|
Renderer& renderer;
|
||||||
@ -176,6 +189,10 @@ namespace FRG {
|
|||||||
void showCreditsScreen();
|
void showCreditsScreen();
|
||||||
void showSettingsScreen();
|
void showSettingsScreen();
|
||||||
void showSettingsGraphicsScreen();
|
void showSettingsGraphicsScreen();
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
void showSettingsNoMusicWebScreen();
|
||||||
|
#endif
|
||||||
void showLanguageScreen();
|
void showLanguageScreen();
|
||||||
void showLoadGameScreen();
|
void showLoadGameScreen();
|
||||||
void showSaveGameScreen();
|
void showSaveGameScreen();
|
||||||
@ -268,6 +285,9 @@ namespace FRG {
|
|||||||
std::shared_ptr<UiNode> creditsScreenRoot;
|
std::shared_ptr<UiNode> creditsScreenRoot;
|
||||||
std::shared_ptr<UiNode> portraitCreditsScreenRoot;
|
std::shared_ptr<UiNode> portraitCreditsScreenRoot;
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
std::shared_ptr<UiNode> settingsScreenNoMusicWebRoot;
|
||||||
|
#endif
|
||||||
std::shared_ptr<UiNode> settingsScreenRoot;
|
std::shared_ptr<UiNode> settingsScreenRoot;
|
||||||
std::shared_ptr<UiNode> settingsGraphicsScreenRoot;
|
std::shared_ptr<UiNode> settingsGraphicsScreenRoot;
|
||||||
std::shared_ptr<UiNode> languageScreenRoot;
|
std::shared_ptr<UiNode> languageScreenRoot;
|
||||||
@ -343,6 +363,7 @@ namespace FRG {
|
|||||||
std::string fullScreenToggleOff;
|
std::string fullScreenToggleOff;
|
||||||
std::string shadowToggleOn;
|
std::string shadowToggleOn;
|
||||||
std::string shadowToggleOff;
|
std::string shadowToggleOff;
|
||||||
|
std::string musicDownloadProgressPrefix;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -105,12 +105,20 @@ namespace FRG
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> readFileFromZIP(const std::string& filename, const std::string& zipfilename) {
|
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
|
#ifdef EMSCRIPTEN
|
||||||
|
|
||||||
|
std::string webZipFilename = getPersistentResourcePath(zipfilename);
|
||||||
|
|
||||||
int zipErr = 0;
|
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) {
|
if (!archive) {
|
||||||
logger() << "Failed to open ZIP: " << zipfilename << " (error code " << zipErr << ")" << std::endl;
|
logger() << "Failed to open ZIP: " << webZipFilename << " (error code " << zipErr << ")" << std::endl;
|
||||||
throw std::runtime_error("Failed to open ZIP: " + zipfilename);
|
throw std::runtime_error("Failed to open ZIP: " + webZipFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cleanFilename = filename;
|
std::string cleanFilename = filename;
|
||||||
@ -120,14 +128,14 @@ namespace FRG
|
|||||||
zip_stat_init(&fileStat);
|
zip_stat_init(&fileStat);
|
||||||
if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0 || !(fileStat.valid & ZIP_STAT_SIZE)) {
|
if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0 || !(fileStat.valid & ZIP_STAT_SIZE)) {
|
||||||
zip_close(archive);
|
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);
|
throw std::runtime_error("Can't stat file in ZIP: " + cleanFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
|
zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
|
||||||
if (!zipFile) {
|
if (!zipFile) {
|
||||||
zip_close(archive);
|
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);
|
throw std::runtime_error("Can't open file in ZIP: " + cleanFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,11 +154,6 @@ namespace FRG
|
|||||||
return fileData;
|
return fileData;
|
||||||
#else
|
#else
|
||||||
|
|
||||||
if (zipfilename.empty()) {
|
|
||||||
std::cerr << "readFileFromZIP: zipfilename empty" << std::endl;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int zipErr = 0;
|
int zipErr = 0;
|
||||||
zip_t* archive = zip_open(zipfilename.c_str(), ZIP_RDONLY, &zipErr);
|
zip_t* archive = zip_open(zipfilename.c_str(), ZIP_RDONLY, &zipErr);
|
||||||
if (!archive) {
|
if (!archive) {
|
||||||
@ -197,6 +200,39 @@ namespace FRG
|
|||||||
#endif
|
#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)
|
bool findString(const char* in, char* list)
|
||||||
{
|
{
|
||||||
size_t thisLength = strlen(in);
|
size_t thisLength = strlen(in);
|
||||||
@ -481,4 +517,31 @@ namespace FRG
|
|||||||
// Если логирование отключено, возвращаем заглушку.
|
// Если логирование отключено, возвращаем заглушку.
|
||||||
return g_nullStream;
|
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);
|
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);
|
bool findString(const char* in, char* list);
|
||||||
|
|
||||||
std::filesystem::path getSaveDirectory();
|
std::filesystem::path getSaveDirectory();
|
||||||
@ -38,4 +40,10 @@ namespace FRG
|
|||||||
void initLogger();
|
void initLogger();
|
||||||
|
|
||||||
std::ostream& logger();
|
std::ostream& logger();
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
std::string getPersistentResourcePath(const std::string& filename);
|
||||||
|
|
||||||
|
bool isZipValid(const std::string& fullPath);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user