Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9c888e2bf | ||
|
|
a743f653dd | ||
|
|
3d3522b905 | ||
|
|
b6cd17449f | ||
|
|
159fac02f0 | ||
|
|
9f17d51fc8 | ||
|
|
ca73148f4b | ||
|
|
0c24d52e6d | ||
|
|
beb4fee0d8 | ||
|
|
a7d17d9afd | ||
|
|
76287dda46 | ||
|
|
9da0ae1016 |
No files matched your search
+94
-13
@@ -1,10 +1,17 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(witcher001 LANGUAGES C CXX)
|
||||
project(ShadowOverBishkekDemo LANGUAGES C CXX)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
message(STATUS "Setting build type to 'Release' as none was specified.")
|
||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
option(STEAMSDK "Enable Steamworks SDK integration" OFF)
|
||||
|
||||
# ===========================================
|
||||
# Поиск системных зависимостей Linux
|
||||
# ===========================================
|
||||
@@ -62,9 +69,9 @@ endif()
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Основной проект witcher001
|
||||
# Основной проект ShadowOverBishkekDemo
|
||||
# ===========================================
|
||||
add_executable(witcher001
|
||||
add_executable(ShadowOverBishkekDemo
|
||||
../src/main.cpp
|
||||
../src/Game.cpp
|
||||
../src/Game.h
|
||||
@@ -158,7 +165,7 @@ add_executable(witcher001
|
||||
)
|
||||
|
||||
# include-пути проекта
|
||||
target_include_directories(witcher001 PRIVATE
|
||||
target_include_directories(ShadowOverBishkekDemo PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
${SDL2_TTF_INCLUDE_DIRS}
|
||||
@@ -166,19 +173,18 @@ target_include_directories(witcher001 PRIVATE
|
||||
${LIBZIP_INCLUDE_DIRS}
|
||||
${LUA_INCLUDE_DIRS}
|
||||
${EIGEN3_INCLUDE_DIRS}
|
||||
# sol2 - header-only, берем напрямую из thirdparty (если он там есть)
|
||||
# или можно установить системный libsol-dev и убрать этот путь
|
||||
# sol2 - header-only, берем напрямую из thirdparty
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/sol2-3.3.0/include"
|
||||
)
|
||||
|
||||
# Убираем WIN32_LEAN_AND_MEAN
|
||||
target_compile_definitions(witcher001 PRIVATE
|
||||
target_compile_definitions(ShadowOverBishkekDemo PRIVATE
|
||||
PNG_ENABLED
|
||||
SDL_MAIN_HANDLED
|
||||
)
|
||||
|
||||
# Линковка директорий для pkg-config (если пакеты не в /usr/lib)
|
||||
target_link_directories(witcher001 PRIVATE
|
||||
target_link_directories(ShadowOverBishkekDemo PRIVATE
|
||||
${SDL2_LIBRARY_DIRS}
|
||||
${SDL2_TTF_LIBRARY_DIRS}
|
||||
${SDL2_MIXER_LIBRARY_DIRS}
|
||||
@@ -187,7 +193,7 @@ target_link_directories(witcher001 PRIVATE
|
||||
)
|
||||
|
||||
# Линковка библиотек
|
||||
target_link_libraries(witcher001 PRIVATE
|
||||
target_link_libraries(ShadowOverBishkekDemo PRIVATE
|
||||
OpenGL::GL
|
||||
OpenGL::GLU
|
||||
ZLIB::ZLIB
|
||||
@@ -202,6 +208,83 @@ target_link_libraries(witcher001 PRIVATE
|
||||
pthread
|
||||
)
|
||||
|
||||
# ===========================================
|
||||
# Интеграция Steamworks SDK (если STEAMSDK=ON)
|
||||
# ===========================================
|
||||
if(STEAMSDK)
|
||||
message(STATUS "Steamworks SDK integration is ENABLED.")
|
||||
|
||||
target_compile_definitions(ShadowOverBishkekDemo PRIVATE STEAMSDK)
|
||||
|
||||
set(STEAM_SDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/steamworks_sdk_164/sdk")
|
||||
|
||||
target_include_directories(ShadowOverBishkekDemo PRIVATE
|
||||
"${STEAM_SDK_DIR}/public"
|
||||
"${STEAM_SDK_DIR}/public/steam"
|
||||
)
|
||||
|
||||
set(STEAM_LIB_DIR "${STEAM_SDK_DIR}/redistributable_bin/linux64")
|
||||
set(STEAM_LIB_NAME "steam_api")
|
||||
set(STEAM_SO_NAME "libsteam_api.so")
|
||||
|
||||
find_library(STEAM_LIBRARY
|
||||
NAMES ${STEAM_LIB_NAME}
|
||||
PATHS ${STEAM_LIB_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
if(STEAM_LIBRARY)
|
||||
target_link_libraries(ShadowOverBishkekDemo PRIVATE ${STEAM_LIBRARY})
|
||||
message(STATUS "Found Steam API library: ${STEAM_LIBRARY}")
|
||||
else()
|
||||
message(FATAL_ERROR "Steam API library (${STEAM_LIB_NAME}) NOT found in ${STEAM_LIB_DIR}!")
|
||||
endif()
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/steam_appid.txt" "4945840")
|
||||
else()
|
||||
message(STATUS "Steamworks SDK integration is DISABLED.")
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
# Настройка RPATH и копирование динамических библиотек
|
||||
# ===========================================
|
||||
|
||||
# Указываем линкеру искать .so файлы в той же папке, где лежит бинарник ($ORIGIN)
|
||||
set_target_properties(ShadowOverBishkekDemo PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN"
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
)
|
||||
|
||||
#FIXING BUILD BUG
|
||||
set(LUA_SO_DIR "/usr/lib/x86_64-linux-gnu/")
|
||||
set (LIBZIP_SO_DIR "/usr/lib/x86_64-linux-gnu/")
|
||||
# Поиск директорий, где лежат библиотеки
|
||||
#get_filename_component(LUA_SO_DIR ${LUA_SO_PATH} DIRECTORY)
|
||||
#get_filename_component(LIBZIP_SO_DIR ${LIBZIP_SO_PATH} DIRECTORY)
|
||||
|
||||
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying shared libraries with sonames..."
|
||||
# Копируем библиотеки и все их симлинки (cp -P сохраняет структуру симлинков)
|
||||
COMMAND sh -c "cp -P ${LUA_SO_DIR}/liblua5.4.so* '$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/'"
|
||||
COMMAND sh -c "cp -P ${LIBZIP_SO_DIR}/libzip.so* '$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/'"
|
||||
)
|
||||
|
||||
# Если включен Steam, копируем libsteam_api.so и steam_appid.txt
|
||||
if(STEAMSDK)
|
||||
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying Steam SDK components..."
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${STEAM_LIB_DIR}/${STEAM_SO_NAME}"
|
||||
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/${STEAM_SO_NAME}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/steam_appid.txt"
|
||||
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/steam_appid.txt"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/steam_appid.txt"
|
||||
"${CMAKE_BINARY_DIR}/steam_appid.txt"
|
||||
)
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
# Копирование ресурсов после сборки
|
||||
# ===========================================
|
||||
@@ -210,13 +293,11 @@ set(RUNTIME_RESOURCE_DIRS
|
||||
"audio"
|
||||
)
|
||||
|
||||
# В Linux (при использовании Make/Ninja) бинарник обычно создается в CMAKE_BINARY_DIR.
|
||||
# Копируем ресурсы рядом с исполняемым файлом.
|
||||
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
|
||||
add_custom_command(TARGET witcher001 POST_BUILD
|
||||
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to binary dir..."
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/../${resdir}"
|
||||
"${CMAKE_BINARY_DIR}/${resdir}"
|
||||
)
|
||||
endforeach()
|
||||
endforeach()
|
||||
+149
-82
@@ -1,16 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(witcher001 LANGUAGES C CXX)
|
||||
project(ShadowOverBishkekDemo LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Опция для интеграции Steamworks SDK (по умолчанию выключена)
|
||||
option(STEAMSDK "Enable Steamworks SDK integration" OFF)
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/ThirdParty.cmake)
|
||||
|
||||
# ===========================================
|
||||
# Основной проект witcher001
|
||||
# Основной проект ShadowOverBishkekDemo
|
||||
# ===========================================
|
||||
add_executable(witcher001
|
||||
add_executable(ShadowOverBishkekDemo WIN32
|
||||
../src/main.cpp
|
||||
../src/Game.cpp
|
||||
../src/Game.h
|
||||
@@ -42,34 +45,34 @@ add_executable(witcher001
|
||||
../src/SparkEmitter.h
|
||||
../src/TeleportZone.h
|
||||
../src/TeleportZone.cpp
|
||||
../src/utils/TaskManager.cpp
|
||||
../src/utils/TaskManager.h
|
||||
../src/render/FrameBuffer.cpp
|
||||
../src/render/FrameBuffer.h
|
||||
../src/render/ShadowMap.cpp
|
||||
../src/render/ShadowMap.h
|
||||
../src/utils/TaskManager.cpp
|
||||
../src/utils/TaskManager.h
|
||||
../src/render/FrameBuffer.cpp
|
||||
../src/render/FrameBuffer.h
|
||||
../src/render/ShadowMap.cpp
|
||||
../src/render/ShadowMap.h
|
||||
../src/UiManager.cpp
|
||||
../src/UiManager.h
|
||||
../src/render/TextRenderer.h
|
||||
../src/render/TextRenderer.cpp
|
||||
../src/MenuManager.h
|
||||
../src/MenuManager.cpp
|
||||
../src/Location.h
|
||||
../src/Location.cpp
|
||||
../src/LocationState.h
|
||||
../src/LocationState.cpp
|
||||
../src/LocationEditor.h
|
||||
../src/LocationEditor.cpp
|
||||
../src/NpcCar.h
|
||||
../src/NpcCar.cpp
|
||||
../src/render/TextRenderer.h
|
||||
../src/render/TextRenderer.cpp
|
||||
../src/MenuManager.h
|
||||
../src/MenuManager.cpp
|
||||
../src/Location.h
|
||||
../src/Location.cpp
|
||||
../src/LocationState.h
|
||||
../src/LocationState.cpp
|
||||
../src/LocationEditor.h
|
||||
../src/LocationEditor.cpp
|
||||
../src/NpcCar.h
|
||||
../src/NpcCar.cpp
|
||||
../src/GameConstants.h
|
||||
../src/GameConstants.cpp
|
||||
../src/GameState.h
|
||||
../src/GameState.cpp
|
||||
../src/ScriptEngine.h
|
||||
../src/ScriptEngine.cpp
|
||||
../src/navigation/PathFinder.h
|
||||
../src/navigation/PathFinder.cpp
|
||||
../src/navigation/PathFinder.h
|
||||
../src/navigation/PathFinder.cpp
|
||||
../src/items/GameObjectLoader.h
|
||||
../src/items/GameObjectLoader.cpp
|
||||
../src/items/Item.h
|
||||
@@ -79,73 +82,121 @@ add_executable(witcher001
|
||||
../src/items/InteractiveObject.h
|
||||
../src/items/InteractiveObject.cpp
|
||||
../src/items/InteractiveObjectState.h
|
||||
../src/dialogue/DialogueTypes.h
|
||||
../src/dialogue/DialogueDatabase.h
|
||||
../src/dialogue/DialogueDatabase.cpp
|
||||
../src/dialogue/DialogueRuntime.h
|
||||
../src/dialogue/DialogueRuntime.cpp
|
||||
../src/dialogue/DialogueOverlay.h
|
||||
../src/dialogue/DialogueOverlay.cpp
|
||||
../src/dialogue/DialogueSystem.h
|
||||
../src/dialogue/DialogueSystem.cpp
|
||||
../src/dialogue/TranslationDatabase.h
|
||||
../src/dialogue/TranslationDatabase.cpp
|
||||
../src/quest/QuestTypes.h
|
||||
../src/quest/QuestJournal.h
|
||||
../src/quest/QuestJournal.cpp
|
||||
../src/cutscene/CutsceneTypes.h
|
||||
../src/cutscene/CutsceneDatabase.h
|
||||
../src/cutscene/CutsceneDatabase.cpp
|
||||
../src/cutscene/CutsceneRuntime.h
|
||||
../src/cutscene/CutsceneRuntime.cpp
|
||||
../src/cutscene/CutsceneOverlay.h
|
||||
../src/cutscene/CutsceneOverlay.cpp
|
||||
../src/render/UiQuad.h
|
||||
../src/dialogue/DialogueTypes.h
|
||||
../src/dialogue/DialogueDatabase.h
|
||||
../src/dialogue/DialogueDatabase.cpp
|
||||
../src/dialogue/DialogueRuntime.h
|
||||
../src/dialogue/DialogueRuntime.cpp
|
||||
../src/dialogue/DialogueOverlay.h
|
||||
../src/dialogue/DialogueOverlay.cpp
|
||||
../src/dialogue/DialogueSystem.h
|
||||
../src/dialogue/DialogueSystem.cpp
|
||||
../src/dialogue/TranslationDatabase.h
|
||||
../src/dialogue/TranslationDatabase.cpp
|
||||
../src/quest/QuestTypes.h
|
||||
../src/quest/QuestJournal.h
|
||||
../src/quest/QuestJournal.cpp
|
||||
../src/cutscene/CutsceneTypes.h
|
||||
../src/cutscene/CutsceneDatabase.h
|
||||
../src/cutscene/CutsceneDatabase.cpp
|
||||
../src/cutscene/CutsceneRuntime.h
|
||||
../src/cutscene/CutsceneRuntime.cpp
|
||||
../src/cutscene/CutsceneOverlay.h
|
||||
../src/cutscene/CutsceneOverlay.cpp
|
||||
../src/render/UiQuad.h
|
||||
)
|
||||
|
||||
# Установка проекта по умолчанию для Visual Studio
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT witcher001)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ShadowOverBishkekDemo)
|
||||
|
||||
# include-пути проекта
|
||||
target_include_directories(witcher001 PRIVATE
|
||||
target_include_directories(ShadowOverBishkekDemo PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
|
||||
)
|
||||
|
||||
set_target_properties(witcher001 PROPERTIES
|
||||
OUTPUT_NAME "witcher001"
|
||||
set_target_properties(ShadowOverBishkekDemo PROPERTIES
|
||||
OUTPUT_NAME "ShadowOverBishkekDemo"
|
||||
)
|
||||
|
||||
# Определения препроцессора:
|
||||
# PNG_ENABLED – включает код PNG в TextureManager
|
||||
# SDL_MAIN_HANDLED – отключает переопределение main -> SDL_main
|
||||
target_compile_definitions(witcher001 PRIVATE
|
||||
target_compile_definitions(ShadowOverBishkekDemo PRIVATE
|
||||
WIN32_LEAN_AND_MEAN
|
||||
PNG_ENABLED
|
||||
SDL_MAIN_HANDLED
|
||||
# DEBUG_LIGHT
|
||||
# SHOW_PATH
|
||||
# SDL_MAIN_HANDLED
|
||||
# DEBUG_LIGHT
|
||||
# SHOW_PATH
|
||||
)
|
||||
|
||||
# Линкуем с SDL2main, если он вообще установлен
|
||||
target_link_libraries(witcher001 PRIVATE SDL2main_external_lib)
|
||||
target_link_libraries(ShadowOverBishkekDemo PRIVATE SDL2main_external_lib)
|
||||
|
||||
# Линкуем сторонние библиотеки
|
||||
target_link_libraries(witcher001 PRIVATE
|
||||
target_link_libraries(ShadowOverBishkekDemo PRIVATE
|
||||
SDL2_external_lib
|
||||
libpng_external_lib
|
||||
zlib_external_lib
|
||||
libzip_external_lib
|
||||
freetype_external_lib
|
||||
SDL2_ttf_external_lib
|
||||
freetype_external_lib
|
||||
SDL2_ttf_external_lib
|
||||
eigen_external_lib
|
||||
boost_external_lib
|
||||
boost_external_lib
|
||||
sol2_external_lib
|
||||
SDL2_mixer_external_lib
|
||||
)
|
||||
|
||||
# Линкуем OpenGL (Windows)
|
||||
if(WIN32)
|
||||
target_link_libraries(witcher001 PRIVATE opengl32)
|
||||
target_link_libraries(ShadowOverBishkekDemo PRIVATE opengl32)
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
# Интеграция Steamworks SDK (если STEAMSDK=ON)
|
||||
# ===========================================
|
||||
if(STEAMSDK)
|
||||
message(STATUS "Steamworks SDK integration is ENABLED.")
|
||||
|
||||
# Макрос STEAMSDK для условной компиляции в C++ коде (#ifdef STEAMSDK)
|
||||
target_compile_definitions(ShadowOverBishkekDemo PRIVATE STEAMSDK)
|
||||
|
||||
set(STEAM_SDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/steamworks_sdk_164/sdk")
|
||||
|
||||
# Подключение заголовочных файлов Steamworks
|
||||
target_include_directories(ShadowOverBishkekDemo PRIVATE
|
||||
"${STEAM_SDK_DIR}/public"
|
||||
"${STEAM_SDK_DIR}/public/steam"
|
||||
)
|
||||
|
||||
# Определение разрядности сборки (x86 vs x64) для выбора версий lib и dll
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(STEAM_LIB_DIR "${STEAM_SDK_DIR}/redistributable_bin/win64")
|
||||
set(STEAM_LIB_NAME "steam_api64")
|
||||
set(STEAM_DLL_NAME "steam_api64.dll")
|
||||
else()
|
||||
set(STEAM_LIB_DIR "${STEAM_SDK_DIR}/redistributable_bin")
|
||||
set(STEAM_LIB_NAME "steam_api")
|
||||
set(STEAM_DLL_NAME "steam_api.dll")
|
||||
endif()
|
||||
|
||||
# Поиск библиотеки линковки (.lib)
|
||||
find_library(STEAM_LIBRARY
|
||||
NAMES ${STEAM_LIB_NAME}
|
||||
PATHS ${STEAM_LIB_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
if(STEAM_LIBRARY)
|
||||
target_link_libraries(ShadowOverBishkekDemo PRIVATE ${STEAM_LIBRARY})
|
||||
message(STATUS "Found Steam API library: ${STEAM_LIBRARY}")
|
||||
else()
|
||||
message(FATAL_ERROR "Steam API library (${STEAM_LIB_NAME}) NOT found in ${STEAM_LIB_DIR}!")
|
||||
endif()
|
||||
|
||||
# Генерация временного файла steam_appid.txt с ID демо-версии (4945840)
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/steam_appid.txt" "4945840")
|
||||
else()
|
||||
message(STATUS "Steamworks SDK integration is DISABLED.")
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
@@ -155,22 +206,22 @@ if (WIN32)
|
||||
|
||||
# SDL2: в Debug - SDL2d.dll, в Release - SDL2.dll
|
||||
set(SDL2_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2_INSTALL_DIR}/bin/SDL2d.dll,${SDL2_INSTALL_DIR}/bin/SDL2.dll>")
|
||||
set(SDL2_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:witcher001>/SDL2d.dll,$<TARGET_FILE_DIR:witcher001>/SDL2.dll>")
|
||||
set(SDL2_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/SDL2d.dll,$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/SDL2.dll>")
|
||||
|
||||
|
||||
set(LIBZIP_DLL_SRC "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/bin/zip.dll,${LIBZIP_BASE_DIR}-Release/bin/zip.dll>")
|
||||
|
||||
set(ZLIB_DLL_SRC "$<IF:$<CONFIG:Debug>,${ZLIB_INSTALL_DIR}/bin/zd.dll,${ZLIB_INSTALL_DIR}/bin/z.dll>")
|
||||
set(ZLIB_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:witcher001>/zd.dll,$<TARGET_FILE_DIR:witcher001>/z.dll>")
|
||||
|
||||
set(SDL2TTF_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/bin/SDL2_ttfd.dll,${SDL2TTF_BASE_DIR}-Release/bin/SDL2_ttf.dll>")
|
||||
|
||||
set(ZLIB_DLL_SRC "$<IF:$<CONFIG:Debug>,${ZLIB_INSTALL_DIR}/bin/zd.dll,${ZLIB_INSTALL_DIR}/bin/z.dll>")
|
||||
set(ZLIB_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/zd.dll,$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/z.dll>")
|
||||
|
||||
set(SDL2TTF_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/bin/SDL2_ttfd.dll,${SDL2TTF_BASE_DIR}-Release/bin/SDL2_ttf.dll>")
|
||||
|
||||
set(SDL2MIXER_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2MIXER_BASE_DIR}-Debug/bin/SDL2_mixerd.dll,${SDL2MIXER_BASE_DIR}-Release/bin/SDL2_mixer.dll>")
|
||||
|
||||
add_custom_command(TARGET witcher001 POST_BUILD
|
||||
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
|
||||
|
||||
# Копируем SDL2 (целевое имя всегда SDL2.dll)
|
||||
# Копируем SDL2
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${SDL2_DLL_SRC}"
|
||||
"${SDL2_DLL_DST}"
|
||||
@@ -178,23 +229,39 @@ add_custom_command(TARGET witcher001 POST_BUILD
|
||||
# Копируем LIBZIP (целевое имя всегда zip.dll)
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${LIBZIP_DLL_SRC}"
|
||||
"$<TARGET_FILE_DIR:witcher001>/zip.dll"
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/zip.dll"
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${ZLIB_DLL_SRC}"
|
||||
"${ZLIB_DLL_DST}"
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${SDL2TTF_DLL_SRC}"
|
||||
"$<TARGET_FILE_DIR:witcher001>"
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${SDL2TTF_DLL_SRC}"
|
||||
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>"
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/SDL_mixer-release-2.8.0/install-$<CONFIG>/bin/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
|
||||
|
||||
# This is only for profiling:
|
||||
#"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/SDL_mixer-release-2.8.0/install-Release/bin/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
|
||||
"$<TARGET_FILE_DIR:witcher001>/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
|
||||
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
|
||||
)
|
||||
|
||||
# Если включен Steam, добавляем шаги копирования DLL и appid в этот же POST_BUILD
|
||||
if(STEAMSDK)
|
||||
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying Steam SDK components..."
|
||||
# Копирование dll к исполняемому файлу
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${STEAM_LIB_DIR}/${STEAM_DLL_NAME}"
|
||||
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/${STEAM_DLL_NAME}"
|
||||
# Копирование steam_appid.txt к исполняемому файлу
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/steam_appid.txt"
|
||||
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/steam_appid.txt"
|
||||
# Дублируем steam_appid.txt в корень сборки (полезно при отладке прямо из MSVC)
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/steam_appid.txt"
|
||||
"${CMAKE_BINARY_DIR}/steam_appid.txt"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
@@ -204,20 +271,20 @@ endif()
|
||||
# Какие папки с ресурсами нужно копировать
|
||||
set(RUNTIME_RESOURCE_DIRS
|
||||
"resources"
|
||||
"audio"
|
||||
"audio"
|
||||
)
|
||||
|
||||
# Копируем ресурсы и шейдеры в папку exe и в корень build/
|
||||
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
|
||||
add_custom_command(TARGET witcher001 POST_BUILD
|
||||
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
|
||||
# 1) туда, где лежит exe (build/Debug, build/Release и т.п.)
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/../${resdir}"
|
||||
"$<TARGET_FILE_DIR:witcher001>/${resdir}"
|
||||
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/${resdir}"
|
||||
# 2) в корень build, если захочешь запускать из этой папки
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/../${resdir}"
|
||||
"${CMAKE_BINARY_DIR}/${resdir}"
|
||||
)
|
||||
endforeach()
|
||||
endforeach()
|
||||
@@ -26,7 +26,7 @@
|
||||
"animationIdlePath": "resources/w/girlfriend/girlfriend_idle003_small.anim",
|
||||
"animationWalkPath": "resources/w/girlfriend/girlfriend_walk003_small.anim",
|
||||
"meshTextures": {
|
||||
"Girl_Low": "resources/w/girlfriend/Girl_Base_color.png"
|
||||
"Girl_Low": "resources/w/girlfriend/Girl_Base_color006.png"
|
||||
},
|
||||
"positionX": 0.799619,
|
||||
"positionY": 0.0,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
{
|
||||
"key": "Аида Дженибековна",
|
||||
"ru": "Аида Дженибековна",
|
||||
"en": "Aida Dzhanybekovna"
|
||||
"en": "Aida Dzhenibekovna"
|
||||
},
|
||||
{
|
||||
"key": "Алтынай",
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
},
|
||||
{
|
||||
"id": "report_card",
|
||||
"name": "Begimai's grade book",
|
||||
"description": "This is Begimai's grade book. This is where a teacher should write grades for each subject or coursework.",
|
||||
"name": "Begimai's record book",
|
||||
"description": "This is Begimai's record book. This is where a teacher should write grades for each subject or coursework.",
|
||||
"icon": "resources/w/ui/img/inv/ItemReportCard002.png",
|
||||
"selectedIcon": "resources/w/ui/img/inv/ItemSelReportCard002.png"
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"objectives": [
|
||||
{ "id": "ghost_lore_aiperi", "text": "Talk to Aiperi", "completed": false },
|
||||
{ "id": "ghost_lore_alik", "text": "Talk to Alik", "completed": false, "visible": false },
|
||||
{ "id": "ghost_lore_teacher", "text": "Talk to Aida Dzhanybekovna", "completed": false }
|
||||
{ "id": "ghost_lore_teacher", "text": "Talk to Aida Dzhenibekovna", "completed": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -31,7 +31,7 @@
|
||||
"description": "Begimai's story is even more mysterious. While her coursework was due, the university was undergoing a general cleaning. By chance, her coursework ended up in the trash bin. As a result, her coursework was thrown away, the professor was not able to find it, and gave her a zero grade. According to Alik, the coursework still lies in a pile of trash in the university courtyard.",
|
||||
"objectives": [
|
||||
{ "id": "ghost_coursework_find", "text": "Find Begimai's coursework", "completed": false },
|
||||
{ "id": "ghost_coursework_mark", "text": "Show coursework to Aida Dzhanybekovna", "completed": false }
|
||||
{ "id": "ghost_coursework_mark", "text": "Show coursework to Aida Dzhenibekovna", "completed": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -41,9 +41,9 @@
|
||||
"autoComplete": true,
|
||||
"description": "A student named Begimai actually attended university last year. She tried to submit a coursework, but due to a series of circumstances, she failed and received a zero grade. This coursework shattered all her plans and hopes, and Begimai jumped out the window. Now she's returned as a ghost, and to be liberated, she must ensure she receives a grade for the coursework.",
|
||||
"objectives": [
|
||||
{ "id": "ghost_release_reportcard", "text": "Find Begimai's grade book", "completed": false },
|
||||
{ "id": "ghost_release_mark", "text": "Put a grade for the coursework in the grade book", "completed": false },
|
||||
{ "id": "ghost_release_show", "text": "Show the grade book to the ghost", "completed": false }
|
||||
{ "id": "ghost_release_reportcard", "text": "Find Begimai's record book", "completed": false },
|
||||
{ "id": "ghost_release_mark", "text": "Put a grade for the coursework in the record book", "completed": false },
|
||||
{ "id": "ghost_release_show", "text": "Show the record book to the ghost", "completed": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -51,7 +51,7 @@
|
||||
"title": "Manas studies",
|
||||
"status": "Available",
|
||||
"autoComplete": true,
|
||||
"description": "There's a Manas Studies module coming up soon, and I've never attended a lecture before. We have a Manas Studies lecture today, so I need to attend it and get the module assignment from my teacher, Aida Dzhanybekovna.",
|
||||
"description": "There's a Manas Studies module coming up soon, and I've never attended a lecture before. We have a Manas Studies lecture today, so I need to attend it and get the module assignment from my teacher, Aida Dzhenibekovna.",
|
||||
"objectives": [
|
||||
{ "id": "study_beginning_lecture", "text": "Attend the lecture", "completed": false },
|
||||
{ "id": "study_beginning_task", "text": "Get an assignment for the module", "completed": false }
|
||||
@@ -61,7 +61,7 @@
|
||||
"id": "study_project",
|
||||
"title": "Essay on Manaschi",
|
||||
"status": "Hidden",
|
||||
"description": "My Manas studies teacher, Aida Dzhanybekovna, gave me an assignment: find a book about Zhusup Mamai's Manaschi in the library and write an essay about it. The book isn't allowed out of the library, but I can use the library's computer to write the essay. The essay must be completed before tomorrow.",
|
||||
"description": "My Manas studies teacher, Aida Dzhenibekovna, gave me an assignment: find a book about Zhusup Mamai's Manaschi in the library and write an essay about it. The book isn't allowed out of the library, but I can use the library's computer to write the essay. The essay must be completed before tomorrow.",
|
||||
"objectives": [
|
||||
{ "id": "study_project_book", "text": "Find the book", "completed": false },
|
||||
{ "id": "study_project_write", "text": "Write the essay", "completed": false },
|
||||
|
||||
@@ -1,290 +0,0 @@
|
||||
{
|
||||
"dialogues": [
|
||||
{
|
||||
"id": "test_cutscene_skip_hold_dialogue",
|
||||
"start": "cutscene_start",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "cutscene_start",
|
||||
"type": "CutsceneStart",
|
||||
"cutsceneId": "test_cutscene_skip_hold_01",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_images_hardcut_dialogue",
|
||||
"start": "cutscene_start",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "cutscene_start",
|
||||
"type": "CutsceneStart",
|
||||
"cutsceneId": "test_cutscene_images_hardcut_01",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_images_crossfade_dialogue",
|
||||
"start": "cutscene_start",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "cutscene_start",
|
||||
"type": "CutsceneStart",
|
||||
"cutsceneId": "test_cutscene_images_crossfade_01",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_images_silent_dialogue",
|
||||
"start": "cutscene_start",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "cutscene_start",
|
||||
"type": "CutsceneStart",
|
||||
"cutsceneId": "test_cutscene_images_silent_01",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cutscenes": [
|
||||
{
|
||||
"id": "test_cutscene_skip_hold_01",
|
||||
"background": "resources/first_cutscene.png",
|
||||
"skippable": true,
|
||||
"durationMs": 12000,
|
||||
"cameraTrack": [
|
||||
{
|
||||
"durationMs": 3000,
|
||||
"from": { "anchor": "Center", "zoom": 1.0, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "TopLeft", "zoom": 1.45, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 3000,
|
||||
"from": { "anchor": "TopLeft", "zoom": 1.45, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "TopRight", "zoom": 1.45, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 3000,
|
||||
"from": { "anchor": "TopRight", "zoom": 1.45, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomRight", "zoom": 1.65, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInCubic"
|
||||
},
|
||||
{
|
||||
"durationMs": 3000,
|
||||
"from": { "anchor": "BottomRight", "zoom": 1.65, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomLeft", "zoom": 1.45, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
}
|
||||
],
|
||||
"lines": [
|
||||
{
|
||||
"speaker": "Narrator",
|
||||
"portrait": "",
|
||||
"text": "This cutscene is long enough to test hold-to-skip.",
|
||||
"durationMs": 2600
|
||||
},
|
||||
{
|
||||
"speaker": "Narrator",
|
||||
"portrait": "",
|
||||
"text": "A normal click must not skip it.",
|
||||
"durationMs": 2600
|
||||
},
|
||||
{
|
||||
"speaker": "Ghost",
|
||||
"portrait": "resources/ghost_avatar.png",
|
||||
"text": "Only the skip button with hold should work.",
|
||||
"durationMs": 2600
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_images_hardcut_01",
|
||||
"background": "resources/first_cutscene.png",
|
||||
"skippable": true,
|
||||
"durationMs": 9000,
|
||||
"cameraTrack": [
|
||||
{
|
||||
"durationMs": 4500,
|
||||
"from": { "anchor": "Center", "zoom": 1.0, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "TopLeft", "zoom": 1.35, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 4500,
|
||||
"from": { "anchor": "TopLeft", "zoom": 1.35, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomRight", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"path": "resources/first_cutscene.png",
|
||||
"startMs": 0,
|
||||
"endMs": 4500,
|
||||
"fadeInMs": 0,
|
||||
"fadeOutMs": 0
|
||||
},
|
||||
{
|
||||
"path": "resources/second_cutscene.png",
|
||||
"startMs": 4500,
|
||||
"endMs": 9000,
|
||||
"fadeInMs": 0,
|
||||
"fadeOutMs": 0
|
||||
}
|
||||
],
|
||||
"lines": [
|
||||
{
|
||||
"speaker": "Narrator",
|
||||
"portrait": "",
|
||||
"text": "First image should switch sharply to the second one.",
|
||||
"durationMs": 2800
|
||||
},
|
||||
{
|
||||
"speaker": "Narrator",
|
||||
"portrait": "",
|
||||
"text": "No fade should be visible here.",
|
||||
"durationMs": 2800
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_images_crossfade_01",
|
||||
"background": "resources/first_cutscene.png",
|
||||
"skippable": true,
|
||||
"durationMs": 10000,
|
||||
"cameraTrack": [
|
||||
{
|
||||
"durationMs": 2500,
|
||||
"from": { "anchor": "Center", "zoom": 1.0, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "Custom", "centerX": 0.35, "centerY": 0.30, "zoom": 1.45, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutQuad"
|
||||
},
|
||||
{
|
||||
"durationMs": 2500,
|
||||
"from": { "anchor": "Custom", "centerX": 0.35, "centerY": 0.30, "zoom": 1.45, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "Custom", "centerX": 0.70, "centerY": 0.32, "zoom": 1.45, "rotationDeg": 0.0 },
|
||||
"easing": "EaseOutCubic"
|
||||
},
|
||||
{
|
||||
"durationMs": 2500,
|
||||
"from": { "anchor": "Custom", "centerX": 0.70, "centerY": 0.32, "zoom": 1.45, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomRight", "zoom": 1.70, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInCubic"
|
||||
},
|
||||
{
|
||||
"durationMs": 2500,
|
||||
"from": { "anchor": "BottomRight", "zoom": 1.70, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomLeft", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"path": "resources/first_cutscene.png",
|
||||
"startMs": 0,
|
||||
"endMs": 6000,
|
||||
"fadeInMs": 0,
|
||||
"fadeOutMs": 0
|
||||
},
|
||||
{
|
||||
"path": "resources/second_cutscene.png",
|
||||
"startMs": 4500,
|
||||
"endMs": 10000,
|
||||
"fadeInMs": 1500,
|
||||
"fadeOutMs": 0
|
||||
}
|
||||
],
|
||||
"lines": [
|
||||
{
|
||||
"speaker": "Narrator",
|
||||
"portrait": "",
|
||||
"text": "The second image should fade over the first one.",
|
||||
"durationMs": 2600
|
||||
},
|
||||
{
|
||||
"speaker": "Ghost",
|
||||
"portrait": "resources/ghost_avatar.png",
|
||||
"text": "This test checks overlap and alpha blending.",
|
||||
"durationMs": 2600
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_images_silent_01",
|
||||
"background": "resources/first_cutscene.png",
|
||||
"skippable": true,
|
||||
"durationMs": 11000,
|
||||
"cameraTrack": [
|
||||
{
|
||||
"durationMs": 2500,
|
||||
"from": { "anchor": "Center", "zoom": 1.0, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "TopLeft", "zoom": 1.35, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 3000,
|
||||
"from": { "anchor": "TopLeft", "zoom": 1.35, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "TopRight", "zoom": 1.35, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 3000,
|
||||
"from": { "anchor": "TopRight", "zoom": 1.35, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomRight", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"easing": "EaseOutCubic"
|
||||
},
|
||||
{
|
||||
"durationMs": 2500,
|
||||
"from": { "anchor": "BottomRight", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomLeft", "zoom": 1.45, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutQuad"
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"path": "resources/first_cutscene.png",
|
||||
"startMs": 0,
|
||||
"endMs": 3500,
|
||||
"fadeInMs": 0,
|
||||
"fadeOutMs": 800
|
||||
},
|
||||
{
|
||||
"path": "resources/second_cutscene.png",
|
||||
"startMs": 3000,
|
||||
"endMs": 7500,
|
||||
"fadeInMs": 800,
|
||||
"fadeOutMs": 1000
|
||||
},
|
||||
{
|
||||
"path": "resources/loading.png",
|
||||
"startMs": 7000,
|
||||
"endMs": 11000,
|
||||
"fadeInMs": 1000,
|
||||
"fadeOutMs": 0
|
||||
}
|
||||
],
|
||||
"lines": []
|
||||
}
|
||||
]
|
||||
}
|
||||
+14
-5
@@ -155,7 +155,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "Почему ты сама не можешь забрать?",
|
||||
"text": "Почему ты сама не можешь забрать нож у Аиды?",
|
||||
"next": "line_9",
|
||||
"chatBubble": "out"
|
||||
},
|
||||
@@ -164,7 +164,16 @@
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "Ты же знаешь, если я встречу Аиду, она 100% даст мне какое-нибудь сложное задание.",
|
||||
"text": "Она опять будет скафнить.",
|
||||
"next": "line_12",
|
||||
"chatBubble": "in"
|
||||
},
|
||||
{
|
||||
"id": "line_12",
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "А может быть даже даст мне какое-нибудь сложное задание.",
|
||||
"next": "line_10",
|
||||
"chatBubble": "in"
|
||||
},
|
||||
@@ -182,7 +191,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "Так что жду тебя в универе! Не вздумай прогулять!",
|
||||
"text": "Так что жду тебя в универе! Не вздумай прогулять! Ошо!",
|
||||
"next": "setflag_1",
|
||||
"chatBubble": "in",
|
||||
"questUnlock": "aiperi_knife"
|
||||
@@ -359,7 +368,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/dialogue/portrait_hero_neutral.png",
|
||||
"text": "Я заказал такси до универа, машина уже ждет!",
|
||||
"text": "Я заказал такси до универа, машина уже едет!",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
@@ -647,7 +656,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Алик",
|
||||
"portrait": "resources/dialogue/portrait_student_boy.png",
|
||||
"text": "За зданием универа есть контейнер с кучей бумажного мусора и макулатурой.",
|
||||
"text": "Возле здания универа лежит куча строительного мусора и макулатуры.",
|
||||
"next": "line_14"
|
||||
},
|
||||
{
|
||||
@@ -1,501 +0,0 @@
|
||||
{
|
||||
"dialogues": [
|
||||
{
|
||||
"id": "dialog_student",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Студент",
|
||||
"portrait": "resources/w/avatar_student.png",
|
||||
"text": "В университете завелись призраки, мне страшно ходить на занятия.",
|
||||
"next": "line_2"
|
||||
},
|
||||
{
|
||||
"id": "line_2",
|
||||
"type": "Line",
|
||||
"speaker": "Hero",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Можешь рассказать подробнее?",
|
||||
"next": "line_3"
|
||||
},
|
||||
{
|
||||
"id": "line_3",
|
||||
"type": "Line",
|
||||
"speaker": "Студент",
|
||||
"portrait": "resources/w/avatar_student.png",
|
||||
"text": "Спроси у Мухтара байке, он все знает.",
|
||||
"next": "line_4"
|
||||
},
|
||||
{
|
||||
"id": "line_4",
|
||||
"type": "Line",
|
||||
"speaker": "Hero",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Хорошо.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dialog_mukhtar",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Мухтар байке",
|
||||
"portrait": "resources/w/avatar_unknown.png",
|
||||
"text": "Здравствуй, мы давно тебя ждем! Ты поможешь нам избавиться от призраков?",
|
||||
"next": "line_2"
|
||||
},
|
||||
{
|
||||
"id": "line_2",
|
||||
"type": "Line",
|
||||
"speaker": "Hero",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Где их найти?",
|
||||
"next": "line_3"
|
||||
},
|
||||
{
|
||||
"id": "line_3",
|
||||
"type": "Line",
|
||||
"speaker": "Мухтар байке",
|
||||
"portrait": "resources/w/avatar_unknown.png",
|
||||
"text": "Заходи в здание универа и поднимайся на второй этаж.",
|
||||
"next": "line_4"
|
||||
},
|
||||
{
|
||||
"id": "line_4",
|
||||
"type": "Line",
|
||||
"speaker": "Мухтар байке",
|
||||
"portrait": "resources/w/avatar_unknown.png",
|
||||
"text": "Ты их встретишь прямо там.",
|
||||
"next": "line_5"
|
||||
},
|
||||
{
|
||||
"id": "line_4",
|
||||
"type": "Line",
|
||||
"speaker": "Hero",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Хорошо, я скоро вернусь!",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dialog_female_student",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Студентка",
|
||||
"portrait": "resources/w/avatar_girl.png",
|
||||
"text": "С этими призраками совсем невозможно ходить на лекции!",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_line_dialogue",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Ghost",
|
||||
"portrait": "resources/ghost_avatar.png",
|
||||
"text": "Наконец-то ты пришел.",
|
||||
"next": "line_2"
|
||||
},
|
||||
{
|
||||
"id": "line_2",
|
||||
"type": "Line",
|
||||
"speaker": "Hero",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Ты сделан из дыма?",
|
||||
"next": "line_3"
|
||||
},
|
||||
{
|
||||
"id": "line_3",
|
||||
"type": "Line",
|
||||
"speaker": "Ghost",
|
||||
"portrait": "resources/ghost_avatar.png",
|
||||
"text": "Ты думаешь, это смешно?",
|
||||
"next": "line_4"
|
||||
},
|
||||
{
|
||||
"id": "line_4",
|
||||
"type": "Line",
|
||||
"speaker": "Hero",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Я думаю что ты пахнешь как выхлоп от Камаза.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ghost_choice_dialogue",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Беспокойный Призрак",
|
||||
"portrait": "resources/w/avatar_ghost.png",
|
||||
"text": "Нечасто я вижу смертных, готовых разговаривать со мной.",
|
||||
"next": "choice_1"
|
||||
},
|
||||
{
|
||||
"id": "choice_1",
|
||||
"type": "Choice",
|
||||
"speaker": "Hero",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "",
|
||||
"choices": [
|
||||
{
|
||||
"id": "main_1",
|
||||
"kind": "Main",
|
||||
"text": "Не мешай студентам учиться!",
|
||||
"next": "line_goods"
|
||||
},
|
||||
{
|
||||
"id": "optional_1",
|
||||
"kind": "Optional",
|
||||
"text": "Почему ты появился здесь?",
|
||||
"next": "line_who"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "line_goods",
|
||||
"type": "Line",
|
||||
"speaker": "Беспокойный Призрак",
|
||||
"portrait": "resources/w/avatar_ghost.png",
|
||||
"text": "Это моя месть студентам за то что они призвали меня.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "line_who",
|
||||
"type": "Line",
|
||||
"speaker": "Беспокойный Призрак",
|
||||
"portrait": "resources/w/avatar_ghost.png",
|
||||
"text": "Группа студентов совершила ритуал и призвала меня сюда. Пока проклятие не спадет, я всегда буду здесь обитать.",
|
||||
"next": "choice_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_condition_dialogue",
|
||||
"start": "set_flag_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "set_flag_1",
|
||||
"type": "SetFlag",
|
||||
"effects": [
|
||||
{ "flag": "met_ghost", "value": 1 }
|
||||
],
|
||||
"next": "condition_1"
|
||||
},
|
||||
{
|
||||
"id": "condition_1",
|
||||
"type": "Condition",
|
||||
"conditions": [
|
||||
{ "flag": "met_ghost", "op": "Equals", "value": 1 }
|
||||
],
|
||||
"trueNext": "line_true",
|
||||
"falseNext": "line_false"
|
||||
},
|
||||
{
|
||||
"id": "line_true",
|
||||
"type": "Line",
|
||||
"speaker": "Ghost",
|
||||
"portrait": "resources/ghost_avatar.png",
|
||||
"text": "Now you know who I am.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "line_false",
|
||||
"type": "Line",
|
||||
"speaker": "Ghost",
|
||||
"portrait": "resources/ghost_avatar.png",
|
||||
"text": "You should not hear this line.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_dialogue",
|
||||
"start": "cutscene_start",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "cutscene_start",
|
||||
"type": "CutsceneStart",
|
||||
"cutsceneId": "test_cutscene_01",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_silent_cutscene_dialogue",
|
||||
"start": "cutscene_start",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "cutscene_start",
|
||||
"type": "CutsceneStart",
|
||||
"cutsceneId": "test_cutscene_silent_01",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_pan_dialogue",
|
||||
"start": "cutscene_start",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "cutscene_start",
|
||||
"type": "CutsceneStart",
|
||||
"cutsceneId": "test_cutscene_pan_01",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_pan_dialogue_silent",
|
||||
"start": "cutscene_start",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "cutscene_start",
|
||||
"type": "CutsceneStart",
|
||||
"cutsceneId": "test_cutscene_pan_02",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dialog_aida",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Асель Дженибековна",
|
||||
"portrait": "resources/w/avatar_teacher.png",
|
||||
"text": "Молодой человек, у меня обед! Я принимаю лабораторные работы только после двух!",
|
||||
"next": "line_2"
|
||||
},
|
||||
{
|
||||
"id": "line_2",
|
||||
"type": "Line",
|
||||
"speaker": "Hero",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Хорошо, Асель Дженибековна.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cutscenes": [
|
||||
{
|
||||
"id": "test_cutscene_01",
|
||||
"background": "resources/first_cutscene.png",
|
||||
"durationMs": 6800,
|
||||
"cameraTrack": [
|
||||
{
|
||||
"durationMs": 2400,
|
||||
"from": { "focusX": 0.50, "focusY": 0.55, "zoom": 1.00, "rotationDeg": 0.0 },
|
||||
"to": { "focusX": 0.63, "focusY": 0.58, "zoom": 1.16, "rotationDeg": -1.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 2200,
|
||||
"from": { "focusX": 0.63, "focusY": 0.58, "zoom": 1.16, "rotationDeg": -1.0 },
|
||||
"to": { "focusX": 0.74, "focusY": 0.52, "zoom": 1.30, "rotationDeg": -2.4 },
|
||||
"easing": "EaseInOutCubic"
|
||||
},
|
||||
{
|
||||
"durationMs": 2200,
|
||||
"from": { "focusX": 0.74, "focusY": 0.52, "zoom": 1.30, "rotationDeg": -2.4 },
|
||||
"to": { "focusX": 0.58, "focusY": 0.46, "zoom": 1.10, "rotationDeg": -0.6 },
|
||||
"easing": "EaseOutSine"
|
||||
}
|
||||
],
|
||||
"lines": [
|
||||
{
|
||||
"speaker": "Narrator",
|
||||
"portrait": "resources/hero.png",
|
||||
"text": "The air in the room turned cold.",
|
||||
"durationMs": 2200
|
||||
},
|
||||
{
|
||||
"speaker": "Ghost",
|
||||
"portrait": "resources/w/avatar_ghost.png",
|
||||
"text": "Some memories never fade.",
|
||||
"durationMs": 2600,
|
||||
"background": "resources/loading.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_silent_01",
|
||||
"background": "resources/first_cutscene.png",
|
||||
"durationMs": 5200,
|
||||
"cameraTrack": [
|
||||
{
|
||||
"durationMs": 2600,
|
||||
"from": { "focusX": 0.40, "focusY": 0.54, "zoom": 1.00, "rotationDeg": 0.0 },
|
||||
"to": { "focusX": 0.58, "focusY": 0.54, "zoom": 1.22, "rotationDeg": 0.8 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 2600,
|
||||
"from": { "focusX": 0.58, "focusY": 0.54, "zoom": 1.22, "rotationDeg": 0.8 },
|
||||
"to": { "focusX": 0.72, "focusY": 0.48, "zoom": 1.34, "rotationDeg": -0.5 },
|
||||
"easing": "EaseOutCubic"
|
||||
}
|
||||
],
|
||||
"lines": []
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_pan_01",
|
||||
"background": "resources/first_cutscene.png",
|
||||
"durationMs": 12000,
|
||||
"cameraTrack": [
|
||||
{
|
||||
"durationMs": 1200,
|
||||
"from": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
|
||||
"easing": "Linear"
|
||||
},
|
||||
{
|
||||
"durationMs": 2500,
|
||||
"from": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "TopLeft", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 2600,
|
||||
"from": { "anchor": "TopLeft", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "TopRight", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 1800,
|
||||
"from": { "anchor": "TopRight", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomRight", "zoom": 1.72, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInCubic"
|
||||
},
|
||||
{
|
||||
"durationMs": 3900,
|
||||
"from": { "anchor": "BottomRight", "zoom": 1.72, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomLeft", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
}
|
||||
],
|
||||
"lines": [
|
||||
{
|
||||
"speaker": "Narrator",
|
||||
"portrait": "resources/hero.png",
|
||||
"text": "The memory begins in silence.",
|
||||
"durationMs": 2200
|
||||
},
|
||||
{
|
||||
"speaker": "Narrator",
|
||||
"portrait": "resources/hero.png",
|
||||
"text": "Something is drawing your eyes across the whole scene.",
|
||||
"durationMs": 2800
|
||||
},
|
||||
{
|
||||
"speaker": "Ghost",
|
||||
"portrait": "resources/ghost_avatar.png",
|
||||
"text": "Do not look away.",
|
||||
"durationMs": 2400
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_pan_02",
|
||||
"background": "resources/first_cutscene.png",
|
||||
"durationMs": 12000,
|
||||
"cameraTrack": [
|
||||
{
|
||||
"durationMs": 1200,
|
||||
"from": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
|
||||
"easing": "Linear"
|
||||
},
|
||||
{
|
||||
"durationMs": 2500,
|
||||
"from": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "TopLeft", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 2600,
|
||||
"from": { "anchor": "TopLeft", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "TopRight", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
},
|
||||
{
|
||||
"durationMs": 1800,
|
||||
"from": { "anchor": "TopRight", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomRight", "zoom": 1.72, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInCubic"
|
||||
},
|
||||
{
|
||||
"durationMs": 3900,
|
||||
"from": { "anchor": "BottomRight", "zoom": 1.72, "rotationDeg": 0.0 },
|
||||
"to": { "anchor": "BottomLeft", "zoom": 1.55, "rotationDeg": 0.0 },
|
||||
"easing": "EaseInOutSine"
|
||||
}
|
||||
],
|
||||
"lines": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
{
|
||||
"key": "Аида Дженибековна",
|
||||
"ru": "Аида Дженибековна",
|
||||
"en": "Aida Dzhanybekovna"
|
||||
"en": "Aida Dzhenibekovna"
|
||||
},
|
||||
{
|
||||
"key": "Опаздывающие, заходите скорее и занимайте свои места! Лекция начинается!",
|
||||
@@ -23,7 +23,7 @@
|
||||
{
|
||||
"key": "Этот мир описан в эпосе Манас как Кайып или Аль-Гайб, но некоторые ученые называют его миром теней.",
|
||||
"ru": "Этот мир описан в эпосе Манас как Кайып или Аль-Гайб, но некоторые ученые называют его миром теней.",
|
||||
"en": "This world is described in the Manas epic as Kayip or Al-Ghayb, but some scholars call it the dark lands."
|
||||
"en": "This world is described in the Manas epic as Kayip or Al-Ghayb, but some scholars call it the Darkland."
|
||||
},
|
||||
{
|
||||
"key": "В этом мире обитают феи, духи и джинны. Простым смертным в этот мир дорога закрыта.",
|
||||
@@ -33,17 +33,17 @@
|
||||
{
|
||||
"key": "Время там течет по другому - за один день в теневом мире могут пройти годы жизни обычного мира.",
|
||||
"ru": "Время там течет по другому - за один день в теневом мире могут пройти годы жизни обычного мира.",
|
||||
"en": "Time flows differently there - in one day in the dark lands, years of life in the ordinary world can pass."
|
||||
"en": "Time flows differently there - in one day in the Darkland, years of life in the ordinary world can pass."
|
||||
},
|
||||
{
|
||||
"key": "Обычно, мир теней никак не пересекается с нашим миром живых людей.",
|
||||
"ru": "Обычно, мир теней никак не пересекается с нашим миром живых людей.",
|
||||
"en": "Usually, the world of dark lands does not intersect with our world of living people."
|
||||
"en": "Usually, the world of the Darkland does not intersect with our world of living people."
|
||||
},
|
||||
{
|
||||
"key": "Но в критические моменты для народа, обитатели теневого мира могут приходить в наш мир.",
|
||||
"ru": "Но в критические моменты для народа, обитатели теневого мира могут приходить в наш мир.",
|
||||
"en": "But in critical moments for the people, the inhabitants of the dark lands can come to our world."
|
||||
"en": "But in critical moments for the people, the inhabitants of the Darkland can come to our world."
|
||||
},
|
||||
{
|
||||
"key": "Совсем недавно закончилась пандемия, а сегодня мир захлестнули кровавые войны.",
|
||||
|
||||
@@ -136,9 +136,9 @@
|
||||
"en": "Before going outside, I have to order a taxi to the university."
|
||||
},
|
||||
{
|
||||
"key": "Я заказал такси до универа, машина уже ждет!",
|
||||
"ru": "Я заказал такси до универа, машина уже ждет!",
|
||||
"en": "I ordered a taxi to the university, the car is already waiting!"
|
||||
"key": "Я заказал такси до универа, машина уже едет!",
|
||||
"ru": "Я заказал такси до универа, машина уже едет!",
|
||||
"en": "I ordered a taxi to the university, the car is already on its way!"
|
||||
},
|
||||
{
|
||||
"key": "Я уже заказал такси, машина уже ждет!",
|
||||
@@ -261,9 +261,9 @@
|
||||
"en": "And where is her coursework now?"
|
||||
},
|
||||
{
|
||||
"key": "За зданием универа есть контейнер с кучей бумажного мусора и макулатурой.",
|
||||
"ru": "За зданием универа есть контейнер с кучей бумажного мусора и макулатурой.",
|
||||
"en": "Behind the university building there is a container with a pile of paper trash and waste."
|
||||
"key": "Возле здания универа лежит куча строительного мусора и макулатуры.",
|
||||
"ru": "Возле здания универа лежит куча строительного мусора и макулатуры.",
|
||||
"en": "There is a pile of construction waste and paper near the university building."
|
||||
},
|
||||
{
|
||||
"key": "Скорее всего, курсовая до сих пор лежит где-то там.",
|
||||
@@ -303,7 +303,7 @@
|
||||
{
|
||||
"key": "А ведь у нас в этом году новая преподавательница, Аида Дженибековна!",
|
||||
"ru": "А ведь у нас в этом году новая преподавательница, Аида Дженибековна!",
|
||||
"en": "But we have a new teacher this year, Aida Dzhanybekovna!"
|
||||
"en": "But we have a new teacher this year, Aida Dzhenibekovna!"
|
||||
},
|
||||
{
|
||||
"key": "Она конечно не сахар, но если ты ходишь на пары и вовремя сдашь ей задание на модуль, то ты считай сдал экзамен.",
|
||||
@@ -318,7 +318,7 @@
|
||||
{
|
||||
"key": "Так что Аида Дженибековна еще норм. Подойди к ней, возьми задание на модуль, и начни ходить на пары. Тогда ты сдашь экзамен успешно.",
|
||||
"ru": "Так что Аида Дженибековна еще норм. Подойди к ней, возьми задание на модуль, и начни ходить на пары. Тогда ты сдашь экзамен успешно.",
|
||||
"en": "So Aida Dzhanybekovna is still okay. Go to her, take the assignment for the module, and start going to class. Then you'll pass the exam successfully."
|
||||
"en": "So Aida Dzhenibekovna is still okay. Go to her, take the assignment for the module, and start going to class. Then you'll pass the exam successfully."
|
||||
},
|
||||
{
|
||||
"key": "Спасибо за информацию!",
|
||||
@@ -348,7 +348,7 @@
|
||||
{
|
||||
"key": "Говорят, ей не очень нравится наша новая преподавательница по манасоведению, Аида Дженибековна.",
|
||||
"ru": "Говорят, ей не очень нравится наша новая преподавательница по манасоведению, Аида Дженибековна.",
|
||||
"en": "They say she doesn't really like our new Manas studies teacher, Aida Dzhanybekovna."
|
||||
"en": "They say she doesn't really like our new Manas studies teacher, Aida Dzhenibekovna."
|
||||
},
|
||||
{
|
||||
"key": "Привет Бекзат! Надеюсь ты нашел то что ищешь.",
|
||||
@@ -456,9 +456,9 @@
|
||||
"en": "Before I leave the gate, I have to order a taxi to the dorm."
|
||||
},
|
||||
{
|
||||
"key": "Я заказал такси до общаги, машина уже ждет!",
|
||||
"ru": "Я заказал такси до общаги, машина уже ждет!",
|
||||
"en": "I ordered a taxi to the dorm, the car is already waiting!"
|
||||
"key": "Я заказал такси до общаги, машина уже едет!",
|
||||
"ru": "Я заказал такси до общаги, машина уже едет!",
|
||||
"en": "I ordered a taxi to the dorm, the car is already on its way!"
|
||||
},
|
||||
{
|
||||
"key": "Ого, пока я залипал в приложении, уже наступила ночь!",
|
||||
@@ -478,7 +478,7 @@
|
||||
{
|
||||
"key": "Кажется, на этой машине в универ приезжает Аида Дженибековна.",
|
||||
"ru": "Кажется, на этой машине в универ приезжает Аида Дженибековна.",
|
||||
"en": "It seems that Aida Dzhanybekovna arrives at the university in this car."
|
||||
"en": "It seems that Aida Dzhenibekovna arrives at the university in this car."
|
||||
},
|
||||
{
|
||||
"key": "Это заклинание не работает днем.",
|
||||
@@ -488,7 +488,7 @@
|
||||
{
|
||||
"key": "Чтобы перейти в теневой мир, мне нужно дождаться ночи.",
|
||||
"ru": "Чтобы перейти в теневой мир, мне нужно дождаться ночи.",
|
||||
"en": "To go into the dark lands, I need to wait until night."
|
||||
"en": "To go into the Darkland, I need to wait until night."
|
||||
},
|
||||
{
|
||||
"key": "Чтобы время пролетело быстро, я могу посидеть в телефоне и позалипать в короткие видео.",
|
||||
@@ -498,7 +498,7 @@
|
||||
{
|
||||
"key": "У меня силы закончились и мне спать хочется. Достаточно теневого мира на сегодня, поехали в общагу.",
|
||||
"ru": "У меня силы закончились и мне спать хочется. Достаточно теневого мира на сегодня, поехали в общагу.",
|
||||
"en": "I'm exhausted and sleepy. Enough of the dark lands for today, let's go to the dorm."
|
||||
"en": "I'm exhausted and sleepy. Enough of the Darkland for today, let's go to the dorm."
|
||||
},
|
||||
{
|
||||
"key": "Ты куда собрался, Бекзат?",
|
||||
@@ -658,7 +658,7 @@
|
||||
{
|
||||
"key": "Аида Дженибековна",
|
||||
"ru": "Аида Дженибековна",
|
||||
"en": "Aida Dzhanybekovna"
|
||||
"en": "Aida Dzhenibekovna"
|
||||
},
|
||||
{
|
||||
"key": "Бекзат, что тебе нужно?",
|
||||
@@ -688,7 +688,7 @@
|
||||
{
|
||||
"key": "Аида Дженибековна",
|
||||
"ru": "Аида Дженибековна",
|
||||
"en": "Aida Dzhanybekovna"
|
||||
"en": "Aida Dzhenibekovna"
|
||||
},
|
||||
{
|
||||
"key": "Зачем тебе в учительскую?",
|
||||
@@ -828,7 +828,7 @@
|
||||
{
|
||||
"key": "Извините, Аида Дженибековна, я могу пойти уже?",
|
||||
"ru": "Извините, Аида Дженибековна, я могу пойти уже?",
|
||||
"en": "Excuse me, Aida Dzhanybekovna, can I go now?"
|
||||
"en": "Excuse me, Aida Dzhenibekovna, can I go now?"
|
||||
},
|
||||
{
|
||||
"key": "Нет, Бекзат. Ты вломился в учительскую, и теперь хочешь просто так уйти? Не получится.",
|
||||
@@ -898,7 +898,7 @@
|
||||
{
|
||||
"key": "Аида Дженибековна, вы помните Бегимай? Она вам курсовую хотела сдать.",
|
||||
"ru": "Аида Дженибековна, вы помните Бегимай? Она вам курсовую хотела сдать.",
|
||||
"en": "Aida Dzhanybekovna, do you remember Begimai? She wanted to submit her term paper to you."
|
||||
"en": "Aida Dzhenibekovna, do you remember Begimai? She wanted to submit her term paper to you."
|
||||
},
|
||||
{
|
||||
"key": "Да, она говорила что приносила курсовую, но у меня ее нигде нет.",
|
||||
@@ -913,7 +913,7 @@
|
||||
{
|
||||
"key": "Бекзат, зачетка Бегимай у тебя. Отправляйся в теневой мир и покажи зачетку призраку Бегимай, чтобы она освободилась.",
|
||||
"ru": "Бекзат, зачетка Бегимай у тебя. Отправляйся в теневой мир и покажи зачетку призраку Бегимай, чтобы она освободилась.",
|
||||
"en": "Bekzat, you have Begimai's grade book. Go to the dark lands and show the grade book to Begimai's ghost so she can be released."
|
||||
"en": "Bekzat, you have Begimai's record book. Go to the Darkland and show the record book to Begimai's ghost so she can be released."
|
||||
},
|
||||
{
|
||||
"key": "Хорошо!",
|
||||
@@ -923,12 +923,12 @@
|
||||
{
|
||||
"key": "Аида Дженибековна, здравствуйте!",
|
||||
"ru": "Аида Дженибековна, здравствуйте!",
|
||||
"en": "Aida Dzhanybekovna, hello!"
|
||||
"en": "Aida Dzhenibekovna, hello!"
|
||||
},
|
||||
{
|
||||
"key": "Я принес вам зачетку Бегимай и ее курсовую работу, посмотрите пожалуйста.",
|
||||
"ru": "Я принес вам зачетку Бегимай и ее курсовую работу, посмотрите пожалуйста.",
|
||||
"en": "I brought you Begimai's grade book and her coursework, please take a look."
|
||||
"en": "I brought you Begimai's record book and her coursework, please take a look."
|
||||
},
|
||||
{
|
||||
"key": "Хорошо, давай посмотрим.",
|
||||
@@ -943,12 +943,12 @@
|
||||
{
|
||||
"key": "Вот держи зачетку с оценкой.",
|
||||
"ru": "Вот держи зачетку с оценкой.",
|
||||
"en": "Here, take the grade book with the grade."
|
||||
"en": "Here, take the record book with the grade."
|
||||
},
|
||||
{
|
||||
"key": "Теперь иди в теневой мир и покажи зачетку Бегимай. Расскажи ей, что она сдала курсовую работу.",
|
||||
"ru": "Теперь иди в теневой мир и покажи зачетку Бегимай. Расскажи ей, что она сдала курсовую работу.",
|
||||
"en": "Now go to the dark lands and show Begimai her grade book. Tell her she passed her coursework."
|
||||
"en": "Now go to the Darkland and show Begimai her record book. Tell her she passed her coursework."
|
||||
},
|
||||
{
|
||||
"key": "Мне стоит вернуть книгу на место, прежде чем уходить из библиотеки.",
|
||||
@@ -958,7 +958,7 @@
|
||||
{
|
||||
"key": "Иначе Аида Дженибековна меня убъет.",
|
||||
"ru": "Иначе Аида Дженибековна меня убъет.",
|
||||
"en": "Otherwise Aida Dzhanybekovna will kill me."
|
||||
"en": "Otherwise Aida Dzhenibekovna will kill me."
|
||||
},
|
||||
{
|
||||
"key": "Куда я попал?",
|
||||
@@ -1023,7 +1023,7 @@
|
||||
{
|
||||
"key": "Я уйду на покой только когда увиду оценку по курсовой в своей зачетке.",
|
||||
"ru": "Я уйду на покой только когда увиду оценку по курсовой в своей зачетке.",
|
||||
"en": "I will retire only when I see the grade for my coursework in my grade book."
|
||||
"en": "I will retire only when I see the grade for my coursework in my record book."
|
||||
},
|
||||
{
|
||||
"key": "А до тех пор, я буду появлятся здесь каждую ночь.",
|
||||
@@ -1048,7 +1048,7 @@
|
||||
{
|
||||
"key": "Это твоя зачетка.",
|
||||
"ru": "Это твоя зачетка.",
|
||||
"en": "This is your grade book."
|
||||
"en": "This is your record book."
|
||||
},
|
||||
{
|
||||
"key": "Тебе поставили за курсовую максимальный балл!",
|
||||
@@ -1143,17 +1143,17 @@
|
||||
{
|
||||
"key": "Тут лежат зачетные книжки студентов.",
|
||||
"ru": "Тут лежат зачетные книжки студентов.",
|
||||
"en": "Students' grade books are kept here."
|
||||
"en": "Students' record books are kept here."
|
||||
},
|
||||
{
|
||||
"key": "Здесь лежит зачетка Бегимай. Я пожалуй, возьму ее.",
|
||||
"ru": "Здесь лежит зачетка Бегимай. Я пожалуй, возьму ее.",
|
||||
"en": "Begimai's grade book is here. I think I'll take it."
|
||||
"en": "Begimai's record book is here. I think I'll take it."
|
||||
},
|
||||
{
|
||||
"key": "Мне еще рано возвращать зачетку Бегимай обратно в шкаф.",
|
||||
"ru": "Мне еще рано возвращать зачетку Бегимай обратно в шкаф.",
|
||||
"en": "It's too early for me to return Begimai's grade book back to the shelf."
|
||||
"en": "It's too early for me to return Begimai's record book back to the shelf."
|
||||
},
|
||||
{
|
||||
"key": "Чтобы заказать такси, я сначала должен выйти на улицу.",
|
||||
@@ -1358,12 +1358,12 @@
|
||||
{
|
||||
"key": "Ко мне попала странная записка, где было написано заклинание. Я прочитал его, и попал в потусторонний мир.",
|
||||
"ru": "Ко мне попала странная записка, где было написано заклинание. Я прочитал его, и попал в потусторонний мир.",
|
||||
"en": "I received a strange note containing a spell. I read it and found myself in the dark lands."
|
||||
"en": "I received a strange note containing a spell. I read it and found myself in the Darkland."
|
||||
},
|
||||
{
|
||||
"key": "Ты что опять в свои игры переиграл? Какой потусторонний мир?",
|
||||
"ru": "Ты что опять в свои игры переиграл? Какой потусторонний мир?",
|
||||
"en": "Are you playing your video games too much? What dark lands?"
|
||||
"en": "Are you playing your video games too much? What Darkland?"
|
||||
},
|
||||
{
|
||||
"key": "Но это правда! Там все было по-другому, и я видел призраков...",
|
||||
@@ -1378,7 +1378,7 @@
|
||||
{
|
||||
"key": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.",
|
||||
"ru": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.",
|
||||
"en": "Aida Dzhanybekovna saw me in the teachers' room and made me write an essay on this stupid book."
|
||||
"en": "Aida Dzhenibekovna saw me in the teachers' room and made me write an essay on this stupid book."
|
||||
},
|
||||
{
|
||||
"key": "Я писал, писал и уснул.",
|
||||
@@ -1448,7 +1448,7 @@
|
||||
{
|
||||
"key": "Поэтому, с этого года у нас этот предмет ведет Аида Дженибековна.",
|
||||
"ru": "Поэтому, с этого года у нас этот предмет ведет Аида Дженибековна.",
|
||||
"en": "Therefore, starting this year, Aida Dzhanybekovna has been teaching this subject."
|
||||
"en": "Therefore, starting this year, Aida Dzhenibekovna has been teaching this subject."
|
||||
},
|
||||
{
|
||||
"key": "Ты не знаешь кого-нибудь, кто был знаком с Бегимай?",
|
||||
@@ -1473,7 +1473,7 @@
|
||||
{
|
||||
"key": "Я лучше прочитаю заклинание, перейду в теневой мир и так выберусь наружу.",
|
||||
"ru": "Я лучше прочитаю заклинание, перейду в теневой мир и так выберусь наружу.",
|
||||
"en": "I'd rather cast a spell, go into the dark lands, and get out that way."
|
||||
"en": "I'd rather cast a spell, go into the Darkland, and get out that way."
|
||||
},
|
||||
{
|
||||
"key": "Я не буду беспокоить Айпери. Я могу выбраться из универа и без ее помощи.",
|
||||
@@ -1588,12 +1588,12 @@
|
||||
{
|
||||
"key": "Я попал в теневой мир, чтобы выбраться из запертого кабинета.",
|
||||
"ru": "Я попал в теневой мир, чтобы выбраться из запертого кабинета.",
|
||||
"en": "I entered the dark lands to escape from the locked room."
|
||||
"en": "I entered the Darkland to escape from the locked room."
|
||||
},
|
||||
{
|
||||
"key": "Но по злой иронии, когда я вернулся из теневого мира, я снова оказался за запертой дверью.",
|
||||
"ru": "Но по злой иронии, когда я вернулся из теневого мира, я снова оказался за запертой дверью.",
|
||||
"en": "But by a cruel irony, when I returned from the dark lands, I found myself behind a locked door again."
|
||||
"en": "But by a cruel irony, when I returned from the Darkland, I found myself behind a locked door again."
|
||||
},
|
||||
{
|
||||
"key": "Хорошо, что уже утро. Надо написать Айпери, может она уже скоро приедет в универ?",
|
||||
@@ -1623,7 +1623,7 @@
|
||||
{
|
||||
"key": "Здравствуйте Аида Дженибековна!",
|
||||
"ru": "Здравствуйте Аида Дженибековна!",
|
||||
"en": "Hello Aida Dzhanybekovna!"
|
||||
"en": "Hello Aida Dzhenibekovna!"
|
||||
},
|
||||
{
|
||||
"key": "Ну что Бекзат, эссе про Жусупа Мамая готово к сдаче?",
|
||||
@@ -1683,12 +1683,12 @@
|
||||
{
|
||||
"key": "Это заклинание переместило тебя в мир теней. ",
|
||||
"ru": "Это заклинание переместило тебя в мир теней.",
|
||||
"en": "This spell transported you to the dark lands."
|
||||
"en": "This spell transported you to the Darkland."
|
||||
},
|
||||
{
|
||||
"key": "Этот мир предназначен для фей и джиннов. Обычные люди туда попасть не могут. Но у тебя это как-то получилось.",
|
||||
"ru": "Этот мир предназначен для фей и джиннов. Обычные люди туда попасть не могут. Но у тебя это как-то получилось.",
|
||||
"en": "These lands are meant for fairies and genies. Ordinary people can't get there. But somehow you made it."
|
||||
"en": "This land is meant for fairies and genies. Ordinary people can't get there. But somehow you made it."
|
||||
},
|
||||
{
|
||||
"key": "А вы откуда все это знаете?",
|
||||
@@ -1723,12 +1723,12 @@
|
||||
{
|
||||
"key": "А на утро бывает так, что все зачетки в шкафу раскиданы как попало.",
|
||||
"ru": "А на утро бывает так, что все зачетки в шкафу раскиданы как попало.",
|
||||
"en": "And in the morning it happens that all the grade books in the closet are scattered all over the place."
|
||||
"en": "And in the morning it happens that all the record books in the closet are scattered all over the place."
|
||||
},
|
||||
{
|
||||
"key": "Призрак обитает где-то в теневом мире. Попади туда и наладь с ним диалог. Узнай, кто он и что ему нужно.",
|
||||
"ru": "Призрак обитает где-то в теневом мире. Попади туда и наладь с ним диалог. Узнай, кто он и что ему нужно.",
|
||||
"en": "The ghost dwells somewhere in the dark lands. Get there and establish a dialogue with him. Find out who he is and what he wants."
|
||||
"en": "The ghost dwells somewhere in the Darkland. Get there and establish a dialogue with him. Find out who he is and what he wants."
|
||||
},
|
||||
{
|
||||
"key": "Если ты успешно избавишься от призрака, я поставлю тебе максимальный балл за модуль.",
|
||||
@@ -1753,7 +1753,7 @@
|
||||
{
|
||||
"key": "Обсудить теневой мир",
|
||||
"ru": "Обсудить теневой мир",
|
||||
"en": "Discuss the dark lands"
|
||||
"en": "Discuss the Darkland"
|
||||
},
|
||||
{
|
||||
"key": "Обсудить записку",
|
||||
@@ -1763,12 +1763,12 @@
|
||||
{
|
||||
"key": "Что это вообще за теневой мир? Откуда он взялся?",
|
||||
"ru": "Что это вообще за теневой мир? Откуда он взялся?",
|
||||
"en": "What is this dark lands anyway? Where did it come from?"
|
||||
"en": "What is this Darkland anyway? Where did it come from?"
|
||||
},
|
||||
{
|
||||
"key": "Теневой мир это мир, в котором обитают феи, духи и джинны. Он существовал всегда.",
|
||||
"ru": "Теневой мир это мир, в котором обитают феи, духи и джинны. Он существовал всегда.",
|
||||
"en": "The Dark Lands is a world inhabited by fairies, spirits, and genies. It has always existed."
|
||||
"en": "The Darkland is a world inhabited by fairies, spirits, and genies. It has always existed."
|
||||
},
|
||||
{
|
||||
"key": "Обычные люди, не воители, туда не попадают обычно. Кроме тебя, тебе это как-то удалось.",
|
||||
@@ -1783,22 +1783,22 @@
|
||||
{
|
||||
"key": "Время в теневом мире",
|
||||
"ru": "Время в теневом мире",
|
||||
"en": "Time in the dark lands"
|
||||
"en": "Time in the Darkland"
|
||||
},
|
||||
{
|
||||
"key": "Призраки в теневом мире",
|
||||
"ru": "Призраки в теневом мире",
|
||||
"en": "Ghosts in the dark lands"
|
||||
"en": "Ghosts in the Darkland"
|
||||
},
|
||||
{
|
||||
"key": "Двери в теневом мире",
|
||||
"ru": "Двери в теневом мире",
|
||||
"en": "Doors in the dark lands"
|
||||
"en": "Doors in the Darkland"
|
||||
},
|
||||
{
|
||||
"key": "Выход из теневого мира",
|
||||
"ru": "Выход из теневого мира",
|
||||
"en": "Exit from the dark lands"
|
||||
"en": "Exit from the Darkland"
|
||||
},
|
||||
{
|
||||
"key": "Достаточно",
|
||||
@@ -1808,37 +1808,37 @@
|
||||
{
|
||||
"key": "Почему в теневом мире нет дверей?",
|
||||
"ru": "Почему в теневом мире нет дверей?",
|
||||
"en": "Why are there no doors in the dark lands?"
|
||||
"en": "Why are there no doors in the Darkland?"
|
||||
},
|
||||
{
|
||||
"key": "Я думаю, в мир теней проецируются только фундаментальные объекты, типа зданий, деревьев, рельефа.",
|
||||
"ru": "Я думаю, в мир теней проецируются только фундаментальные объекты, типа зданий, деревьев, рельефа.",
|
||||
"en": "I think that only fundamental objects, such as buildings, trees, and terrain, are projected into the dark lands."
|
||||
"en": "I think that only fundamental objects, such as buildings, trees, and terrain, are projected into the Darkland."
|
||||
},
|
||||
{
|
||||
"key": "Это происходит потому, что в теневом мире время течет сильно медленнее, чем в нашем мире.",
|
||||
"ru": "Это происходит потому, что в теневом мире время течет сильно медленнее, чем в нашем мире.",
|
||||
"en": "This happens because in the dark lands time flows much slower than in our world."
|
||||
"en": "This happens because in the Darkland time flows much slower than in our world."
|
||||
},
|
||||
{
|
||||
"key": "Двери у нас слишком часто открываются и закрываются, поэтому в теневом мире они \"размываются\" или попадают в суперпозицию.",
|
||||
"ru": "Двери у нас слишком часто открываются и закрываются, поэтому в теневом мире они \"размываются\" или попадают в суперпозицию.",
|
||||
"en": "Our doors open and close too often, so in the dark lands they become \"blurred\" or fall into some sort of superposition."
|
||||
"en": "Our doors open and close too often, so in the Darkland they become \"blurred\" or fall into some sort of superposition."
|
||||
},
|
||||
{
|
||||
"key": "Поэтому в мире теней ты можешь спокойно пройти через дверной проем. Но сквозь стены ты пройти не сможешь, они более плотные.",
|
||||
"ru": "Поэтому в мире теней ты можешь спокойно пройти через дверной проем. Но сквозь стены ты пройти не сможешь, они более плотные.",
|
||||
"en": "So, in the dark lands, you can easily pass through a doorway. But you won't be able to pass through walls; they're denser."
|
||||
"en": "So, in the Darkland, you can easily pass through a doorway. But you won't be able to pass through walls; they're denser."
|
||||
},
|
||||
{
|
||||
"key": "Откуда в теневом мире призраки?",
|
||||
"ru": "Откуда в теневом мире призраки?",
|
||||
"en": "Where do ghosts come from in the dark lands?"
|
||||
"en": "Where do ghosts come from in the Darkland?"
|
||||
},
|
||||
{
|
||||
"key": "Это их мир, и они там живут. Я думаю что увидев тебя, обитатели теневого мира тоже были шокированы.",
|
||||
"ru": "Это их мир, и они там живут. Я думаю что увидев тебя, обитатели теневого мира тоже были шокированы.",
|
||||
"en": "This is their world, and they live there. I think the inhabitants of the dark lands were also shocked when they saw you."
|
||||
"en": "This is their world, and they live there. I think the inhabitants of the Darkland were also shocked when they saw you."
|
||||
},
|
||||
{
|
||||
"key": "Но кое в чем ты прав. Здесь в универе, призраков раньше не было. Всякая чертовщина начала происходить совсем недавно.",
|
||||
@@ -1863,12 +1863,12 @@
|
||||
{
|
||||
"key": "Каждый раз, когда я выпадаю из теневого мира, наступает утро. Хотя я там был всего несколько минут.",
|
||||
"ru": "Каждый раз, когда я выпадаю из теневого мира, наступает утро. Хотя я там был всего несколько минут.",
|
||||
"en": "Every time I emerge from the dark lands, morning comes, even though I was only there for a few minutes."
|
||||
"en": "Every time I emerge from the Darkland, morning comes, even though I was only there for a few minutes."
|
||||
},
|
||||
{
|
||||
"key": "Время в мире теней течет значительно медленнее, чем в нашем.",
|
||||
"ru": "Время в мире теней течет значительно медленнее, чем в нашем.",
|
||||
"en": "Time in the dark lands flows much slower than in ours."
|
||||
"en": "Time in the Darkland flows much slower than in ours."
|
||||
},
|
||||
{
|
||||
"key": "И похоже, что течение времени там нелинейное.",
|
||||
@@ -1878,22 +1878,22 @@
|
||||
{
|
||||
"key": "Это объясняет, почему в теневом мире есть деревья и строения, но отсутствуют двери, автомобили, и прочее.",
|
||||
"ru": "Это объясняет, почему в теневом мире есть деревья и строения, но отсутствуют двери, автомобили, и прочее.",
|
||||
"en": "This explains why the dark lands has trees and buildings, but no doors, cars, etc."
|
||||
"en": "This explains why the Darkland has trees and buildings, but no doors, cars, etc."
|
||||
},
|
||||
{
|
||||
"key": "Объекты в нашем мире должны долго находится на одном месте, прежде чем их проекция появится в мире теней.",
|
||||
"ru": "Объекты в нашем мире должны долго находится на одном месте, прежде чем их проекция появится в мире теней.",
|
||||
"en": "Objects in our world must remain in one place for a long time before their projection appears in the dark lands."
|
||||
"en": "Objects in our world must remain in one place for a long time before their projection appears in the Darkland."
|
||||
},
|
||||
{
|
||||
"key": "Меня постоянно выкидывает из теневого мира. Как я могу там остаться подольше?",
|
||||
"ru": "Меня постоянно выкидывает из теневого мира. Как я могу там остаться подольше?",
|
||||
"en": "I'm constantly being thrown out of the dark lands. How can I stay there any longer?"
|
||||
"en": "I'm constantly being thrown out of the Darkland. How can I stay there any longer?"
|
||||
},
|
||||
{
|
||||
"key": "Если ты потеряешь сознание в теневом мире, то ты принудительно вернешься в наш мир.",
|
||||
"ru": "Если ты потеряешь сознание в теневом мире, то ты принудительно вернешься в наш мир.",
|
||||
"en": "If you lose consciousness in the dark lands, you will be forcibly returned to our world."
|
||||
"en": "If you lose consciousness in the Darkland, you will be forcibly returned to our world."
|
||||
},
|
||||
{
|
||||
"key": "Также избегай света луны, луна тоже может вернуть тебя в мир людей. Лучше вообще не выходи из помещения. ",
|
||||
@@ -1903,17 +1903,17 @@
|
||||
{
|
||||
"key": "Если ты задержишься, и в человеческом мире настанет утро, тебя тоже выбросит из теневого мира.",
|
||||
"ru": "Если ты задержишься, и в человеческом мире настанет утро, тебя тоже выбросит из теневого мира.",
|
||||
"en": "If you stay too long and morning comes in the human world, you will be thrown out of the dark lands too."
|
||||
"en": "If you stay too long and morning comes in the human world, you will be thrown out of the Darkland too."
|
||||
},
|
||||
{
|
||||
"key": "Но ты не беспокойся. Даже если тебя выбросит из теневого мира, ты всегда сможешь повторить свою попытку на следующую ночь.",
|
||||
"ru": "Но ты не беспокойся. Даже если тебя выбросит из теневого мира, ты всегда сможешь повторить свою попытку на следующую ночь.",
|
||||
"en": "But don't worry. Even if you're thrown out of the dark lands, you can always try again the next night."
|
||||
"en": "But don't worry. Even if you're thrown out of the Darkland, you can always try again the next night."
|
||||
},
|
||||
{
|
||||
"key": "У тебя есть еще вопросы про теневой мир?",
|
||||
"ru": "У тебя есть еще вопросы про теневой мир?",
|
||||
"en": "Do you have any more questions about the dark lands?"
|
||||
"en": "Do you have any more questions about the Darkland?"
|
||||
},
|
||||
{
|
||||
"key": "Ладно, я узнал достаточно.",
|
||||
@@ -1938,7 +1938,7 @@
|
||||
{
|
||||
"key": "Когда я узнала, что в университете начались паранормальные явления, я сразу поняла что это призрак в теневом мире.",
|
||||
"ru": "Когда я узнала, что в университете начались паранормальные явления, я сразу поняла что это призрак в теневом мире.",
|
||||
"en": "When I learned that paranormal phenomena had begun at the university, I immediately realized that it was a ghost in the dark lands."
|
||||
"en": "When I learned that paranormal phenomena had begun at the university, I immediately realized that it was a ghost in the Darkland."
|
||||
},
|
||||
{
|
||||
"key": "Я знаю заклинание, которое тебе дали. У этого заклинания есть еще одно условие - оно сработает только на людей, которые чисты сердцем.",
|
||||
@@ -1953,12 +1953,12 @@
|
||||
{
|
||||
"key": "Тогда я предположила, что Айпери уж точно сможет прочитать заклинание и попасть в теневой мир.",
|
||||
"ru": "Тогда я предположила, что Айпери уж точно сможет прочитать заклинание и попасть в теневой мир.",
|
||||
"en": "Then I assumed that Aiperi would definitely be able to cast the spell and enter the dark lands."
|
||||
"en": "Then I assumed that Aiperi would definitely be able to cast the spell and enter the Darkland."
|
||||
},
|
||||
{
|
||||
"key": "Как раз поэтому я хотела вернуть нож ей лично. И заодно отправить ее в теневой мир.",
|
||||
"ru": "Как раз поэтому я хотела вернуть нож ей лично. И заодно отправить ее в теневой мир.",
|
||||
"en": "That's exactly why I wanted to return the knife to her personally. And at the same time, ask her to go to the dark lands."
|
||||
"en": "That's exactly why I wanted to return the knife to her personally. And at the same time, ask her to go to the Darkland."
|
||||
},
|
||||
{
|
||||
"key": "Но ты смешал все мои планы. Сначала ты забрал нож, а потом кто-то подсунул тебе записку.",
|
||||
@@ -2068,7 +2068,7 @@
|
||||
{
|
||||
"key": "Переходи в теневой мир, а затем иди в лекционный зал.",
|
||||
"ru": "Переходи в теневой мир, а затем иди в лекционный зал.",
|
||||
"en": "Go to the dark lands and then go to the lecture hall."
|
||||
"en": "Go to the Darkland and then go to the lecture hall."
|
||||
},
|
||||
{
|
||||
"key": "Постарайся поговорить с призраком, и узнать, что ему нужно.",
|
||||
@@ -2093,7 +2093,7 @@
|
||||
{
|
||||
"key": "Иди к шкафу в учительской. Среди зачетных книжек найти зачетку Бегимай и принеси мне. Я поставлю ей оценку за курсовую работу.",
|
||||
"ru": "Иди к шкафу в учительской. Среди зачетных книжек найти зачетку Бегимай и принеси мне. Я поставлю ей оценку за курсовую работу.",
|
||||
"en": "Go to the cabinet in the teachers' room. Find Begimai's grade book among the grade books and bring it to me. I'll give her a grade for her coursework."
|
||||
"en": "Go to the cabinet in the teachers' room. Find Begimai's record book among the record books and bring it to me. I'll give her a grade for her coursework."
|
||||
},
|
||||
{
|
||||
"key": "Ты уже нашел курсовую работу Бегимай?",
|
||||
@@ -2153,7 +2153,7 @@
|
||||
{
|
||||
"key": "Теперь поищи в шкафу зачетку Бегимай, и принеси мне, я выставлю ей оценку.",
|
||||
"ru": "Теперь поищи в шкафу зачетку Бегимай, и принеси мне, я выставлю ей оценку.",
|
||||
"en": "Now look in the cabinet for Begimai's grade book and bring it to me, I'll give her a grade."
|
||||
"en": "Now look in the cabinet for Begimai's record book and bring it to me, I'll give her a grade."
|
||||
},
|
||||
{
|
||||
"key": "Еще нет.",
|
||||
@@ -2173,7 +2173,7 @@
|
||||
{
|
||||
"key": "Но если я возьму кусочек, Аида Дженибековна меня убьет.",
|
||||
"ru": "Но если я возьму кусочек, Аида Дженибековна меня убьет.",
|
||||
"en": "But if I take a piece, Aida Dzhanybekovna will kill me."
|
||||
"en": "But if I take a piece, Aida Dzhenibekovna will kill me."
|
||||
},
|
||||
{
|
||||
"key": "Здесь лежат книги по юриспруденции и праву.",
|
||||
@@ -2183,12 +2183,12 @@
|
||||
{
|
||||
"key": "Бекзат, помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.",
|
||||
"ru": "Бекзат, помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.",
|
||||
"en": "Bekzat, remember how we chipped in for a cake for Aida Dzhanybekovna? I even brought a tablecloth, plates, and a silver cake knife. And I still haven't gotten anything back."
|
||||
"en": "Bekzat, remember how we chipped in for a cake for Aida Dzhenibekovna? I even brought a tablecloth, plates, and a silver cake knife. And I still haven't gotten anything back."
|
||||
},
|
||||
{
|
||||
"key": "Помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.",
|
||||
"ru": "Помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.",
|
||||
"en": "Remember when we chipped in for a cake for Aida Dzhanybekovna? I even brought a tablecloth, plates, and a silver cake knife. And I still haven't gotten anything back."
|
||||
"en": "Remember when we chipped in for a cake for Aida Dzhenibekovna? I even brought a tablecloth, plates, and a silver cake knife. And I still haven't gotten anything back."
|
||||
},
|
||||
{
|
||||
"key": "А во вторых, мне как-то не хочется попадаться на глаза Аиде.",
|
||||
@@ -2198,7 +2198,7 @@
|
||||
{
|
||||
"key": "Да... Только не говори Аиде Дженибековной что я здесь.",
|
||||
"ru": "Да... Только не говори Аиде Дженибековной что я здесь.",
|
||||
"en": "Yes... Just don't tell Aida Dzhanybekovna that I'm here."
|
||||
"en": "Yes... Just don't tell Aida Dzhenibekovna that I'm here."
|
||||
},
|
||||
{
|
||||
"key": "Постарайся только Аиде Дженибековной особо на глаза не попадаться. ",
|
||||
@@ -2233,7 +2233,7 @@
|
||||
{
|
||||
"key": "Я уйду на покой только когда увижу оценку по курсовой в своей зачетке.",
|
||||
"ru": "Я уйду на покой только когда увижу оценку по курсовой в своей зачетке.",
|
||||
"en": "I will rest in peace only when I see the grade for my coursework in my grade book."
|
||||
"en": "I will rest in peace only when I see the grade for my coursework in my record book."
|
||||
},
|
||||
{
|
||||
"key": "А до тех пор, я буду появляться здесь каждую ночь.",
|
||||
@@ -2248,17 +2248,67 @@
|
||||
{
|
||||
"key": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.",
|
||||
"ru": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.",
|
||||
"en": "Aida Dzhanybekovna saw me in the teachers' room and made me write an essay on this stupid book."
|
||||
"en": "Aida Dzhenibekovna saw me in the teachers' room and made me write an essay on this stupid book."
|
||||
},
|
||||
{
|
||||
"key": "Прошу прощения Аида Дженибековна, я правда старался, но у меня возникли обстоятельства непреодолимой силы.",
|
||||
"ru": "Прошу прощения Аида Дженибековна, я правда старался, но у меня возникли обстоятельства непреодолимой силы.",
|
||||
"en": "I apologize, Aida Dzhanybekovna, I really tried, but I encountered force majeure circumstances."
|
||||
"en": "I apologize, Aida Dzhenibekovna, I really tried, but I encountered force majeure circumstances."
|
||||
},
|
||||
{
|
||||
"key": "Бекзат, я сейчас занята, не мешай мне!",
|
||||
"ru": "Бекзат, я сейчас занята, не мешай мне!",
|
||||
"en": "Bekzat, I'm busy now, don't disturb me!"
|
||||
},
|
||||
{
|
||||
"key": "Почему ты сама не можешь забрать нож у Аиды?",
|
||||
"ru": "Почему ты сама не можешь забрать нож у Аиды?",
|
||||
"en": "Why can't you take the knife from Aida yourself?"
|
||||
},
|
||||
{
|
||||
"key": "Она опять будет скафнить.",
|
||||
"ru": "Она опять будет скафнить.",
|
||||
"en": "She will annoy me again."
|
||||
},
|
||||
{
|
||||
"key": "А может быть даже даст мне какое-нибудь сложное задание.",
|
||||
"ru": "А может быть даже даст мне какое-нибудь сложное задание.",
|
||||
"en": "And maybe she will even give me some difficult assignment."
|
||||
},
|
||||
{
|
||||
"key": "Я даже уверена, она меня ищет. Она хочет мне какое-то задание дать.",
|
||||
"ru": "Я даже уверена, она меня ищет. Она хочет мне какое-то задание дать.",
|
||||
"en": "I'm even sure she's looking for me. She wants to give me some kind of assignment."
|
||||
},
|
||||
{
|
||||
"key": "Пока ты не принесешь мне нож из учительской, никуда я тебя не выпущу. Ошо!",
|
||||
"ru": "Пока ты не принесешь мне нож из учительской, никуда я тебя не выпущу. Ошо!",
|
||||
"en": "Until you bring me a knife from the teachers' room, I won't let you go anywhere."
|
||||
},
|
||||
{
|
||||
"key": "Я слышала она не смогла сдать курсовую по манасоведению.",
|
||||
"ru": "Я слышала она не смогла сдать курсовую по манасоведению.",
|
||||
"en": "I heard she couldn't pass her coursework on Manas studies."
|
||||
},
|
||||
{
|
||||
"key": "Она прыгнула с окна и разбилась насмерть деп.",
|
||||
"ru": "Она прыгнула с окна и разбилась насмерть деп.",
|
||||
"en": "She jumped from the window and fell to her death, they said."
|
||||
},
|
||||
{
|
||||
"key": "Да она надоела, все время скафнит.",
|
||||
"ru": "Да она надоела, все время скафнит.",
|
||||
"en": "Yes, she's annoying, she's always nagging."
|
||||
},
|
||||
{
|
||||
"key": "Если я ее встречу, она на 100% даст мне какое-нибудь сложное задание.",
|
||||
"ru": "Если я ее встречу, она на 100% даст мне какое-нибудь сложное задание.",
|
||||
"en": "If I meet her, she will 100% give me some difficult task."
|
||||
},
|
||||
{
|
||||
"key": "Так что жду тебя в универе! Не вздумай прогулять! Ошо!",
|
||||
"ru": "Так что жду тебя в универе! Не вздумай прогулять! Ошо!",
|
||||
"en": "So I'm waiting for you at the university! Don't even think about skipping!"
|
||||
}
|
||||
]
|
||||
}
|
||||
+13
-4
@@ -134,7 +134,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/dialogue/portrait_hero_neutral.png",
|
||||
"text": "Я заказал такси до общаги, машина уже ждет!",
|
||||
"text": "Я заказал такси до общаги, машина уже едет!",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
@@ -334,7 +334,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "Почему ты сама не можешь забрать?",
|
||||
"text": "Почему ты сама не можешь забрать нож у Аиды?",
|
||||
"next": "line_9",
|
||||
"chatBubble": "out"
|
||||
},
|
||||
@@ -343,7 +343,16 @@
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "Ты же знаешь, если я встречу Аиду, она 100% даст мне какое-нибудь сложное задание.",
|
||||
"text": "Она опять будет скафнить.",
|
||||
"next": "line_12",
|
||||
"chatBubble": "in"
|
||||
},
|
||||
{
|
||||
"id": "line_12",
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "А может быть даже даст мне какое-нибудь сложное задание.",
|
||||
"next": "line_10",
|
||||
"chatBubble": "in"
|
||||
},
|
||||
@@ -361,7 +370,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "Так что жду тебя в универе! Не вздумай прогулять!",
|
||||
"text": "Так что жду тебя в универе! Не вздумай прогулять! Ошо!",
|
||||
"next": "setflag_1",
|
||||
"chatBubble": "in",
|
||||
"questUnlock": "aiperi_knife"
|
||||
+40
-7
@@ -220,7 +220,15 @@
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_aiperi.png",
|
||||
"text": "У меня есть ощущение что если я ее встречу, она даст мне какое-то очень сложное задание.",
|
||||
"text": "Она опять будет скафнить.",
|
||||
"next": "line_29"
|
||||
},
|
||||
{
|
||||
"id": "line_29",
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_aiperi.png",
|
||||
"text": "А может быть даже даст мне какое-нибудь сложное задание.",
|
||||
"next": "line_16"
|
||||
},
|
||||
{
|
||||
@@ -285,6 +293,14 @@
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/dialogue/portrait_hero_neutral.png",
|
||||
"text": "Почему?",
|
||||
"next": "line_30"
|
||||
},
|
||||
{
|
||||
"id": "line_30",
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_aiperi.png",
|
||||
"text": "Она опять будет скафнить.",
|
||||
"next": "line_25"
|
||||
},
|
||||
{
|
||||
@@ -292,7 +308,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_aiperi.png",
|
||||
"text": "Она меня ищет, я уверена, она хочет мне какое-то задание дать.",
|
||||
"text": "Я даже уверена, она меня ищет. Она хочет мне какое-то задание дать.",
|
||||
"next": "line_26"
|
||||
},
|
||||
{
|
||||
@@ -338,7 +354,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_aiperi.png",
|
||||
"text": "Пока ты не принесешь мне нож из учительской, никуда я тебя не выпущу.",
|
||||
"text": "Пока ты не принесешь мне нож из учительской, никуда я тебя не выпущу. Ошо!",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
@@ -2385,7 +2401,15 @@
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_aiperi.png",
|
||||
"text": "Я слышала она не смогла сдать курсовую по манасоведению, прыгнула с окна и разбилась насмерть.",
|
||||
"text": "Я слышала она не смогла сдать курсовую по манасоведению.",
|
||||
"next": "line_34"
|
||||
},
|
||||
{
|
||||
"id": "line_34",
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_aiperi.png",
|
||||
"text": "Она прыгнула с окна и разбилась насмерть деп.",
|
||||
"next": "line_21"
|
||||
},
|
||||
{
|
||||
@@ -2571,7 +2595,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "Почему ты сама не можешь забрать?",
|
||||
"text": "Почему ты сама не можешь забрать нож у Аиды?",
|
||||
"next": "line_9",
|
||||
"chatBubble": "out"
|
||||
},
|
||||
@@ -2580,7 +2604,16 @@
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "Ты же знаешь, если я встречу Аиду, она 100% даст мне какое-нибудь сложное задание.",
|
||||
"text": "Да она надоела, все время скафнит.",
|
||||
"next": "line_12",
|
||||
"chatBubble": "in"
|
||||
},
|
||||
{
|
||||
"id": "line_12",
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "Если я ее встречу, она на 100% даст мне какое-нибудь сложное задание.",
|
||||
"next": "line_10",
|
||||
"chatBubble": "in"
|
||||
},
|
||||
@@ -2598,7 +2631,7 @@
|
||||
"type": "Line",
|
||||
"speaker": "Айпери",
|
||||
"portrait": "resources/dialogue/portrait_phone.png",
|
||||
"text": "Так что жду тебя в универе! Не вздумай прогулять!",
|
||||
"text": "Так что жду тебя в универе! Не вздумай прогулять! Ошо!",
|
||||
"next": "setflag_1",
|
||||
"chatBubble": "in",
|
||||
"questUnlock": "aiperi_knife"
|
||||
@@ -45,7 +45,6 @@ end
|
||||
|
||||
function lection_hall_zone001_enter_callback()
|
||||
local lection_is_over = game_api.getIntValue("lection_is_over")
|
||||
|
||||
if (lection_is_over == 0) then
|
||||
game_api.player_stop()
|
||||
game_api.start_cutscene("lection_cutscene001")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -8,9 +8,9 @@
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "hint2",
|
||||
"width": 72,
|
||||
"height": 354,
|
||||
"y": -100,
|
||||
"width": 36,
|
||||
"height": 177,
|
||||
"y": -30,
|
||||
"x": 0,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,18 +7,61 @@
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "aboutBkg",
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"texture": "resources/w/ui/img/main/bkg.png"
|
||||
"width": 2992,
|
||||
"height": 1632,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"texture": "resources/w/ui/img/main/MenuBkg001_v001.png"
|
||||
},
|
||||
{
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "aboutImage",
|
||||
"name": "aboutPicture",
|
||||
"width": 720,
|
||||
"height": 720,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"texture": "resources/w/ui/img/main/aboutPage002.png"
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "steamButton",
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"width": 800,
|
||||
"height": 500,
|
||||
"texture": "resources/w/ui/img/main/about.png"
|
||||
"y": 200,
|
||||
"width": 200,
|
||||
"height": 60,
|
||||
"text": "Добавить игру в Wishlist!",
|
||||
"fontSize": 40,
|
||||
"color": [0.55, 0.9, 1.0, 1.0],
|
||||
"textCentered": true
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "telegramButton",
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"y": -240,
|
||||
"x": -150,
|
||||
"width": 200,
|
||||
"height": 60,
|
||||
"text": "Telegram-канал",
|
||||
"fontSize": 40,
|
||||
"color": [0.55, 0.9, 1.0, 1.0],
|
||||
"textCentered": true
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "discordButton",
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"y": -240,
|
||||
"x": 150,
|
||||
"width": 200,
|
||||
"height": 60,
|
||||
"text": "Наш Discord",
|
||||
"fontSize": 40,
|
||||
"color": [0.55, 0.9, 1.0, 1.0],
|
||||
"textCentered": true
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
|
||||
@@ -7,34 +7,351 @@
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "creditsBkg",
|
||||
"width": 2992,
|
||||
"height": 1632,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"texture": "resources/w/ui/img/main/MenuBkg001_v001.png"
|
||||
},
|
||||
{
|
||||
"type": "LinearLayout",
|
||||
"name": "creditsLinearLayout",
|
||||
"orientation": "vertical",
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "top",
|
||||
"horizontal_align": "left",
|
||||
"animations": {
|
||||
"slowScroll": {
|
||||
"repeat": true,
|
||||
"steps": [
|
||||
{
|
||||
"type": "move",
|
||||
"to": [
|
||||
0,
|
||||
2600
|
||||
],
|
||||
"duration": 30.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"width": 300,
|
||||
"height": 3000,
|
||||
"spacing": 20,
|
||||
"children": [
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "smallPause",
|
||||
"width": 300,
|
||||
"height": 700,
|
||||
"text": "",
|
||||
"fontSize": 40,
|
||||
"textCentered": true,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsTitleText",
|
||||
"width": 300,
|
||||
"height": 60,
|
||||
"text": "Тень над Бишкеком Демо",
|
||||
"fontSize": 40,
|
||||
"textCentered": true,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText1",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Продюсер:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText2",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Владислав Хорев",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText3",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Гейм-дизайн:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText4",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Владислав Хорев",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText5",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Лейла Боброва",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText6",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Сценарий:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText7",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Владислав Хорев",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText8",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Лейла Боброва",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText9",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Разработка:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText10",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Владислав Хорев",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText11",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Владислав Кан",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText12",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Бексултан Алмазбеков",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText13",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Сергей Зотов",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText14",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Персонажи и анимации:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText15",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Дмитрий Прохорчик",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText16",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Роман Тресцов",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText17",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "3D ассеты:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText18",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Дмитрий Прохорчик",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText19",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Ishmaladi (turbosquid.com)",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText20",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Арты:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText21",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Maggie Tem",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText22",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "UI/UX:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText23",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Кенже Казматова",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText24",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Музыка:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText25",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Pazetic_Ocean",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText26",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Звуки:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText27",
|
||||
"width": 300,
|
||||
"height": 300,
|
||||
"text": "IENBA, ImAFoley, stradie,\nItsTheGoodstuff, uwesoundboiz,\nAnthousai, Foxfire-,\nn-lerche, CogFireStudios,\nadcbicycle, AntoineRomo,\nSmallConfusion, joedeshon,\nefectirijillo (FreeSound.org)",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText28",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Некоторые арты, 3D модели\nи текстуры были созданы\nс помощью нейросетей.",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "creditsBackButton",
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"texture": "resources/w/ui/img/main/bkg.png"
|
||||
},
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "creditsImage",
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"width": 800,
|
||||
"height": 500,
|
||||
"texture": "resources/w/ui/img/main/about.png"
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "skipButton",
|
||||
"horizontal_gravity": "right",
|
||||
"vertical_gravity": "top",
|
||||
"x": -23,
|
||||
"y": -35,
|
||||
"width": 200,
|
||||
"height": 200,
|
||||
"clickZoneWidth": 176,
|
||||
"clickZoneHeight": 176,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"textures": {
|
||||
"normal": "resources/w/ui/img/RedClose001_State=Default001.png",
|
||||
"hover": "resources/w/ui/img/RedClose001_State=Selected001.png",
|
||||
"pressed": "resources/w/ui/img/RedClose001_State=Tap001.png"
|
||||
"normal": "resources/transparent.png",
|
||||
"hover": "resources/transparent.png",
|
||||
"pressed": "resources/transparent.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"horizontal_align": "center",
|
||||
"vertical_align": "center",
|
||||
"spacing": 24,
|
||||
"y": 150,
|
||||
"y": 140,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
@@ -55,7 +55,7 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuStartButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"height": 44,
|
||||
"text": "Начать игру",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
@@ -64,7 +64,7 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuLoadButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"height": 44,
|
||||
"text": "Загрузить игру",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
@@ -73,7 +73,7 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuSettingsButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"height": 44,
|
||||
"text": "Настройки",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
@@ -82,7 +82,7 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuLanguageButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"height": 44,
|
||||
"text": "Язык / Language",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
@@ -91,8 +91,17 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuAboutButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"text": "О нас",
|
||||
"height": 44,
|
||||
"text": "О игре",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "menuCreditsButton",
|
||||
"width": 300,
|
||||
"height": 44,
|
||||
"text": "Авторы",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
},
|
||||
@@ -100,7 +109,7 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuExitButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"height": 44,
|
||||
"text": "Выход",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
@@ -116,7 +125,7 @@
|
||||
"y": 10,
|
||||
"width": 160,
|
||||
"height": 36,
|
||||
"text": "v. 0.7",
|
||||
"text": "v. 1.0.1",
|
||||
"fontSize": 24,
|
||||
"textCentered": false,
|
||||
"topAligned": true,
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "hint2",
|
||||
"width": 72,
|
||||
"height": 354,
|
||||
"y": -100,
|
||||
"width": 36,
|
||||
"height": 177,
|
||||
"y": -30,
|
||||
"x": 0,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "FrameLayout",
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "aboutBkg",
|
||||
"width": 2992,
|
||||
"height": 1632,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"texture": "resources/w/ui/img/main/MenuBkg001_v001.png"
|
||||
},
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "aboutPicture",
|
||||
"width": 720,
|
||||
"height": 720,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"texture": "resources/w/ui_en/img/main/aboutPage002.png"
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "steamButton",
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"y": 200,
|
||||
"width": 200,
|
||||
"height": 60,
|
||||
"text": "Add to your Steam wishlist!",
|
||||
"fontSize": 40,
|
||||
"color": [0.55, 0.9, 1.0, 1.0],
|
||||
"textCentered": true
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "telegramButton",
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"y": -240,
|
||||
"x": -150,
|
||||
"width": 200,
|
||||
"height": 60,
|
||||
"text": "Telegram channel",
|
||||
"fontSize": 40,
|
||||
"color": [0.55, 0.9, 1.0, 1.0],
|
||||
"textCentered": true
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "discordButton",
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"y": -240,
|
||||
"x": 150,
|
||||
"width": 200,
|
||||
"height": 60,
|
||||
"text": "Our Discord",
|
||||
"fontSize": 40,
|
||||
"color": [0.55, 0.9, 1.0, 1.0],
|
||||
"textCentered": true
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "aboutBackButton",
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "bottom",
|
||||
"y": 40,
|
||||
"width": 200,
|
||||
"height": 60,
|
||||
"text": "Back",
|
||||
"fontSize": 40,
|
||||
"textCentered": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "FrameLayout",
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "creditsBkg",
|
||||
"width": 2992,
|
||||
"height": 1632,
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "center",
|
||||
"texture": "resources/w/ui/img/main/MenuBkg001_v001.png"
|
||||
},
|
||||
{
|
||||
"type": "LinearLayout",
|
||||
"name": "creditsLinearLayout",
|
||||
"orientation": "vertical",
|
||||
"horizontal_gravity": "center",
|
||||
"vertical_gravity": "top",
|
||||
"horizontal_align": "left",
|
||||
"animations": {
|
||||
"slowScroll": {
|
||||
"repeat": true,
|
||||
"steps": [
|
||||
{
|
||||
"type": "move",
|
||||
"to": [
|
||||
0,
|
||||
2600
|
||||
],
|
||||
"duration": 30.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"width": 300,
|
||||
"height": 3000,
|
||||
"spacing": 20,
|
||||
"children": [
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "smallPause",
|
||||
"width": 300,
|
||||
"height": 700,
|
||||
"text": "",
|
||||
"fontSize": 40,
|
||||
"textCentered": true,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsTitleText",
|
||||
"width": 300,
|
||||
"height": 60,
|
||||
"text": "Shadow over Bishkek Demo",
|
||||
"fontSize": 40,
|
||||
"textCentered": true,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText1",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Producer:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText2",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Vladislav Khorev",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText3",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Game Design:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText4",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Vladislav Khorev",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText5",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Leila Bobrova",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText6",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Story:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText7",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Vladislav Khorev",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText8",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Leila Bobrova",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText9",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Development:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText10",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Vladislav Khorev",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText11",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Vladislav Kan",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText12",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Beksultan Almazbekov",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText13",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Sergei Zotov",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText14",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Characters and animations:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText15",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Dmitry Prohorchik",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText16",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Roman Trestcov",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText17",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "3D Assets:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText18",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Dmitry Prohorchik",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText19",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Ishmaladi (turbosquid.com)",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText20",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Arts:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText21",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Maggie Tem",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText22",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "UI/UX:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText23",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Kenje Kazmatova",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText24",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Music:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText25",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Pazetic_Ocean",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText26",
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"text": "Sounds:",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 0.85, 0.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText27",
|
||||
"width": 300,
|
||||
"height": 300,
|
||||
"text": "IENBA, ImAFoley, stradie,\nItsTheGoodstuff, uwesoundboiz,\nAnthousai, Foxfire-,\nn-lerche, CogFireStudios,\nadcbicycle, AntoineRomo,\nSmallConfusion, joedeshon,\nefectirijillo (FreeSound.org)",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "creditsText28",
|
||||
"width": 300,
|
||||
"height": 40,
|
||||
"text": "Some art, 3D models\nand textures were created\nusing neural networks.",
|
||||
"fontSize": 32,
|
||||
"textCentered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "creditsBackButton",
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"textures": {
|
||||
"normal": "resources/transparent.png",
|
||||
"hover": "resources/transparent.png",
|
||||
"pressed": "resources/transparent.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@
|
||||
"horizontal_align": "center",
|
||||
"vertical_align": "center",
|
||||
"spacing": 24,
|
||||
"y": 150,
|
||||
"y": 140,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
@@ -55,7 +55,7 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuStartButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"height": 44,
|
||||
"text": "Start Game",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
@@ -64,7 +64,7 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuLoadButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"height": 44,
|
||||
"text": "Load Game",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
@@ -73,7 +73,7 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuSettingsButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"height": 44,
|
||||
"text": "Settings",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
@@ -82,7 +82,7 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuLanguageButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"height": 44,
|
||||
"text": "Язык / Language",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
@@ -91,8 +91,17 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuAboutButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"text": "About Us",
|
||||
"height": 44,
|
||||
"text": "About Game",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "menuCreditsButton",
|
||||
"width": 300,
|
||||
"height": 44,
|
||||
"text": "Credits",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
},
|
||||
@@ -100,7 +109,7 @@
|
||||
"type": "TextButton",
|
||||
"name": "menuExitButton",
|
||||
"width": 300,
|
||||
"height": 50,
|
||||
"height": 44,
|
||||
"text": "Exit",
|
||||
"fontSize": 48,
|
||||
"textCentered": true
|
||||
@@ -116,7 +125,7 @@
|
||||
"y": 10,
|
||||
"width": 160,
|
||||
"height": 36,
|
||||
"text": "v. 0.7",
|
||||
"text": "v. 1.0.1",
|
||||
"fontSize": 24,
|
||||
"textCentered": false,
|
||||
"topAligned": true,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "AudioPlayerAsync.h"
|
||||
#include <iostream>
|
||||
#include "utils/Utils.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
AudioPlayerAsync::AudioPlayerAsync() {}
|
||||
@@ -36,7 +37,7 @@ bool AudioPlayerAsync::init() {
|
||||
Mix_AllocateChannels(16);
|
||||
|
||||
initialized = true;
|
||||
std::cout << "AudioPlayerAsync initialized with SDL2_mixer" << std::endl;
|
||||
ZL::logger() << "AudioPlayerAsync initialized with SDL2_mixer" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -60,7 +61,7 @@ void AudioPlayerAsync::shutdown() {
|
||||
Mix_CloseAudio();
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
initialized = false;
|
||||
std::cout << "AudioPlayerAsync shutdown" << std::endl;
|
||||
ZL::logger() << "AudioPlayerAsync shutdown" << std::endl;
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, int channel) {
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Bone name not found: " << name << std::endl;
|
||||
logger() << "Bone name not found: " << name << std::endl;
|
||||
throw std::runtime_error("Bone name not found: " + name);
|
||||
|
||||
return -1;
|
||||
@@ -435,7 +435,7 @@ namespace ZL
|
||||
meshNamesOrdered.push_back(meshName);
|
||||
}
|
||||
|
||||
std::cout << "Loaded " << numberMeshes << " meshes from " << fileName << std::endl;
|
||||
logger() << "Loaded " << numberMeshes << " meshes from " << fileName << std::endl;
|
||||
|
||||
// ---- Animation Keyframes ----
|
||||
std::getline(f, tempLine); // === Animation Keyframes ===
|
||||
@@ -509,7 +509,7 @@ namespace ZL
|
||||
|
||||
if (startBones.size() > MAX_GPU_BONES)
|
||||
{
|
||||
std::cout << "Warning: model has " << startBones.size()
|
||||
logger() << "Warning: model has " << startBones.size()
|
||||
<< " bones, exceeding GPU skinning limit of " << MAX_GPU_BONES << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -706,7 +706,7 @@ namespace ZL
|
||||
|
||||
if (startBones.size() > MAX_GPU_BONES)
|
||||
{
|
||||
std::cout << "Warning: model has " << startBones.size()
|
||||
logger() << "Warning: model has " << startBones.size()
|
||||
<< " bones, exceeding GPU skinning limit of " << MAX_GPU_BONES << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -736,7 +736,7 @@ namespace ZL
|
||||
|
||||
if (startingKeyFrame == -1)
|
||||
{
|
||||
std::cout << "Exception here: frame number is out of range of keyframes. Frame: " << frame << std::endl;
|
||||
logger() << "Exception here: frame number is out of range of keyframes. Frame: " << frame << std::endl;
|
||||
throw std::runtime_error("Exception here");
|
||||
}
|
||||
|
||||
@@ -794,7 +794,7 @@ namespace ZL
|
||||
{
|
||||
if (md.verticesBoneWeight[i][j].boneIndex == -1)
|
||||
{
|
||||
std::cout << "Exception here: bone index is -1 but weight is > 0" << std::endl;
|
||||
logger() << "Exception here: bone index is -1 but weight is > 0" << std::endl;
|
||||
throw std::runtime_error("Bones loaded incorrectly - bone index is -1 but weight is > 0");
|
||||
}
|
||||
vMoved = true;
|
||||
|
||||
+3
-3
@@ -613,7 +613,7 @@ bool Character::prepareGpuSkinning() {
|
||||
|
||||
void Character::tryAttackCombo()
|
||||
{
|
||||
std::cout << "Try Attack Combo" << std::endl;
|
||||
logger() << "Try Attack Combo" << std::endl;
|
||||
auto it = animations.find(state.currentState);
|
||||
if (it == animations.end()) return;
|
||||
|
||||
@@ -622,7 +622,7 @@ void Character::tryAttackCombo()
|
||||
if (static_cast<int>(anim.currentFrame) >= 20 &&
|
||||
(state.currentState == AnimationState::ACTION_ATTACK ||
|
||||
state.currentState == AnimationState::ACTION_ATTACK_2)) {
|
||||
std::cout << "Try Attack Combo Success" << std::endl;
|
||||
logger() << "Try Attack Combo Success" << std::endl;
|
||||
anim.currentFrame = anim.model.startingFrame;
|
||||
}
|
||||
}
|
||||
@@ -1234,7 +1234,7 @@ void Character::setupHitSparks(std::shared_ptr<Texture> sparkTexture) {
|
||||
}
|
||||
|
||||
void Character::applyDamage(float damageAmount, const Eigen::Vector3f& attackDirection, int attackerIndex) {
|
||||
std::cout << "Character::applyDamage" << std::endl;
|
||||
logger() << "Character::applyDamage" << std::endl;
|
||||
state.hp -= damageAmount;
|
||||
if (state.hp < 0) state.hp = 0;
|
||||
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@ public:
|
||||
std::string weaponAttachBoneName = "RightHand";
|
||||
|
||||
SparkEmitter hitSparkEmitter;
|
||||
AudioPlayerAsync* audioPlayer;
|
||||
AudioPlayerAsync* audioPlayer = nullptr;
|
||||
|
||||
static Dialogue::TranslationDatabase nameTranslations;
|
||||
|
||||
|
||||
+3
-1
@@ -23,12 +23,14 @@ int Environment::height = CONST_DEFAULT_HEIGHT;
|
||||
float Environment::zoom = DEFAULT_ZOOM;
|
||||
|
||||
|
||||
bool Environment::enableLogging = false;
|
||||
|
||||
SDL_Window* Environment::window = nullptr;
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
bool Environment::isFullscreen = false;
|
||||
#else
|
||||
bool Environment::isFullscreen = false;
|
||||
bool Environment::isFullscreen = true;
|
||||
#endif
|
||||
|
||||
bool Environment::isHighDPIEnabled = false;
|
||||
|
||||
@@ -31,6 +31,8 @@ public:
|
||||
static int height;
|
||||
static float zoom;
|
||||
|
||||
static bool enableLogging;
|
||||
|
||||
static Eigen::Matrix3f inverseShipMatrix;
|
||||
|
||||
static SDL_Window* window;
|
||||
|
||||
+39
-61
@@ -43,7 +43,7 @@ namespace ZL
|
||||
Game* Game::s_instance = nullptr;
|
||||
|
||||
void Game::onResourcesZipLoaded(unsigned /*handle*/, void* /*userData*/, const char* /*filename*/) {
|
||||
std::cout << "Resources.zip loaded successfully--0" << std::endl;
|
||||
logger() << "Resources.zip loaded successfully--0" << std::endl;
|
||||
if (s_instance) {
|
||||
s_instance->resourcesDownloadProgress = 1.0f;
|
||||
// Builds the loadSteps queue (cheap, no GL work); the actual loading
|
||||
@@ -54,7 +54,7 @@ namespace ZL
|
||||
}
|
||||
|
||||
void Game::onResourcesZipError(unsigned /*handle*/, void* /*userData*/, int /*httpStatus*/) {
|
||||
std::cout << "Failed to download resources.zip" << std::endl;
|
||||
logger() << "Failed to download resources.zip" << std::endl;
|
||||
}
|
||||
|
||||
void Game::onResourcesZipProgress(unsigned /*handle*/, void* /*userData*/, int percentComplete) {
|
||||
@@ -78,11 +78,6 @@ namespace ZL
|
||||
}
|
||||
|
||||
Game::~Game() {
|
||||
#ifndef EMSCRIPTEN
|
||||
// In Emscripten, SDL must stay alive across context loss/restore cycles
|
||||
// so the window remains valid when the game object is re-created.
|
||||
SDL_Quit();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Game::setup() {
|
||||
@@ -134,7 +129,7 @@ namespace ZL
|
||||
// Asynchronously download resources.zip; setupPart2() is called on completion.
|
||||
// The loading screen stays visible until the download finishes.
|
||||
s_instance = this;
|
||||
std::cout << "Load resurces step 1-" << std::endl;
|
||||
logger() << "Load resurces step 1-" << std::endl;
|
||||
emscripten_async_wget2("resources.zip", "resources.zip", "GET", nullptr, nullptr,
|
||||
onResourcesZipLoaded, onResourcesZipError, onResourcesZipProgress);
|
||||
#else
|
||||
@@ -316,7 +311,7 @@ namespace ZL
|
||||
loadSteps.push([this]() {
|
||||
try {
|
||||
menuManager.setup(gameState.inventory, CONST_ZIP_FILE);
|
||||
std::cout << "UI loaded successfully" << std::endl;
|
||||
logger() << "UI loaded successfully" << std::endl;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Failed to load UI: " << e.what() << std::endl;
|
||||
@@ -366,10 +361,10 @@ namespace ZL
|
||||
if (audioPlayer->init()) {
|
||||
audioPlayer->setMusicVolume(100);
|
||||
audioPlayer->setSoundVolume(80);
|
||||
std::cout << "Audio initialized successfully" << std::endl;
|
||||
logger() << "Audio initialized successfully" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "Audio initialization failed" << std::endl;
|
||||
logger() << "Audio initialization failed" << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -480,12 +475,12 @@ namespace ZL
|
||||
nlohmann::json root;
|
||||
gameState.save(root);
|
||||
const std::string path = "start_state.json";
|
||||
std::cout << "[reset] saveInitialState called, to " << path << std::endl;
|
||||
logger() << "[reset] saveInitialState called, to " << path << std::endl;
|
||||
|
||||
std::ofstream file(path);
|
||||
if (file.is_open()) {
|
||||
file << root.dump(2);
|
||||
std::cout << "[reset] Saved initial state snapshot to " << path << std::endl;
|
||||
logger() << "[reset] Saved initial state snapshot to " << path << std::endl;
|
||||
} else {
|
||||
std::cerr << "[reset] Could not open " << path << " for writing" << std::endl;
|
||||
}
|
||||
@@ -506,7 +501,7 @@ namespace ZL
|
||||
LocationSetup uniInteriorParams;
|
||||
uniInteriorParams.gameObjectsJsonPath = "resources/config/gameobjects_uni_interior_x.json";
|
||||
uniInteriorParams.npcsJsonPath = "resources/config/npcs_uni_interior.json";
|
||||
uniInteriorParams.dialoguesJsonPath = "resources/dialogue/uni_interior_dialogues_009.json";
|
||||
uniInteriorParams.dialoguesJsonPath = "resources/dialogue/uni_interior_dialogues_010.json";
|
||||
|
||||
uniInteriorParams.navigationJsonPaths = {
|
||||
"resources/navigation/uni_interior4_unlocked_n2.txt", //0
|
||||
@@ -586,7 +581,7 @@ namespace ZL
|
||||
gameState.locations["uni_interior"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
|
||||
gameState.locations["uni_interior"]->requestReturnToMainMenu = [this]() {
|
||||
gameState.currentLocationName.clear();
|
||||
menuManager.showMainMenu();
|
||||
menuManager.showMenuAfterGameCompleted();
|
||||
};
|
||||
gameState.locations["uni_interior"]->requestSetChatUnread = [this](int idx, bool unread, const std::string& msg) {
|
||||
menuManager.setChatUnread(idx, unread, msg);
|
||||
@@ -643,7 +638,7 @@ namespace ZL
|
||||
uniExteriorParams.scriptPath = "resources/start_uni_exterior.lua";
|
||||
uniExteriorParams.playerPosition = Eigen::Vector3f(5, 0, -18.4);
|
||||
uniExteriorParams.npcsJsonPath = "resources/config/npcs_uni_exterior.json";
|
||||
uniExteriorParams.dialoguesJsonPath = "resources/dialogue/uni_exterior_dialogues002.json";
|
||||
uniExteriorParams.dialoguesJsonPath = "resources/dialogue/uni_exterior_dialogues003.json";
|
||||
|
||||
|
||||
gameState.locations["uni_exterior"] = std::make_shared<Location>(renderer, gameState.inventory);
|
||||
@@ -656,7 +651,7 @@ namespace ZL
|
||||
};
|
||||
gameState.locations["uni_exterior"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
|
||||
gameState.locations["uni_exterior"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
|
||||
gameState.locations["uni_exterior"]->requestReturnToMainMenu = [this]() { menuManager.showMainMenu(); };
|
||||
//gameState.locations["uni_exterior"]->requestReturnToMainMenu = [this]() { menuManager.showMenuAfterGameCompleted(); };
|
||||
gameState.locations["uni_exterior"]->requestSetChatUnread = [this](int idx, bool unread, const std::string& msg) {
|
||||
menuManager.setChatUnread(idx, unread, msg);
|
||||
};
|
||||
@@ -685,7 +680,6 @@ namespace ZL
|
||||
|
||||
//Exterior set up taxi end
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -697,7 +691,7 @@ namespace ZL
|
||||
params_dorm.gameObjectsJsonPath = "resources/config/gameobjects_dorm_new_x.json";
|
||||
|
||||
params_dorm.npcsJsonPath = "resources/config/npcs_dorm.json";
|
||||
params_dorm.dialoguesJsonPath = "resources/dialogue/dorm_dialogues003.json";
|
||||
params_dorm.dialoguesJsonPath = "resources/dialogue/dorm_dialogues004.json";
|
||||
|
||||
/*
|
||||
params_dorm.navigationJsonPaths = {
|
||||
@@ -741,7 +735,7 @@ namespace ZL
|
||||
gameState.locations["location_dorm"]->requestNightDayTransition = [this](bool isNight, bool isDawn) { gameState.isNight = isNight; gameState.isDawn = isDawn; };
|
||||
gameState.locations["location_dorm"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
|
||||
gameState.locations["location_dorm"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
|
||||
gameState.locations["location_dorm"]->requestReturnToMainMenu = [this]() { menuManager.showMainMenu(); };
|
||||
//gameState.locations["location_dorm"]->requestReturnToMainMenu = [this]() { menuManager.showMenuAfterGameCompleted(); };
|
||||
gameState.locations["location_dorm"]->requestSetChatUnread = [this](int idx, bool unread, const std::string& msg) {
|
||||
menuManager.setChatUnread(idx, unread, msg);
|
||||
};
|
||||
@@ -777,8 +771,6 @@ namespace ZL
|
||||
void Game::createLocationsStep5()
|
||||
{
|
||||
|
||||
|
||||
|
||||
rearmDormOneShotCallbacks();
|
||||
|
||||
menuManager.callTaxiFunc = [this]()
|
||||
@@ -796,7 +788,7 @@ namespace ZL
|
||||
|
||||
// Teleport callbacks: destination name and position come from the teleport zone data.
|
||||
auto teleportCallback = [this](const std::string& destName, const Eigen::Vector3f& destPos, float destRotY) {
|
||||
std::cout << "[TELEPORT] " << " -> " << destName << std::endl;
|
||||
logger() << "[TELEPORT] " << " -> " << destName << std::endl;
|
||||
auto it = gameState.locations.find(destName);
|
||||
if (it == gameState.locations.end()) {
|
||||
std::cerr << "[TELEPORT] Unknown destination location: " << destName << std::endl;
|
||||
@@ -903,12 +895,12 @@ namespace ZL
|
||||
void Game::createShadowMapAndProparateToLocations()
|
||||
{
|
||||
if (shadowMap) {
|
||||
std::cout << "Shadow map already exists, skipping creation" << std::endl;
|
||||
logger() << "Shadow map already exists, skipping creation" << std::endl;
|
||||
return;
|
||||
}
|
||||
shadowMap = std::make_shared<ShadowMap>(2048, 40.0f, 0.1f, 100.0f);
|
||||
shadowMap->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f));
|
||||
std::cout << "Shadow map initialized" << std::endl;
|
||||
logger() << "Shadow map initialized" << std::endl;
|
||||
|
||||
for (auto& [name, loc] : gameState.locations) {
|
||||
loc->shadowMap = shadowMap;
|
||||
@@ -1186,7 +1178,7 @@ namespace ZL
|
||||
|
||||
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
const bool wasPortrait = Environment::width < Environment::height;
|
||||
|
||||
//ZL::logger() << "Window resized" << std::endl;
|
||||
if (Environment::isHighDPIEnabled) {
|
||||
// Если High DPI включен, запрашиваем реальное физическое разрешение буфера
|
||||
int drawW, drawH;
|
||||
@@ -1210,10 +1202,13 @@ namespace ZL
|
||||
menuManager.onOrientationChanged();
|
||||
}
|
||||
|
||||
std::cout << "Window resized (Render Size): " << Environment::width << "x" << Environment::height << std::endl;
|
||||
logger() << "Window resized (Render Size): " << Environment::width << "x" << Environment::height << std::endl;
|
||||
//space.clearTextRendererCache();
|
||||
}
|
||||
|
||||
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
}
|
||||
|
||||
// Suppress all input while portrait overlay is shown
|
||||
//if (mobileIsPortrait) continue;
|
||||
|
||||
@@ -1300,7 +1295,7 @@ namespace ZL
|
||||
if (Environment::zoom > zoomMax) {
|
||||
Environment::zoom = zoomMax;
|
||||
}
|
||||
std::cout << "Current zoom: " << Environment::zoom << std::endl;
|
||||
//logger() << "Current zoom: " << Environment::zoom << std::endl;
|
||||
// Tutorial step3 → step4: any mouse-wheel scroll counts as "zoom gesture".
|
||||
if (gameState.tutorialStep == TutorialStep::Step3) {
|
||||
menuManager.advanceTutorialStep();
|
||||
@@ -1309,7 +1304,7 @@ namespace ZL
|
||||
|
||||
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_8:
|
||||
/*case SDLK_8:
|
||||
if (editorMode == EditorMode::InteractiveObjects && currentLocation()) {
|
||||
currentLocation()->editor.selectInteractiveObject(8);
|
||||
} else {
|
||||
@@ -1345,7 +1340,7 @@ namespace ZL
|
||||
currentLocation()->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0);
|
||||
} else {
|
||||
currentLocation()->switchNavigation(event.key.keysym.sym - SDLK_0);
|
||||
std::cout << "Switched to nav mesh " << (event.key.keysym.sym - SDLK_0) << std::endl;
|
||||
logger() << "Switched to nav mesh " << (event.key.keysym.sym - SDLK_0) << std::endl;
|
||||
}
|
||||
break;
|
||||
case SDLK_f:
|
||||
@@ -1376,50 +1371,50 @@ namespace ZL
|
||||
const char* modeName = (editorMode == EditorMode::Navigation) ? "Navigation"
|
||||
: (editorMode == EditorMode::InteractiveObjects) ? "InteractiveObjects"
|
||||
: "None";
|
||||
std::cout << "[EDITOR] Mode: " << modeName << std::endl;
|
||||
logger() << "[EDITOR] Mode: " << modeName << std::endl;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_o:
|
||||
x = x + 0.05;
|
||||
std::cout << "current x: " << x << std::endl;
|
||||
logger() << "current x: " << x << std::endl;
|
||||
//y = y + 0.002;
|
||||
//currentLocation->player->hp = 200;
|
||||
//currentLocation->npcs[0]->walkSpeed += 0.01f;
|
||||
//std::cout << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl;
|
||||
//logger() << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl;
|
||||
break;
|
||||
|
||||
case SDLK_k:
|
||||
x = x - 0.05;
|
||||
std::cout << "current x: " << x << std::endl;
|
||||
std::cout << "Player pos: " << currentLocation()->player->state.position.transpose() << std::endl;
|
||||
std::cout << "Player rotation: " << currentLocation()->player->state.facingAngle*180.0/M_PI << std::endl;
|
||||
logger() << "current x: " << x << std::endl;
|
||||
logger() << "Player pos: " << currentLocation()->player->state.position.transpose() << std::endl;
|
||||
logger() << "Player rotation: " << currentLocation()->player->state.facingAngle*180.0/M_PI << std::endl;
|
||||
//currentLocation->npcs[0]->walkSpeed -= 0.01f;
|
||||
//std::cout << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl;
|
||||
//logger() << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl;
|
||||
break;
|
||||
|
||||
case SDLK_p:
|
||||
gameState.currentLocationName = "uni_interior";
|
||||
currentLocation()->player->state.position = Eigen::Vector3f(-0.0189243, 0, -13.4314);
|
||||
currentLocation()->player->setTarget(currentLocation()->player->state.position);
|
||||
//std::cout << "Switched to location " << ((currentLocation == locations["location1"]) ? "1" : "2") << std::endl;
|
||||
//logger() << "Switched to location " << ((currentLocation == locations["location1"]) ? "1" : "2") << std::endl;
|
||||
break;
|
||||
|
||||
case SDLK_l:
|
||||
//x = x - 1;
|
||||
//std::cout << "current x: " << x << std::endl;
|
||||
std::cout << "Azimuth: " << currentLocation()->state.cameraAzimuth << std::endl;
|
||||
std::cout << "Inclination: " << currentLocation()->state.cameraInclination << std::endl;
|
||||
//logger() << "current x: " << x << std::endl;
|
||||
logger() << "Azimuth: " << currentLocation()->state.cameraAzimuth << std::endl;
|
||||
logger() << "Inclination: " << currentLocation()->state.cameraInclination << std::endl;
|
||||
break;
|
||||
|
||||
case SDLK_c:
|
||||
std::cout << "SLOW-MO activated!" << std::endl;
|
||||
logger() << "SLOW-MO activated!" << std::endl;
|
||||
activateSlowMoEffect();
|
||||
break;
|
||||
|
||||
case SDLK_i:
|
||||
y = y - 1;
|
||||
std::cout << "current y: " << y << std::endl;
|
||||
logger() << "current y: " << y << std::endl;
|
||||
break;
|
||||
|
||||
case SDLK_b:
|
||||
@@ -1429,12 +1424,6 @@ namespace ZL
|
||||
break;
|
||||
|
||||
case SDLK_j:
|
||||
/*
|
||||
if (editorMode != EditorMode::None && currentLocation()) {
|
||||
currentLocation()->editor.placeTree();
|
||||
} else {
|
||||
menuManager.toggleQuestJournal();
|
||||
}*/
|
||||
currentLocation()->editor.placeTree();
|
||||
break;
|
||||
|
||||
@@ -1444,8 +1433,7 @@ namespace ZL
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_RETURN:
|
||||
|
||||
case SDLK_RETURN:*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1464,16 +1452,6 @@ namespace ZL
|
||||
//menuManager.uiManager.onKeyPress((unsigned char)event.text.text[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type == SDL_KEYUP) {
|
||||
if (event.key.keysym.sym == SDLK_r) {
|
||||
std::cout << "Camera position: x=" << x << " y=" << y << " z=" << z << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type == SDL_KEYUP) {
|
||||
|
||||
}
|
||||
}
|
||||
// Drain one loading step per frame so the window/tab keeps pumping events
|
||||
// and repainting between steps instead of freezing for the whole load.
|
||||
|
||||
@@ -95,10 +95,12 @@ void GameState::load(const nlohmann::json& in)
|
||||
money = in.value("money", money);
|
||||
|
||||
// --- Script globals ---
|
||||
globalInts.clear();
|
||||
if (in.contains("globalInts") && in["globalInts"].is_object()) {
|
||||
for (const auto& [k, v] : in["globalInts"].items())
|
||||
globalInts[k] = v.get<int>();
|
||||
}
|
||||
globalFloats.clear();
|
||||
if (in.contains("globalFloats") && in["globalFloats"].is_object()) {
|
||||
for (const auto& [k, v] : in["globalFloats"].items())
|
||||
globalFloats[k] = v.get<float>();
|
||||
|
||||
@@ -67,7 +67,7 @@ void loadLanguageSetting() {
|
||||
// Если файла настроек нет (content пустой), определяем язык системы и выходим
|
||||
if (content.empty()) {
|
||||
g_currentLanguage = detectSystemLanguage();
|
||||
std::cout << "[settings] No settings.json found. System language detected: "
|
||||
logger() << "[settings] No settings.json found. System language detected: "
|
||||
<< languageToCode(g_currentLanguage) << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
+60
-51
@@ -148,7 +148,7 @@ namespace ZL
|
||||
player->setTarget(params.playerPosition);
|
||||
player->setupHitSparks(sparkTexture);
|
||||
player->audioPlayer = audioPlayer;
|
||||
std::cout << "Load resurces step 9" << std::endl;
|
||||
logger() << "Load resurces step 9" << std::endl;
|
||||
SDL_PumpEvents();
|
||||
|
||||
// Load NPCs from JSON. Aggressive (canAttack) NPCs need a player target
|
||||
@@ -287,7 +287,7 @@ namespace ZL
|
||||
teleportZones.push_back(std::move(tz));
|
||||
}
|
||||
|
||||
std::cout << "[TELEPORT] Loaded " << teleportZones.size() << " teleport(s) from " << jsonPath << std::endl;
|
||||
logger() << "[TELEPORT] Loaded " << teleportZones.size() << " teleport(s) from " << jsonPath << std::endl;
|
||||
|
||||
}
|
||||
|
||||
@@ -338,7 +338,7 @@ namespace ZL
|
||||
}
|
||||
|
||||
|
||||
std::cout << "[TRIGGER] Loaded " << triggerZones.size() << " trigger zone(s) from " << jsonPath << std::endl;
|
||||
logger() << "[TRIGGER] Loaded " << triggerZones.size() << " trigger zone(s) from " << jsonPath << std::endl;
|
||||
}
|
||||
|
||||
void Location::loadPointLights(const std::string& jsonPath, const char* zipFile)
|
||||
@@ -400,7 +400,7 @@ namespace ZL
|
||||
pointLights.push_back(std::move(pl));
|
||||
}
|
||||
|
||||
std::cout << "[LIGHTS] Loaded " << pointLights.size() << " light(s) from " << jsonPath << std::endl;
|
||||
logger() << "[LIGHTS] Loaded " << pointLights.size() << " light(s) from " << jsonPath << std::endl;
|
||||
}
|
||||
|
||||
void Location::updateTriggerZones(const Eigen::Vector3f& playerPos)
|
||||
@@ -500,28 +500,28 @@ namespace ZL
|
||||
if (editorMode == EditorMode::Navigation) {
|
||||
editor.buildNavMeshes();
|
||||
}
|
||||
std::cout << "[NAV] Switched to navigation map " << index << "\n";
|
||||
logger() << "[NAV] Switched to navigation map " << index << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
InteractiveObject* Location::raycastInteractiveObjects(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir) {
|
||||
if (interactiveObjects.empty()) {
|
||||
//std::cout << "[RAYCAST] No interactive objects to check" << std::endl;
|
||||
//logger() << "[RAYCAST] No interactive objects to check" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//std::cout << "[RAYCAST] Starting raycast with " << interactiveObjects.size() << " objects" << std::endl;
|
||||
//std::cout << "[RAYCAST] Ray origin: (" << rayOrigin.x() << ", " << rayOrigin.y() << ", " << rayOrigin.z() << ")" << std::endl;
|
||||
//std::cout << "[RAYCAST] Ray dir: (" << rayDir.x() << ", " << rayDir.y() << ", " << rayDir.z() << ")" << std::endl;
|
||||
//logger() << "[RAYCAST] Starting raycast with " << interactiveObjects.size() << " objects" << std::endl;
|
||||
//logger() << "[RAYCAST] Ray origin: (" << rayOrigin.x() << ", " << rayOrigin.y() << ", " << rayOrigin.z() << ")" << std::endl;
|
||||
//logger() << "[RAYCAST] Ray dir: (" << rayDir.x() << ", " << rayDir.y() << ", " << rayDir.z() << ")" << std::endl;
|
||||
|
||||
float closestDistance = FLT_MAX;
|
||||
InteractiveObject* closestObject = nullptr;
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
//std::cout << "[RAYCAST] Checking object: " << intObj.loadedObject.name << " (active: " << intObj.isActive << ")" << std::endl;
|
||||
//logger() << "[RAYCAST] Checking object: " << intObj.loadedObject.name << " (active: " << intObj.isActive << ")" << std::endl;
|
||||
|
||||
if (!intObj.state.isActive || intObj.state.isAnimating) {
|
||||
//std::cout << "[RAYCAST] -> Object inactive or animating, skipping" << std::endl;
|
||||
//logger() << "[RAYCAST] -> Object inactive or animating, skipping" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -531,7 +531,7 @@ namespace ZL
|
||||
if (!intObj.loadedObject.texture) continue;
|
||||
}
|
||||
|
||||
//std::cout << "[RAYCAST] Position: (" << intObj.position.x() << ", " << intObj.position.y() << ", "
|
||||
//logger() << "[RAYCAST] Position: (" << intObj.position.x() << ", " << intObj.position.y() << ", "
|
||||
// << intObj.position.z() << "), Radius: " << intObj.interactionRadius << std::endl;
|
||||
|
||||
const bool hasBox = !(intObj.state.boundsMin.isZero() && intObj.state.boundsMax.isZero());
|
||||
@@ -581,10 +581,10 @@ namespace ZL
|
||||
}
|
||||
/*
|
||||
if (closestObject) {
|
||||
std::cout << "[RAYCAST] *** RAYCAST SUCCESS: Found object " << closestObject->loadedObject.name << " ***" << std::endl;
|
||||
logger() << "[RAYCAST] *** RAYCAST SUCCESS: Found object " << closestObject->loadedObject.name << " ***" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "[RAYCAST] No objects hit" << std::endl;
|
||||
logger() << "[RAYCAST] No objects hit" << std::endl;
|
||||
}
|
||||
*/
|
||||
return closestObject;
|
||||
@@ -600,11 +600,11 @@ namespace ZL
|
||||
Character* closestNpc = nullptr;
|
||||
float closestDist = maxDistance;
|
||||
|
||||
//std::cout << "[RAYCAST_NPC] Starting raycast with " << npcs.size() << " npcs" << std::endl;
|
||||
//logger() << "[RAYCAST_NPC] Starting raycast with " << npcs.size() << " npcs" << std::endl;
|
||||
|
||||
for (auto& npc : npcs) {
|
||||
if (npc->getHp() <= 0.f) {
|
||||
//std::cout << "[RAYCAST_NPC] " << npc->state.npcId << " is dead, skipping" << std::endl;
|
||||
//logger() << "[RAYCAST_NPC] " << npc->state.npcId << " is dead, skipping" << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (!npc->state.enabled) continue;
|
||||
@@ -640,7 +640,7 @@ namespace ZL
|
||||
|
||||
if (entryT > exitT) continue;
|
||||
|
||||
//std::cout << "[RAYCAST_NPC] " << npc->npcId << " hit at t=" << entryT << std::endl;
|
||||
//logger() << "[RAYCAST_NPC] " << npc->npcId << " hit at t=" << entryT << std::endl;
|
||||
|
||||
if (entryT < closestDist) {
|
||||
closestDist = entryT;
|
||||
@@ -649,10 +649,10 @@ namespace ZL
|
||||
}
|
||||
|
||||
if (closestNpc) {
|
||||
//std::cout << "[RAYCAST_NPC] HIT: " << closestNpc->npcId << std::endl;
|
||||
//logger() << "[RAYCAST_NPC] HIT: " << closestNpc->npcId << std::endl;
|
||||
}
|
||||
else {
|
||||
//std::cout << "[RAYCAST_NPC] No NPC hit" << std::endl;
|
||||
//logger() << "[RAYCAST_NPC] No NPC hit" << std::endl;
|
||||
}
|
||||
|
||||
return closestNpc;
|
||||
@@ -1603,7 +1603,7 @@ namespace ZL
|
||||
|
||||
if (npc->state.enabled && npc->state.canAttack && (!npc->state.pathWaypoints.empty() || npc->state.battle_state != 0) && npc->getHp() > 0.f)
|
||||
{
|
||||
//std::cout << "[NPC] NPC '" << npc->state.npcId << "' is attacking target index " << npc->state.attackTargetIndex << std::endl;
|
||||
//logger() << "[NPC] NPC '" << npc->state.npcId << "' is attacking target index " << npc->state.attackTargetIndex << std::endl;
|
||||
npcAttacking = true;
|
||||
}
|
||||
}
|
||||
@@ -1640,17 +1640,17 @@ namespace ZL
|
||||
|
||||
// If player is close enough to pick up the item
|
||||
if (distToObject <= obj->state.approachRadius) {
|
||||
std::cout << "[PICKUP] Player reached object! Distance: " << distToObject << std::endl;
|
||||
std::cout << "[PICKUP] Calling Lua callback for: " << obj->loadedObject.name << std::endl;
|
||||
logger() << "[PICKUP] Player reached object! Distance: " << distToObject << std::endl;
|
||||
logger() << "[PICKUP] Calling Lua callback for: " << obj->loadedObject.name << std::endl;
|
||||
|
||||
// Call custom activate function if specified, otherwise use fallback
|
||||
try {
|
||||
if (!obj->state.activateFunctionName.empty()) {
|
||||
std::cout << "[PICKUP] Using custom function: " << obj->state.activateFunctionName << std::endl;
|
||||
logger() << "[PICKUP] Using custom function: " << obj->state.activateFunctionName << std::endl;
|
||||
scriptEngine.callActivateFunction(obj->state.activateFunctionName);
|
||||
}
|
||||
else {
|
||||
std::cout << "[PICKUP] Using fallback callback" << std::endl;
|
||||
logger() << "[PICKUP] Using fallback callback" << std::endl;
|
||||
scriptEngine.callItemPickupCallback(obj->loadedObject.name);
|
||||
}
|
||||
}
|
||||
@@ -1666,7 +1666,7 @@ namespace ZL
|
||||
if (state.targetInteractNpcIndex >= 0 && player) {
|
||||
float distToNpc = (player->state.position - npcs[state.targetInteractNpcIndex]->state.position).norm();
|
||||
if (distToNpc <= NPC_TALK_DISTANCE) {
|
||||
std::cout << "[NPC] Player reached NPC index " << state.targetInteractNpcIndex
|
||||
logger() << "[NPC] Player reached NPC index " << state.targetInteractNpcIndex
|
||||
<< " (distance " << distToNpc << "); firing on_npc_interact" << std::endl;
|
||||
// Stop the player at the talk distance and have the NPC turn to face them.
|
||||
player->setTarget(player->state.position);
|
||||
@@ -1686,7 +1686,7 @@ namespace ZL
|
||||
if (auto* tz = getTargetTeleportZone(); tz && player) {
|
||||
float dist = (player->state.position - tz->position).norm();
|
||||
if (dist <= tz->radius) {
|
||||
std::cout << "[TELEPORT] Player reached teleport zone '" << tz->id << "'" << std::endl;
|
||||
logger() << "[TELEPORT] Player reached teleport zone '" << tz->id << "'" << std::endl;
|
||||
if (onTeleport) onTeleport(tz->destinationLocation, tz->destinationPosition, tz->destinationRotationY);
|
||||
state.targetTeleportZoneIndex = -1;
|
||||
return;
|
||||
@@ -1710,7 +1710,7 @@ namespace ZL
|
||||
const bool isInside = (dist <= autoR);
|
||||
if (isInside && !tz.automaticPlayerInside) {
|
||||
tz.automaticPlayerInside = true;
|
||||
std::cout << "[TELEPORT] Auto-teleport triggered by zone '" << tz.id << "'" << std::endl;
|
||||
logger() << "[TELEPORT] Auto-teleport triggered by zone '" << tz.id << "'" << std::endl;
|
||||
if (onTeleport) onTeleport(tz.destinationLocation, tz.destinationPosition, tz.destinationRotationY);
|
||||
return;
|
||||
} else if (!isInside) {
|
||||
@@ -1743,10 +1743,10 @@ namespace ZL
|
||||
toTarget.y() = 0.f;
|
||||
if (toTarget.norm() < car.waypointReachRadius) {
|
||||
car.currentWaypoint = car.currentWaypoint + 1;
|
||||
std::cout << "Waypoint current: " << car.currentWaypoint << std::endl;
|
||||
logger() << "Waypoint current: " << car.currentWaypoint << std::endl;
|
||||
if (car.currentWaypoint == car.waypoints.size())
|
||||
{
|
||||
std::cout << "waypointsOver" << std::endl;
|
||||
logger() << "waypointsOver" << std::endl;
|
||||
waypointsOver = true;
|
||||
}
|
||||
}
|
||||
@@ -1895,17 +1895,17 @@ namespace ZL
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << "[CLICK] Camera position: (" << camPos.x() << ", " << camPos.y() << ", " << camPos.z() << ")" << std::endl;
|
||||
std::cout << "[CLICK] Ray direction: (" << rayDir.x() << ", " << rayDir.y() << ", " << rayDir.z() << ")" << std::endl;
|
||||
//logger() << "[CLICK] Camera position: (" << camPos.x() << ", " << camPos.y() << ", " << camPos.z() << ")" << std::endl;
|
||||
//logger() << "[CLICK] Ray direction: (" << rayDir.x() << ", " << rayDir.y() << ", " << rayDir.z() << ")" << std::endl;
|
||||
|
||||
// First check if we clicked on interactive object
|
||||
InteractiveObject* clickedObject = raycastInteractiveObjects(camPos, rayDir);
|
||||
if (clickedObject && player && clickedObject->state.isActive) {
|
||||
std::cout << "[CLICK] *** SUCCESS: Clicked on interactive object: " << clickedObject->loadedObject.name << " ***" << std::endl;
|
||||
std::cout << "[CLICK] Object position: (" << clickedObject->state.position.x() << ", "
|
||||
<< clickedObject->state.position.y() << ", " << clickedObject->state.position.z() << ")" << std::endl;
|
||||
std::cout << "[CLICK] Player position: (" << player->state.position.x() << ", "
|
||||
<< player->state.position.y() << ", " << player->state.position.z() << ")" << std::endl;
|
||||
//logger() << "[CLICK] *** SUCCESS: Clicked on interactive object: " << clickedObject->loadedObject.name << " ***" << std::endl;
|
||||
//logger() << "[CLICK] Object position: (" << clickedObject->state.position.x() << ", "
|
||||
// << clickedObject->state.position.y() << ", " << clickedObject->state.position.z() << ")" << std::endl;
|
||||
//logger() << "[CLICK] Player position: (" << player->state.position.x() << ", "
|
||||
// << player->state.position.y() << ", " << player->state.position.z() << ")" << std::endl;
|
||||
|
||||
state.targetInteractiveObjectIndex = findInteractiveObjectIndex(clickedObject);
|
||||
state.targetInteractNpcIndex = -1;
|
||||
@@ -1914,11 +1914,11 @@ namespace ZL
|
||||
? clickedObject->state.interactionPosition
|
||||
: clickedObject->state.position);
|
||||
player->state.attackTargetIndex = CharacterState::kNoTarget;
|
||||
std::cout << "[CLICK] Player moving to object..." << std::endl;
|
||||
//logger() << "[CLICK] Player moving to object..." << std::endl;
|
||||
}
|
||||
else {
|
||||
|
||||
//std::cout << "Raycast npc step 1" << std::endl;
|
||||
//logger() << "Raycast npc step 1" << std::endl;
|
||||
// Check if we clicked on an NPC
|
||||
Character* clickedNpc = raycastNpcs(camPos, rayDir);
|
||||
if (clickedNpc && player) {
|
||||
@@ -1931,25 +1931,25 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
if (npcIndex != -1) {
|
||||
//std::cout << "Raycast npc step 2" << std::endl;
|
||||
//logger() << "Raycast npc step 2" << std::endl;
|
||||
state.targetInteractiveObjectIndex = -1;
|
||||
state.targetTeleportZoneIndex = -1;
|
||||
|
||||
if (clickedNpc->state.canAttack) {
|
||||
//std::cout << "Raycast npc step 3" << std::endl;
|
||||
//std::cout << "player->state.attackTargetIndex=" << player->state.attackTargetIndex << std::endl;
|
||||
//std::cout << "npcIndex=" << npcIndex << std::endl;
|
||||
//logger() << "Raycast npc step 3" << std::endl;
|
||||
//logger() << "player->state.attackTargetIndex=" << player->state.attackTargetIndex << std::endl;
|
||||
//logger() << "npcIndex=" << npcIndex << std::endl;
|
||||
// Hostile NPC: combat logic walks the player in via attackTargetIndex.
|
||||
if (lastPlayerAttackTarget == npcIndex)
|
||||
{
|
||||
//std::cout << "Raycast npc step 4" << std::endl;
|
||||
//logger() << "Raycast npc step 4" << std::endl;
|
||||
player->tryAttackCombo();
|
||||
}
|
||||
player->state.attackTargetIndex = npcIndex;
|
||||
state.targetInteractNpcIndex = -1;
|
||||
|
||||
if (distance <= clickedNpc->state.interactionRadius) {
|
||||
std::cout << "[CLICK] Hostile NPC " << npcIndex << " in range; firing on_npc_interact" << std::endl;
|
||||
//logger() << "[CLICK] Hostile NPC " << npcIndex << " in range; firing on_npc_interact" << std::endl;
|
||||
scriptEngine.callNpcInteractCallback(npcIndex);
|
||||
}
|
||||
}
|
||||
@@ -1959,15 +1959,15 @@ namespace ZL
|
||||
|
||||
if (distance <= NPC_TALK_DISTANCE) {
|
||||
// Already in talk range — fire immediately, stop, and face the player.
|
||||
std::cout << "[CLICK] *** SUCCESS: Clicked on NPC index: " << npcIndex << " (in range) ***" << std::endl;
|
||||
//logger() << "[CLICK] *** SUCCESS: Clicked on NPC index: " << npcIndex << " (in range) ***" << std::endl;
|
||||
player->setTarget(player->state.position);
|
||||
npcs[npcIndex]->state.faceTargetIndex = CharacterState::kPlayerIndex;
|
||||
scriptEngine.callNpcInteractCallback(npcIndex);
|
||||
state.targetInteractNpcIndex = -1;
|
||||
}
|
||||
else {
|
||||
std::cout << "[CLICK] NPC " << npcIndex << " out of talk range (distance " << distance
|
||||
<< " > " << NPC_TALK_DISTANCE << "); walking to NPC..." << std::endl;
|
||||
//logger() << "[CLICK] NPC " << npcIndex << " out of talk range (distance " << distance
|
||||
// << " > " << NPC_TALK_DISTANCE << "); walking to NPC..." << std::endl;
|
||||
player->setTarget(clickedNpc->state.position);
|
||||
state.targetInteractNpcIndex = npcIndex;
|
||||
}
|
||||
@@ -1979,7 +1979,7 @@ namespace ZL
|
||||
// Unproject click to ground plane for walk target / teleport detection
|
||||
float t = -camPos.y() / rayDir.y();
|
||||
Eigen::Vector3f hit = camPos + rayDir * t;
|
||||
std::cout << "[CLICK] Clicked on ground at: (" << hit.x() << ", " << hit.z() << ")" << std::endl;
|
||||
//logger() << "[CLICK] Clicked on ground at: (" << hit.x() << ", " << hit.z() << ")" << std::endl;
|
||||
|
||||
TeleportZone* clickedTeleport = nullptr;
|
||||
for (auto& tz : teleportZones) {
|
||||
@@ -1989,7 +1989,7 @@ namespace ZL
|
||||
}
|
||||
|
||||
if (clickedTeleport) {
|
||||
std::cout << "[CLICK] Clicked teleport zone '" << clickedTeleport->id << "'" << std::endl;
|
||||
//logger() << "[CLICK] Clicked teleport zone '" << clickedTeleport->id << "'" << std::endl;
|
||||
state.targetTeleportZoneIndex = findTeleportZoneIndex(clickedTeleport);
|
||||
state.targetInteractiveObjectIndex = -1;
|
||||
state.targetInteractNpcIndex = -1;
|
||||
@@ -2004,10 +2004,10 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << "[CLICK] No valid target found" << std::endl;
|
||||
//logger() << "[CLICK] No valid target found" << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "========================================\n" << std::endl;
|
||||
//logger() << "========================================\n" << std::endl;
|
||||
}
|
||||
void Location::handleUp(int64_t fingerId, int mx, int my)
|
||||
{
|
||||
@@ -2071,6 +2071,15 @@ namespace ZL
|
||||
{
|
||||
state.taxiCar.waypoints = state.taxiCarWaypoints;
|
||||
state.taxiCar.mode = NpcCar::Mode::FOLLOW_WAYPOINTS;
|
||||
|
||||
// For the dorm - disable trigger zone because the player already ordered taxi
|
||||
for (auto& tz : triggerZones) {
|
||||
if (tz.id == "taxi_zone001") {
|
||||
tz.enabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ---- Save / Load ----
|
||||
|
||||
+18
-18
@@ -91,15 +91,15 @@ namespace ZL
|
||||
if (dx * dx + dz * dz <= removeRadius * removeRadius) {
|
||||
navigationEditorPoints.erase(it);
|
||||
rebuildPointsMesh();
|
||||
std::cout << "[NAV_EDITOR] Removed point, " << navigationEditorPoints.size() << " remaining\n";
|
||||
logger() << "[NAV_EDITOR] Removed point, " << navigationEditorPoints.size() << " remaining\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
std::cout << "[NAV_EDITOR] No point found within " << removeRadius << " units of click\n";
|
||||
logger() << "[NAV_EDITOR] No point found within " << removeRadius << " units of click\n";
|
||||
} else {
|
||||
navigationEditorPoints.push_back(hit);
|
||||
rebuildPointsMesh();
|
||||
std::cout << "[NAV_EDITOR] Added point (" << hit.x() << ", " << hit.z()
|
||||
logger() << "[NAV_EDITOR] Added point (" << hit.x() << ", " << hit.z()
|
||||
<< "), total: " << navigationEditorPoints.size() << "\n";
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ namespace ZL
|
||||
void LocationEditor::handleRightClick()
|
||||
{
|
||||
if (navigationEditorPoints.size() < 3) {
|
||||
std::cout << "[NAV_EDITOR] Need at least 3 points to form a polygon (have "
|
||||
logger() << "[NAV_EDITOR] Need at least 3 points to form a polygon (have "
|
||||
<< navigationEditorPoints.size() << "); clearing\n";
|
||||
navigationEditorPoints.clear();
|
||||
rebuildPointsMesh();
|
||||
@@ -121,7 +121,7 @@ namespace ZL
|
||||
}
|
||||
|
||||
if (loc.navigation) loc.navigation->addObstaclePolygon(poly);
|
||||
std::cout << "[NAV_EDITOR] Added obstacle '" << poly.name << "' with "
|
||||
logger() << "[NAV_EDITOR] Added obstacle '" << poly.name << "' with "
|
||||
<< poly.polygon.size() << " vertices\n";
|
||||
|
||||
{
|
||||
@@ -150,12 +150,12 @@ namespace ZL
|
||||
if (!loc.navigation) return;
|
||||
|
||||
if (loc.navigation->saveConfig(baseName + ".json"))
|
||||
std::cout << "[NAV_EDITOR] Saved config to: " << baseName << ".json\n";
|
||||
logger() << "[NAV_EDITOR] Saved config to: " << baseName << ".json\n";
|
||||
else
|
||||
std::cerr << "[NAV_EDITOR] Failed to save config to: " << baseName << ".json\n";
|
||||
|
||||
if (loc.navigation->saveGrid(baseName + ".txt"))
|
||||
std::cout << "[NAV_EDITOR] Saved grid to: " << baseName << ".txt\n";
|
||||
logger() << "[NAV_EDITOR] Saved grid to: " << baseName << ".txt\n";
|
||||
else
|
||||
std::cerr << "[NAV_EDITOR] Failed to save grid to: " << baseName << ".txt\n";
|
||||
}
|
||||
@@ -163,7 +163,7 @@ namespace ZL
|
||||
void LocationEditor::reload()
|
||||
{
|
||||
loc.setupNavigation(loc.navigationMapPaths);
|
||||
std::cout << "[NAV_EDITOR] Reloaded navigation maps (" << loc.navigationMapPaths.size() << " maps)\n";
|
||||
logger() << "[NAV_EDITOR] Reloaded navigation maps (" << loc.navigationMapPaths.size() << " maps)\n";
|
||||
}
|
||||
|
||||
static VertexDataStruct CreateBoundsBoxMesh(const Eigen::Vector3f corners[8], const Eigen::Vector3f& color)
|
||||
@@ -290,14 +290,14 @@ namespace ZL
|
||||
void LocationEditor::selectInteractiveObject(int index)
|
||||
{
|
||||
if (index < 0 || index >= static_cast<int>(loc.interactiveObjects.size())) {
|
||||
std::cout << "[IO_EDITOR] Index " << index << " out of range ("
|
||||
logger() << "[IO_EDITOR] Index " << index << " out of range ("
|
||||
<< loc.interactiveObjects.size() << " objects)\n";
|
||||
return;
|
||||
}
|
||||
selectedInteractiveObjectIndex = index;
|
||||
boundsClickCount = 0;
|
||||
buildInteractiveObjectBoundsMeshes();
|
||||
std::cout << "[IO_EDITOR] Selected object " << index
|
||||
logger() << "[IO_EDITOR] Selected object " << index
|
||||
<< " (" << loc.interactiveObjects[index].loadedObject.name << ")\n";
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ namespace ZL
|
||||
{
|
||||
if (selectedInteractiveObjectIndex < 0 ||
|
||||
selectedInteractiveObjectIndex >= static_cast<int>(loc.interactiveObjects.size())) {
|
||||
std::cout << "[IO_EDITOR] No valid object selected\n";
|
||||
logger() << "[IO_EDITOR] No valid object selected\n";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ namespace ZL
|
||||
target.state.hasInteractionPosition = true;
|
||||
boundsClickCount = 0; // cancel any in-progress bounds placement
|
||||
buildInteractiveObjectBoundsMeshes();
|
||||
std::cout << "[IO_EDITOR] Interaction position set on object " << selectedInteractiveObjectIndex
|
||||
logger() << "[IO_EDITOR] Interaction position set on object " << selectedInteractiveObjectIndex
|
||||
<< " (" << target.loadedObject.name << ")"
|
||||
<< " at (" << worldHit.x() << ", " << worldHit.z() << ")\n";
|
||||
return;
|
||||
@@ -343,7 +343,7 @@ namespace ZL
|
||||
if (boundsClickCount == 0) {
|
||||
boundsClickA = worldHit;
|
||||
boundsClickCount = 1;
|
||||
std::cout << "[IO_EDITOR] First corner placed at world ("
|
||||
logger() << "[IO_EDITOR] First corner placed at world ("
|
||||
<< worldHit.x() << ", " << worldHit.z() << ") — click again for second corner\n";
|
||||
} else {
|
||||
const Eigen::Vector3f localA = worldToLocal(boundsClickA);
|
||||
@@ -358,7 +358,7 @@ namespace ZL
|
||||
boundsClickCount = 0;
|
||||
buildInteractiveObjectBoundsMeshes();
|
||||
|
||||
std::cout << "[IO_EDITOR] Bounds set on object " << selectedInteractiveObjectIndex
|
||||
logger() << "[IO_EDITOR] Bounds set on object " << selectedInteractiveObjectIndex
|
||||
<< " (" << target.loadedObject.name << ")"
|
||||
<< " min=(" << target.state.boundsMin.x() << ", " << target.state.boundsMin.z() << ")"
|
||||
<< " max=(" << target.state.boundsMax.x() << ", " << target.state.boundsMax.z() << ")\n";
|
||||
@@ -419,7 +419,7 @@ namespace ZL
|
||||
std::ofstream out(filename);
|
||||
if (out.is_open()) {
|
||||
out << j.dump(4);
|
||||
std::cout << "[IO_EDITOR] Saved " << loc.interactiveObjects.size()
|
||||
logger() << "[IO_EDITOR] Saved " << loc.interactiveObjects.size()
|
||||
<< " interactive object(s) to " << filename << "\n";
|
||||
} else {
|
||||
std::cerr << "[IO_EDITOR] Failed to open " << filename << " for writing\n";
|
||||
@@ -485,7 +485,7 @@ namespace ZL
|
||||
loc.gameObjects[data2.name] = std::move(obj2);
|
||||
editorPlacedObjects.push_back(data2);
|
||||
|
||||
std::cout << "[GAME_EDITOR] Placed '" << data.name << "' at ("
|
||||
logger() << "[GAME_EDITOR] Placed '" << data.name << "' at ("
|
||||
<< data.positionX << ", " << data.positionZ
|
||||
<< ") scale=" << data.scale << " rotY=" << data.rotationY << "\n";
|
||||
}
|
||||
@@ -493,7 +493,7 @@ namespace ZL
|
||||
void LocationEditor::saveObjects()
|
||||
{
|
||||
if (editorPlacedObjects.empty()) {
|
||||
std::cout << "[GAME_EDITOR] No editor-placed objects to save\n";
|
||||
logger() << "[GAME_EDITOR] No editor-placed objects to save\n";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -527,7 +527,7 @@ namespace ZL
|
||||
/*std::ofstream out(filename);
|
||||
if (out.is_open()) {
|
||||
out << j.dump(4);
|
||||
std::cout << "[GAME_EDITOR] Saved " << editorPlacedObjects.size()
|
||||
logger() << "[GAME_EDITOR] Saved " << editorPlacedObjects.size()
|
||||
<< " object(s) to " << filename << "\n";
|
||||
} else {
|
||||
std::cerr << "[GAME_EDITOR] Failed to open " << filename << " for writing\n";
|
||||
|
||||
+127
-9
@@ -16,6 +16,9 @@ namespace ZL {
|
||||
static const std::string EMPTY_LANGUAGE_RU = u8"(пусто)";
|
||||
static const std::string EMPTY_LANGUAGE_EN = "(empty)";
|
||||
|
||||
static const std::string BACK_TO_MAIN_MENU_RU = u8"Вернуться в меню";
|
||||
static const std::string BACK_TO_MAIN_MENU_EN = "Back to Main Menu";
|
||||
|
||||
|
||||
static int questStatusPriority(Quest::QuestStatus status) {
|
||||
switch (status) {
|
||||
@@ -215,6 +218,7 @@ namespace ZL {
|
||||
portraitQuestJournalItemRoot = loadLocalizedUi("resources/w/ui/portrait_screen_journal_item.json", zipFile);
|
||||
mainMenuRoot = loadLocalizedUi("resources/w/ui/screen_main_menu.json", zipFile);
|
||||
aboutScreenRoot = loadLocalizedUi("resources/w/ui/screen_about.json", zipFile);
|
||||
creditsScreenRoot = loadLocalizedUi("resources/w/ui/screen_credits.json", zipFile);
|
||||
settingsScreenRoot = loadLocalizedUi("resources/w/ui/screen_settings.json", zipFile);
|
||||
settingsGraphicsScreenRoot = loadLocalizedUi("resources/w/ui/screen_settings_graphics.json", zipFile);
|
||||
languageScreenRoot = loadLocalizedUi("resources/w/ui/screen_language.json", zipFile);
|
||||
@@ -344,6 +348,8 @@ namespace ZL {
|
||||
topUiManager.clearMenuStack();
|
||||
uiManager.replaceRoot(mainMenuRoot);
|
||||
|
||||
|
||||
|
||||
uiManager.setTextButtonCallback("menuStartButton", [this](const std::string&) {
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
enterGameplay();
|
||||
@@ -353,6 +359,10 @@ namespace ZL {
|
||||
showAboutScreen();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
});
|
||||
uiManager.setTextButtonCallback("menuCreditsButton", [this](const std::string&) {
|
||||
showCreditsScreen();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
});
|
||||
uiManager.setTextButtonCallback("menuSettingsButton", [this](const std::string&) {
|
||||
showSettingsScreen();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
@@ -378,6 +388,81 @@ namespace ZL {
|
||||
#endif
|
||||
}
|
||||
|
||||
void MenuManager::showMenuAfterGameCompleted() {
|
||||
//gameState_.currentLocationName.clear();
|
||||
|
||||
audioPlayer_.crossFadeMusicAsync("audio/main menu final.ogg");
|
||||
|
||||
uiState_ = GameUiState::MainMenu;
|
||||
uiManager.clearMenuStack();
|
||||
topUiManager.replaceRoot(nullptr);
|
||||
topUiManager.clearMenuStack();
|
||||
uiManager.replaceRoot(mainMenuRoot);
|
||||
|
||||
|
||||
|
||||
uiManager.setTextButtonCallback("menuStartButton", [this](const std::string&) {
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
enterGameplay();
|
||||
|
||||
});
|
||||
uiManager.setTextButtonCallback("menuAboutButton", [this](const std::string&) {
|
||||
showAboutScreen();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
});
|
||||
uiManager.setTextButtonCallback("menuCreditsButton", [this](const std::string&) {
|
||||
showCreditsScreen();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
});
|
||||
uiManager.setTextButtonCallback("menuSettingsButton", [this](const std::string&) {
|
||||
showSettingsScreen();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
});
|
||||
uiManager.setTextButtonCallback("menuLanguageButton", [this](const std::string&) {
|
||||
showLanguageScreen();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
});
|
||||
uiManager.setTextButtonCallback("menuLoadButton", [this](const std::string&) {
|
||||
showLoadGameScreen();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
});
|
||||
|
||||
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
uiManager.setTextButtonCallback("menuExitButton", [this](const std::string&) {
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
Environment::exitGameLoop = true;
|
||||
});
|
||||
#else
|
||||
uiManager.setNodeVisible("menuExitButton", false);
|
||||
#endif
|
||||
|
||||
//Now open the about screen:
|
||||
uiManager.pushMenuFromSavedRoot(aboutScreenRoot);
|
||||
uiManager.findTextButton("aboutBackButton")->text = (g_currentLanguage == Language::Russian) ? BACK_TO_MAIN_MENU_RU : BACK_TO_MAIN_MENU_EN;
|
||||
uiManager.setTextButtonCallback("aboutBackButton", [this](const std::string&) {
|
||||
uiManager.popMenu();
|
||||
uiManager.updateAllLayouts();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
});
|
||||
|
||||
uiManager.setTextButtonCallback("steamButton", [this](const std::string&) {
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
SDL_OpenURL("https://store.steampowered.com/app/4801010/");
|
||||
});
|
||||
|
||||
uiManager.setTextButtonCallback("telegramButton", [this](const std::string&) {
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
SDL_OpenURL("https://telegram.me/fishrungames");
|
||||
});
|
||||
|
||||
uiManager.setTextButtonCallback("discordButton", [this](const std::string&) {
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
SDL_OpenURL("https://discord.gg/S8jgGHXjw");
|
||||
});
|
||||
}
|
||||
|
||||
void MenuManager::showAboutScreen() {
|
||||
uiManager.pushMenuFromSavedRoot(aboutScreenRoot);
|
||||
uiManager.setTextButtonCallback("aboutBackButton", [this](const std::string&) {
|
||||
@@ -385,6 +470,35 @@ namespace ZL {
|
||||
uiManager.updateAllLayouts();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
});
|
||||
|
||||
uiManager.setTextButtonCallback("steamButton", [this](const std::string&) {
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
SDL_OpenURL("https://store.steampowered.com/app/4801010/");
|
||||
});
|
||||
|
||||
uiManager.setTextButtonCallback("telegramButton", [this](const std::string&) {
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
SDL_OpenURL("https://telegram.me/fishrungames");
|
||||
});
|
||||
|
||||
uiManager.setTextButtonCallback("discordButton", [this](const std::string&) {
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
SDL_OpenURL("https://discord.gg/S8jgGHXjw");
|
||||
});
|
||||
}
|
||||
|
||||
void MenuManager::showCreditsScreen() {
|
||||
uiManager.pushMenuFromSavedRoot(creditsScreenRoot);
|
||||
uiManager.setButtonCallback("creditsBackButton", [this](const std::string&) {
|
||||
uiManager.popMenu();
|
||||
uiManager.updateAllLayouts();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
});
|
||||
|
||||
|
||||
uiManager.stopAnimationOnNode("creditsLinearLayout", "slowScroll");
|
||||
uiManager.resetAnimationOnNode("creditsLinearLayout");
|
||||
uiManager.startAnimationOnNode("creditsLinearLayout", "slowScroll");
|
||||
}
|
||||
|
||||
void MenuManager::showSettingsScreen() {
|
||||
@@ -490,9 +604,11 @@ namespace ZL {
|
||||
void MenuManager::showSettingsGraphicsScreen() {
|
||||
uiManager.pushMenuFromSavedRoot(settingsGraphicsScreenRoot);
|
||||
uiManager.setTextButtonCallback("settingsBackButton", [this](const std::string&) {
|
||||
//saveSettings();
|
||||
saveSettings();
|
||||
uiManager.popMenu();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
uiManager.updateAllLayouts();
|
||||
topUiManager.updateAllLayouts();
|
||||
});
|
||||
|
||||
|
||||
@@ -530,6 +646,8 @@ namespace ZL {
|
||||
Environment::setFullscreen(!Environment::isFullscreen);
|
||||
refreshFullscreenToggle();
|
||||
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
|
||||
uiManager.updateAllLayouts();
|
||||
topUiManager.updateAllLayouts();
|
||||
});
|
||||
|
||||
uiManager.setTextButtonCallback("shadowToggleButton", [this, refreshShadowToggle](const std::string&) {
|
||||
@@ -756,7 +874,7 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void MenuManager::selectInventoryItem(int index) {
|
||||
std::cout << "MenuManager::selectInventoryItem: " << index << std::endl;
|
||||
logger() << "MenuManager::selectInventoryItem: " << index << std::endl;
|
||||
const auto& items = inventory->getItems();
|
||||
if (index < 0 || index >= static_cast<int>(items.size())) return;
|
||||
|
||||
@@ -862,7 +980,7 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void MenuManager::toggleQuestJournal() {
|
||||
std::cout << "[quest] toggleQuestJournal: " << (isQuestJournalOpen() ? "closing" : "opening") << std::endl;
|
||||
logger() << "[quest] toggleQuestJournal: " << (isQuestJournalOpen() ? "closing" : "opening") << std::endl;
|
||||
if (uiState_ == GameUiState::QuestJournal) {
|
||||
closeQuestJournal();
|
||||
}
|
||||
@@ -1274,7 +1392,7 @@ namespace ZL {
|
||||
|
||||
void MenuManager::tutorialShowTaxiHint()
|
||||
{
|
||||
std::cout << "tutorialShowTaxiHint" << std::endl;
|
||||
logger() << "tutorialShowTaxiHint" << std::endl;
|
||||
gameState_.tutorialNeedOpenTaxiScreen = true;
|
||||
if (uiState_ == GameUiState::Gameplay)
|
||||
{
|
||||
@@ -1889,7 +2007,7 @@ namespace ZL {
|
||||
|
||||
void MenuManager::setDarklandsMode(bool enabled)
|
||||
{
|
||||
std::cout << "MenuManager::setDarklandsMode called" << std::endl;
|
||||
logger() << "MenuManager::setDarklandsMode called" << std::endl;
|
||||
|
||||
if (gameState_.currentLocationName == "uni_interior") {
|
||||
if (enabled && gameState_.uniIntTutorialState == UniIntTutorialState::Step11) {
|
||||
@@ -1979,11 +2097,11 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void MenuManager::applyCurrentHealthBar() {
|
||||
std::cout << "MenuManager::applyCurrentHealthBar called step 1" << std::endl;
|
||||
std::cout << "currentPlayerMaxHp_ is " << gameState_.playerMaxHp << std::endl;
|
||||
logger() << "MenuManager::applyCurrentHealthBar called step 1" << std::endl;
|
||||
logger() << "currentPlayerMaxHp_ is " << gameState_.playerMaxHp << std::endl;
|
||||
if (gameState_.playerMaxHp <= 0.f) return;
|
||||
std::cout << "MenuManager::applyCurrentHealthBar called step 2" << std::endl;
|
||||
std::cout << "currentPlayerHp_ is " << gameState_.playerHp << std::endl;
|
||||
logger() << "MenuManager::applyCurrentHealthBar called step 2" << std::endl;
|
||||
logger() << "currentPlayerHp_ is " << gameState_.playerHp << std::endl;
|
||||
|
||||
const float fraction = std::clamp(gameState_.playerHp / gameState_.playerMaxHp, 0.f, 1.f);
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace ZL {
|
||||
bool isQuestJournalOpen() const { return uiState_ == GameUiState::QuestJournal; }
|
||||
|
||||
void showMainMenu();
|
||||
void showMenuAfterGameCompleted();
|
||||
bool isMainMenuOpen() const { return uiState_ == GameUiState::MainMenu; }
|
||||
|
||||
void openPhoneScreen();
|
||||
@@ -172,6 +173,7 @@ namespace ZL {
|
||||
//void closePortraitJournalItemCompletely();
|
||||
|
||||
void showAboutScreen();
|
||||
void showCreditsScreen();
|
||||
void showSettingsScreen();
|
||||
void showSettingsGraphicsScreen();
|
||||
void showLanguageScreen();
|
||||
@@ -262,6 +264,7 @@ namespace ZL {
|
||||
|
||||
std::shared_ptr<UiNode> mainMenuRoot;
|
||||
std::shared_ptr<UiNode> aboutScreenRoot;
|
||||
std::shared_ptr<UiNode> creditsScreenRoot;
|
||||
std::shared_ptr<UiNode> settingsScreenRoot;
|
||||
std::shared_ptr<UiNode> settingsGraphicsScreenRoot;
|
||||
std::shared_ptr<UiNode> languageScreenRoot;
|
||||
|
||||
+40
-19
@@ -5,6 +5,7 @@
|
||||
#include <unordered_map>
|
||||
#include "Location.h"
|
||||
#include "items/ItemRegistry.h"
|
||||
#include "utils/Utils.h"
|
||||
|
||||
#define SOL_ALL_SAFETIES_ON 1
|
||||
#include <sol/sol.hpp>
|
||||
@@ -87,6 +88,26 @@ namespace ZL {
|
||||
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string, sol::lib::table);
|
||||
|
||||
|
||||
// --- Переопределение функции print ---
|
||||
lua.set_function("print", [](sol::this_state ts, sol::variadic_args va) {
|
||||
sol::state_view lua_view(ts);
|
||||
sol::function tostring = lua_view["tostring"];
|
||||
|
||||
auto& log = logger();
|
||||
log << "[script] ";
|
||||
|
||||
bool first = true;
|
||||
for (auto arg : va) {
|
||||
if (!first) log << "\t"; // Разделяем аргументы табуляцией, как в стандартном Lua print
|
||||
std::string str = tostring(arg);
|
||||
log << str;
|
||||
first = false;
|
||||
}
|
||||
log << std::endl;
|
||||
});
|
||||
// -------------------------------------
|
||||
|
||||
auto api = lua.create_named_table("game_api");
|
||||
|
||||
// npc_walk_to(index, x, y, z [, on_arrived])
|
||||
@@ -124,14 +145,14 @@ namespace ZL {
|
||||
}
|
||||
|
||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
||||
std::cout << "[script] npc_rotate_to: index " << index << " angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
||||
logger() << "[script] npc_rotate_to: index " << index << " angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
||||
npcs[index]->state.targetFacingAngle = rad;
|
||||
});
|
||||
|
||||
api.set_function("player_rotate_to",
|
||||
[loc](float angle) {
|
||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
||||
std::cout << "[script] player_rotate_to: angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
||||
logger() << "[script] player_rotate_to: angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
||||
loc->player->state.targetFacingAngle = rad;
|
||||
});
|
||||
|
||||
@@ -172,12 +193,12 @@ namespace ZL {
|
||||
const Item* item = ItemRegistry::instance().findById(itemId);
|
||||
if (item) {
|
||||
if (inventory->hasItem(itemId)) {
|
||||
std::cout << "[script] has_item: " << item->name << " returns true" << std::endl;
|
||||
logger() << "[script] has_item: " << item->name << " returns true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "[script] has_item: " << item->name << " returns false" << std::endl;
|
||||
logger() << "[script] has_item: " << item->name << " returns false" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -192,7 +213,7 @@ namespace ZL {
|
||||
const Item* item = ItemRegistry::instance().findById(itemId);
|
||||
if (item) {
|
||||
inventory->addItem(item->id);
|
||||
std::cout << "[script] pickup_item: " << item->name << std::endl;
|
||||
logger() << "[script] pickup_item: " << item->name << std::endl;
|
||||
} else {
|
||||
std::cerr << "[script] pickup_item: item '" << itemId << "' not found in ItemRegistry\n";
|
||||
}
|
||||
@@ -200,7 +221,7 @@ namespace ZL {
|
||||
|
||||
// remove_item(item_id)
|
||||
api.set_function("remove_item", [loc, inventory](const std::string& id) {
|
||||
std::cout << "[script] remove_item: " << id << std::endl;
|
||||
logger() << "[script] remove_item: " << id << std::endl;
|
||||
inventory->removeItem(id);
|
||||
});
|
||||
|
||||
@@ -209,7 +230,7 @@ namespace ZL {
|
||||
for (auto& intObj : loc->interactiveObjects) {
|
||||
if (intObj.loadedObject.name == objectName) {
|
||||
intObj.state.isActive = false;
|
||||
std::cout << "[script] deactivate_interactive_object: " << objectName << std::endl;
|
||||
logger() << "[script] deactivate_interactive_object: " << objectName << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -221,7 +242,7 @@ namespace ZL {
|
||||
for (auto& intObj : loc->interactiveObjects) {
|
||||
if (intObj.loadedObject.name == objectName) {
|
||||
intObj.state.isActive = true;
|
||||
std::cout << "[script] activate_interactive_object: " << objectName << std::endl;
|
||||
logger() << "[script] activate_interactive_object: " << objectName << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -232,7 +253,7 @@ namespace ZL {
|
||||
for (auto& intObj : loc->interactiveObjects) {
|
||||
if (intObj.loadedObject.name == objectName) {
|
||||
intObj.state.rotationY = value * static_cast<float>(M_PI) / 180.f;
|
||||
std::cout << "[script] set_object_rotation: " << objectName << " " << value << std::endl;
|
||||
logger() << "[script] set_object_rotation: " << objectName << " " << value << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -243,7 +264,7 @@ namespace ZL {
|
||||
for (auto& intObj : loc->interactiveObjects) {
|
||||
if (intObj.loadedObject.name == objectName) {
|
||||
intObj.state.alpha = value;
|
||||
std::cout << "[script] set_object_alpha: " <<objectName << " " << value << std::endl;
|
||||
logger() << "[script] set_object_alpha: " <<objectName << " " << value << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -336,13 +357,13 @@ namespace ZL {
|
||||
api.set_function("set_night",
|
||||
[loc]() {
|
||||
loc->requestNightDayTransition(true, false);
|
||||
std::cout << "Set night called" << std::endl;
|
||||
logger() << "Set night called" << std::endl;
|
||||
});
|
||||
|
||||
api.set_function("set_dawn",
|
||||
[loc]() {
|
||||
loc->requestNightDayTransition(true, true);
|
||||
std::cout << "Set dawn called" << std::endl;
|
||||
logger() << "Set dawn called" << std::endl;
|
||||
});
|
||||
|
||||
|
||||
@@ -857,7 +878,7 @@ namespace ZL {
|
||||
std::cerr << "[SCRIPT] on_npc_interact error: " << err.what() << "\n";
|
||||
}
|
||||
else {
|
||||
std::cout << "[SCRIPT] on_npc_interact called with index " << npcIndex << std::endl;
|
||||
logger() << "[SCRIPT] on_npc_interact called with index " << npcIndex << std::endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -883,7 +904,7 @@ namespace ZL {
|
||||
}
|
||||
|
||||
sol::state& lua = impl->lua;
|
||||
std::cout << "[SCRIPT] Looking for activate function: " << functionName << std::endl;
|
||||
logger() << "[SCRIPT] Looking for activate function: " << functionName << std::endl;
|
||||
|
||||
sol::function activateFunc = lua[functionName];
|
||||
|
||||
@@ -891,7 +912,7 @@ namespace ZL {
|
||||
throw std::runtime_error("[SCRIPT] Lua function not found: " + functionName);
|
||||
}
|
||||
|
||||
std::cout << "[SCRIPT] Found function! Calling: " << functionName << std::endl;
|
||||
logger() << "[SCRIPT] Found function! Calling: " << functionName << std::endl;
|
||||
auto result = activateFunc();
|
||||
|
||||
if (!result.valid()) {
|
||||
@@ -899,7 +920,7 @@ namespace ZL {
|
||||
throw std::runtime_error("[SCRIPT] Error executing " + functionName + ": " + std::string(err.what()));
|
||||
}
|
||||
|
||||
std::cout << "[SCRIPT] Function executed successfully!" << std::endl;
|
||||
logger() << "[SCRIPT] Function executed successfully!" << std::endl;
|
||||
}
|
||||
|
||||
void ScriptEngine::callItemPickupCallback(const std::string& objectName) {
|
||||
@@ -914,18 +935,18 @@ namespace ZL {
|
||||
sol::function activateFunc = lua["on_item_pickup"];
|
||||
|
||||
if (activateFunc.valid()) {
|
||||
std::cout << "[SCRIPT] Callback found! Calling with argument: " << objectName << std::endl;
|
||||
logger() << "[SCRIPT] Callback found! Calling with argument: " << objectName << std::endl;
|
||||
auto result = activateFunc(objectName);
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
std::cerr << "[SCRIPT] on_item_pickup callback error: " << err.what() << "\n";
|
||||
}
|
||||
else {
|
||||
std::cout << "[SCRIPT] Callback executed successfully!" << std::endl;
|
||||
logger() << "[SCRIPT] Callback executed successfully!" << std::endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << "[SCRIPT] Fallback: on_item_pickup not found" << std::endl;
|
||||
logger() << "[SCRIPT] Fallback: on_item_pickup not found" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-22
@@ -474,7 +474,7 @@ namespace ZL {
|
||||
}
|
||||
|
||||
bool SparkEmitter::loadFromJsonFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||
std::cout << "Loading spark config from: " << path << std::endl;
|
||||
logger() << "Loading spark config from: " << path << std::endl;
|
||||
|
||||
std::string content;
|
||||
try {
|
||||
@@ -498,13 +498,13 @@ namespace ZL {
|
||||
json j;
|
||||
try {
|
||||
j = json::parse(content);
|
||||
std::cout << "JSON parsed successfully" << std::endl;
|
||||
logger() << "JSON parsed successfully" << std::endl;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "JSON parse error: " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file 9!");
|
||||
}
|
||||
std::cout << "JSON content: " << j.dump(2) << std::endl;
|
||||
logger() << "JSON content: " << j.dump(2) << std::endl;
|
||||
|
||||
auto requireKey = [&](const std::string& key) {
|
||||
if (!j.contains(key)) {
|
||||
@@ -531,13 +531,13 @@ namespace ZL {
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
std::cout << "Max particles: " << maxParticles << std::endl;
|
||||
logger() << "Max particles: " << maxParticles << std::endl;
|
||||
|
||||
particleSize = j["particleSize"].get<float>();
|
||||
std::cout << "Particle size: " << particleSize << std::endl;
|
||||
logger() << "Particle size: " << particleSize << std::endl;
|
||||
|
||||
biasX = j["biasX"].get<float>();
|
||||
std::cout << "Bias X: " << biasX << std::endl;
|
||||
logger() << "Bias X: " << biasX << std::endl;
|
||||
|
||||
// emissionPoints
|
||||
std::vector<Vector3f> points;
|
||||
@@ -546,7 +546,7 @@ namespace ZL {
|
||||
if (el.contains("position") && el["position"].is_array()) {
|
||||
auto arr = el["position"];
|
||||
points.push_back(Vector3f{ arr[0].get<float>(), arr[1].get<float>(), arr[2].get<float>() });
|
||||
std::cout << "Fixed point: [" << arr[0] << ", " << arr[1] << ", " << arr[2] << "]" << std::endl;
|
||||
logger() << "Fixed point: [" << arr[0] << ", " << arr[1] << ", " << arr[2] << "]" << std::endl;
|
||||
}
|
||||
else if (el.contains("positionRange") && el["positionRange"].is_object()) {
|
||||
auto pr = el["positionRange"];
|
||||
@@ -564,18 +564,18 @@ namespace ZL {
|
||||
for (int k = 0; k < count; ++k) {
|
||||
Vector3f randomPoint{ dx(gen), dy(gen), dz(gen) };
|
||||
points.push_back(randomPoint);
|
||||
std::cout << "Random point " << k + 1 << ": [" << randomPoint(0)
|
||||
logger() << "Random point " << k + 1 << ": [" << randomPoint(0)
|
||||
<< ", " << randomPoint(1) << ", " << randomPoint(2) << "]" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!points.empty()) {
|
||||
setEmissionPoints(points);
|
||||
std::cout << "Total emission points: " << emissionPoints.size() << std::endl;
|
||||
logger() << "Total emission points: " << emissionPoints.size() << std::endl;
|
||||
}
|
||||
else {
|
||||
setEmissionPoints({});
|
||||
std::cout << "Emission points parsed but empty" << std::endl;
|
||||
logger() << "Emission points parsed but empty" << std::endl;
|
||||
//throw std::runtime_error("Failed to load spark emitter config file 10!");
|
||||
}
|
||||
}
|
||||
@@ -589,7 +589,7 @@ namespace ZL {
|
||||
auto a = j["speedRange"];
|
||||
speedRange.min = a[0].get<float>();
|
||||
speedRange.max = a[1].get<float>();
|
||||
std::cout << "Speed range: [" << speedRange.min << ", " << speedRange.max << "]" << std::endl;
|
||||
logger() << "Speed range: [" << speedRange.min << ", " << speedRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "speedRange missing or invalid" << std::endl;
|
||||
@@ -600,7 +600,7 @@ namespace ZL {
|
||||
auto a = j["zSpeedRange"];
|
||||
zSpeedRange.min = a[0].get<float>();
|
||||
zSpeedRange.max = a[1].get<float>();
|
||||
std::cout << "Z speed range: [" << zSpeedRange.min << ", " << zSpeedRange.max << "]" << std::endl;
|
||||
logger() << "Z speed range: [" << zSpeedRange.min << ", " << zSpeedRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "zSpeedRange missing or invalid" << std::endl;
|
||||
@@ -611,7 +611,7 @@ namespace ZL {
|
||||
auto a = j["scaleRange"];
|
||||
scaleRange.min = a[0].get<float>();
|
||||
scaleRange.max = a[1].get<float>();
|
||||
std::cout << "Scale range: [" << scaleRange.min << ", " << scaleRange.max << "]" << std::endl;
|
||||
logger() << "Scale range: [" << scaleRange.min << ", " << scaleRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "scaleRange missing or invalid" << std::endl;
|
||||
@@ -622,7 +622,7 @@ namespace ZL {
|
||||
auto a = j["lifeTimeRange"];
|
||||
lifeTimeRange.min = a[0].get<float>();
|
||||
lifeTimeRange.max = a[1].get<float>();
|
||||
std::cout << "Life time range: [" << lifeTimeRange.min << ", " << lifeTimeRange.max << "]" << std::endl;
|
||||
logger() << "Life time range: [" << lifeTimeRange.min << ", " << lifeTimeRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "lifeTimeRange missing or invalid" << std::endl;
|
||||
@@ -632,17 +632,17 @@ namespace ZL {
|
||||
// texture
|
||||
if (j.contains("texture") && j["texture"].is_string()) {
|
||||
std::string texPath = j["texture"].get<std::string>();
|
||||
std::cout << "Loading texture: " << texPath << " From zip file: " << zipFile << std::endl;
|
||||
logger() << "Loading texture: " << texPath << " From zip file: " << zipFile << std::endl;
|
||||
|
||||
try {
|
||||
std::cout << "Loading texture step 1" << std::endl;
|
||||
logger() << "Loading texture step 1" << std::endl;
|
||||
auto texData = CreateTextureDataFromPng(texPath.c_str(), zipFile.c_str());
|
||||
std::cout << "Loading texture step 2" << std::endl;
|
||||
logger() << "Loading texture step 2" << std::endl;
|
||||
texture = std::make_shared<Texture>(texData);
|
||||
std::cout << "Texture loaded successfully, ID: " << texture->getTexID() << std::endl;
|
||||
logger() << "Texture loaded successfully, ID: " << texture->getTexID() << std::endl;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cout << "Texture load error: " << e.what() << std::endl;
|
||||
logger() << "Texture load error: " << e.what() << std::endl;
|
||||
std::cerr << "Texture load error: " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file 16!");
|
||||
}
|
||||
@@ -652,11 +652,11 @@ namespace ZL {
|
||||
throw std::runtime_error("Failed to load spark emitter config file 17!");
|
||||
}
|
||||
|
||||
std::cout << "Working with shaders 1" << std::endl;
|
||||
logger() << "Working with shaders 1" << std::endl;
|
||||
// shaders
|
||||
if (j.contains("shaderProgramName") && j["shaderProgramName"].is_string()) {
|
||||
shaderProgramName = j["shaderProgramName"].get<std::string>();
|
||||
std::cout << "Shader program name: " << shaderProgramName << std::endl;
|
||||
logger() << "Shader program name: " << shaderProgramName << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "shaderProgramName missing or invalid" << std::endl;
|
||||
@@ -665,7 +665,7 @@ namespace ZL {
|
||||
|
||||
drawDataDirty = true;
|
||||
configured = true;
|
||||
std::cout << "SparkEmitter configuration loaded successfully!" << std::endl;
|
||||
logger() << "SparkEmitter configuration loaded successfully!" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -81,7 +81,7 @@ namespace ZL
|
||||
numberVertices = std::stoi(match.str());
|
||||
}
|
||||
else {
|
||||
std::cout << "Vertices header not found or invalid: " << tempLine << std::endl;
|
||||
logger() << "Vertices header not found or invalid: " << tempLine << std::endl;
|
||||
throw std::runtime_error("Vertices header not found or invalid.");
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace ZL
|
||||
// UV - [7], [8]
|
||||
|
||||
if (floatValues.size() < 9) {
|
||||
std::cout << "Malformed vertex line at index " << i << ": " << tempLine << std::endl;
|
||||
logger() << "Malformed vertex line at index " << i << ": " << tempLine << std::endl;
|
||||
throw std::runtime_error("Malformed vertex line at index " + std::to_string(i));
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace ZL
|
||||
numberTriangles = std::stoi(match.str());
|
||||
}
|
||||
else {
|
||||
std::cout << "Triangles header not found or invalid: " << tempLine << std::endl;
|
||||
logger() << "Triangles header not found or invalid: " << tempLine << std::endl;
|
||||
throw std::runtime_error("Triangles header not found.");
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace ZL
|
||||
}
|
||||
|
||||
if (indices.size() != 3) {
|
||||
std::cout << "Malformed triangle line at index " << i << ": " << tempLine << std::endl;
|
||||
logger() << "Malformed triangle line at index " << i << ": " << tempLine << std::endl;
|
||||
throw std::runtime_error("Malformed triangle line at index " + std::to_string(i));
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ namespace ZL
|
||||
result.NormalData[i](2) = originalNorm(0);
|
||||
}
|
||||
|
||||
std::cout << "Model loaded: " << numberVertices << " verts, " << numberTriangles << " tris." << std::endl;
|
||||
logger() << "Model loaded: " << numberVertices << " verts, " << numberTriangles << " tris." << std::endl;
|
||||
|
||||
s_meshCache[key] = result;
|
||||
return result;
|
||||
@@ -266,7 +266,7 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Binary model loaded: " << numVertices << " verts, " << numTriangles << " tris." << std::endl;
|
||||
logger() << "Binary model loaded: " << numVertices << " verts, " << numTriangles << " tris." << std::endl;
|
||||
|
||||
s_meshCache[key] = result;
|
||||
return result;
|
||||
|
||||
+101
-42
@@ -182,7 +182,7 @@ namespace ZL {
|
||||
mesh.RefreshVBO();
|
||||
}
|
||||
|
||||
void UiTextButton::draw(Renderer& renderer) const {
|
||||
void UiTextButton::draw(Renderer& renderer, float accumX, float accumY) const {
|
||||
|
||||
// Draw background texture (optional)
|
||||
const std::shared_ptr<Texture>* tex = nullptr;
|
||||
@@ -221,16 +221,18 @@ namespace ZL {
|
||||
? wrapTextByPixels(text, *textRenderer, rect.w - textPaddingX * 2.0f, scale)
|
||||
: text;
|
||||
|
||||
float tx = rect.x + rect.w / 2.0f + animOffsetX;
|
||||
// Прибавляем accumX и accumY
|
||||
float tx = rect.x + rect.w / 2.0f + animOffsetX + accumX;
|
||||
if (!textCentered) {
|
||||
tx = rect.x + textPaddingX + animOffsetX;
|
||||
tx = rect.x + textPaddingX + animOffsetX + accumX;
|
||||
}
|
||||
|
||||
float ty;
|
||||
if (topAligned) {
|
||||
ty = rect.y + rect.h - textPaddingY - static_cast<float>(fontSize) + animOffsetY;
|
||||
} else {
|
||||
ty = rect.y + rect.h * 0.5f + textPaddingY + animOffsetY;
|
||||
ty = rect.y + rect.h - textPaddingY - static_cast<float>(fontSize) + animOffsetY + accumY;
|
||||
}
|
||||
else {
|
||||
ty = rect.y + rect.h * 0.5f + textPaddingY + animOffsetY + accumY;
|
||||
}
|
||||
|
||||
textRenderer->drawText(displayText, tx, ty, scale, textCentered, color);
|
||||
@@ -238,24 +240,19 @@ namespace ZL {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void UiTextView::draw(Renderer& renderer) const {
|
||||
void UiTextView::draw(Renderer& renderer, float accumX, float accumY) const {
|
||||
(void)renderer;
|
||||
if (!textRenderer || text.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float scale = 1.0f;
|
||||
|
||||
// Backward compatibility:
|
||||
// Old UI files, including the original inventory panel, positioned TextView
|
||||
// around the rect center. If a TextView does not explicitly request wrapping,
|
||||
// top alignment, padding or line limiting, keep that old behavior.
|
||||
const bool usesModernRectText = wrap || topAligned || paddingX != 0.0f || paddingY != 0.0f || maxLines > 0;
|
||||
if (!usesModernRectText) {
|
||||
textRenderer->drawText(
|
||||
text,
|
||||
rect.x + rect.w * 0.5f,
|
||||
rect.y + rect.h * 0.5f,
|
||||
rect.x + rect.w * 0.5f + accumX,
|
||||
rect.y + rect.h * 0.5f + accumY,
|
||||
scale,
|
||||
textCentered,
|
||||
color
|
||||
@@ -272,12 +269,10 @@ namespace ZL {
|
||||
float ty = rect.y + rect.h * 0.5f;
|
||||
|
||||
if (topAligned) {
|
||||
// TextRenderer expects a baseline position. This offset places the first
|
||||
// visible line close to the top inside the TextView rectangle.
|
||||
ty = rect.y + rect.h - paddingY - static_cast<float>(fontSize);
|
||||
}
|
||||
|
||||
textRenderer->drawText(finalText, tx, ty, scale, textCentered, color);
|
||||
textRenderer->drawText(finalText, tx + accumX, ty + accumY, scale, textCentered, color);
|
||||
}
|
||||
|
||||
void UiSlider::buildTrackMesh() {
|
||||
@@ -537,7 +532,7 @@ namespace ZL {
|
||||
if (!t.contains(key) || !t[key].is_string()) return nullptr;
|
||||
std::string path = t[key].get<std::string>();
|
||||
try {
|
||||
std::cout << "UiManager: loading texture for button '" << btn->name << "' : " << path << " Zip file: " << zipFile << std::endl;
|
||||
logger() << "UiManager: loading texture for button '" << btn->name << "' : " << path << " Zip file: " << zipFile << std::endl;
|
||||
return renderer.textureManager.LoadFromPng(path, zipFile, true);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
@@ -571,7 +566,7 @@ namespace ZL {
|
||||
if (!t.contains(key) || !t[key].is_string()) return nullptr;
|
||||
std::string path = t[key].get<std::string>();
|
||||
try {
|
||||
std::cout << "UiManager: --loading texture for slider '" << s->name << "' : " << path << " Zip file: " << zipFile << std::endl;
|
||||
logger() << "UiManager: --loading texture for slider '" << s->name << "' : " << path << " Zip file: " << zipFile << std::endl;
|
||||
return renderer.textureManager.LoadFromPng(path, zipFile, true);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
@@ -1480,60 +1475,61 @@ namespace ZL {
|
||||
renderer.LoadIdentity();
|
||||
renderer.RenderUniform1f("uAlpha", 1.0f); // ensure alpha is fully opaque unless a widget overrides it
|
||||
|
||||
std::function<void(const std::shared_ptr<UiNode>&)> drawNode =
|
||||
[&](const std::shared_ptr<UiNode>& node) {
|
||||
std::function<void(const std::shared_ptr<UiNode>&, float, float)> drawNode =
|
||||
[&](const std::shared_ptr<UiNode>& node, float accumX, float accumY) {
|
||||
if (!node || !node->visible) return;
|
||||
|
||||
// Считаем полное смещение для текущего узла и его потомков
|
||||
float curAccumX = accumX + node->animOffsetX;
|
||||
float curAccumY = accumY + node->animOffsetY;
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ node->screenRect.x + node->screenRect.w * 0.5f,
|
||||
renderer.TranslateMatrix({ node->animOffsetX, node->animOffsetY, 0.0f });
|
||||
|
||||
renderer.TranslateMatrix({ node->screenRect.x + node->screenRect.w * 0.5f,
|
||||
node->screenRect.y + node->screenRect.h * 0.5f, 0.0f });
|
||||
renderer.ScaleMatrix({ node->scaleX, node->scaleY, 1.0f });
|
||||
renderer.TranslateMatrix({ -(node->screenRect.x + node->screenRect.w * 0.5f),
|
||||
renderer.TranslateMatrix({ -(node->screenRect.x + node->screenRect.w * 0.5f),
|
||||
-(node->screenRect.y + node->screenRect.h * 0.5f), 0.0f });
|
||||
|
||||
|
||||
// 1. Сначала изображения
|
||||
if (node->staticImage) {
|
||||
node->staticImage->draw(renderer);
|
||||
}
|
||||
|
||||
// 1b. Chat bubble (composite widget)
|
||||
if (node->chatBubble) {
|
||||
node->chatBubble->draw(renderer, node->scaleX, node->scaleY, projW, projH);
|
||||
}
|
||||
|
||||
// 2. Потом кнопки
|
||||
if (node->button) {
|
||||
node->button->draw(renderer);
|
||||
}
|
||||
if (node->textButton) {
|
||||
node->textButton->draw(renderer);
|
||||
// Передаем накопленные смещения
|
||||
node->textButton->draw(renderer, curAccumX, curAccumY);
|
||||
}
|
||||
|
||||
// 3. Потом слайдеры
|
||||
|
||||
if (node->slider) {
|
||||
node->slider->draw(renderer);
|
||||
}
|
||||
|
||||
// 4. Потом текстовые поля
|
||||
|
||||
if (node->textField) {
|
||||
node->textField->draw(renderer);
|
||||
}
|
||||
|
||||
// 5. ПОСЛЕДНИЙ - ТЕКСТ (поверх всего!)
|
||||
|
||||
if (node->textView) {
|
||||
node->textView->draw(renderer);
|
||||
// Передаем накопленные смещения
|
||||
node->textView->draw(renderer, curAccumX, curAccumY);
|
||||
}
|
||||
|
||||
// Рисуем детей
|
||||
for (auto& child : node->children) {
|
||||
drawNode(child);
|
||||
// Прокидываем сумму дальше
|
||||
drawNode(child, curAccumX, curAccumY);
|
||||
}
|
||||
|
||||
renderer.PopMatrix();
|
||||
};
|
||||
|
||||
drawNode(root);
|
||||
drawNode(root, 0.0f, 0.0f);
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
@@ -1625,6 +1621,12 @@ namespace ZL {
|
||||
node->textButton->animScaleX = act.origScaleX;
|
||||
node->textButton->animScaleY = act.origScaleY;
|
||||
}
|
||||
else
|
||||
{
|
||||
//logger() << "[animation] Resetting animation for node: " << node->name << " y=" << act.origOffsetY << std::endl;
|
||||
node->animOffsetX = act.origOffsetX;
|
||||
node->animOffsetY = act.origOffsetY;
|
||||
}
|
||||
act.stepIndex = 0;
|
||||
act.elapsedMs = 0.0f;
|
||||
act.stepStarted = false;
|
||||
@@ -1650,6 +1652,12 @@ namespace ZL {
|
||||
node->textButton->animOffsetX = step.toX;
|
||||
node->textButton->animOffsetY = step.toY;
|
||||
}
|
||||
else
|
||||
{
|
||||
//logger() << "[animation] step.durationMs <= 0.0f: " << node->name << " y=" << step.toY << std::endl;
|
||||
node->animOffsetX = step.toX;
|
||||
node->animOffsetY = step.toY;
|
||||
}
|
||||
}
|
||||
else if (step.type == "scale") {
|
||||
if (node->button) {
|
||||
@@ -1681,8 +1689,9 @@ namespace ZL {
|
||||
act.origScaleY = node->textButton->animScaleY;
|
||||
}
|
||||
else {
|
||||
act.origOffsetX = act.origOffsetY = 0.0f;
|
||||
act.origScaleX = act.origScaleY = 1.0f;
|
||||
//logger() << "[animation] !act.stepStarted && act.stepIndex == 0 && act.elapsedMs == 0.0f: " << node->name << " y=" << node->animOffsetY << std::endl;
|
||||
act.origOffsetX = node->animOffsetX;
|
||||
act.origOffsetY = node->animOffsetY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1703,8 +1712,11 @@ namespace ZL {
|
||||
act.startScaleY = node->textButton->animScaleY;
|
||||
}
|
||||
else {
|
||||
act.startOffsetX = act.startOffsetY = 0.0f;
|
||||
act.startScaleX = act.startScaleY = 1.0f;
|
||||
//logger() << "[animation] !act.stepStarted && prevElapsed == 0.0f: " << node->name << " y=" << node->animOffsetY << std::endl;
|
||||
act.startOffsetX = node->animOffsetX;
|
||||
act.startOffsetY = node->animOffsetY;
|
||||
act.startScaleX = node->scaleX;
|
||||
act.startScaleY = node->scaleY;
|
||||
}
|
||||
if (step.type == "move") {
|
||||
act.endOffsetX = step.toX;
|
||||
@@ -1731,6 +1743,12 @@ namespace ZL {
|
||||
node->textButton->animOffsetX = nx;
|
||||
node->textButton->animOffsetY = ny;
|
||||
}
|
||||
else {
|
||||
//logger() << "[animation] if (step.type ==move): " << node->name << " y=" << ny << std::endl;
|
||||
|
||||
node->animOffsetX = nx;
|
||||
node->animOffsetY = ny;
|
||||
}
|
||||
}
|
||||
else if (step.type == "scale") {
|
||||
float sx = act.startScaleX + (act.endScaleX - act.startScaleX) * te;
|
||||
@@ -1986,12 +2004,46 @@ namespace ZL {
|
||||
aa.origScaleX = node->textButton->animScaleX;
|
||||
aa.origScaleY = node->textButton->animScaleY;
|
||||
}
|
||||
else
|
||||
{
|
||||
//logger() << "[animation] startAnimationOnNode: " << node->name << " y=" << node->animOffsetY << std::endl;
|
||||
aa.origOffsetX = node->animOffsetX;
|
||||
aa.origOffsetY = node->animOffsetY;
|
||||
}
|
||||
auto cbIt = animCallbacks.find({ nodeName, animName });
|
||||
if (cbIt != animCallbacks.end()) aa.onComplete = cbIt->second;
|
||||
nodeActiveAnims[node].push_back(std::move(aa));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiManager::resetAnimationOnNode(const std::string& nodeName)
|
||||
{
|
||||
if (!root) return false;
|
||||
auto node = findNodeByName(root, nodeName);
|
||||
if (!node) return false;
|
||||
|
||||
if (node->button) {
|
||||
node->button->animOffsetX = 0;
|
||||
node->button->animOffsetY = 0;
|
||||
node->button->animScaleX = 1.0f;
|
||||
node->button->animScaleY = 1.0f;
|
||||
}
|
||||
else if (node->textButton) {
|
||||
node->textButton->animOffsetX = 0;
|
||||
node->textButton->animOffsetY = 0;
|
||||
node->textButton->animScaleX = 1.0f;
|
||||
node->textButton->animScaleY = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
//logger() << "[animation] startAnimationOnNode: " << node->name << " y=" << node->animOffsetY << std::endl;
|
||||
node->animOffsetX = 0;
|
||||
node->animOffsetY = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiManager::stopAnimationOnNode(const std::string& nodeName, const std::string& animName) {
|
||||
if (!root) return false;
|
||||
auto node = findNodeByName(root, nodeName);
|
||||
@@ -2042,6 +2094,13 @@ namespace ZL {
|
||||
aa.origScaleX = n->textButton->animScaleX;
|
||||
aa.origScaleY = n->textButton->animScaleY;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
//logger() << "[animation] startAnimation: " << n->name << " y=" << n->animOffsetY << std::endl;
|
||||
aa.origOffsetX = n->animOffsetX;
|
||||
aa.origOffsetY = n->animOffsetY;
|
||||
}
|
||||
auto cbIt = animCallbacks.find({ n->name, animName });
|
||||
if (cbIt != animCallbacks.end()) aa.onComplete = cbIt->second;
|
||||
nodeActiveAnims[n].push_back(std::move(aa));
|
||||
|
||||
+5
-2
@@ -187,7 +187,7 @@ namespace ZL {
|
||||
float animScaleY = 1.0f;
|
||||
|
||||
void buildMesh();
|
||||
void draw(Renderer& renderer) const;
|
||||
void draw(Renderer& renderer, float accumX = 0.0f, float accumY = 0.0f) const;
|
||||
};
|
||||
|
||||
struct UiTextView {
|
||||
@@ -206,7 +206,7 @@ namespace ZL {
|
||||
|
||||
std::unique_ptr<TextRenderer> textRenderer;
|
||||
|
||||
void draw(Renderer& renderer) const;
|
||||
void draw(Renderer& renderer, float accumX = 0.0f, float accumY = 0.0f) const;
|
||||
};
|
||||
|
||||
struct UiTextField {
|
||||
@@ -313,6 +313,8 @@ namespace ZL {
|
||||
|
||||
float scaleX = 1.0f;
|
||||
float scaleY = 1.0f;
|
||||
float animOffsetX = 0.0f;
|
||||
float animOffsetY = 0.0f;
|
||||
|
||||
bool visible = true;
|
||||
|
||||
@@ -475,6 +477,7 @@ namespace ZL {
|
||||
void update(float deltaMs);
|
||||
void startAnimation(const std::string& animName);
|
||||
bool startAnimationOnNode(const std::string& nodeName, const std::string& animName);
|
||||
bool resetAnimationOnNode(const std::string& nodeName);
|
||||
bool stopAnimationOnNode(const std::string& nodeName, const std::string& animName);
|
||||
bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function<void()> cb);
|
||||
void updateAllLayouts();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include "utils/Utils.h"
|
||||
|
||||
namespace ZL::Cutscene {
|
||||
|
||||
@@ -68,7 +69,7 @@ bool CutsceneRuntime::start(const std::string& cutsceneId) {
|
||||
onLineStarted(firstLine.luaCallback);
|
||||
}
|
||||
|
||||
std::cout << "[CUTSCENE] start id=" << cutsceneId
|
||||
logger() << "[CUTSCENE] start id=" << cutsceneId
|
||||
<< " lines=" << def->lines.size()
|
||||
<< " totalDuration=" << cutsceneTotalDurationMs
|
||||
<< std::endl;
|
||||
@@ -176,7 +177,7 @@ void CutsceneRuntime::skip() {
|
||||
}
|
||||
|
||||
void CutsceneRuntime::finish() {
|
||||
std::cout << "[CUTSCENE] finish id=" << activeCutsceneId << std::endl;
|
||||
logger() << "[CUTSCENE] finish id=" << activeCutsceneId << std::endl;
|
||||
const std::string finishedId = activeCutsceneId;
|
||||
stop();
|
||||
if (onFinished && !finishedId.empty()) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "../Character.h"
|
||||
#include <SDL.h>
|
||||
|
||||
|
||||
namespace ZL {
|
||||
|
||||
using json = nlohmann::json;
|
||||
@@ -73,7 +74,7 @@ namespace ZL {
|
||||
objects.push_back(std::move(data));
|
||||
}
|
||||
|
||||
std::cout << "Loaded " << objects.size() << " static objects from " << jsonPath << std::endl;
|
||||
logger() << "Loaded " << objects.size() << " static objects from " << jsonPath << std::endl;
|
||||
return objects;
|
||||
}
|
||||
|
||||
@@ -115,7 +116,7 @@ namespace ZL {
|
||||
objects.push_back(std::move(data));
|
||||
}
|
||||
|
||||
std::cout << "Loaded " << objects.size() << " interactive objects from " << jsonPath << std::endl;
|
||||
logger() << "Loaded " << objects.size() << " interactive objects from " << jsonPath << std::endl;
|
||||
return objects;
|
||||
}
|
||||
|
||||
@@ -199,7 +200,7 @@ namespace ZL {
|
||||
// Service the OS message queue between loads so the window doesn't
|
||||
// get flagged as "not responding" during long location loads.
|
||||
SDL_PumpEvents();
|
||||
std::cout << "Loading game object: " << data.name << std::endl;
|
||||
logger() << "Loading game object: " << data.name << std::endl;
|
||||
try {
|
||||
LoadedGameObject obj = buildLoadedObject(data, renderer, zipPath);
|
||||
|
||||
@@ -208,13 +209,13 @@ namespace ZL {
|
||||
|
||||
obj.mesh.RefreshVBO();
|
||||
gameObjects[data.name] = std::move(obj);
|
||||
std::cout << "Successfully loaded: " << data.name << std::endl;
|
||||
logger() << "Successfully loaded: " << data.name << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "GameObjectLoader: Failed to load '" << data.name << "': " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Total game objects loaded: " << gameObjects.size() << std::endl;
|
||||
logger() << "Total game objects loaded: " << gameObjects.size() << std::endl;
|
||||
return gameObjects;
|
||||
}
|
||||
|
||||
@@ -230,17 +231,17 @@ namespace ZL {
|
||||
|
||||
for (const auto& data : loadInteractiveFromJson(jsonPath, zipPath)) {
|
||||
SDL_PumpEvents();
|
||||
std::cout << "Loading interactive object: " << data.base.name << std::endl;
|
||||
logger() << "Loading interactive object: " << data.base.name << std::endl;
|
||||
try {
|
||||
InteractiveObject intObj = InteractiveObject::createFromState(
|
||||
data.toInteractiveObjectState(), renderer, zipPath);
|
||||
intObj.loadedObject.name = data.base.name;
|
||||
|
||||
if (!data.activateFunctionName.empty())
|
||||
std::cout << "Successfully loaded interactive: " << data.base.name
|
||||
logger() << "Successfully loaded interactive: " << data.base.name
|
||||
<< " (function: " << data.activateFunctionName << ")" << std::endl;
|
||||
else
|
||||
std::cout << "Successfully loaded interactive: " << data.base.name << std::endl;
|
||||
logger() << "Successfully loaded interactive: " << data.base.name << std::endl;
|
||||
|
||||
interactiveObjects.push_back(std::move(intObj));
|
||||
} catch (const std::exception& e) {
|
||||
@@ -248,7 +249,7 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Total interactive objects loaded: " << interactiveObjects.size() << std::endl;
|
||||
logger() << "Total interactive objects loaded: " << interactiveObjects.size() << std::endl;
|
||||
return interactiveObjects;
|
||||
}
|
||||
|
||||
@@ -301,7 +302,7 @@ namespace ZL {
|
||||
|
||||
npcs.push_back(std::move(data));
|
||||
}
|
||||
std::cout << "Successfully loaded " << npcs.size() << " NPCs from " << jsonPath << std::endl;
|
||||
logger() << "Successfully loaded " << npcs.size() << " NPCs from " << jsonPath << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error loading NPCs from JSON: " << e.what() << std::endl;
|
||||
}
|
||||
@@ -369,11 +370,11 @@ namespace ZL {
|
||||
|
||||
for (const auto& npcData : loadNpcsFromJson(jsonPath, zipPath)) {
|
||||
SDL_PumpEvents();
|
||||
std::cout << "Loading NPC: " << npcData.name << std::endl;
|
||||
logger() << "Loading NPC: " << npcData.name << std::endl;
|
||||
try {
|
||||
auto npc = Character::createFromState(npcData.toCharacterState(), renderer, zipPath.c_str());
|
||||
if (npc) {
|
||||
std::cout << "Successfully loaded NPC: " << npcData.name << " at ("
|
||||
logger() << "Successfully loaded NPC: " << npcData.name << " at ("
|
||||
<< npcData.positionX << ", " << npcData.positionY << ", " << npcData.positionZ << ")" << std::endl;
|
||||
npc->index = index;
|
||||
npcs.push_back(std::move(npc));
|
||||
@@ -384,7 +385,7 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Total NPCs loaded: " << npcs.size() << std::endl;
|
||||
logger() << "Total NPCs loaded: " << npcs.size() << std::endl;
|
||||
return npcs;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -3,19 +3,20 @@
|
||||
#include "external/nlohmann/json.hpp"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include "../utils/Utils.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
void Inventory::addItem(const std::string& itemId) {
|
||||
itemIds.push_back(itemId);
|
||||
std::cout << "Item added to inventory: " << itemId << std::endl;
|
||||
logger() << "Item added to inventory: " << itemId << std::endl;
|
||||
if (onItemAdded) onItemAdded(itemId);
|
||||
}
|
||||
|
||||
void Inventory::removeItem(const std::string& itemId) {
|
||||
auto it = std::find(itemIds.begin(), itemIds.end(), itemId);
|
||||
if (it != itemIds.end()) {
|
||||
std::cout << "Item removed from inventory: " << itemId << std::endl;
|
||||
logger() << "Item removed from inventory: " << itemId << std::endl;
|
||||
if (onItemRemoved) onItemRemoved(itemId);
|
||||
itemIds.erase(it);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ void ItemRegistry::loadFromJson(const std::string& jsonPath, const std::string&
|
||||
items_[id] = std::move(item);
|
||||
}
|
||||
|
||||
std::cout << "[ItemRegistry] Loaded " << items_.size() << " items from " << jsonPath << "\n";
|
||||
logger() << "[ItemRegistry] Loaded " << items_.size() << " items from " << jsonPath << "\n";
|
||||
}
|
||||
|
||||
const Item* ItemRegistry::findById(const std::string& id) const {
|
||||
|
||||
+293
-227
@@ -11,133 +11,127 @@
|
||||
#include <emscripten/html5.h>
|
||||
#endif
|
||||
|
||||
|
||||
// For Emscripten the game is heap-allocated so it can be destroyed and
|
||||
// re-created when the WebGL context is lost and restored (e.g. fullscreen).
|
||||
// For Android and Desktop a plain global value is used (no context loss).
|
||||
#ifdef EMSCRIPTEN
|
||||
ZL::Game* g_game = nullptr;
|
||||
//static SDL_Window* win_x = nullptr;
|
||||
static SDL_GLContext glContext_x; // Не указатель, а сам объект (который суть void*)
|
||||
|
||||
#else
|
||||
ZL::Game game;
|
||||
#ifdef STEAMSDK
|
||||
#include "steam_api.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#if defined(EMSCRIPTEN)
|
||||
static SDL_GLContext glContext_x;
|
||||
#endif
|
||||
|
||||
// Единый указатель на объект игры для всех платформ
|
||||
ZL::Game* g_game = nullptr;
|
||||
|
||||
|
||||
void MainLoop() {
|
||||
g_game->update();
|
||||
}
|
||||
#else
|
||||
void MainLoop() {
|
||||
game.update();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
|
||||
EM_BOOL onWebGLContextLost(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
|
||||
return EM_TRUE;
|
||||
return EM_TRUE;
|
||||
}
|
||||
|
||||
EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
|
||||
return EM_TRUE;
|
||||
return EM_TRUE;
|
||||
}
|
||||
|
||||
static void applyResize(int logicalW, int logicalH) {
|
||||
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
|
||||
double dpr = emscripten_get_device_pixel_ratio();
|
||||
//double dpr = 1; // low quality
|
||||
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
|
||||
double dpr = emscripten_get_device_pixel_ratio();
|
||||
//double dpr = 1; // low quality
|
||||
|
||||
// Вычисляем реальные физические пиксели
|
||||
int physicalW = static_cast<int>(logicalW * dpr);
|
||||
int physicalH = static_cast<int>(logicalH * dpr);
|
||||
// Вычисляем реальные физические пиксели
|
||||
int physicalW = static_cast<int>(logicalW * dpr);
|
||||
int physicalH = static_cast<int>(logicalH * dpr);
|
||||
|
||||
// Устанавливаем размер внутреннего буфера канваса
|
||||
emscripten_set_canvas_element_size("#canvas", physicalW, physicalH);
|
||||
// Устанавливаем размер внутреннего буфера канваса
|
||||
emscripten_set_canvas_element_size("#canvas", physicalW, physicalH);
|
||||
|
||||
// Сообщаем SDL о новом размере.
|
||||
// ВАЖНО: SDL2 в Emscripten ожидает здесь именно физические пиксели
|
||||
// для корректной работы последующих вызовов glViewport.
|
||||
if (ZL::Environment::window) {
|
||||
SDL_SetWindowSize(ZL::Environment::window, physicalW, physicalH);
|
||||
}
|
||||
// Сообщаем SDL о новом размере.
|
||||
// ВАЖНО: SDL2 в Emscripten ожидает здесь именно физические пиксели
|
||||
// для корректной работы последующих вызовов glViewport.
|
||||
if (ZL::Environment::window) {
|
||||
SDL_SetWindowSize(ZL::Environment::window, physicalW, physicalH);
|
||||
}
|
||||
|
||||
// Пушим событие, чтобы движок пересчитал матрицы проекции
|
||||
SDL_Event e = {};
|
||||
e.type = SDL_WINDOWEVENT;
|
||||
e.window.event = SDL_WINDOWEVENT_RESIZED;
|
||||
e.window.data1 = physicalW;
|
||||
e.window.data2 = physicalH;
|
||||
SDL_PushEvent(&e);
|
||||
// Пушим событие, чтобы движок пересчитал матрицы проекции
|
||||
SDL_Event e = {};
|
||||
e.type = SDL_WINDOWEVENT;
|
||||
e.window.event = SDL_WINDOWEVENT_RESIZED;
|
||||
e.window.data1 = physicalW;
|
||||
e.window.data2 = physicalH;
|
||||
SDL_PushEvent(&e);
|
||||
|
||||
std::cout << "Resized, new size: " << logicalW << "x" << logicalH
|
||||
<< " (physical: " << physicalW << "x" << physicalH
|
||||
logger() << "Resized, new size: " << logicalW << "x" << logicalH
|
||||
<< " (physical: " << physicalW << "x" << physicalH
|
||||
<< ", DPR: " << dpr << ")" << std::endl;
|
||||
}
|
||||
|
||||
EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*userData*/) {
|
||||
// Use the event's window dimensions — querying the canvas element would
|
||||
// return its old fixed size (e.g. 1280x720) before it has been resized.
|
||||
applyResize(e->windowInnerWidth, e->windowInnerHeight);
|
||||
return EM_FALSE;
|
||||
// Use the event's window dimensions — querying the canvas element would
|
||||
// return its old fixed size (e.g. 1280x720) before it has been resized.
|
||||
applyResize(e->windowInnerWidth, e->windowInnerHeight);
|
||||
return EM_FALSE;
|
||||
}
|
||||
|
||||
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
|
||||
ZL::Environment::isFullscreen = e->isFullscreen;
|
||||
ZL::Environment::isFullscreen = e->isFullscreen;
|
||||
|
||||
// Вместо window.innerWidth, попробуйте запросить размер целевого элемента
|
||||
// так как после перехода в FS именно он растягивается на весь экран.
|
||||
double clientW, clientH;
|
||||
emscripten_get_element_css_size("#canvas", &clientW, &clientH);
|
||||
applyResize(clientW, clientH);
|
||||
return EM_FALSE;
|
||||
// Вместо window.innerWidth, попробуйте запросить размер целевого элемента
|
||||
// так как после перехода в FS именно он растягивается на весь экран.
|
||||
double clientW, clientH;
|
||||
emscripten_get_element_css_size("#canvas", &clientW, &clientH);
|
||||
applyResize(clientW, clientH);
|
||||
return EM_FALSE;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); // Для WebGL 2.0
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); // Для WebGL 2.0
|
||||
|
||||
ZL::Environment::window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
||||
glContext_x = SDL_GL_CreateContext(ZL::Environment::window);
|
||||
ZL::Environment::window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
||||
glContext_x = SDL_GL_CreateContext(ZL::Environment::window);
|
||||
|
||||
SDL_GL_MakeCurrent(ZL::Environment::window, glContext_x);
|
||||
|
||||
SDL_GL_MakeCurrent(ZL::Environment::window, glContext_x);
|
||||
|
||||
g_game = new ZL::Game();
|
||||
g_game->setup();
|
||||
|
||||
|
||||
emscripten_set_webglcontextlost_callback("#canvas", nullptr, EM_TRUE, onWebGLContextLost);
|
||||
emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored);
|
||||
emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored);
|
||||
|
||||
// Keep Environment::width/height in sync when the canvas is resized.
|
||||
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
|
||||
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
|
||||
|
||||
// 2. ИНИЦИАЛИЗАЦИЯ РАЗМЕРОВ:
|
||||
// Получаем реальные размеры окна браузера на момент запуска
|
||||
int canvasW = EM_ASM_INT({ return window.innerWidth; });
|
||||
int canvasH = EM_ASM_INT({ return window.innerHeight; });
|
||||
// Keep Environment::width/height in sync when the canvas is resized.
|
||||
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
|
||||
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
|
||||
|
||||
// 2. ИНИЦИАЛИЗАЦИЯ РАЗМЕРОВ:
|
||||
// Получаем реальные размеры окна браузера на момент запуска
|
||||
int canvasW = EM_ASM_INT({ return window.innerWidth; });
|
||||
int canvasH = EM_ASM_INT({ return window.innerHeight; });
|
||||
|
||||
// Вызываем вашу функцию — она сама применит DPR, выставит физический размер
|
||||
// канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции.
|
||||
applyResize(canvasW, canvasH);
|
||||
|
||||
// Вызываем вашу функцию — она сама применит DPR, выставит физический размер
|
||||
// канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции.
|
||||
applyResize(canvasW, canvasH);
|
||||
|
||||
// Prevent mouse clicks from generating fake SDL_FINGERDOWN events (desktop browser)
|
||||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
||||
// Prevent touch events from generating fake SDL_MOUSEBUTTONDOWN events (mobile browser),
|
||||
// since we now handle SDL_FINGERDOWN directly for multi-touch support.
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
|
||||
|
||||
ZL::emscriptenInitFileSystem();
|
||||
ZL::emscriptenInitFileSystem();
|
||||
|
||||
emscripten_set_main_loop(MainLoop, 0, 1);
|
||||
|
||||
|
||||
return 0;
|
||||
emscripten_set_main_loop(MainLoop, 0, 1);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -147,82 +141,83 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
extern "C" int SDL_main(int argc, char* argv[]) {
|
||||
// Инициализация SDL перед получением информации о дисплее
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL init failed: %s", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL init failed: %s", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_DisplayMode displayMode;
|
||||
if (SDL_GetCurrentDisplayMode(0, &displayMode) != 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
ZL::Environment::width = displayMode.w;
|
||||
ZL::Environment::height = displayMode.h;
|
||||
SDL_DisplayMode displayMode;
|
||||
if (SDL_GetCurrentDisplayMode(0, &displayMode) != 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
ZL::Environment::width = displayMode.w;
|
||||
ZL::Environment::height = displayMode.h;
|
||||
|
||||
__android_log_print(ANDROID_LOG_INFO, "Game", "Display resolution: %dx%d",
|
||||
ZL::Environment::width, ZL::Environment::height);
|
||||
__android_log_print(ANDROID_LOG_INFO, "Game", "Display resolution: %dx%d",
|
||||
ZL::Environment::width, ZL::Environment::height);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
|
||||
ZL::Environment::window = SDL_CreateWindow(
|
||||
"Shadow Over Bishkek",
|
||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
ZL::Environment::width, ZL::Environment::height,
|
||||
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
|
||||
);
|
||||
ZL::Environment::window = SDL_CreateWindow(
|
||||
"Shadow Over Bishkek",
|
||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
ZL::Environment::width, ZL::Environment::height,
|
||||
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
|
||||
);
|
||||
|
||||
if (!ZL::Environment::window) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "Failed to create window: %s", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "Failed to create window: %s", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);
|
||||
if (!ctx) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_CreateContext failed: %s", SDL_GetError());
|
||||
SDL_DestroyWindow(ZL::Environment::window);
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
if (!ctx) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_CreateContext failed: %s", SDL_GetError());
|
||||
SDL_DestroyWindow(ZL::Environment::window);
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (SDL_GL_MakeCurrent(ZL::Environment::window, ctx) != 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_MakeCurrent failed: %s", SDL_GetError());
|
||||
SDL_GL_DeleteContext(ctx);
|
||||
SDL_DestroyWindow(ZL::Environment::window);
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
if (SDL_GL_MakeCurrent(ZL::Environment::window, ctx) != 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_MakeCurrent failed: %s", SDL_GetError());
|
||||
SDL_GL_DeleteContext(ctx);
|
||||
SDL_DestroyWindow(ZL::Environment::window);
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Настройка VSync
|
||||
if (SDL_GL_SetSwapInterval(1) < 0) {
|
||||
__android_log_print(ANDROID_LOG_WARN, "Game", "Unable to set VSync: %s", SDL_GetError());
|
||||
}
|
||||
// Настройка VSync
|
||||
if (SDL_GL_SetSwapInterval(1) < 0) {
|
||||
__android_log_print(ANDROID_LOG_WARN, "Game", "Unable to set VSync: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
// Настройка игры
|
||||
try {
|
||||
game.setup();
|
||||
} catch (const std::exception &e) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "Game setup failed: %s", e.what());
|
||||
return 1;
|
||||
}
|
||||
// Настройка игры
|
||||
try {
|
||||
game.setup();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Game", "Game setup failed: %s", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (!game.shouldExit()) {
|
||||
game.update();
|
||||
SDL_Delay(16);
|
||||
}
|
||||
while (!game.shouldExit()) {
|
||||
game.update();
|
||||
SDL_Delay(16);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
@@ -231,113 +226,184 @@ extern "C" int SDL_main(int argc, char* argv[]) {
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__linux__)
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try
|
||||
{
|
||||
// Read the persisted fullscreen preference (if any) before creating the
|
||||
// window, so it opens directly in the right mode instead of flashing
|
||||
// windowed -> fullscreen once settings are loaded later during setupPart2().
|
||||
void readSettings()
|
||||
{
|
||||
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2");
|
||||
//SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "1");
|
||||
const std::string settingsContent = ZL::readSavedTextFile("settings.json");
|
||||
if (!settingsContent.empty()) {
|
||||
try {
|
||||
const nlohmann::json settingsRoot = nlohmann::json::parse(settingsContent);
|
||||
if (settingsRoot.contains("fullscreen")) {
|
||||
ZL::Environment::isFullscreen = settingsRoot["fullscreen"].get<bool>();
|
||||
}
|
||||
|
||||
constexpr int CONST_WIDTH = ZL::Environment::CONST_DEFAULT_WIDTH;
|
||||
constexpr int CONST_HEIGHT = ZL::Environment::CONST_DEFAULT_HEIGHT;
|
||||
if (settingsRoot.contains("isHighDPIEnabled")) {
|
||||
ZL::Environment::isHighDPIEnabled = settingsRoot["isHighDPIEnabled"].get<bool>();
|
||||
}
|
||||
|
||||
ZL::Environment::width = CONST_WIDTH;
|
||||
ZL::Environment::height = CONST_HEIGHT;
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||
SDL_Log("SDL init failed: %s", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
if (settingsRoot.contains("enableLogging")) {
|
||||
ZL::Environment::enableLogging = settingsRoot["enableLogging"].get<bool>();
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "[settings] Failed to parse settings.json: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the persisted fullscreen preference (if any) before creating the
|
||||
// window, so it opens directly in the right mode instead of flashing
|
||||
// windowed -> fullscreen once settings are loaded later during setupPart2().
|
||||
{
|
||||
const std::string settingsContent = ZL::readSavedTextFile("settings.json");
|
||||
if (!settingsContent.empty()) {
|
||||
try {
|
||||
const nlohmann::json settingsRoot = nlohmann::json::parse(settingsContent);
|
||||
if (settingsRoot.contains("fullscreen")) {
|
||||
ZL::Environment::isFullscreen = settingsRoot["fullscreen"].get<bool>();
|
||||
}
|
||||
if (settingsRoot.contains("isHighDPIEnabled")) {
|
||||
ZL::Environment::isHighDPIEnabled = settingsRoot["isHighDPIEnabled"].get<bool>();
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "[settings] Failed to parse settings.json: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
|
||||
if (ZL::Environment::isFullscreen) {
|
||||
// windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
}
|
||||
|
||||
ZL::Environment::isFullscreen = false;
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
SDL_GLContext ctx = nullptr;
|
||||
try
|
||||
{
|
||||
readSettings();
|
||||
if (ZL::Environment::enableLogging)
|
||||
{
|
||||
ZL::initLogger();
|
||||
}
|
||||
|
||||
ZL::logger() << "Log started!" << std::endl;
|
||||
|
||||
#ifdef STEAMSDK
|
||||
// Передаем явный App ID демо-версии
|
||||
if (SteamAPI_RestartAppIfNecessary(4945840)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!SteamAPI_Init()) {
|
||||
std::cerr << "SteamAPI_Init() failed! Steam client might not be running.\n";
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2");
|
||||
|
||||
constexpr int CONST_WIDTH = ZL::Environment::CONST_DEFAULT_WIDTH;
|
||||
constexpr int CONST_HEIGHT = ZL::Environment::CONST_DEFAULT_HEIGHT;
|
||||
|
||||
ZL::Environment::width = CONST_WIDTH;
|
||||
ZL::Environment::height = CONST_HEIGHT;
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||
SDL_Log("SDL init failed: %s", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
|
||||
if (ZL::Environment::isFullscreen) {
|
||||
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
}
|
||||
|
||||
SDL_DisplayMode dm;
|
||||
if (SDL_GetCurrentDisplayMode(0, &dm) == 0) {
|
||||
ZL::logger() << "Desktop resolution: " << dm.w << "x" << dm.h << " @ " << dm.refresh_rate << "Hz\n";
|
||||
}
|
||||
else {
|
||||
SDL_Log("SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_DisplayMode dm;
|
||||
if (SDL_GetCurrentDisplayMode(0, &dm) == 0) {
|
||||
std::cout << "Desktop resolution: " << dm.w << "x" << dm.h << " @ " << dm.refresh_rate << "Hz\n";
|
||||
}
|
||||
else {
|
||||
SDL_Log("SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
int windowWidth = ZL::Environment::width;
|
||||
int windowHeight = ZL::Environment::height;
|
||||
|
||||
//In case if the game is launched on a small screen
|
||||
if (dm.w <= 1280 && dm.h <= 720) {
|
||||
windowWidth = round(dm.w / 1.5);
|
||||
windowWidth = round(dm.w / 1.5);
|
||||
windowHeight = round(dm.h / 1.5);
|
||||
}
|
||||
|
||||
ZL::Environment::window = SDL_CreateWindow(
|
||||
"Shadow Over Bishkek",
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
windowWidth, windowHeight,
|
||||
windowFlags
|
||||
);
|
||||
ZL::Environment::window = SDL_CreateWindow(
|
||||
"Shadow Over Bishkek Demo",
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
windowWidth, windowHeight,
|
||||
windowFlags
|
||||
);
|
||||
|
||||
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);
|
||||
SDL_GL_MakeCurrent(ZL::Environment::window, ctx);
|
||||
ctx = SDL_GL_CreateContext(ZL::Environment::window);
|
||||
SDL_GL_MakeCurrent(ZL::Environment::window, ctx);
|
||||
|
||||
// On HiDPI displays the drawable (physical) size is larger than the
|
||||
// logical window size. Use the drawable size so the GL framebuffer
|
||||
// covers every physical pixel and the image stays sharp.
|
||||
if (ZL::Environment::isHighDPIEnabled)
|
||||
{
|
||||
int drawW, drawH;
|
||||
SDL_GL_GetDrawableSize(ZL::Environment::window, &drawW, &drawH);
|
||||
ZL::Environment::width = drawW;
|
||||
ZL::Environment::height = drawH;
|
||||
std::cout << "HiDPI enabled, drawable size: " << drawW << "x" << drawH << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ZL::Environment::isHighDPIEnabled) {
|
||||
int drawW, drawH;
|
||||
SDL_GL_GetDrawableSize(ZL::Environment::window, &drawW, &drawH);
|
||||
ZL::Environment::width = drawW;
|
||||
ZL::Environment::height = drawH;
|
||||
ZL::logger() << "HiDPI enabled, drawable size: " << drawW << "x" << drawH << std::endl;
|
||||
}
|
||||
else {
|
||||
int winW, winH;
|
||||
SDL_GetWindowSize(ZL::Environment::window, &winW, &winH);
|
||||
ZL::Environment::width = winW;
|
||||
ZL::Environment::height = winH;
|
||||
std::cout << "HiDPI disabled, window size: " << winW << "x" << winH << std::endl;
|
||||
}
|
||||
SDL_GetWindowSize(ZL::Environment::window, &winW, &winH);
|
||||
ZL::Environment::width = winW;
|
||||
ZL::Environment::height = winH;
|
||||
ZL::logger() << "HiDPI disabled, window size: " << winW << "x" << winH << std::endl;
|
||||
}
|
||||
|
||||
game.setup();
|
||||
// Динамическое создание объекта игры
|
||||
g_game = new ZL::Game();
|
||||
g_game->setup();
|
||||
|
||||
while (!game.shouldExit()) {
|
||||
game.update();
|
||||
SDL_Delay(2);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
while (!g_game->shouldExit()) {
|
||||
g_game->update();
|
||||
SDL_Delay(2);
|
||||
|
||||
return 0;
|
||||
#ifdef STEAMSDK
|
||||
SteamAPI_RunCallbacks();
|
||||
#endif
|
||||
}
|
||||
|
||||
//ZL::logger() << "Quitting step 1: " << std::endl;
|
||||
// Явное уничтожение объекта игры ДО завершения работы Steamworks
|
||||
delete g_game;
|
||||
//ZL::logger() << "Quitting step 2: " << std::endl;
|
||||
g_game = nullptr;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception caught: " << e.what() << std::endl;
|
||||
if (g_game) {
|
||||
delete g_game;
|
||||
g_game = nullptr;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << "Unknown exception happened: " << std::endl;
|
||||
if (g_game) {
|
||||
delete g_game;
|
||||
g_game = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//ZL::logger() << "Quitting step 3: " << std::endl;
|
||||
|
||||
#ifdef STEAMSDK
|
||||
// Деинициализация Steamworks происходит только тогда, когда все объекты игры гарантированно уничтожены
|
||||
SteamAPI_Shutdown();
|
||||
#endif
|
||||
//ZL::logger() << "Quitting step 4: " << std::endl;
|
||||
|
||||
// Очистка ресурсов SDL
|
||||
if (ctx) {
|
||||
//ZL::logger() << "Quitting step 5: " << std::endl;
|
||||
SDL_GL_DeleteContext(ctx);
|
||||
}
|
||||
//ZL::logger() << "Quitting step 6: " << std::endl;
|
||||
if (ZL::Environment::window) {
|
||||
//ZL::logger() << "Quitting step 7: " << std::endl;
|
||||
SDL_DestroyWindow(ZL::Environment::window);
|
||||
}
|
||||
//ZL::logger() << "Quitting step 8: " << std::endl;
|
||||
#ifndef EMSCRIPTEN
|
||||
// In Emscripten, SDL must stay alive across context loss/restore cycles
|
||||
// so the window remains valid when the game object is re-created.
|
||||
SDL_Quit();
|
||||
#endif
|
||||
//ZL::logger() << "Quitting step 9: " << std::endl;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -100,7 +100,7 @@ void PathFinder::build(const std::string& configPath,
|
||||
if (isTxt) {
|
||||
if (loadGrid(configPath, zipPath)) {
|
||||
ready = true;
|
||||
std::cout << "[nav] Loaded precomputed grid: " << gridWidth << "x" << gridDepth
|
||||
logger() << "[nav] Loaded precomputed grid: " << gridWidth << "x" << gridDepth
|
||||
<< ", cell=" << cellSize << "\n";
|
||||
} else {
|
||||
std::cerr << "[nav] Failed to load precomputed grid: " << configPath << "\n";
|
||||
@@ -120,7 +120,7 @@ void PathFinder::build(const std::string& configPath,
|
||||
rebuildWalkableGrid();
|
||||
ready = true;
|
||||
|
||||
std::cout << "[nav] Polygon grid built: " << gridWidth << "x" << gridDepth
|
||||
logger() << "[nav] Polygon grid built: " << gridWidth << "x" << gridDepth
|
||||
<< ", cell=" << cellSize << ", areas=" << areas.size() << "\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -329,7 +329,7 @@ namespace ZL {
|
||||
size_t error = glGetError();
|
||||
if (error != GL_NO_ERROR)
|
||||
{
|
||||
std::cout << "OpenGL error: " << error << std::endl;
|
||||
logger() << "OpenGL error: " << error << std::endl;
|
||||
throw std::runtime_error("Gl error");
|
||||
}
|
||||
}
|
||||
@@ -338,7 +338,7 @@ namespace ZL {
|
||||
size_t error = glGetError();
|
||||
if (error != GL_NO_ERROR)
|
||||
{
|
||||
std::cout << "OpenGL error: " << error << " happened in file: " << file << " at line: " << line << std::endl;
|
||||
logger() << "OpenGL error: " << error << " happened in file: " << file << " at line: " << line << std::endl;
|
||||
throw std::runtime_error("Gl error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ZL {
|
||||
|
||||
ShaderResource::ShaderResource(const std::string &vertexCode, const std::string &fragmentCode) {
|
||||
|
||||
std::cout << "Started creating shader resource" << std::endl;
|
||||
logger() << "Started creating shader resource" << std::endl;
|
||||
|
||||
const int CONST_INFOLOG_LENGTH = 256;
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace ZL {
|
||||
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &fragmentShaderCompiled);
|
||||
glGetShaderInfoLog(fragmentShader, CONST_INFOLOG_LENGTH, &infoLogLength2, infoLog2);
|
||||
|
||||
std::cout << "Creating shader resource step 1" << std::endl;
|
||||
//logger() << "Creating shader resource step 1" << std::endl;
|
||||
|
||||
|
||||
if (!vertexShaderCompiled) {
|
||||
@@ -53,11 +53,11 @@ namespace ZL {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "ShaderManager",
|
||||
"Failed to compile vertex shader: %s", infoLog);
|
||||
#endif
|
||||
std::cout << "Vertex shader compilation failed: " << infoLog << std::endl;
|
||||
logger() << "Vertex shader compilation failed: " << infoLog << std::endl;
|
||||
throw std::runtime_error("Failed to compile vertex shader code!");
|
||||
}
|
||||
|
||||
std::cout << "Creating shader resource step 2" << std::endl;
|
||||
//logger() << "Creating shader resource step 2" << std::endl;
|
||||
|
||||
|
||||
if (!fragmentShaderCompiled) {
|
||||
@@ -65,12 +65,12 @@ namespace ZL {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "ShaderManager",
|
||||
"Failed to compile fragment shader: %s", infoLog);
|
||||
#endif
|
||||
std::cout << "Fragment shader compilation failed: " << infoLog << std::endl;
|
||||
logger() << "Fragment shader compilation failed: " << infoLog << std::endl;
|
||||
|
||||
throw std::runtime_error("Failed to compile fragment shader code!");
|
||||
}
|
||||
|
||||
std::cout << "Creating shader resource step 4" << std::endl;
|
||||
//logger() << "Creating shader resource step 4" << std::endl;
|
||||
|
||||
|
||||
shaderProgram = glCreateProgram();
|
||||
@@ -86,7 +86,7 @@ namespace ZL {
|
||||
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &programLinked);
|
||||
glGetProgramInfoLog(shaderProgram, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
|
||||
|
||||
std::cout << "Creating shader resource step 4" << std::endl;
|
||||
//logger() << "Creating shader resource step 4" << std::endl;
|
||||
|
||||
if (!programLinked) {
|
||||
shaderProgram = 0;
|
||||
@@ -94,11 +94,11 @@ namespace ZL {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "ShaderManager",
|
||||
"Failed to link shader program: %s", infoLog);
|
||||
#endif
|
||||
std::cout << "Failed to link shader program: " << infoLog << std::endl;
|
||||
logger() << "Failed to link shader program: " << infoLog << std::endl;
|
||||
throw std::runtime_error("Failed to link shader program!");
|
||||
}
|
||||
|
||||
std::cout << "Creating shader resource step 5" << std::endl;
|
||||
//logger() << "Creating shader resource step 5" << std::endl;
|
||||
|
||||
|
||||
//================= Parsing all uniforms ================
|
||||
@@ -121,7 +121,7 @@ namespace ZL {
|
||||
uniformList[uniformName] = glGetUniformLocation(shaderProgram, uniformName);
|
||||
}
|
||||
|
||||
std::cout << "Creating shader resource step 6" << std::endl;
|
||||
//logger() << "Creating shader resource step 6" << std::endl;
|
||||
|
||||
|
||||
//================= Parsing all attributes ================
|
||||
@@ -138,7 +138,7 @@ namespace ZL {
|
||||
attribList[attribName] = glGetAttribLocation(shaderProgram, attribName);
|
||||
}
|
||||
|
||||
std::cout << "Creating shader resource step 7" << std::endl;
|
||||
//logger() << "Creating shader resource step 7" << std::endl;
|
||||
|
||||
|
||||
#ifdef __ANDROID__
|
||||
@@ -173,7 +173,7 @@ namespace ZL {
|
||||
"Fragment shader: %s", fragmentShaderFileName.c_str());
|
||||
#endif
|
||||
|
||||
std::cout <<"Loading shader: " << shaderName << " " << vertexShaderFileName << " " << fragmentShaderFileName <<std::endl;
|
||||
logger() <<"Loading shader: " << shaderName << " " << vertexShaderFileName << " " << fragmentShaderFileName <<std::endl;
|
||||
|
||||
std::string vertexShader;
|
||||
std::string fragmentShader;
|
||||
@@ -204,7 +204,7 @@ namespace ZL {
|
||||
}
|
||||
|
||||
if (shaderResourceMap.find(shaderName) == shaderResourceMap.end()) {
|
||||
std::cout << "Shader does not exist: " << shaderName << std::endl;
|
||||
logger() << "Shader does not exist: " << shaderName << std::endl;
|
||||
throw std::runtime_error("Shader does not exist!");
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << "TextureManager: resized texture from "
|
||||
logger() << "TextureManager: resized texture from "
|
||||
<< src.width << "x" << src.height
|
||||
<< " to " << newW << "x" << newH << " (POT)" << std::endl;
|
||||
return dst;
|
||||
@@ -353,13 +353,13 @@ namespace ZL
|
||||
}
|
||||
|
||||
void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg) {
|
||||
std::cout << "PNG Warning: " << warning_msg << std::endl;
|
||||
logger() << "PNG Warning: " << warning_msg << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void user_error_fn(png_structp png_ptr, png_const_charp error_msg) {
|
||||
|
||||
std::cout << "PNG Error: " << error_msg << std::endl;
|
||||
logger() << "PNG Error: " << error_msg << std::endl;
|
||||
longjmp(png_jmpbuf(png_ptr), 1);
|
||||
}
|
||||
|
||||
@@ -474,7 +474,7 @@ namespace ZL
|
||||
fileArr = !ZIPFileName.empty() ? readFileFromZIP(fullFileName, ZIPFileName) : readFile(fullFileName);
|
||||
|
||||
if (fileArr.empty()) {
|
||||
std::cout << "Could not read file data into memory: " << fullFileName << " zip file name: " << ZIPFileName << std::endl;
|
||||
logger() << "Could not read file data into memory: " << fullFileName << " zip file name: " << ZIPFileName << std::endl;
|
||||
throw std::runtime_error("Could not read file data into memory");
|
||||
}
|
||||
|
||||
@@ -539,7 +539,7 @@ namespace ZL
|
||||
{
|
||||
for (auto it = textureMap.begin(); it != textureMap.end(); ) {
|
||||
if (it->first.substr(0, prefix.size()) == prefix) {
|
||||
std::cout << "Unloading texture with prefix: " << it->first << std::endl;
|
||||
logger() << "Unloading texture with prefix: " << it->first << std::endl;
|
||||
it = textureMap.erase(it);
|
||||
}
|
||||
else {
|
||||
|
||||
+91
-4
@@ -11,6 +11,7 @@
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
#include <SDL.h>
|
||||
#include "../Environment.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
@@ -108,7 +109,7 @@ namespace ZL
|
||||
int zipErr = 0;
|
||||
zip_t* archive = zip_open(zipfilename.c_str(), ZIP_RDONLY, &zipErr);
|
||||
if (!archive) {
|
||||
std::cout << "Failed to open ZIP: " << zipfilename << " (error code " << zipErr << ")" << std::endl;
|
||||
logger() << "Failed to open ZIP: " << zipfilename << " (error code " << zipErr << ")" << std::endl;
|
||||
throw std::runtime_error("Failed to open ZIP: " + zipfilename);
|
||||
}
|
||||
|
||||
@@ -119,14 +120,14 @@ namespace ZL
|
||||
zip_stat_init(&fileStat);
|
||||
if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0 || !(fileStat.valid & ZIP_STAT_SIZE)) {
|
||||
zip_close(archive);
|
||||
std::cout << "Failed to stat file in ZIP: " << cleanFilename << " in " << zipfilename << std::endl;
|
||||
logger() << "Failed to stat file in ZIP: " << cleanFilename << " in " << zipfilename << std::endl;
|
||||
throw std::runtime_error("Can't stat file in ZIP: " + cleanFilename);
|
||||
}
|
||||
|
||||
zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
|
||||
if (!zipFile) {
|
||||
zip_close(archive);
|
||||
std::cout << "Failed to open file in ZIP: " << cleanFilename << std::endl;
|
||||
logger() << "Failed to open file in ZIP: " << cleanFilename << std::endl;
|
||||
throw std::runtime_error("Can't open file in ZIP: " + cleanFilename);
|
||||
}
|
||||
|
||||
@@ -138,7 +139,7 @@ namespace ZL
|
||||
zip_close(archive);
|
||||
|
||||
if (bytesRead < 0 || static_cast<zip_uint64_t>(bytesRead) != fileStat.size) {
|
||||
std::cout << "Failed to read file from ZIP: " << cleanFilename << " (bytes read: " << bytesRead << ", expected: " << fileStat.size << ")" << std::endl;
|
||||
logger() << "Failed to read file from ZIP: " << cleanFilename << " (bytes read: " << bytesRead << ", expected: " << fileStat.size << ")" << std::endl;
|
||||
throw std::runtime_error("Error reading data from ZIP: " + cleanFilename);
|
||||
}
|
||||
|
||||
@@ -348,4 +349,90 @@ namespace ZL
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// --- Инфраструктура для подавления вывода (когда лог выключен) ---
|
||||
class NullBuffer : public std::streambuf {
|
||||
protected:
|
||||
int overflow(int c) override { return c; }
|
||||
};
|
||||
|
||||
class NullStream : public std::ostream {
|
||||
NullBuffer buf;
|
||||
public:
|
||||
NullStream() : std::ostream(&buf) {}
|
||||
};
|
||||
|
||||
// --- Инфраструктура для дублирования вывода (Tee-поток) ---
|
||||
class TeeBuffer : public std::streambuf {
|
||||
std::streambuf* out1; // Буфер cout
|
||||
std::streambuf* out2; // Буфер файла
|
||||
protected:
|
||||
int overflow(int c) override {
|
||||
if (c != EOF) {
|
||||
out1->sputc(c); // Игнорируем потенциальный EOF от отключенной консоли
|
||||
int r2 = out2->sputc(c);
|
||||
if (r2 == EOF) return EOF; // Возвращаем ошибку только если сломалась запись в файл
|
||||
}
|
||||
return c;
|
||||
}
|
||||
int sync() override {
|
||||
out1->pubsync(); // Игнорируем ошибку сброса консоли
|
||||
int r2 = out2->pubsync();
|
||||
return (r2 == -1) ? -1 : 0; // Сообщаем об ошибке потоку только если не удалось сбросить в файл
|
||||
}
|
||||
public:
|
||||
TeeBuffer(std::streambuf* sb1, std::streambuf* sb2) : out1(sb1), out2(sb2) {}
|
||||
};
|
||||
|
||||
// --- Глобальные переменные логгера ---
|
||||
namespace {
|
||||
std::ofstream g_logFile;
|
||||
std::unique_ptr<TeeBuffer> g_teeBuffer;
|
||||
std::unique_ptr<std::ostream> g_teeStream;
|
||||
NullStream g_nullStream;
|
||||
}
|
||||
|
||||
// --- Инициализация логгера ---
|
||||
void initLogger() {
|
||||
namespace fs = std::filesystem;
|
||||
try {
|
||||
fs::path dir = getSaveDirectory();
|
||||
|
||||
// Если директория не существует, ofstream не сможет создать файл,
|
||||
// поэтому форсируем создание всей иерархии папок.
|
||||
if (!fs::exists(dir)) {
|
||||
fs::create_directories(dir);
|
||||
}
|
||||
|
||||
fs::path logPath = dir / "log.txt";
|
||||
|
||||
// std::ios::trunc очищает файл при каждом новом запуске
|
||||
g_logFile.open(logPath, std::ios::out | std::ios::trunc);
|
||||
|
||||
if (g_logFile.is_open()) {
|
||||
g_teeBuffer = std::make_unique<TeeBuffer>(std::cout.rdbuf(), g_logFile.rdbuf());
|
||||
g_teeStream = std::make_unique<std::ostream>(g_teeBuffer.get());
|
||||
}
|
||||
else {
|
||||
std::cerr << "[logger] Could not open " << logPath << " for writing" << std::endl;
|
||||
}
|
||||
}
|
||||
catch (const fs::filesystem_error& e) {
|
||||
std::cerr << "[logger] Filesystem error during init: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Сама функция логгера ---
|
||||
std::ostream& logger() {
|
||||
if (Environment::enableLogging) {
|
||||
if (g_teeStream) {
|
||||
return *g_teeStream;
|
||||
}
|
||||
// Фолбэк на стандартный вывод, если файл по какой-то причине не открылся
|
||||
return std::cout;
|
||||
}
|
||||
|
||||
// Если логирование отключено, возвращаем заглушку.
|
||||
return g_nullStream;
|
||||
}
|
||||
};
|
||||
@@ -35,4 +35,7 @@ namespace ZL
|
||||
|
||||
bool saveJsonToFile(const nlohmann::json& root, const std::string& filename);
|
||||
|
||||
void initLogger();
|
||||
|
||||
std::ostream& logger();
|
||||
}
|
||||
Reference in new issue
Block a user