Compare commits

..

No commits in common. "main" and "witcher001-linux" have entirely different histories.

427 changed files with 3977 additions and 8567 deletions

3
.gitattributes vendored
View File

@ -5,6 +5,3 @@
*.wav filter=lfs diff=lfs merge=lfs -text *.wav filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text *.ogg filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text *.mp3 filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.dll filter=lfs diff=lfs merge=lfs -text
*.so filter=lfs diff=lfs merge=lfs -text

5
.gitignore vendored
View File

@ -409,8 +409,3 @@ web_resources/
pc_resources/ pc_resources/
resources_hd/ resources_hd/
web_resources_x2/ web_resources_x2/
android_resources/
.artifacts/
*.zip

View File

@ -185,44 +185,3 @@ make -j$(nproc) -C build #Компилируем
Для постройки без звука Для постройки без звука
rm -rf build #Очищаем build папку rm -rf build #Очищаем build папку
cmake -B build -DAUDIO=1 #Пересоздаём конфигурацию CMake cmake -B build -DAUDIO=1 #Пересоздаём конфигурацию CMake
# Cmake Build NSIS and Portable for Windows:
```
cmake --build . --config Release
cpack -C Release
```
Если есть такая ошибка:
CPack Error: Cannot find NSIS compiler makensis: likely it is not installed, or not in your PATH
CPack Error: Could not read NSIS registry value. This is usually caused by NSIS not being installed. Please install NSIS from http://nsis.sourceforge.net
CPack Error: Cannot initialize the generator NSIS
То нужно установить nsis отсюда: https://nsis.sourceforge.io/Download
# Steam windows
```
cmake -DSTEAMSDK=ON ..
cmake --build . --config Release
```
# Steam Linux
```
docker run -it --rm -v "${PWD}:/work2" -w /work2 registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest bash
apt-get update
apt-get install libboost-dev libeigen3-dev liblua5.4-dev libzip-dev libglu1-mesa-dev
cmake -DSTEAMSDK=ON -DCMAKE_BUILD_TYPE=Release ..
cmake --build . -j 4
```

View File

@ -1,128 +0,0 @@
#!/usr/bin/env python3
"""
optimize_web_resources.py - Resize PNG images for the web build.
Copies <src> to <dst>/resources/, then downscales all PNG files:
- resources/w/ui/img/** -> 1/8 original size
- all other PNGs -> 1/4 original size
Files listed in EXCEPTIONS are copied unchanged.
Usage (manual):
python optimize_web_resources.py [--src resources] [--dst web_resources]
Usage (CMake):
${Python3_EXECUTABLE} optimize_web_resources.py --src <abs_src> --dst <abs_dst>
"""
import argparse
import os
import shutil
try:
from PIL import Image
except ImportError:
raise SystemExit("Pillow is required: pip install Pillow")
# Paths relative to the resources directory — these files are never resized.
EXCEPTIONS = {
"loading.png",
"loading_land003_en.png",
"loading_land003_ru.png",
"black.png",
"transparent.png",
"w/blue.png",
"w/red.png",
"w/spark.png",
"w/star.png",
"w/star_red.png",
"w/white.png",
"w/ui/img/main/aboutPage003android.png",
"w/ui_en/img/main/aboutPage003android.png",
"w/exterior/texAtlas002.png",
}
# Path prefixes (relative to resources root, forward-slash) that get 8x reduction.
HIGH_REDUCTION_PREFIXES = (
"w/ui/img",
)
LOW_REDUCTION_POSTFIXES = (
"texAtlas002.png",
"Building_work014.png",
"Building_Floors_tex004.png",
"Building_Walls_tex006.png",
"asphalt003small.png",
"Ext_Building004.png",
"Plane3_tex002.png",
"darklands_building002.png",
"Staircase001.png",
"Staircase_Obj002.png"
)
def _resize_factor(rel: str) -> int:
norm = rel.replace("\\", "/")
for prefix in HIGH_REDUCTION_PREFIXES:
if norm.startswith(prefix):
return 8
for prefix in LOW_REDUCTION_POSTFIXES:
if norm.endswith(prefix):
return 2
return 4
def _next_pot(n: int) -> int:
if n < 1:
return 1
p = 1
while p < n:
p <<= 1
return p
def optimize(src_dir: str, dst_parent: str) -> None:
src_dir = os.path.abspath(src_dir)
dst_resources = os.path.join(os.path.abspath(dst_parent), "resources")
print(f"Copying {src_dir} -> {dst_resources}")
if os.path.exists(dst_resources):
shutil.rmtree(dst_resources)
shutil.copytree(src_dir, dst_resources)
total = 0
for root, _dirs, files in os.walk(dst_resources):
for filename in files:
if not filename.lower().endswith(".png"):
continue
full_path = os.path.join(root, filename)
rel = os.path.relpath(full_path, dst_resources).replace("\\", "/")
if rel in EXCEPTIONS:
print(f" skip (exception): {rel}")
continue
factor = _resize_factor(rel)
with Image.open(full_path) as img:
orig_w, orig_h = img.size
new_w = _next_pot(max(1, orig_w // factor))
new_h = _next_pot(max(1, orig_h // factor))
resized = img.resize((new_w, new_h), Image.LANCZOS)
resized.save(full_path)
print(f" {rel}: {orig_w}x{orig_h} -> {new_w}x{new_h} (/{factor} + POT)")
total += 1
print(f"Done. Resized {total} PNG file(s).")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Optimize PNG images for web build")
parser.add_argument("--src", default="resources",
help="Source resources directory (default: resources)")
parser.add_argument("--dst", default="android_resources",
help="Destination parent directory; will contain a 'resources' subdir "
"(default: android_resources)")
args = parser.parse_args()
optimize(args.src, args.dst)

View File

@ -27,18 +27,7 @@ except ImportError:
# Paths relative to the resources directory — these files are never resized. # Paths relative to the resources directory — these files are never resized.
EXCEPTIONS = { EXCEPTIONS = {
"loading.png", "loading.png",
"loading_land003_en.png",
"loading_land003_ru.png",
"black.png", "black.png",
"transparent.png",
"w/blue.png",
"w/red.png",
"w/spark.png",
"w/star.png",
"w/star_red.png",
"w/white.png",
"w/ui/img/main/aboutPage003android.png",
"w/ui_en/img/main/aboutPage003android.png"
} }
# Path prefixes (relative to resources root, forward-slash) that get 8x reduction. # Path prefixes (relative to resources root, forward-slash) that get 8x reduction.
@ -46,28 +35,13 @@ HIGH_REDUCTION_PREFIXES = (
"w/ui/img", "w/ui/img",
) )
LOW_REDUCTION_POSTFIXES = (
"texAtlas002.png",
"Building_work014.png",
"Building_Floors_tex004.png",
"Building_Walls_tex006.png",
"asphalt003small.png",
"Ext_Building004.png",
"Plane3_tex002.png",
"darklands_building002.png",
"Staircase001.png",
"Staircase_Obj002.png"
)
def _resize_factor(rel: str) -> int: def _resize_factor(rel: str) -> int:
norm = rel.replace("\\", "/") norm = rel.replace("\\", "/")
for prefix in HIGH_REDUCTION_PREFIXES: for prefix in HIGH_REDUCTION_PREFIXES:
if norm.startswith(prefix): if norm.startswith(prefix):
return 8 return 8
for prefix in LOW_REDUCTION_POSTFIXES: return 4
if norm.endswith(prefix):
return 2
return 8
def _next_pot(n: int) -> int: def _next_pot(n: int) -> int:

View File

@ -61,5 +61,5 @@ app/jni/libpng
app/jni/SDL app/jni/SDL
app/jni/zlib app/jni/zlib
app/src/main/assets app/src/main/assets/resources

View File

@ -1 +1 @@
### Android version ### Перед запуском в папке ```app/jni/```(рядом с src) нужно создать три папки с исходниками библиотек ```libpng```, ```SDL```, ```zlib```

View File

@ -1,4 +1,4 @@
def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY') def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY');
def buildAsApplication = !buildAsLibrary def buildAsApplication = !buildAsLibrary
if (buildAsApplication) { if (buildAsApplication) {
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
@ -9,33 +9,30 @@ else {
android { android {
if (buildAsApplication) { if (buildAsApplication) {
namespace "fishrungames.shadowoverbishkek" namespace "org.libsdl.app"
} }
compileSdkVersion 37 compileSdkVersion 34
defaultConfig { defaultConfig {
minSdkVersion 21 minSdkVersion 19
targetSdkVersion 37 targetSdkVersion 34
versionCode 4 versionCode 1
versionName "1.0.7" versionName "1.0"
externalNativeBuild { externalNativeBuild {
/*ndkBuild { /*ndkBuild {
arguments "APP_PLATFORM=android-19" arguments "APP_PLATFORM=android-19"
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}*/ }*/
cmake { cmake {
arguments /*"-DANDROID_APP_PLATFORM=android-19",*/ "-DANDROID_STL=c++_static" arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static"
cppFlags "-std=c++17 -frtti -fexceptions" cppFlags "-std=c++11 -frtti -fexceptions"
abiFilters 'arm64-v8a', 'x86_64' abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
} }
} }
} }
buildTypes { buildTypes {
release { release {
// Enables code optimizations. minifyEnabled false
minifyEnabled = true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// Enables resource shrinking.
shrinkResources = true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
applicationVariants.all { variant -> applicationVariants.all { variant ->
@ -65,7 +62,7 @@ android {
libraryVariants.all { variant -> libraryVariants.all { variant ->
variant.outputs.all { output -> variant.outputs.all { output ->
if (output.outputFileName != null && output.outputFileName.endsWith(".aar")) { if (output.outputFileName != null && output.outputFileName.endsWith(".aar")) {
output.outputFileName = "fishrungames.shadowoverbishkek.aar" output.outputFileName = "org.libsdl.app.aar"
} }
} }
} }
@ -73,6 +70,5 @@ android {
} }
dependencies { dependencies {
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
} }

View File

@ -21,7 +21,7 @@ set(TP_ROOT "${THIRDPARTY_DIR}")
# ============================================================================== # ==============================================================================
# --- ZLIB --- # --- ZLIB ---
#add_subdirectory("${TP_ROOT}/zlib-1.3.2" zlib-build) add_subdirectory("${TP_ROOT}/zlib-1.3.1" zlib-build)
# --- LIBPNG --- # --- LIBPNG ---
set(PNG_STATIC ON CACHE BOOL "Build static library" FORCE) set(PNG_STATIC ON CACHE BOOL "Build static library" FORCE)
@ -39,10 +39,6 @@ add_subdirectory("${TP_ROOT}/libpng-1.6.51" libpng-build)
# --- SDL2 --- # --- SDL2 ---
# Android-версия SDL требует специфичных настроек, но add_subdirectory обычно подхватывает их сама # Android-версия SDL требует специфичных настроек, но add_subdirectory обычно подхватывает их сама
add_subdirectory("${TP_ROOT}/SDL-release-2.32.10" sdl-build) add_subdirectory("${TP_ROOT}/SDL-release-2.32.10" sdl-build)
add_subdirectory("${TP_ROOT}/SDL_mixer-release-2.8.0" sdl-mixer-build)
add_subdirectory("${TP_ROOT}/SDL_ttf-release-2.24.0" sdl-ttf-build)
# --- LIBZIP --- # --- LIBZIP ---
# Отключаем поиск системных крипто-библиотек, так как в NDK их может не быть в стандартных путях # Отключаем поиск системных крипто-библиотек, так как в NDK их может не быть в стандартных путях

View File

@ -33,145 +33,62 @@ endforeach()
# Создаем кастомную цель, которая будет запускать процесс копирования # Создаем кастомную цель, которая будет запускать процесс копирования
add_custom_target(sync_resources ALL DEPENDS ${RES_OUTPUTS}) add_custom_target(sync_resources ALL DEPENDS ${RES_OUTPUTS})
set(LUA_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/lua-5.4.8")
file(GLOB LUA_SOURCES "${LUA_SRC_DIR}/*.c")
list(REMOVE_ITEM LUA_SOURCES
"${LUA_SRC_DIR}/lua.c"
"${LUA_SRC_DIR}/luac.c"
"${LUA_SRC_DIR}/onelua.c"
)
add_library(lua_static STATIC ${LUA_SOURCES})
target_include_directories(lua_static PUBLIC "${LUA_SRC_DIR}")
add_library(main SHARED add_library(main SHARED
SDL_android_main.c SDL_android_main.c
../../../../src/main.cpp ../../../../src/BoneAnimatedModel.cpp
../../../../src/Game.cpp
../../../../src/Game.h
../../../../src/Character.cpp
../../../../src/Character.h
../../../../src/CharacterState.cpp
../../../../src/CharacterState.h
../../../../src/Environment.cpp ../../../../src/Environment.cpp
../../../../src/Environment.h ../../../../src/Game.cpp
../../../../src/Localization.cpp ../../../../src/main.cpp
../../../../src/Localization.h ../../../../src/Projectile.cpp
../../../../src/render/Renderer.cpp
../../../../src/render/Renderer.h
../../../../src/render/ShaderManager.cpp
../../../../src/render/ShaderManager.h
../../../../src/render/TextureManager.cpp
../../../../src/render/TextureManager.h
../../../../src/TextModel.cpp
../../../../src/TextModel.h
../../../../src/AudioPlayerAsync.cpp
../../../../src/AudioPlayerAsync.h
../../../../src/BoneAnimatedModelNew.cpp
../../../../src/BoneAnimatedModelNew.h
../../../../src/render/OpenGlExtensions.cpp
../../../../src/render/OpenGlExtensions.h
../../../../src/utils/Utils.cpp
../../../../src/utils/Utils.h
../../../../src/SparkEmitter.cpp ../../../../src/SparkEmitter.cpp
../../../../src/SparkEmitter.h ../../../../src/TextModel.cpp
../../../../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/UiManager.cpp ../../../../src/UiManager.cpp
../../../../src/UiManager.h ../../../../src/utils/Perlin.cpp
../../../../src/render/TextRenderer.h ../../../../src/utils/TaskManager.cpp
../../../../src/render/TextRenderer.cpp ../../../../src/utils/Utils.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.cpp
../../../../src/items/GameObjectLoader.h ../../../../src/planet/PlanetData.cpp
../../../../src/items/GameObjectLoader.cpp ../../../../src/planet/PlanetObject.cpp
../../../../src/items/Item.h ../../../../src/planet/StoneObject.cpp
../../../../src/items/Item.cpp ../../../../src/render/FrameBuffer.cpp
../../../../src/items/ItemRegistry.h ../../../../src/render/ShadowMap.cpp
../../../../src/items/ItemRegistry.cpp ../../../../src/render/Renderer.cpp
../../../../src/items/InteractiveObject.h ../../../../src/render/ShaderManager.cpp
../../../../src/items/InteractiveObject.cpp ../../../../src/render/TextureManager.cpp
../../../../src/items/InteractiveObjectState.h ../../../../src/render/OpenGlExtensions.cpp
../../../../src/dialogue/DialogueTypes.h )
../../../../src/dialogue/DialogueDatabase.h
../../../../src/dialogue/DialogueDatabase.cpp # Подключаем заголовки
../../../../src/dialogue/DialogueRuntime.h target_include_directories(main PRIVATE
../../../../src/dialogue/DialogueRuntime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../SDL/include
../../../../src/dialogue/DialogueOverlay.h ${CMAKE_CURRENT_SOURCE_DIR}/../zlib
../../../../src/dialogue/DialogueOverlay.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libpng
../../../../src/dialogue/DialogueSystem.h ${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/eigen-5.0.0
../../../../src/dialogue/DialogueSystem.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/boost_1_90_0
../../../../src/dialogue/TranslationDatabase.h ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src
../../../../src/dialogue/TranslationDatabase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libzip
../../../../src/quest/QuestTypes.h )
../../../../src/quest/QuestJournal.h
../../../../src/quest/QuestJournal.cpp # ВАЖНО: Линкуемся с png_static (статика) или png_shared (динамика)
../../../../src/cutscene/CutsceneTypes.h # Так как мы установили PNG_STATIC=ON и PNG_SHARED=OFF,
../../../../src/cutscene/CutsceneDatabase.h # должна создаться цель png_static
../../../../src/cutscene/CutsceneDatabase.cpp target_link_libraries(main
../../../../src/cutscene/CutsceneRuntime.h png_static # ЭТО ПРАВИЛЬНОЕ ИМЯ ЦЕЛИ!
../../../../src/cutscene/CutsceneRuntime.cpp z
../../../../src/cutscene/CutsceneOverlay.h SDL2
../../../../src/cutscene/CutsceneOverlay.cpp
../../../../src/render/UiQuad.h
) )
find_library(OPENGLES2_LIB GLESv2)
find_library(OPENGLES3_LIB GLESv3)
target_link_libraries(main target_link_libraries(main
${OPENGLES3_LIB} ${OPENGLES2_LIB} # OpenGL ES 2.0/3.0
png_static
z
SDL2
SDL2_ttf
SDL2_mixer
log log
android android
OpenSLES OpenSLES
dl dl
zip zip
lua_static
#freetype
)
# Подключаем заголовки
target_include_directories(main PRIVATE
#${CMAKE_CURRENT_SOURCE_DIR}/../SDL/include
${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/SDL_ttf-release-2.24.0/external/freetype/include
#${CMAKE_CURRENT_SOURCE_DIR}/../zlib
#${CMAKE_CURRENT_SOURCE_DIR}/../libpng
${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/eigen-5.0.0
${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/boost_1_90_0
${CMAKE_CURRENT_SOURCE_DIR}/../../../../src
${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/sol2-3.3.0/include
#${CMAKE_CURRENT_SOURCE_DIR}/../libzip
) )
add_dependencies(main sync_resources) add_dependencies(main sync_resources)

View File

@ -16,9 +16,6 @@
# public *; # public *;
#} #}
# SDL2 specific rules
-keep class org.libsdl.app.** { *; }
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLInputConnection { -keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLInputConnection {
void nativeCommitText(java.lang.String, int); void nativeCommitText(java.lang.String, int);
void nativeGenerateScancodeForUnichar(char); void nativeGenerateScancodeForUnichar(char);
@ -99,6 +96,3 @@
void hapticRun(int, float, int); void hapticRun(int, float, int);
void hapticStop(int); void hapticStop(int);
} }
# Keep our main activity
-keep class fishrungames.shadowoverbishkek.ShadowOverBishkekActivity { *; }

View File

@ -8,7 +8,7 @@
android:installLocation="auto"> android:installLocation="auto">
<!-- OpenGL ES 2.0 --> <!-- OpenGL ES 2.0 -->
<uses-feature android:glEsVersion="0x00030000" /> <uses-feature android:glEsVersion="0x00020000" />
<!-- Touchscreen support --> <!-- Touchscreen support -->
<uses-feature <uses-feature
@ -52,7 +52,7 @@
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> --> <!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
<!-- Create a Java class extending SDLActivity and place it in a <!-- Create a Java class extending SDLActivity and place it in a
directory under shadowoverbishkek/main/java matching the package, e.g. shadowoverbishkek/main/java/com/gamemaker/game/MyGame.java directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java
then replace "SDLActivity" with the name of your class (e.g. "MyGame") then replace "SDLActivity" with the name of your class (e.g. "MyGame")
in the XML below. in the XML below.
@ -62,14 +62,14 @@
<application android:label="@string/app_name" <application android:label="@string/app_name"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:allowBackup="true" android:allowBackup="true"
android:theme="@style/Theme.App.Starting" android:theme="@style/AppTheme"
android:hardwareAccelerated="true" > android:hardwareAccelerated="true" >
<!-- Example of setting SDL hints from AndroidManifest.xml: <!-- Example of setting SDL hints from AndroidManifest.xml:
<meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/> <meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/>
--> -->
<activity android:name=".ShadowOverBishkekActivity" <activity android:name="SDLActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:alwaysRetainTaskState="true" android:alwaysRetainTaskState="true"
android:launchMode="singleInstance" android:launchMode="singleInstance"

View File

@ -1,13 +0,0 @@
package fishrungames.shadowoverbishkek;
import android.os.Bundle;
import androidx.core.splashscreen.SplashScreen;
import org.libsdl.app.SDLActivity;
public class ShadowOverBishkekActivity extends SDLActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
SplashScreen.installSplashScreen(this);
super.onCreate(savedInstanceState);
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,3 +0,0 @@
<resources>
<string name="app_name">Тень над Бишкеком</string>
</resources>

View File

@ -1,3 +1,3 @@
<resources> <resources>
<string name="app_name">Shadow Over Bishkek</string> <string name="app_name">Game</string>
</resources> </resources>

View File

@ -4,13 +4,4 @@
<style name="AppTheme" parent="android:Theme.NoTitleBar.Fullscreen"> <style name="AppTheme" parent="android:Theme.NoTitleBar.Fullscreen">
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
</style> </style>
<!-- Тема для Splash Screen -->
<style name="Theme.App.Starting" parent="Theme.SplashScreen">
<!-- Черный фон -->
<item name="windowSplashScreenBackground">#000000</item>
<!-- Ваша картинка 512x512 -->
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
<!-- Ссылка на основную тему, которая применится после загрузки -->
<item name="postSplashScreenTheme">@style/AppTheme</item>
</style>
</resources> </resources>

View File

@ -6,7 +6,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:9.3.0' classpath 'com.android.tools.build:gradle:8.1.1'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

View File

@ -9,18 +9,7 @@
# Specifies the JVM arguments used for the daemon process. # Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
android.builtInKotlin=false org.gradle.jvmargs=-Xmx1536m
android.defaults.buildfeatures.resvalues=true
android.dependency.useConstraints=true
android.enableAppCompileTimeRClass=false
android.newDsl=false
android.r8.optimizedResourceShrinking=false
android.r8.strictFullModeForKeepRules=false
android.sdk.defaultTargetSdkToCompileSdkIfUnset=false
android.uniquePackageNames=false
android.usesSdkInManifest.disallowed=false
org.gradle.jvmargs=-Xmx4096m
android.useAndroidX=true
# When configured, Gradle will run in incubating parallel mode. # When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit # This option should only be used with decoupled projects. More details, visit

View File

@ -1,12 +0,0 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/73bcfb608d1fde9fb62e462f834a3299/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/846ee0d876d26a26f37aa1ce8de73224/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9482ddec596298c84656d31d16652665/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/39701d92e1756bb2f141eb67cd4c660e/redirect
toolchainVersion=21

View File

@ -1,6 +1,6 @@
#Sat Jan 10 10:31:05 MSK 2026 #Sat Jan 10 10:31:05 MSK 2026
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -1,17 +1,10 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(ShadowOverBishkekDemo LANGUAGES C CXX) project(witcher001 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 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
option(STEAMSDK "Enable Steamworks SDK integration" OFF)
# =========================================== # ===========================================
# Поиск системных зависимостей Linux # Поиск системных зависимостей Linux
# =========================================== # ===========================================
@ -69,9 +62,9 @@ endif()
# =========================================== # ===========================================
# Основной проект ShadowOverBishkekDemo # Основной проект witcher001
# =========================================== # ===========================================
add_executable(ShadowOverBishkekDemo add_executable(witcher001
../src/main.cpp ../src/main.cpp
../src/Game.cpp ../src/Game.cpp
../src/Game.h ../src/Game.h
@ -165,7 +158,7 @@ add_executable(ShadowOverBishkekDemo
) )
# include-пути проекта # include-пути проекта
target_include_directories(ShadowOverBishkekDemo PRIVATE target_include_directories(witcher001 PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_SOURCE_DIR}/../src"
${SDL2_INCLUDE_DIRS} ${SDL2_INCLUDE_DIRS}
${SDL2_TTF_INCLUDE_DIRS} ${SDL2_TTF_INCLUDE_DIRS}
@ -173,18 +166,19 @@ target_include_directories(ShadowOverBishkekDemo PRIVATE
${LIBZIP_INCLUDE_DIRS} ${LIBZIP_INCLUDE_DIRS}
${LUA_INCLUDE_DIRS} ${LUA_INCLUDE_DIRS}
${EIGEN3_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS}
# sol2 - header-only, берем напрямую из thirdparty # sol2 - header-only, берем напрямую из thirdparty (если он там есть)
# или можно установить системный libsol-dev и убрать этот путь
"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/sol2-3.3.0/include" "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/sol2-3.3.0/include"
) )
# Убираем WIN32_LEAN_AND_MEAN # Убираем WIN32_LEAN_AND_MEAN
target_compile_definitions(ShadowOverBishkekDemo PRIVATE target_compile_definitions(witcher001 PRIVATE
PNG_ENABLED PNG_ENABLED
SDL_MAIN_HANDLED SDL_MAIN_HANDLED
) )
# Линковка директорий для pkg-config (если пакеты не в /usr/lib) # Линковка директорий для pkg-config (если пакеты не в /usr/lib)
target_link_directories(ShadowOverBishkekDemo PRIVATE target_link_directories(witcher001 PRIVATE
${SDL2_LIBRARY_DIRS} ${SDL2_LIBRARY_DIRS}
${SDL2_TTF_LIBRARY_DIRS} ${SDL2_TTF_LIBRARY_DIRS}
${SDL2_MIXER_LIBRARY_DIRS} ${SDL2_MIXER_LIBRARY_DIRS}
@ -193,7 +187,7 @@ target_link_directories(ShadowOverBishkekDemo PRIVATE
) )
# Линковка библиотек # Линковка библиотек
target_link_libraries(ShadowOverBishkekDemo PRIVATE target_link_libraries(witcher001 PRIVATE
OpenGL::GL OpenGL::GL
OpenGL::GLU OpenGL::GLU
ZLIB::ZLIB ZLIB::ZLIB
@ -208,93 +202,18 @@ target_link_libraries(ShadowOverBishkekDemo PRIVATE
pthread 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()
# =========================================== # ===========================================
# Копирование ресурсов после сборки # Копирование ресурсов после сборки
# =========================================== # ===========================================
set(RUNTIME_RESOURCE_DIRS set(RUNTIME_RESOURCE_DIRS
"resources" "resources"
"music" "audio"
) )
# В Linux (при использовании Make/Ninja) бинарник обычно создается в CMAKE_BINARY_DIR.
# Копируем ресурсы рядом с исполняемым файлом.
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS) foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD add_custom_command(TARGET witcher001 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to binary dir..." COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to binary dir..."
COMMAND ${CMAKE_COMMAND} -E copy_directory COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/../${resdir}" "${CMAKE_SOURCE_DIR}/../${resdir}"

View File

@ -8,19 +8,13 @@ if(NOT CMAKE_MAKE_PROGRAM AND WIN32)
endif() endif()
endif() endif()
project(ShadowOverBishkekDemo LANGUAGES C CXX) project(bishkek-witcher LANGUAGES C CXX)
option(OPTIMIZE_WEB_RESOURCES "Resize PNG images for web build (requires Python3 + Pillow)" ON) option(OPTIMIZE_WEB_RESOURCES "Resize PNG images for web build (requires Python3 + Pillow)" ON)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (CMAKE_BUILD_TYPE STREQUAL "Release")
# Максимальная оптимизация кода и LTO (Link-Time Optimization)
add_compile_options(-O3 -flto)
add_link_options(-O3 -flto)
endif()
# --- АВТО-ЗАГРУЗКА ЗАВИСИМОСТЕЙ --- # --- АВТО-ЗАГРУЗКА ЗАВИСИМОСТЕЙ ---
include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/FetchDependencies.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/FetchDependencies.cmake")
# Теперь гарантированно есть папка ../thirdparty со всеми исходниками # Теперь гарантированно есть папка ../thirdparty со всеми исходниками
@ -115,8 +109,6 @@ set(SOURCES
../src/LocationState.cpp ../src/LocationState.cpp
../src/LocationEditor.h ../src/LocationEditor.h
../src/LocationEditor.cpp ../src/LocationEditor.cpp
../src/NpcCar.h
../src/NpcCar.cpp
../src/GameConstants.h ../src/GameConstants.h
../src/GameConstants.cpp ../src/GameConstants.cpp
../src/GameState.h ../src/GameState.h
@ -158,10 +150,10 @@ set(SOURCES
../src/render/UiQuad.h ../src/render/UiQuad.h
) )
add_executable(ShadowOverBishkekDemo ${SOURCES}) add_executable(bishkek-witcher ${SOURCES})
# Настройка путей к инклудам (используем скачанные исходники) # Настройка путей к инклудам (используем скачанные исходники)
target_include_directories(ShadowOverBishkekDemo PRIVATE target_include_directories(bishkek-witcher PRIVATE
../src ../src
../thirdparty/eigen-5.0.0 ../thirdparty/eigen-5.0.0
../thirdparty/boost_1_90_0 ../thirdparty/boost_1_90_0
@ -176,7 +168,7 @@ set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE)
add_subdirectory("../thirdparty/libzip-1.11.4" libzip-build) add_subdirectory("../thirdparty/libzip-1.11.4" libzip-build)
target_link_libraries(ShadowOverBishkekDemo PRIVATE zip z lua_static websocket.js) target_link_libraries(bishkek-witcher PRIVATE zip z lua_static websocket.js)
# Эмскриптен-флаги # Эмскриптен-флаги
set(EMSCRIPTEN_FLAGS set(EMSCRIPTEN_FLAGS
@ -193,7 +185,7 @@ set(EMSCRIPTEN_FLAGS
"-DNETWORK" "-DNETWORK"
) )
target_compile_options(ShadowOverBishkekDemo PRIVATE ${EMSCRIPTEN_FLAGS} "-O2") target_compile_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_FLAGS} "-O2")
# Only loading.png and the shaders used before resources.zip is ready are preloaded. # Only loading.png and the shaders used before resources.zip is ready are preloaded.
# resources.zip is downloaded asynchronously at runtime and served as a separate file. # resources.zip is downloaded asynchronously at runtime and served as a separate file.
@ -203,7 +195,6 @@ set(EMSCRIPTEN_LINK_FLAGS
#"-sPTHREAD_POOL_SIZE=4" #"-sPTHREAD_POOL_SIZE=4"
"-sALLOW_MEMORY_GROWTH=1" "-sALLOW_MEMORY_GROWTH=1"
"-sFULL_ES3=1" "-sFULL_ES3=1"
"-lidbfs.js"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loading.png@resources/loading.png" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loading.png@resources/loading.png"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loadingProgressBar.png@resources/loadingProgressBar.png" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loadingProgressBar.png@resources/loadingProgressBar.png"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loadingProgressBarFrame.png@resources/loadingProgressBarFrame.png" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loadingProgressBarFrame.png@resources/loadingProgressBarFrame.png"
@ -211,14 +202,15 @@ set(EMSCRIPTEN_LINK_FLAGS
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_uni_interior.lua@resources/start_uni_interior.lua" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_uni_interior.lua@resources/start_uni_interior.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_uni_exterior.lua@resources/start_uni_exterior.lua" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_uni_exterior.lua@resources/start_uni_exterior.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_dorm.lua@resources/start_dorm.lua" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_dorm.lua@resources/start_dorm.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../audio@audio"
) )
# Применяем настройки линковки # Применяем настройки линковки
target_link_options(ShadowOverBishkekDemo PRIVATE ${EMSCRIPTEN_LINK_FLAGS}) target_link_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
# Для совместимости со старыми версиями CMake, если target_link_options недостаточно # Для совместимости со старыми версиями CMake, если target_link_options недостаточно
string(REPLACE ";" " " EMSCRIPTEN_LINK_FLAGS_STR "${EMSCRIPTEN_LINK_FLAGS}") string(REPLACE ";" " " EMSCRIPTEN_LINK_FLAGS_STR "${EMSCRIPTEN_LINK_FLAGS}")
set_target_properties(ShadowOverBishkekDemo PROPERTIES set_target_properties(bishkek-witcher PROPERTIES
LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS_STR}" LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS_STR}"
SUFFIX ".html" SUFFIX ".html"
) )
@ -262,40 +254,23 @@ else()
) )
endif() endif()
# music resources
set(MUSIC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../music")
set(MUSIC_ZIP "${CMAKE_CURRENT_BINARY_DIR}/music.zip")
get_filename_component(MUSIC_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
add_custom_command(
OUTPUT "${MUSIC_ZIP}"
COMMAND ${CMAKE_COMMAND} -E tar "cf" "${MUSIC_ZIP}" --format=zip "music"
WORKING_DIRECTORY "${MUSIC_PARENT_DIR}"
DEPENDS "${MUSIC_PARENT_DIR}/music"
)
add_custom_target(pack_resources DEPENDS "${RESOURCES_ZIP}") add_custom_target(pack_resources DEPENDS "${RESOURCES_ZIP}")
add_dependencies(ShadowOverBishkekDemo pack_resources) add_dependencies(bishkek-witcher pack_resources)
add_custom_target(pack_music DEPENDS "${MUSIC_ZIP}")
add_dependencies(ShadowOverBishkekDemo pack_music)
# Определяем путь к директории установки (относительно папки билда) # Определяем путь к директории установки (относительно папки билда)
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/public") set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/public")
# 1. Устанавливаем основной HTML файл # 1. Устанавливаем основной HTML файл
install(TARGETS ShadowOverBishkekDemo install(TARGETS bishkek-witcher
RUNTIME DESTINATION . RUNTIME DESTINATION .
) )
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных) # 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
install(FILES install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/ShadowOverBishkekDemo.js" "${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.js"
"${CMAKE_CURRENT_BINARY_DIR}/ShadowOverBishkekDemo.wasm" "${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.wasm"
"${CMAKE_CURRENT_BINARY_DIR}/ShadowOverBishkekDemo.data" "${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.data"
DESTINATION . DESTINATION .
) )
@ -303,15 +278,10 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/index.html"
DESTINATION . DESTINATION .
) )
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/favicon.ico"
DESTINATION .
)
# resources.zip is served separately and downloaded asynchronously at runtime # resources.zip is served separately and downloaded asynchronously at runtime
install(FILES "${RESOURCES_ZIP}" DESTINATION .) install(FILES "${RESOURCES_ZIP}" DESTINATION .)
install(FILES "${MUSIC_ZIP}" DESTINATION .)
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD add_custom_command(TARGET bishkek-witcher POST_BUILD
COMMAND ${CMAKE_COMMAND} --install . COMMAND ${CMAKE_COMMAND} --install .
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
COMMENT "Automatically deploying to public directory..." COMMENT "Automatically deploying to public directory..."

View File

@ -37,14 +37,3 @@ Run:
``` ```
emrun --no_browser --port 8080 public emrun --no_browser --port 8080 public
``` ```
# Release build
```
mkdir build_release
cd build_release
emcmake cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ..
cmake --build . --config Release
cmake --install . --prefix ../dist
```

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@ -39,7 +39,7 @@
function loadGameScript() { function loadGameScript() {
var s = document.createElement('script'); var s = document.createElement('script');
s.src = 'ShadowOverBishkekDemo.js'; s.src = 'bishkek-witcher.js';
s.async = true; s.async = true;
document.body.appendChild(s); document.body.appendChild(s);
} }

View File

@ -0,0 +1,2 @@
<!doctypehtml><html lang=en-us><head><meta charset=utf-8><meta content="text/html; charset=utf-8"http-equiv=Content-Type><title>Emscripten-Generated Code</title><style>body{font-family:arial;margin:0;padding:none}.emscripten{padding-right:0;margin-left:auto;margin-right:auto;display:block}div.emscripten{text-align:center}div.emscripten_border{border:1px solid #000}canvas.emscripten{border:0 none;background-color:#000}#emscripten_logo{display:inline-block;margin:0;padding:6px;width:265px}.spinner{height:30px;width:30px;margin:0;margin-top:20px;margin-left:20px;display:inline-block;vertical-align:top;-webkit-animation:rotation .8s linear infinite;-moz-animation:rotation .8s linear infinite;-o-animation:rotation .8s linear infinite;animation:rotation .8s linear infinite;border-left:5px solid #ebebeb;border-right:5px solid #ebebeb;border-bottom:5px solid #ebebeb;border-top:5px solid #787878;border-radius:100%;background-color:#bdd72e}@-webkit-keyframes rotation{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@-moz-keyframes rotation{from{-moz-transform:rotate(0)}to{-moz-transform:rotate(360deg)}}@-o-keyframes rotation{from{-o-transform:rotate(0)}to{-o-transform:rotate(360deg)}}@keyframes rotation{from{transform:rotate(0)}to{transform:rotate(360deg)}}#status{display:inline-block;vertical-align:top;margin-top:30px;margin-left:20px;font-weight:700;color:#787878}#progress{height:20px;width:300px}#controls{display:inline-block;float:right;vertical-align:top;margin-top:30px;margin-right:20px}#output{width:100%;height:200px;margin:0 auto;margin-top:10px;border-left:0;border-right:0px;padding-left:0;padding-right:0;display:block;background-color:#000;color:#fff;font-family:'Lucida Console',Monaco,monospace;outline:0}</style></head><body><script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script><a href=http://emscripten.org><img id=emscripten_logo src=""></a><div class=spinner id=spinner></div><div class=emscripten id=status>Downloading...</div><span id=controls><span><input type=checkbox id=resize>Resize canvas</span> <span><input type=checkbox id=pointerLock checked>Lock/hide mouse pointer    </span><span><input type=button onclick='Module.requestFullscreen(document.getElementById("pointerLock").checked,document.getElementById("resize").checked)'value=Fullscreen></span></span><div class=emscripten><progress hidden id=progress max=100 value=0></progress></div><div class=emscripten_border><canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas></div><textarea id=output rows=8></textarea><script>var statusElement=document.getElementById("status"),progressElement=document.getElementById("progress"),spinnerElement=document.getElementById("spinner"),canvasElement=document.getElementById("canvas"),outputElement=document.getElementById("output");outputElement&&(outputElement.value=""),canvasElement.addEventListener("webglcontextlost",(e=>{alert("WebGL context lost. You will need to reload the page."),e.preventDefault()}),!1);var Module={print(...e){if(console.log(...e),outputElement){var t=e.join(" ");outputElement.value+=t+"\n",outputElement.scrollTop=outputElement.scrollHeight}},canvas:canvasElement,setStatus(e){if(Module.setStatus.last||(Module.setStatus.last={time:Date.now(),text:""}),e!==Module.setStatus.last.text){var t=e.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/),n=Date.now();t&&n-Module.setStatus.last.time<30||(Module.setStatus.last.time=n,Module.setStatus.last.text=e,t?(e=t[1],progressElement.value=100*parseInt(t[2]),progressElement.max=100*parseInt(t[4]),progressElement.hidden=!1,spinnerElement.hidden=!1):(progressElement.value=null,progressElement.max=null,progressElement.hidden=!0,e||(spinnerElement.style.display="none")),statusElement.innerHTML=e)}},totalDependencies:0,monitorRunDependencies(e){this.totalDependencies=Math.max(this.totalDependencies,e),Module.setStatus(e?"Preparing... ("+(this.totalDependencies-e)+"/"+this.totalDependencies+")":"All downloads complete.")}};Module.setStatus("Downloading..."),window.onerror=e=>{Module.setStatus("Exception thrown, see JavaScript console"),spinnerElement.style.display="none",Module.setStatus=e=>{e&&console.error("[post-exception status] "+e)}}</script><script async src="space-game001.js" crossorigin="anonymous"></script></body></html>

View File

@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Sky Trek Tales</title>
<style>
body, html {
margin: 0; padding: 0; width: 100%; height: 100%;
overflow: hidden; background-color: #000;
position: fixed;
}
#canvas {
display: block;
position: absolute;
top: 0; left: 0;
width: 100vw; height: 100vh;
border: none;
}
#fs-button {
position: absolute;
top: 10px; right: 10px;
padding: 10px;
z-index: 10;
background: rgba(255,255,255,0.3);
color: white; border: 1px solid white;
cursor: pointer;
font-family: sans-serif;
border-radius: 5px;
}
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
/* Nick modal */
#nickOverlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.85);
z-index: 9999;
}
#nickBox {
background: #111;
border: 1px solid #444;
padding: 24px;
width: 320px;
box-shadow: 0 8px 24px rgba(0,0,0,0.6);
text-align: center;
}
#nickBox h2 { margin: 0 0 12px 0; font-size: 18px; color: #eee; }
#nickBox input[type="text"] {
width: 100%;
padding: 10px;
font-size: 16px;
box-sizing: border-box;
margin-bottom: 12px;
border: 1px solid #333;
background: #000;
color: #fff;
}
#nickBox button {
padding: 10px 16px;
font-size: 16px;
background: #2a9fd6;
border: none;
color: #fff;
cursor: pointer;
}
</style>
</head>
<body>
<button id="fs-button">Fullscreen</button>
<div id="status">Downloading...</div>
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
<div id="nickOverlay" style="display:none;">
<div id="nickBox">
<h2>Enter your nickname</h2>
<input id="nickInput" type="text" maxlength="32" placeholder="Player" />
<div>
<button id="nickSubmit">Start</button>
</div>
</div>
</div>
<script>
// Utility: подготовить глобальный Module до загрузки Emscripten-скрипта
function prepareModuleEnvironment() {
window.Module = window.Module || {};
var canvasEl = document.getElementById('canvas');
// Устанавливаем canvas для Emscripten, чтобы createContext не падал
window.Module.canvas = canvasEl;
// Подготовим заглушку setStatus, если ещё нет
window.Module.setStatus = window.Module.setStatus || function (text) {
var statusElement = document.getElementById("status");
statusElement.innerHTML = text;
statusElement.style.display = text ? 'block' : 'none';
};
}
// Show overlay only if no nickname saved.
function loadGameScript() {
var s = document.createElement('script');
s.src = 'space-game001.js';
s.async = true;
document.body.appendChild(s);
}
function showNickOverlay() {
var overlay = document.getElementById('nickOverlay');
overlay.style.display = 'flex';
var input = document.getElementById('nickInput');
input.focus();
}
function hideNickOverlay() {
var overlay = document.getElementById('nickOverlay');
overlay.style.display = 'none';
}
function saveNickAndStart(nick) {
try {
if (!nick || nick.trim() === '') nick = 'Player';
localStorage.setItem('spacegame_nick', nick);
} catch (e) {
console.warn('localStorage not available', e);
}
hideNickOverlay();
// перед загрузкой скрипта гарантируем, что Module.canvas задан
prepareModuleEnvironment();
loadGameScript();
}
document.getElementById('fs-button').addEventListener('click', function() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(function(e) {
console.error('Fullscreen error: ' + e.message);
});
} else {
document.exitFullscreen();
}
});
document.addEventListener('DOMContentLoaded', function() {
// Готовим Module сразу — даже если откроется модалка, поле canvas будет доступно для скрипта (если он загружается позже)
prepareModuleEnvironment();
var stored = null;
try {
stored = localStorage.getItem('spacegame_nick');
} catch (e) {
console.warn('localStorage not available', e);
}
if (stored && stored.trim() !== '') {
// Nick is present — start immediately
loadGameScript();
} else {
// Show modal to request nickname before loading WASM
showNickOverlay();
var submit = document.getElementById('nickSubmit');
var input = document.getElementById('nickInput');
submit.addEventListener('click', function() {
saveNickAndStart(input.value);
});
input.addEventListener('input', function() {
// Strip any character that is not a-z, A-Z, 0-9 or space
var pos = this.selectionStart;
var cleaned = this.value.replace(/[^a-zA-Z0-9 ]/g, '');
if (cleaned !== this.value) {
this.value = cleaned;
this.setSelectionRange(pos - 1, pos - 1);
}
});
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
saveNickAndStart(input.value);
}
});
}
});
window.addEventListener("orientationchange", function() {
// Chrome на Android обновляет innerWidth/Height не мгновенно.
// Ждем завершения анимации поворота.
setTimeout(() => {
// В Emscripten это вызовет ваш onWindowResized в C++
window.dispatchEvent(new Event('resize'));
}, 200);
});
</script>
</body>
</html>

View File

@ -1,19 +1,16 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(ShadowOverBishkekDemo LANGUAGES C CXX) project(witcher001 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Опция для интеграции Steamworks SDK (по умолчанию выключена)
option(STEAMSDK "Enable Steamworks SDK integration" OFF)
include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/ThirdParty.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/ThirdParty.cmake)
# =========================================== # ===========================================
# Основной проект ShadowOverBishkekDemo # Основной проект witcher001
# =========================================== # ===========================================
add_executable(ShadowOverBishkekDemo WIN32 add_executable(witcher001
../src/main.cpp ../src/main.cpp
../src/Game.cpp ../src/Game.cpp
../src/Game.h ../src/Game.h
@ -104,32 +101,36 @@ add_executable(ShadowOverBishkekDemo WIN32
../src/cutscene/CutsceneOverlay.h ../src/cutscene/CutsceneOverlay.h
../src/cutscene/CutsceneOverlay.cpp ../src/cutscene/CutsceneOverlay.cpp
../src/render/UiQuad.h ../src/render/UiQuad.h
app.rc
) )
# Установка проекта по умолчанию для Visual Studio # Установка проекта по умолчанию для Visual Studio
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ShadowOverBishkekDemo) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT witcher001)
# include-пути проекта # include-пути проекта
target_include_directories(ShadowOverBishkekDemo PRIVATE target_include_directories(witcher001 PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_SOURCE_DIR}/../src"
) )
set_target_properties(ShadowOverBishkekDemo PROPERTIES set_target_properties(witcher001 PROPERTIES
OUTPUT_NAME "ShadowOverBishkekDemo" OUTPUT_NAME "witcher001"
) )
# Определения препроцессора # Определения препроцессора:
target_compile_definitions(ShadowOverBishkekDemo PRIVATE # PNG_ENABLED включает код PNG в TextureManager
# SDL_MAIN_HANDLED отключает переопределение main -> SDL_main
target_compile_definitions(witcher001 PRIVATE
WIN32_LEAN_AND_MEAN WIN32_LEAN_AND_MEAN
PNG_ENABLED PNG_ENABLED
SDL_MAIN_HANDLED
# DEBUG_LIGHT
# SHOW_PATH
) )
# Линкуем с SDL2main, если он вообще установлен # Линкуем с SDL2main, если он вообще установлен
target_link_libraries(ShadowOverBishkekDemo PRIVATE SDL2main_external_lib) target_link_libraries(witcher001 PRIVATE SDL2main_external_lib)
# Линкуем сторонние библиотеки # Линкуем сторонние библиотеки
target_link_libraries(ShadowOverBishkekDemo PRIVATE target_link_libraries(witcher001 PRIVATE
SDL2_external_lib SDL2_external_lib
libpng_external_lib libpng_external_lib
zlib_external_lib zlib_external_lib
@ -144,186 +145,79 @@ target_link_libraries(ShadowOverBishkekDemo PRIVATE
# Линкуем OpenGL (Windows) # Линкуем OpenGL (Windows)
if(WIN32) if(WIN32)
target_link_libraries(ShadowOverBishkekDemo PRIVATE opengl32) target_link_libraries(witcher001 PRIVATE opengl32)
endif() endif()
# =========================================== # ===========================================
# Интеграция Steamworks SDK (если STEAMSDK=ON) # Копирование SDL2d.dll и zlibd.dll рядом с exe
# ===========================================
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"
)
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()
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()
# ===========================================
# Копирование DLL и ресурсов для локального запуска в Visual Studio
# =========================================== # ===========================================
if (WIN32) 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_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:ShadowOverBishkekDemo>/SDL2d.dll,$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/SDL2.dll>") set(SDL2_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:witcher001>/SDL2d.dll,$<TARGET_FILE_DIR:witcher001>/SDL2.dll>")
set(LIBZIP_DLL_SRC "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/bin/zip.dll,${LIBZIP_BASE_DIR}-Release/bin/zip.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_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(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(SDL2TTF_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/bin/SDL2_ttfd.dll,${SDL2TTF_BASE_DIR}-Release/bin/SDL2_ttf.dll>")
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD 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
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..." COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SDL2_DLL_SRC}" "${SDL2_DLL_DST}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${LIBZIP_DLL_SRC}" "$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/zip.dll" # Копируем SDL2 (целевое имя всегда SDL2.dll)
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ZLIB_DLL_SRC}" "${ZLIB_DLL_DST}" COMMAND ${CMAKE_COMMAND} -E copy_if_different
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SDL2TTF_DLL_SRC}" "$<TARGET_FILE_DIR:ShadowOverBishkekDemo>" "${SDL2_DLL_SRC}"
"${SDL2_DLL_DST}"
# Копируем 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
"${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 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" "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/SDL_mixer-release-2.8.0/install-$<CONFIG>/bin/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
)
if(STEAMSDK) # This is only for profiling:
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD #"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/SDL_mixer-release-2.8.0/install-Release/bin/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
COMMAND ${CMAKE_COMMAND} -E echo "Copying Steam SDK components..." "$<TARGET_FILE_DIR:witcher001>/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${STEAM_LIB_DIR}/${STEAM_DLL_NAME}"
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/${STEAM_DLL_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()
endif() endif()
# Копирование ресурсов локально # ===========================================
set(RUNTIME_RESOURCE_DIRS "resources" "music") # Копирование ресурсов после сборки
# ===========================================
# Какие папки с ресурсами нужно копировать
set(RUNTIME_RESOURCE_DIRS
"resources"
"audio"
)
# Копируем ресурсы и шейдеры в папку exe и в корень build/
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS) foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD add_custom_command(TARGET witcher001 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..." COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
# 1) туда, где лежит exe (build/Debug, build/Release и т.п.)
COMMAND ${CMAKE_COMMAND} -E copy_directory COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/../${resdir}" "${CMAKE_SOURCE_DIR}/../${resdir}"
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/${resdir}" "$<TARGET_FILE_DIR:witcher001>/${resdir}"
# 2) в корень build, если захочешь запускать из этой папки
COMMAND ${CMAKE_COMMAND} -E copy_directory COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/../${resdir}" "${CMAKE_SOURCE_DIR}/../${resdir}"
"${CMAKE_BINARY_DIR}/${resdir}" "${CMAKE_BINARY_DIR}/${resdir}"
) )
endforeach() endforeach()
# ===========================================
# ПРАВИЛА ИНСТАЛЛЯЦИИ (Для CPack и создания пакетов)
# ===========================================
# 1. Основной исполняемый файл
install(TARGETS ShadowOverBishkekDemo DESTINATION .)
# 2. Необходимые DLL
if(WIN32)
install(FILES
"${SDL2_INSTALL_DIR}/bin/SDL2.dll"
"${LIBZIP_BASE_DIR}-Release/bin/zip.dll"
"${ZLIB_INSTALL_DIR}/bin/z.dll"
"${SDL2TTF_BASE_DIR}-Release/bin/SDL2_ttf.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/SDL_mixer-release-2.8.0/install-Release/bin/SDL2_mixer.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/concrt140.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/msvcp140.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/msvcp140_1.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/msvcp140_2.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/msvcp140_atomic_wait.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/msvcp140_clr0400.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/msvcp140_codecvt_ids.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/msvcp140d.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/OpenAL32.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/vcamp140.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/vccorlib140.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/vcomp140.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/vcruntime140.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/vcruntime140_1.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/vcruntime140_1_clr0400.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/vcruntime140_threads.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dist_dlls/wrap_oal.dll"
DESTINATION .
)
if(STEAMSDK)
install(FILES
"${STEAM_LIB_DIR}/${STEAM_DLL_NAME}"
"${CMAKE_CURRENT_BINARY_DIR}/steam_appid.txt"
DESTINATION .
)
endif()
endif()
# 3. Директории с ресурсами
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
install(DIRECTORY "${CMAKE_SOURCE_DIR}/../${resdir}" DESTINATION .)
endforeach()
# ===========================================
# НАСТРОЙКА CPACK (Создание ZIP Portable и NSIS Installer)
# ===========================================
set(CPACK_PACKAGE_NAME "ShadowOverBishkekDemo")
set(CPACK_PACKAGE_VENDOR "Fish Run Games")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Shadow Over Bishkek Demo Version")
set(CPACK_PACKAGE_VERSION_MAJOR "1")
set(CPACK_PACKAGE_VERSION_MINOR "0")
set(CPACK_PACKAGE_VERSION_PATCH "5")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Fish Run Games/Shadow Over Bishkek Demo")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt")
# Настройки NSIS инсталлятора
set(CPACK_NSIS_DISPLAY_NAME "Shadow Over Bishkek Demo")
set(CPACK_NSIS_PACKAGE_NAME "Shadow Over Bishkek Demo")
set(CPACK_NSIS_HELP_LINK "https://fishrungames.com/contact.html")
set(CPACK_NSIS_URL_INFO_ABOUT "https://shadowoverbishkek.com/")
set(CPACK_NSIS_MODIFY_PATH OFF)
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
# Иконки и ярлыки в меню Пуск и на Рабочем столе
set(CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/icon.ico")
set(CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}/icon.ico")
set(CPACK_NSIS_CREATE_ICONS_EXTRA
"CreateShortCut '$DESKTOP\\\\Shadow Over Bishkek Demo.lnk' '$INSTDIR\\\\ShadowOverBishkekDemo.exe'"
)
# Форматы генераторов: ZIP (Portable) и NSIS (Установщик)
set(CPACK_GENERATOR "ZIP;NSIS")
include(CPack)

View File

@ -1,21 +0,0 @@
END USER LICENSE AGREEMENT (EULA)
Game: Shadow Over Bishkek (Demo Version)
Developer: Fish Run Games
1. GRANT OF LICENSE
Fish Run Games grants you a non-exclusive, non-transferable, limited license to download, install, and play the Shadow Over Bishkek Demo for personal, non-commercial entertainment purposes.
2. OWNERSHIP & INTELLECTUAL PROPERTY
The game, including all code, graphics, audio, characters, storyline, and assets, is the intellectual property of Fish Run Games and is protected by copyright and other intellectual property laws. You may not reverse engineer, decompile, modify, or redistribute the software without express written permission.
3. DISCLAIMER OF WARRANTY
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4. USER DATA AND SAVES
Save files and local configuration options are stored locally on your device. Fish Run Games is not responsible for any loss of save data resulting from software updates, system crashes, or uninstallation.
5. TERMINATION
This license is effective until terminated. Your rights under this license will terminate automatically without notice from Fish Run Games if you fail to comply with any term(s) of this agreement.
Copyright (c) 2026 Fish Run Games. All rights reserved.

View File

@ -1 +0,0 @@
IDI_ICON1 ICON "icon.ico"

BIN
proj-windows/dist_dlls/OpenAL32.dll (Stored with Git LFS)

Binary file not shown.

BIN
proj-windows/dist_dlls/concrt140.dll (Stored with Git LFS)

Binary file not shown.

BIN
proj-windows/dist_dlls/msvcp140.dll (Stored with Git LFS)

Binary file not shown.

BIN
proj-windows/dist_dlls/msvcp140_1.dll (Stored with Git LFS)

Binary file not shown.

BIN
proj-windows/dist_dlls/msvcp140_2.dll (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
proj-windows/dist_dlls/msvcp140d.dll (Stored with Git LFS)

Binary file not shown.

BIN
proj-windows/dist_dlls/vcamp140.dll (Stored with Git LFS)

Binary file not shown.

BIN
proj-windows/dist_dlls/vccorlib140.dll (Stored with Git LFS)

Binary file not shown.

BIN
proj-windows/dist_dlls/vcomp140.dll (Stored with Git LFS)

Binary file not shown.

BIN
proj-windows/dist_dlls/vcruntime140.dll (Stored with Git LFS)

Binary file not shown.

BIN
proj-windows/dist_dlls/vcruntime140_1.dll (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
proj-windows/dist_dlls/wrap_oal.dll (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

View File

@ -3,7 +3,7 @@
{ {
"id": "main_hall", "id": "main_hall",
"positionX": -3.1, "positionX": -3.1,
"positionY": 4.8, "positionY": 6.0,
"positionZ": 0.0, "positionZ": 0.0,
"directionX": 0.0, "directionX": 0.0,
"directionY": -1.0, "directionY": -1.0,
@ -20,7 +20,7 @@
"limitX": 3.75, "limitX": 3.75,
"limitY": 2.75, "limitY": 2.75,
"positionX": 4.95, "positionX": 4.95,
"positionY": 3.6, "positionY": 3.0,
"positionZ": -14.25, "positionZ": -14.25,
"directionX": 0.0, "directionX": 0.0,
"directionY": -1.0, "directionY": -1.0,
@ -55,7 +55,7 @@
"limitX": 3.75, "limitX": 3.75,
"limitY": 2.75, "limitY": 2.75,
"positionX": -4.95, "positionX": -4.95,
"positionY": 4.2, "positionY": 4.0,
"positionZ": -19.95, "positionZ": -19.95,
"directionX": 0.0, "directionX": 0.0,
"directionY": -1.0, "directionY": -1.0,

View File

@ -26,7 +26,7 @@
"animationIdlePath": "resources/w/girlfriend/girlfriend_idle003_small.anim", "animationIdlePath": "resources/w/girlfriend/girlfriend_idle003_small.anim",
"animationWalkPath": "resources/w/girlfriend/girlfriend_walk003_small.anim", "animationWalkPath": "resources/w/girlfriend/girlfriend_walk003_small.anim",
"meshTextures": { "meshTextures": {
"Girl_Low": "resources/w/girlfriend/Girl_Base_color006.png" "Girl_Low": "resources/w/girlfriend/Girl_Base_color.png"
}, },
"positionX": 0.799619, "positionX": 0.799619,
"positionY": 0.0, "positionY": 0.0,

View File

@ -1122,9 +1122,9 @@
"waypointReachRadius": 3.0, "waypointReachRadius": 3.0,
"waypoints": [] "waypoints": []
}, },
"taxiCarTriggerPositionX": 0.0, "taxiCarTriggerPositionX": -1.7014118346046923e+38,
"taxiCarTriggerPositionY": 0.0, "taxiCarTriggerPositionY": -1.7014118346046923e+38,
"taxiCarTriggerPositionZ": 0.0, "taxiCarTriggerPositionZ": -1.7014118346046923e+38,
"taxiDefaultWaypoints": [] "taxiDefaultWaypoints": []
}, },
"tutorialInteractiveObjectsLocked": false "tutorialInteractiveObjectsLocked": false
@ -1476,6 +1476,7 @@
"morning_can_open_door_index": 0, "morning_can_open_door_index": 0,
"morning_did_open_door_index": 0, "morning_did_open_door_index": 0,
"player_hold_book": false, "player_hold_book": false,
"player_hold_knife": false,
"teacher_arrived_to_library": false, "teacher_arrived_to_library": false,
"teacher_door_opened": false, "teacher_door_opened": false,
"teacher_told_about_book": false, "teacher_told_about_book": false,
@ -1651,7 +1652,7 @@
} }
] ]
}, },
"savedAt": "2026-07-24 23:40", "savedAt": "2026-07-14 14:00",
"taxiIsCalled": false, "taxiIsCalled": false,
"tutorialJournalPickedUp": false, "tutorialJournalPickedUp": false,
"tutorialJournalScreenOpened": false, "tutorialJournalScreenOpened": false,

View File

@ -13,7 +13,7 @@
{ {
"key": "Аида Дженибековна", "key": "Аида Дженибековна",
"ru": "Аида Дженибековна", "ru": "Аида Дженибековна",
"en": "Aida Dzhenibekovna" "en": "Aida Dzhanybekovna"
}, },
{ {
"key": "Алтынай", "key": "Алтынай",

View File

@ -44,8 +44,8 @@
}, },
{ {
"id": "report_card", "id": "report_card",
"name": "Begimai's record book", "name": "Begimai's grade book",
"description": "This is Begimai's record book. This is where a teacher should write grades for each subject or coursework.", "description": "This is Begimai's grade book. This is where a teacher should write grades for each subject or coursework.",
"icon": "resources/w/ui/img/inv/ItemReportCard002.png", "icon": "resources/w/ui/img/inv/ItemReportCard002.png",
"selectedIcon": "resources/w/ui/img/inv/ItemSelReportCard002.png" "selectedIcon": "resources/w/ui/img/inv/ItemSelReportCard002.png"
}, },

View File

@ -20,7 +20,7 @@
"objectives": [ "objectives": [
{ "id": "ghost_lore_aiperi", "text": "Talk to Aiperi", "completed": false }, { "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_alik", "text": "Talk to Alik", "completed": false, "visible": false },
{ "id": "ghost_lore_teacher", "text": "Talk to Aida Dzhenibekovna", "completed": false } { "id": "ghost_lore_teacher", "text": "Talk to Aida Dzhanybekovna", "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.", "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": [ "objectives": [
{ "id": "ghost_coursework_find", "text": "Find Begimai's coursework", "completed": false }, { "id": "ghost_coursework_find", "text": "Find Begimai's coursework", "completed": false },
{ "id": "ghost_coursework_mark", "text": "Show coursework to Aida Dzhenibekovna", "completed": false } { "id": "ghost_coursework_mark", "text": "Show coursework to Aida Dzhanybekovna", "completed": false }
] ]
}, },
{ {
@ -41,9 +41,9 @@
"autoComplete": true, "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.", "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": [ "objectives": [
{ "id": "ghost_release_reportcard", "text": "Find Begimai's record book", "completed": false }, { "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 record 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 record book to the ghost", "completed": false } { "id": "ghost_release_show", "text": "Show the grade book to the ghost", "completed": false }
] ]
}, },
{ {
@ -51,7 +51,7 @@
"title": "Manas studies", "title": "Manas studies",
"status": "Available", "status": "Available",
"autoComplete": true, "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 Dzhenibekovna.", "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.",
"objectives": [ "objectives": [
{ "id": "study_beginning_lecture", "text": "Attend the lecture", "completed": false }, { "id": "study_beginning_lecture", "text": "Attend the lecture", "completed": false },
{ "id": "study_beginning_task", "text": "Get an assignment for the module", "completed": false } { "id": "study_beginning_task", "text": "Get an assignment for the module", "completed": false }
@ -61,7 +61,7 @@
"id": "study_project", "id": "study_project",
"title": "Essay on Manaschi", "title": "Essay on Manaschi",
"status": "Hidden", "status": "Hidden",
"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.", "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.",
"objectives": [ "objectives": [
{ "id": "study_project_book", "text": "Find the book", "completed": false }, { "id": "study_project_book", "text": "Find the book", "completed": false },
{ "id": "study_project_write", "text": "Write the essay", "completed": false }, { "id": "study_project_write", "text": "Write the essay", "completed": false },

View File

@ -0,0 +1,290 @@
{
"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": []
}
]
}

View File

@ -155,7 +155,7 @@
"type": "Line", "type": "Line",
"speaker": "Бекзат", "speaker": "Бекзат",
"portrait": "resources/dialogue/portrait_phone.png", "portrait": "resources/dialogue/portrait_phone.png",
"text": "Почему ты сама не можешь забрать нож у Аиды?", "text": "Почему ты сама не можешь забрать?",
"next": "line_9", "next": "line_9",
"chatBubble": "out" "chatBubble": "out"
}, },
@ -164,16 +164,7 @@
"type": "Line", "type": "Line",
"speaker": "Айпери", "speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png", "portrait": "resources/dialogue/portrait_phone.png",
"text": "Она опять будет скафнить.", "text": "Ты же знаешь, если я встречу Аиду, она 100% даст мне какое-нибудь сложное задание.",
"next": "line_12",
"chatBubble": "in"
},
{
"id": "line_12",
"type": "Line",
"speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png",
"text": "А может быть даже даст мне какое-нибудь сложное задание.",
"next": "line_10", "next": "line_10",
"chatBubble": "in" "chatBubble": "in"
}, },
@ -191,7 +182,7 @@
"type": "Line", "type": "Line",
"speaker": "Айпери", "speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png", "portrait": "resources/dialogue/portrait_phone.png",
"text": "Так что жду тебя в универе! Не вздумай прогулять! Ошо!", "text": "Так что жду тебя в универе! Не вздумай прогулять!",
"next": "setflag_1", "next": "setflag_1",
"chatBubble": "in", "chatBubble": "in",
"questUnlock": "aiperi_knife" "questUnlock": "aiperi_knife"
@ -368,7 +359,7 @@
"type": "Line", "type": "Line",
"speaker": "Бекзат", "speaker": "Бекзат",
"portrait": "resources/dialogue/portrait_hero_neutral.png", "portrait": "resources/dialogue/portrait_hero_neutral.png",
"text": "Я заказал такси до универа, машина уже едет!", "text": "Я заказал такси до универа, машина уже ждет!",
"next": "end_1" "next": "end_1"
}, },
{ {
@ -656,7 +647,7 @@
"type": "Line", "type": "Line",
"speaker": "Алик", "speaker": "Алик",
"portrait": "resources/dialogue/portrait_student_boy.png", "portrait": "resources/dialogue/portrait_student_boy.png",
"text": "Возле здания универа лежит куча строительного мусора и макулатуры.", "text": "За зданием универа есть контейнер с кучей бумажного мусора и макулатурой.",
"next": "line_14" "next": "line_14"
}, },
{ {

View File

@ -0,0 +1,501 @@
{
"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": []
}
]
}

View File

@ -3,7 +3,7 @@
{ {
"key": "Аида Дженибековна", "key": "Аида Дженибековна",
"ru": "Аида Дженибековна", "ru": "Аида Дженибековна",
"en": "Aida Dzhenibekovna" "en": "Aida Dzhanybekovna"
}, },
{ {
"key": "Опаздывающие, заходите скорее и занимайте свои места! Лекция начинается!", "key": "Опаздывающие, заходите скорее и занимайте свои места! Лекция начинается!",
@ -23,7 +23,7 @@
{ {
"key": "Этот мир описан в эпосе Манас как Кайып или Аль-Гайб, но некоторые ученые называют его миром теней.", "key": "Этот мир описан в эпосе Манас как Кайып или Аль-Гайб, но некоторые ученые называют его миром теней.",
"ru": "Этот мир описан в эпосе Манас как Кайып или Аль-Гайб, но некоторые ученые называют его миром теней.", "ru": "Этот мир описан в эпосе Манас как Кайып или Аль-Гайб, но некоторые ученые называют его миром теней.",
"en": "This world is described in the Manas epic as Kayip or Al-Ghayb, but some scholars call it the Darkland." "en": "This world is described in the Manas epic as Kayip or Al-Ghayb, but some scholars call it the dark lands."
}, },
{ {
"key": "В этом мире обитают феи, духи и джинны. Простым смертным в этот мир дорога закрыта.", "key": "В этом мире обитают феи, духи и джинны. Простым смертным в этот мир дорога закрыта.",
@ -33,17 +33,17 @@
{ {
"key": "Время там течет по другому - за один день в теневом мире могут пройти годы жизни обычного мира.", "key": "Время там течет по другому - за один день в теневом мире могут пройти годы жизни обычного мира.",
"ru": "Время там течет по другому - за один день в теневом мире могут пройти годы жизни обычного мира.", "ru": "Время там течет по другому - за один день в теневом мире могут пройти годы жизни обычного мира.",
"en": "Time flows differently there - in one day in the Darkland, years of life in the ordinary world can pass." "en": "Time flows differently there - in one day in the dark lands, years of life in the ordinary world can pass."
}, },
{ {
"key": "Обычно, мир теней никак не пересекается с нашим миром живых людей.", "key": "Обычно, мир теней никак не пересекается с нашим миром живых людей.",
"ru": "Обычно, мир теней никак не пересекается с нашим миром живых людей.", "ru": "Обычно, мир теней никак не пересекается с нашим миром живых людей.",
"en": "Usually, the world of the Darkland does not intersect with our world of living people." "en": "Usually, the world of dark lands does not intersect with our world of living people."
}, },
{ {
"key": "Но в критические моменты для народа, обитатели теневого мира могут приходить в наш мир.", "key": "Но в критические моменты для народа, обитатели теневого мира могут приходить в наш мир.",
"ru": "Но в критические моменты для народа, обитатели теневого мира могут приходить в наш мир.", "ru": "Но в критические моменты для народа, обитатели теневого мира могут приходить в наш мир.",
"en": "But in critical moments for the people, the inhabitants of the Darkland can come to our world." "en": "But in critical moments for the people, the inhabitants of the dark lands can come to our world."
}, },
{ {
"key": "Совсем недавно закончилась пандемия, а сегодня мир захлестнули кровавые войны.", "key": "Совсем недавно закончилась пандемия, а сегодня мир захлестнули кровавые войны.",

View File

@ -136,9 +136,9 @@
"en": "Before going outside, I have to order a taxi to the university." "en": "Before going outside, I have to order a taxi to the university."
}, },
{ {
"key": "Я заказал такси до универа, машина уже едет!", "key": "Я заказал такси до универа, машина уже ждет!",
"ru": "Я заказал такси до универа, машина уже едет!", "ru": "Я заказал такси до универа, машина уже ждет!",
"en": "I ordered a taxi to the university, the car is already on its way!" "en": "I ordered a taxi to the university, the car is already waiting!"
}, },
{ {
"key": "Я уже заказал такси, машина уже ждет!", "key": "Я уже заказал такси, машина уже ждет!",
@ -261,9 +261,9 @@
"en": "And where is her coursework now?" "en": "And where is her coursework now?"
}, },
{ {
"key": "Возле здания универа лежит куча строительного мусора и макулатуры.", "key": "За зданием универа есть контейнер с кучей бумажного мусора и макулатурой.",
"ru": "Возле здания универа лежит куча строительного мусора и макулатуры.", "ru": "За зданием универа есть контейнер с кучей бумажного мусора и макулатурой.",
"en": "There is a pile of construction waste and paper near the university building." "en": "Behind the university building there is a container with a pile of paper trash and waste."
}, },
{ {
"key": "Скорее всего, курсовая до сих пор лежит где-то там.", "key": "Скорее всего, курсовая до сих пор лежит где-то там.",
@ -303,7 +303,7 @@
{ {
"key": "А ведь у нас в этом году новая преподавательница, Аида Дженибековна!", "key": "А ведь у нас в этом году новая преподавательница, Аида Дженибековна!",
"ru": "А ведь у нас в этом году новая преподавательница, Аида Дженибековна!", "ru": "А ведь у нас в этом году новая преподавательница, Аида Дженибековна!",
"en": "But we have a new teacher this year, Aida Dzhenibekovna!" "en": "But we have a new teacher this year, Aida Dzhanybekovna!"
}, },
{ {
"key": "Она конечно не сахар, но если ты ходишь на пары и вовремя сдашь ей задание на модуль, то ты считай сдал экзамен.", "key": "Она конечно не сахар, но если ты ходишь на пары и вовремя сдашь ей задание на модуль, то ты считай сдал экзамен.",
@ -318,7 +318,7 @@
{ {
"key": "Так что Аида Дженибековна еще норм. Подойди к ней, возьми задание на модуль, и начни ходить на пары. Тогда ты сдашь экзамен успешно.", "key": "Так что Аида Дженибековна еще норм. Подойди к ней, возьми задание на модуль, и начни ходить на пары. Тогда ты сдашь экзамен успешно.",
"ru": "Так что Аида Дженибековна еще норм. Подойди к ней, возьми задание на модуль, и начни ходить на пары. Тогда ты сдашь экзамен успешно.", "ru": "Так что Аида Дженибековна еще норм. Подойди к ней, возьми задание на модуль, и начни ходить на пары. Тогда ты сдашь экзамен успешно.",
"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." "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."
}, },
{ {
"key": "Спасибо за информацию!", "key": "Спасибо за информацию!",
@ -348,7 +348,7 @@
{ {
"key": "Говорят, ей не очень нравится наша новая преподавательница по манасоведению, Аида Дженибековна.", "key": "Говорят, ей не очень нравится наша новая преподавательница по манасоведению, Аида Дженибековна.",
"ru": "Говорят, ей не очень нравится наша новая преподавательница по манасоведению, Аида Дженибековна.", "ru": "Говорят, ей не очень нравится наша новая преподавательница по манасоведению, Аида Дженибековна.",
"en": "They say she doesn't really like our new Manas studies teacher, Aida Dzhenibekovna." "en": "They say she doesn't really like our new Manas studies teacher, Aida Dzhanybekovna."
}, },
{ {
"key": "Привет Бекзат! Надеюсь ты нашел то что ищешь.", "key": "Привет Бекзат! Надеюсь ты нашел то что ищешь.",
@ -456,9 +456,9 @@
"en": "Before I leave the gate, I have to order a taxi to the dorm." "en": "Before I leave the gate, I have to order a taxi to the dorm."
}, },
{ {
"key": "Я заказал такси до общаги, машина уже едет!", "key": "Я заказал такси до общаги, машина уже ждет!",
"ru": "Я заказал такси до общаги, машина уже едет!", "ru": "Я заказал такси до общаги, машина уже ждет!",
"en": "I ordered a taxi to the dorm, the car is already on its way!" "en": "I ordered a taxi to the dorm, the car is already waiting!"
}, },
{ {
"key": "Ого, пока я залипал в приложении, уже наступила ночь!", "key": "Ого, пока я залипал в приложении, уже наступила ночь!",
@ -478,7 +478,7 @@
{ {
"key": "Кажется, на этой машине в универ приезжает Аида Дженибековна.", "key": "Кажется, на этой машине в универ приезжает Аида Дженибековна.",
"ru": "Кажется, на этой машине в универ приезжает Аида Дженибековна.", "ru": "Кажется, на этой машине в универ приезжает Аида Дженибековна.",
"en": "It seems that Aida Dzhenibekovna arrives at the university in this car." "en": "It seems that Aida Dzhanybekovna arrives at the university in this car."
}, },
{ {
"key": "Это заклинание не работает днем.", "key": "Это заклинание не работает днем.",
@ -488,7 +488,7 @@
{ {
"key": "Чтобы перейти в теневой мир, мне нужно дождаться ночи.", "key": "Чтобы перейти в теневой мир, мне нужно дождаться ночи.",
"ru": "Чтобы перейти в теневой мир, мне нужно дождаться ночи.", "ru": "Чтобы перейти в теневой мир, мне нужно дождаться ночи.",
"en": "To go into the Darkland, I need to wait until night." "en": "To go into the dark lands, I need to wait until night."
}, },
{ {
"key": "Чтобы время пролетело быстро, я могу посидеть в телефоне и позалипать в короткие видео.", "key": "Чтобы время пролетело быстро, я могу посидеть в телефоне и позалипать в короткие видео.",
@ -498,7 +498,7 @@
{ {
"key": "У меня силы закончились и мне спать хочется. Достаточно теневого мира на сегодня, поехали в общагу.", "key": "У меня силы закончились и мне спать хочется. Достаточно теневого мира на сегодня, поехали в общагу.",
"ru": "У меня силы закончились и мне спать хочется. Достаточно теневого мира на сегодня, поехали в общагу.", "ru": "У меня силы закончились и мне спать хочется. Достаточно теневого мира на сегодня, поехали в общагу.",
"en": "I'm exhausted and sleepy. Enough of the Darkland for today, let's go to the dorm." "en": "I'm exhausted and sleepy. Enough of the dark lands for today, let's go to the dorm."
}, },
{ {
"key": "Ты куда собрался, Бекзат?", "key": "Ты куда собрался, Бекзат?",
@ -658,7 +658,7 @@
{ {
"key": "Аида Дженибековна", "key": "Аида Дженибековна",
"ru": "Аида Дженибековна", "ru": "Аида Дженибековна",
"en": "Aida Dzhenibekovna" "en": "Aida Dzhanybekovna"
}, },
{ {
"key": "Бекзат, что тебе нужно?", "key": "Бекзат, что тебе нужно?",
@ -688,7 +688,7 @@
{ {
"key": "Аида Дженибековна", "key": "Аида Дженибековна",
"ru": "Аида Дженибековна", "ru": "Аида Дженибековна",
"en": "Aida Dzhenibekovna" "en": "Aida Dzhanybekovna"
}, },
{ {
"key": "Зачем тебе в учительскую?", "key": "Зачем тебе в учительскую?",
@ -828,7 +828,7 @@
{ {
"key": "Извините, Аида Дженибековна, я могу пойти уже?", "key": "Извините, Аида Дженибековна, я могу пойти уже?",
"ru": "Извините, Аида Дженибековна, я могу пойти уже?", "ru": "Извините, Аида Дженибековна, я могу пойти уже?",
"en": "Excuse me, Aida Dzhenibekovna, can I go now?" "en": "Excuse me, Aida Dzhanybekovna, can I go now?"
}, },
{ {
"key": "Нет, Бекзат. Ты вломился в учительскую, и теперь хочешь просто так уйти? Не получится.", "key": "Нет, Бекзат. Ты вломился в учительскую, и теперь хочешь просто так уйти? Не получится.",
@ -898,7 +898,7 @@
{ {
"key": "Аида Дженибековна, вы помните Бегимай? Она вам курсовую хотела сдать.", "key": "Аида Дженибековна, вы помните Бегимай? Она вам курсовую хотела сдать.",
"ru": "Аида Дженибековна, вы помните Бегимай? Она вам курсовую хотела сдать.", "ru": "Аида Дженибековна, вы помните Бегимай? Она вам курсовую хотела сдать.",
"en": "Aida Dzhenibekovna, do you remember Begimai? She wanted to submit her term paper to you." "en": "Aida Dzhanybekovna, do you remember Begimai? She wanted to submit her term paper to you."
}, },
{ {
"key": "Да, она говорила что приносила курсовую, но у меня ее нигде нет.", "key": "Да, она говорила что приносила курсовую, но у меня ее нигде нет.",
@ -913,7 +913,7 @@
{ {
"key": "Бекзат, зачетка Бегимай у тебя. Отправляйся в теневой мир и покажи зачетку призраку Бегимай, чтобы она освободилась.", "key": "Бекзат, зачетка Бегимай у тебя. Отправляйся в теневой мир и покажи зачетку призраку Бегимай, чтобы она освободилась.",
"ru": "Бекзат, зачетка Бегимай у тебя. Отправляйся в теневой мир и покажи зачетку призраку Бегимай, чтобы она освободилась.", "ru": "Бекзат, зачетка Бегимай у тебя. Отправляйся в теневой мир и покажи зачетку призраку Бегимай, чтобы она освободилась.",
"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." "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."
}, },
{ {
"key": "Хорошо!", "key": "Хорошо!",
@ -923,12 +923,12 @@
{ {
"key": "Аида Дженибековна, здравствуйте!", "key": "Аида Дженибековна, здравствуйте!",
"ru": "Аида Дженибековна, здравствуйте!", "ru": "Аида Дженибековна, здравствуйте!",
"en": "Aida Dzhenibekovna, hello!" "en": "Aida Dzhanybekovna, hello!"
}, },
{ {
"key": "Я принес вам зачетку Бегимай и ее курсовую работу, посмотрите пожалуйста.", "key": "Я принес вам зачетку Бегимай и ее курсовую работу, посмотрите пожалуйста.",
"ru": "Я принес вам зачетку Бегимай и ее курсовую работу, посмотрите пожалуйста.", "ru": "Я принес вам зачетку Бегимай и ее курсовую работу, посмотрите пожалуйста.",
"en": "I brought you Begimai's record book and her coursework, please take a look." "en": "I brought you Begimai's grade book and her coursework, please take a look."
}, },
{ {
"key": "Хорошо, давай посмотрим.", "key": "Хорошо, давай посмотрим.",
@ -943,12 +943,12 @@
{ {
"key": "Вот держи зачетку с оценкой.", "key": "Вот держи зачетку с оценкой.",
"ru": "Вот держи зачетку с оценкой.", "ru": "Вот держи зачетку с оценкой.",
"en": "Here, take the record book with the grade." "en": "Here, take the grade book with the grade."
}, },
{ {
"key": "Теперь иди в теневой мир и покажи зачетку Бегимай. Расскажи ей, что она сдала курсовую работу.", "key": "Теперь иди в теневой мир и покажи зачетку Бегимай. Расскажи ей, что она сдала курсовую работу.",
"ru": "Теперь иди в теневой мир и покажи зачетку Бегимай. Расскажи ей, что она сдала курсовую работу.", "ru": "Теперь иди в теневой мир и покажи зачетку Бегимай. Расскажи ей, что она сдала курсовую работу.",
"en": "Now go to the Darkland and show Begimai her record book. Tell her she passed her coursework." "en": "Now go to the dark lands and show Begimai her grade book. Tell her she passed her coursework."
}, },
{ {
"key": "Мне стоит вернуть книгу на место, прежде чем уходить из библиотеки.", "key": "Мне стоит вернуть книгу на место, прежде чем уходить из библиотеки.",
@ -958,7 +958,7 @@
{ {
"key": "Иначе Аида Дженибековна меня убъет.", "key": "Иначе Аида Дженибековна меня убъет.",
"ru": "Иначе Аида Дженибековна меня убъет.", "ru": "Иначе Аида Дженибековна меня убъет.",
"en": "Otherwise Aida Dzhenibekovna will kill me." "en": "Otherwise Aida Dzhanybekovna will kill me."
}, },
{ {
"key": "Куда я попал?", "key": "Куда я попал?",
@ -1023,7 +1023,7 @@
{ {
"key": "Я уйду на покой только когда увиду оценку по курсовой в своей зачетке.", "key": "Я уйду на покой только когда увиду оценку по курсовой в своей зачетке.",
"ru": "Я уйду на покой только когда увиду оценку по курсовой в своей зачетке.", "ru": "Я уйду на покой только когда увиду оценку по курсовой в своей зачетке.",
"en": "I will retire only when I see the grade for my coursework in my record book." "en": "I will retire only when I see the grade for my coursework in my grade book."
}, },
{ {
"key": "А до тех пор, я буду появлятся здесь каждую ночь.", "key": "А до тех пор, я буду появлятся здесь каждую ночь.",
@ -1048,7 +1048,7 @@
{ {
"key": "Это твоя зачетка.", "key": "Это твоя зачетка.",
"ru": "Это твоя зачетка.", "ru": "Это твоя зачетка.",
"en": "This is your record book." "en": "This is your grade book."
}, },
{ {
"key": "Тебе поставили за курсовую максимальный балл!", "key": "Тебе поставили за курсовую максимальный балл!",
@ -1143,17 +1143,17 @@
{ {
"key": "Тут лежат зачетные книжки студентов.", "key": "Тут лежат зачетные книжки студентов.",
"ru": "Тут лежат зачетные книжки студентов.", "ru": "Тут лежат зачетные книжки студентов.",
"en": "Students' record books are kept here." "en": "Students' grade books are kept here."
}, },
{ {
"key": "Здесь лежит зачетка Бегимай. Я пожалуй, возьму ее.", "key": "Здесь лежит зачетка Бегимай. Я пожалуй, возьму ее.",
"ru": "Здесь лежит зачетка Бегимай. Я пожалуй, возьму ее.", "ru": "Здесь лежит зачетка Бегимай. Я пожалуй, возьму ее.",
"en": "Begimai's record book is here. I think I'll take it." "en": "Begimai's grade book is here. I think I'll take it."
}, },
{ {
"key": "Мне еще рано возвращать зачетку Бегимай обратно в шкаф.", "key": "Мне еще рано возвращать зачетку Бегимай обратно в шкаф.",
"ru": "Мне еще рано возвращать зачетку Бегимай обратно в шкаф.", "ru": "Мне еще рано возвращать зачетку Бегимай обратно в шкаф.",
"en": "It's too early for me to return Begimai's record book back to the shelf." "en": "It's too early for me to return Begimai's grade book back to the shelf."
}, },
{ {
"key": "Чтобы заказать такси, я сначала должен выйти на улицу.", "key": "Чтобы заказать такси, я сначала должен выйти на улицу.",
@ -1358,12 +1358,12 @@
{ {
"key": "Ко мне попала странная записка, где было написано заклинание. Я прочитал его, и попал в потусторонний мир.", "key": "Ко мне попала странная записка, где было написано заклинание. Я прочитал его, и попал в потусторонний мир.",
"ru": "Ко мне попала странная записка, где было написано заклинание. Я прочитал его, и попал в потусторонний мир.", "ru": "Ко мне попала странная записка, где было написано заклинание. Я прочитал его, и попал в потусторонний мир.",
"en": "I received a strange note containing a spell. I read it and found myself in the Darkland." "en": "I received a strange note containing a spell. I read it and found myself in the dark lands."
}, },
{ {
"key": "Ты что опять в свои игры переиграл? Какой потусторонний мир?", "key": "Ты что опять в свои игры переиграл? Какой потусторонний мир?",
"ru": "Ты что опять в свои игры переиграл? Какой потусторонний мир?", "ru": "Ты что опять в свои игры переиграл? Какой потусторонний мир?",
"en": "Are you playing your video games too much? What Darkland?" "en": "Are you playing your video games too much? What dark lands?"
}, },
{ {
"key": "Но это правда! Там все было по-другому, и я видел призраков...", "key": "Но это правда! Там все было по-другому, и я видел призраков...",
@ -1378,7 +1378,7 @@
{ {
"key": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.", "key": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.",
"ru": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.", "ru": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.",
"en": "Aida Dzhenibekovna saw me in the teachers' room and made me write an essay on this stupid book." "en": "Aida Dzhanybekovna saw me in the teachers' room and made me write an essay on this stupid book."
}, },
{ {
"key": "Я писал, писал и уснул.", "key": "Я писал, писал и уснул.",
@ -1448,7 +1448,7 @@
{ {
"key": "Поэтому, с этого года у нас этот предмет ведет Аида Дженибековна.", "key": "Поэтому, с этого года у нас этот предмет ведет Аида Дженибековна.",
"ru": "Поэтому, с этого года у нас этот предмет ведет Аида Дженибековна.", "ru": "Поэтому, с этого года у нас этот предмет ведет Аида Дженибековна.",
"en": "Therefore, starting this year, Aida Dzhenibekovna has been teaching this subject." "en": "Therefore, starting this year, Aida Dzhanybekovna has been teaching this subject."
}, },
{ {
"key": "Ты не знаешь кого-нибудь, кто был знаком с Бегимай?", "key": "Ты не знаешь кого-нибудь, кто был знаком с Бегимай?",
@ -1473,7 +1473,7 @@
{ {
"key": "Я лучше прочитаю заклинание, перейду в теневой мир и так выберусь наружу.", "key": "Я лучше прочитаю заклинание, перейду в теневой мир и так выберусь наружу.",
"ru": "Я лучше прочитаю заклинание, перейду в теневой мир и так выберусь наружу.", "ru": "Я лучше прочитаю заклинание, перейду в теневой мир и так выберусь наружу.",
"en": "I'd rather cast a spell, go into the Darkland, and get out that way." "en": "I'd rather cast a spell, go into the dark lands, and get out that way."
}, },
{ {
"key": "Я не буду беспокоить Айпери. Я могу выбраться из универа и без ее помощи.", "key": "Я не буду беспокоить Айпери. Я могу выбраться из универа и без ее помощи.",
@ -1588,12 +1588,12 @@
{ {
"key": "Я попал в теневой мир, чтобы выбраться из запертого кабинета.", "key": "Я попал в теневой мир, чтобы выбраться из запертого кабинета.",
"ru": "Я попал в теневой мир, чтобы выбраться из запертого кабинета.", "ru": "Я попал в теневой мир, чтобы выбраться из запертого кабинета.",
"en": "I entered the Darkland to escape from the locked room." "en": "I entered the dark lands to escape from the locked room."
}, },
{ {
"key": "Но по злой иронии, когда я вернулся из теневого мира, я снова оказался за запертой дверью.", "key": "Но по злой иронии, когда я вернулся из теневого мира, я снова оказался за запертой дверью.",
"ru": "Но по злой иронии, когда я вернулся из теневого мира, я снова оказался за запертой дверью.", "ru": "Но по злой иронии, когда я вернулся из теневого мира, я снова оказался за запертой дверью.",
"en": "But by a cruel irony, when I returned from the Darkland, I found myself behind a locked door again." "en": "But by a cruel irony, when I returned from the dark lands, I found myself behind a locked door again."
}, },
{ {
"key": "Хорошо, что уже утро. Надо написать Айпери, может она уже скоро приедет в универ?", "key": "Хорошо, что уже утро. Надо написать Айпери, может она уже скоро приедет в универ?",
@ -1623,7 +1623,7 @@
{ {
"key": "Здравствуйте Аида Дженибековна!", "key": "Здравствуйте Аида Дженибековна!",
"ru": "Здравствуйте Аида Дженибековна!", "ru": "Здравствуйте Аида Дженибековна!",
"en": "Hello Aida Dzhenibekovna!" "en": "Hello Aida Dzhanybekovna!"
}, },
{ {
"key": "Ну что Бекзат, эссе про Жусупа Мамая готово к сдаче?", "key": "Ну что Бекзат, эссе про Жусупа Мамая готово к сдаче?",
@ -1683,12 +1683,12 @@
{ {
"key": "Это заклинание переместило тебя в мир теней. ", "key": "Это заклинание переместило тебя в мир теней. ",
"ru": "Это заклинание переместило тебя в мир теней.", "ru": "Это заклинание переместило тебя в мир теней.",
"en": "This spell transported you to the Darkland." "en": "This spell transported you to the dark lands."
}, },
{ {
"key": "Этот мир предназначен для фей и джиннов. Обычные люди туда попасть не могут. Но у тебя это как-то получилось.", "key": "Этот мир предназначен для фей и джиннов. Обычные люди туда попасть не могут. Но у тебя это как-то получилось.",
"ru": "Этот мир предназначен для фей и джиннов. Обычные люди туда попасть не могут. Но у тебя это как-то получилось.", "ru": "Этот мир предназначен для фей и джиннов. Обычные люди туда попасть не могут. Но у тебя это как-то получилось.",
"en": "This land is meant for fairies and genies. Ordinary people can't get there. But somehow you made it." "en": "These lands are meant for fairies and genies. Ordinary people can't get there. But somehow you made it."
}, },
{ {
"key": "А вы откуда все это знаете?", "key": "А вы откуда все это знаете?",
@ -1723,12 +1723,12 @@
{ {
"key": "А на утро бывает так, что все зачетки в шкафу раскиданы как попало.", "key": "А на утро бывает так, что все зачетки в шкафу раскиданы как попало.",
"ru": "А на утро бывает так, что все зачетки в шкафу раскиданы как попало.", "ru": "А на утро бывает так, что все зачетки в шкафу раскиданы как попало.",
"en": "And in the morning it happens that all the record books in the closet are scattered all over the place." "en": "And in the morning it happens that all the grade books in the closet are scattered all over the place."
}, },
{ {
"key": "Призрак обитает где-то в теневом мире. Попади туда и наладь с ним диалог. Узнай, кто он и что ему нужно.", "key": "Призрак обитает где-то в теневом мире. Попади туда и наладь с ним диалог. Узнай, кто он и что ему нужно.",
"ru": "Призрак обитает где-то в теневом мире. Попади туда и наладь с ним диалог. Узнай, кто он и что ему нужно.", "ru": "Призрак обитает где-то в теневом мире. Попади туда и наладь с ним диалог. Узнай, кто он и что ему нужно.",
"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." "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."
}, },
{ {
"key": "Если ты успешно избавишься от призрака, я поставлю тебе максимальный балл за модуль.", "key": "Если ты успешно избавишься от призрака, я поставлю тебе максимальный балл за модуль.",
@ -1753,7 +1753,7 @@
{ {
"key": "Обсудить теневой мир", "key": "Обсудить теневой мир",
"ru": "Обсудить теневой мир", "ru": "Обсудить теневой мир",
"en": "Discuss the Darkland" "en": "Discuss the dark lands"
}, },
{ {
"key": "Обсудить записку", "key": "Обсудить записку",
@ -1763,12 +1763,12 @@
{ {
"key": "Что это вообще за теневой мир? Откуда он взялся?", "key": "Что это вообще за теневой мир? Откуда он взялся?",
"ru": "Что это вообще за теневой мир? Откуда он взялся?", "ru": "Что это вообще за теневой мир? Откуда он взялся?",
"en": "What is this Darkland anyway? Where did it come from?" "en": "What is this dark lands anyway? Where did it come from?"
}, },
{ {
"key": "Теневой мир это мир, в котором обитают феи, духи и джинны. Он существовал всегда.", "key": "Теневой мир это мир, в котором обитают феи, духи и джинны. Он существовал всегда.",
"ru": "Теневой мир это мир, в котором обитают феи, духи и джинны. Он существовал всегда.", "ru": "Теневой мир это мир, в котором обитают феи, духи и джинны. Он существовал всегда.",
"en": "The Darkland is a world inhabited by fairies, spirits, and genies. It has always existed." "en": "The Dark Lands is a world inhabited by fairies, spirits, and genies. It has always existed."
}, },
{ {
"key": "Обычные люди, не воители, туда не попадают обычно. Кроме тебя, тебе это как-то удалось.", "key": "Обычные люди, не воители, туда не попадают обычно. Кроме тебя, тебе это как-то удалось.",
@ -1783,22 +1783,22 @@
{ {
"key": "Время в теневом мире", "key": "Время в теневом мире",
"ru": "Время в теневом мире", "ru": "Время в теневом мире",
"en": "Time in the Darkland" "en": "Time in the dark lands"
}, },
{ {
"key": "Призраки в теневом мире", "key": "Призраки в теневом мире",
"ru": "Призраки в теневом мире", "ru": "Призраки в теневом мире",
"en": "Ghosts in the Darkland" "en": "Ghosts in the dark lands"
}, },
{ {
"key": "Двери в теневом мире", "key": "Двери в теневом мире",
"ru": "Двери в теневом мире", "ru": "Двери в теневом мире",
"en": "Doors in the Darkland" "en": "Doors in the dark lands"
}, },
{ {
"key": "Выход из теневого мира", "key": "Выход из теневого мира",
"ru": "Выход из теневого мира", "ru": "Выход из теневого мира",
"en": "Exit from the Darkland" "en": "Exit from the dark lands"
}, },
{ {
"key": "Достаточно", "key": "Достаточно",
@ -1808,37 +1808,37 @@
{ {
"key": "Почему в теневом мире нет дверей?", "key": "Почему в теневом мире нет дверей?",
"ru": "Почему в теневом мире нет дверей?", "ru": "Почему в теневом мире нет дверей?",
"en": "Why are there no doors in the Darkland?" "en": "Why are there no doors in the dark lands?"
}, },
{ {
"key": "Я думаю, в мир теней проецируются только фундаментальные объекты, типа зданий, деревьев, рельефа.", "key": "Я думаю, в мир теней проецируются только фундаментальные объекты, типа зданий, деревьев, рельефа.",
"ru": "Я думаю, в мир теней проецируются только фундаментальные объекты, типа зданий, деревьев, рельефа.", "ru": "Я думаю, в мир теней проецируются только фундаментальные объекты, типа зданий, деревьев, рельефа.",
"en": "I think that only fundamental objects, such as buildings, trees, and terrain, are projected into the Darkland." "en": "I think that only fundamental objects, such as buildings, trees, and terrain, are projected into the dark lands."
}, },
{ {
"key": "Это происходит потому, что в теневом мире время течет сильно медленнее, чем в нашем мире.", "key": "Это происходит потому, что в теневом мире время течет сильно медленнее, чем в нашем мире.",
"ru": "Это происходит потому, что в теневом мире время течет сильно медленнее, чем в нашем мире.", "ru": "Это происходит потому, что в теневом мире время течет сильно медленнее, чем в нашем мире.",
"en": "This happens because in the Darkland time flows much slower than in our world." "en": "This happens because in the dark lands time flows much slower than in our world."
}, },
{ {
"key": "Двери у нас слишком часто открываются и закрываются, поэтому в теневом мире они \"размываются\" или попадают в суперпозицию.", "key": "Двери у нас слишком часто открываются и закрываются, поэтому в теневом мире они \"размываются\" или попадают в суперпозицию.",
"ru": "Двери у нас слишком часто открываются и закрываются, поэтому в теневом мире они \"размываются\" или попадают в суперпозицию.", "ru": "Двери у нас слишком часто открываются и закрываются, поэтому в теневом мире они \"размываются\" или попадают в суперпозицию.",
"en": "Our doors open and close too often, so in the Darkland they become \"blurred\" or fall into some sort of superposition." "en": "Our doors open and close too often, so in the dark lands they become \"blurred\" or fall into some sort of superposition."
}, },
{ {
"key": "Поэтому в мире теней ты можешь спокойно пройти через дверной проем. Но сквозь стены ты пройти не сможешь, они более плотные.", "key": "Поэтому в мире теней ты можешь спокойно пройти через дверной проем. Но сквозь стены ты пройти не сможешь, они более плотные.",
"ru": "Поэтому в мире теней ты можешь спокойно пройти через дверной проем. Но сквозь стены ты пройти не сможешь, они более плотные.", "ru": "Поэтому в мире теней ты можешь спокойно пройти через дверной проем. Но сквозь стены ты пройти не сможешь, они более плотные.",
"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." "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."
}, },
{ {
"key": "Откуда в теневом мире призраки?", "key": "Откуда в теневом мире призраки?",
"ru": "Откуда в теневом мире призраки?", "ru": "Откуда в теневом мире призраки?",
"en": "Where do ghosts come from in the Darkland?" "en": "Where do ghosts come from in the dark lands?"
}, },
{ {
"key": "Это их мир, и они там живут. Я думаю что увидев тебя, обитатели теневого мира тоже были шокированы.", "key": "Это их мир, и они там живут. Я думаю что увидев тебя, обитатели теневого мира тоже были шокированы.",
"ru": "Это их мир, и они там живут. Я думаю что увидев тебя, обитатели теневого мира тоже были шокированы.", "ru": "Это их мир, и они там живут. Я думаю что увидев тебя, обитатели теневого мира тоже были шокированы.",
"en": "This is their world, and they live there. I think the inhabitants of the Darkland were also shocked when they saw you." "en": "This is their world, and they live there. I think the inhabitants of the dark lands were also shocked when they saw you."
}, },
{ {
"key": "Но кое в чем ты прав. Здесь в универе, призраков раньше не было. Всякая чертовщина начала происходить совсем недавно.", "key": "Но кое в чем ты прав. Здесь в универе, призраков раньше не было. Всякая чертовщина начала происходить совсем недавно.",
@ -1863,12 +1863,12 @@
{ {
"key": "Каждый раз, когда я выпадаю из теневого мира, наступает утро. Хотя я там был всего несколько минут.", "key": "Каждый раз, когда я выпадаю из теневого мира, наступает утро. Хотя я там был всего несколько минут.",
"ru": "Каждый раз, когда я выпадаю из теневого мира, наступает утро. Хотя я там был всего несколько минут.", "ru": "Каждый раз, когда я выпадаю из теневого мира, наступает утро. Хотя я там был всего несколько минут.",
"en": "Every time I emerge from the Darkland, morning comes, even though I was only there for a few minutes." "en": "Every time I emerge from the dark lands, morning comes, even though I was only there for a few minutes."
}, },
{ {
"key": "Время в мире теней течет значительно медленнее, чем в нашем.", "key": "Время в мире теней течет значительно медленнее, чем в нашем.",
"ru": "Время в мире теней течет значительно медленнее, чем в нашем.", "ru": "Время в мире теней течет значительно медленнее, чем в нашем.",
"en": "Time in the Darkland flows much slower than in ours." "en": "Time in the dark lands flows much slower than in ours."
}, },
{ {
"key": "И похоже, что течение времени там нелинейное.", "key": "И похоже, что течение времени там нелинейное.",
@ -1878,22 +1878,22 @@
{ {
"key": "Это объясняет, почему в теневом мире есть деревья и строения, но отсутствуют двери, автомобили, и прочее.", "key": "Это объясняет, почему в теневом мире есть деревья и строения, но отсутствуют двери, автомобили, и прочее.",
"ru": "Это объясняет, почему в теневом мире есть деревья и строения, но отсутствуют двери, автомобили, и прочее.", "ru": "Это объясняет, почему в теневом мире есть деревья и строения, но отсутствуют двери, автомобили, и прочее.",
"en": "This explains why the Darkland has trees and buildings, but no doors, cars, etc." "en": "This explains why the dark lands has trees and buildings, but no doors, cars, etc."
}, },
{ {
"key": "Объекты в нашем мире должны долго находится на одном месте, прежде чем их проекция появится в мире теней.", "key": "Объекты в нашем мире должны долго находится на одном месте, прежде чем их проекция появится в мире теней.",
"ru": "Объекты в нашем мире должны долго находится на одном месте, прежде чем их проекция появится в мире теней.", "ru": "Объекты в нашем мире должны долго находится на одном месте, прежде чем их проекция появится в мире теней.",
"en": "Objects in our world must remain in one place for a long time before their projection appears in the Darkland." "en": "Objects in our world must remain in one place for a long time before their projection appears in the dark lands."
}, },
{ {
"key": "Меня постоянно выкидывает из теневого мира. Как я могу там остаться подольше?", "key": "Меня постоянно выкидывает из теневого мира. Как я могу там остаться подольше?",
"ru": "Меня постоянно выкидывает из теневого мира. Как я могу там остаться подольше?", "ru": "Меня постоянно выкидывает из теневого мира. Как я могу там остаться подольше?",
"en": "I'm constantly being thrown out of the Darkland. How can I stay there any longer?" "en": "I'm constantly being thrown out of the dark lands. How can I stay there any longer?"
}, },
{ {
"key": "Если ты потеряешь сознание в теневом мире, то ты принудительно вернешься в наш мир.", "key": "Если ты потеряешь сознание в теневом мире, то ты принудительно вернешься в наш мир.",
"ru": "Если ты потеряешь сознание в теневом мире, то ты принудительно вернешься в наш мир.", "ru": "Если ты потеряешь сознание в теневом мире, то ты принудительно вернешься в наш мир.",
"en": "If you lose consciousness in the Darkland, you will be forcibly returned to our world." "en": "If you lose consciousness in the dark lands, you will be forcibly returned to our world."
}, },
{ {
"key": "Также избегай света луны, луна тоже может вернуть тебя в мир людей. Лучше вообще не выходи из помещения. ", "key": "Также избегай света луны, луна тоже может вернуть тебя в мир людей. Лучше вообще не выходи из помещения. ",
@ -1903,17 +1903,17 @@
{ {
"key": "Если ты задержишься, и в человеческом мире настанет утро, тебя тоже выбросит из теневого мира.", "key": "Если ты задержишься, и в человеческом мире настанет утро, тебя тоже выбросит из теневого мира.",
"ru": "Если ты задержишься, и в человеческом мире настанет утро, тебя тоже выбросит из теневого мира.", "ru": "Если ты задержишься, и в человеческом мире настанет утро, тебя тоже выбросит из теневого мира.",
"en": "If you stay too long and morning comes in the human world, you will be thrown out of the Darkland too." "en": "If you stay too long and morning comes in the human world, you will be thrown out of the dark lands too."
}, },
{ {
"key": "Но ты не беспокойся. Даже если тебя выбросит из теневого мира, ты всегда сможешь повторить свою попытку на следующую ночь.", "key": "Но ты не беспокойся. Даже если тебя выбросит из теневого мира, ты всегда сможешь повторить свою попытку на следующую ночь.",
"ru": "Но ты не беспокойся. Даже если тебя выбросит из теневого мира, ты всегда сможешь повторить свою попытку на следующую ночь.", "ru": "Но ты не беспокойся. Даже если тебя выбросит из теневого мира, ты всегда сможешь повторить свою попытку на следующую ночь.",
"en": "But don't worry. Even if you're thrown out of the Darkland, you can always try again the next night." "en": "But don't worry. Even if you're thrown out of the dark lands, you can always try again the next night."
}, },
{ {
"key": "У тебя есть еще вопросы про теневой мир?", "key": "У тебя есть еще вопросы про теневой мир?",
"ru": "У тебя есть еще вопросы про теневой мир?", "ru": "У тебя есть еще вопросы про теневой мир?",
"en": "Do you have any more questions about the Darkland?" "en": "Do you have any more questions about the dark lands?"
}, },
{ {
"key": "Ладно, я узнал достаточно.", "key": "Ладно, я узнал достаточно.",
@ -1938,7 +1938,7 @@
{ {
"key": "Когда я узнала, что в университете начались паранормальные явления, я сразу поняла что это призрак в теневом мире.", "key": "Когда я узнала, что в университете начались паранормальные явления, я сразу поняла что это призрак в теневом мире.",
"ru": "Когда я узнала, что в университете начались паранормальные явления, я сразу поняла что это призрак в теневом мире.", "ru": "Когда я узнала, что в университете начались паранормальные явления, я сразу поняла что это призрак в теневом мире.",
"en": "When I learned that paranormal phenomena had begun at the university, I immediately realized that it was a ghost in the Darkland." "en": "When I learned that paranormal phenomena had begun at the university, I immediately realized that it was a ghost in the dark lands."
}, },
{ {
"key": "Я знаю заклинание, которое тебе дали. У этого заклинания есть еще одно условие - оно сработает только на людей, которые чисты сердцем.", "key": "Я знаю заклинание, которое тебе дали. У этого заклинания есть еще одно условие - оно сработает только на людей, которые чисты сердцем.",
@ -1953,12 +1953,12 @@
{ {
"key": "Тогда я предположила, что Айпери уж точно сможет прочитать заклинание и попасть в теневой мир.", "key": "Тогда я предположила, что Айпери уж точно сможет прочитать заклинание и попасть в теневой мир.",
"ru": "Тогда я предположила, что Айпери уж точно сможет прочитать заклинание и попасть в теневой мир.", "ru": "Тогда я предположила, что Айпери уж точно сможет прочитать заклинание и попасть в теневой мир.",
"en": "Then I assumed that Aiperi would definitely be able to cast the spell and enter the Darkland." "en": "Then I assumed that Aiperi would definitely be able to cast the spell and enter the dark lands."
}, },
{ {
"key": "Как раз поэтому я хотела вернуть нож ей лично. И заодно отправить ее в теневой мир.", "key": "Как раз поэтому я хотела вернуть нож ей лично. И заодно отправить ее в теневой мир.",
"ru": "Как раз поэтому я хотела вернуть нож ей лично. И заодно отправить ее в теневой мир.", "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 Darkland." "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."
}, },
{ {
"key": "Но ты смешал все мои планы. Сначала ты забрал нож, а потом кто-то подсунул тебе записку.", "key": "Но ты смешал все мои планы. Сначала ты забрал нож, а потом кто-то подсунул тебе записку.",
@ -2068,7 +2068,7 @@
{ {
"key": "Переходи в теневой мир, а затем иди в лекционный зал.", "key": "Переходи в теневой мир, а затем иди в лекционный зал.",
"ru": "Переходи в теневой мир, а затем иди в лекционный зал.", "ru": "Переходи в теневой мир, а затем иди в лекционный зал.",
"en": "Go to the Darkland and then go to the lecture hall." "en": "Go to the dark lands and then go to the lecture hall."
}, },
{ {
"key": "Постарайся поговорить с призраком, и узнать, что ему нужно.", "key": "Постарайся поговорить с призраком, и узнать, что ему нужно.",
@ -2093,7 +2093,7 @@
{ {
"key": "Иди к шкафу в учительской. Среди зачетных книжек найти зачетку Бегимай и принеси мне. Я поставлю ей оценку за курсовую работу.", "key": "Иди к шкафу в учительской. Среди зачетных книжек найти зачетку Бегимай и принеси мне. Я поставлю ей оценку за курсовую работу.",
"ru": "Иди к шкафу в учительской. Среди зачетных книжек найти зачетку Бегимай и принеси мне. Я поставлю ей оценку за курсовую работу.", "ru": "Иди к шкафу в учительской. Среди зачетных книжек найти зачетку Бегимай и принеси мне. Я поставлю ей оценку за курсовую работу.",
"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." "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."
}, },
{ {
"key": "Ты уже нашел курсовую работу Бегимай?", "key": "Ты уже нашел курсовую работу Бегимай?",
@ -2153,7 +2153,7 @@
{ {
"key": "Теперь поищи в шкафу зачетку Бегимай, и принеси мне, я выставлю ей оценку.", "key": "Теперь поищи в шкафу зачетку Бегимай, и принеси мне, я выставлю ей оценку.",
"ru": "Теперь поищи в шкафу зачетку Бегимай, и принеси мне, я выставлю ей оценку.", "ru": "Теперь поищи в шкафу зачетку Бегимай, и принеси мне, я выставлю ей оценку.",
"en": "Now look in the cabinet for Begimai's record book and bring it to me, I'll give her a grade." "en": "Now look in the cabinet for Begimai's grade book and bring it to me, I'll give her a grade."
}, },
{ {
"key": "Еще нет.", "key": "Еще нет.",
@ -2173,7 +2173,7 @@
{ {
"key": "Но если я возьму кусочек, Аида Дженибековна меня убьет.", "key": "Но если я возьму кусочек, Аида Дженибековна меня убьет.",
"ru": "Но если я возьму кусочек, Аида Дженибековна меня убьет.", "ru": "Но если я возьму кусочек, Аида Дженибековна меня убьет.",
"en": "But if I take a piece, Aida Dzhenibekovna will kill me." "en": "But if I take a piece, Aida Dzhanybekovna will kill me."
}, },
{ {
"key": "Здесь лежат книги по юриспруденции и праву.", "key": "Здесь лежат книги по юриспруденции и праву.",
@ -2183,12 +2183,12 @@
{ {
"key": "Бекзат, помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.", "key": "Бекзат, помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.",
"ru": "Бекзат, помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.", "ru": "Бекзат, помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.",
"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." "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."
}, },
{ {
"key": "Помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.", "key": "Помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.",
"ru": "Помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.", "ru": "Помнишь мы скидывались на торт для Аиды Дженибековной? Я тогда еще приносила скатерть, тарелки и серебряный нож для торта. И я до сих пор не получила назад ничего.",
"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." "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."
}, },
{ {
"key": "А во вторых, мне как-то не хочется попадаться на глаза Аиде.", "key": "А во вторых, мне как-то не хочется попадаться на глаза Аиде.",
@ -2198,7 +2198,7 @@
{ {
"key": "Да... Только не говори Аиде Дженибековной что я здесь.", "key": "Да... Только не говори Аиде Дженибековной что я здесь.",
"ru": "Да... Только не говори Аиде Дженибековной что я здесь.", "ru": "Да... Только не говори Аиде Дженибековной что я здесь.",
"en": "Yes... Just don't tell Aida Dzhenibekovna that I'm here." "en": "Yes... Just don't tell Aida Dzhanybekovna that I'm here."
}, },
{ {
"key": "Постарайся только Аиде Дженибековной особо на глаза не попадаться. ", "key": "Постарайся только Аиде Дженибековной особо на глаза не попадаться. ",
@ -2233,7 +2233,7 @@
{ {
"key": "Я уйду на покой только когда увижу оценку по курсовой в своей зачетке.", "key": "Я уйду на покой только когда увижу оценку по курсовой в своей зачетке.",
"ru": "Я уйду на покой только когда увижу оценку по курсовой в своей зачетке.", "ru": "Я уйду на покой только когда увижу оценку по курсовой в своей зачетке.",
"en": "I will rest in peace only when I see the grade for my coursework in my record book." "en": "I will rest in peace only when I see the grade for my coursework in my grade book."
}, },
{ {
"key": "А до тех пор, я буду появляться здесь каждую ночь.", "key": "А до тех пор, я буду появляться здесь каждую ночь.",
@ -2248,67 +2248,17 @@
{ {
"key": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.", "key": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.",
"ru": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.", "ru": "Аида Дженибековна увидела меня в учительской, и заставила писать эссе по этой тупой книге.",
"en": "Aida Dzhenibekovna saw me in the teachers' room and made me write an essay on this stupid book." "en": "Aida Dzhanybekovna saw me in the teachers' room and made me write an essay on this stupid book."
}, },
{ {
"key": "Прошу прощения Аида Дженибековна, я правда старался, но у меня возникли обстоятельства непреодолимой силы.", "key": "Прошу прощения Аида Дженибековна, я правда старался, но у меня возникли обстоятельства непреодолимой силы.",
"ru": "Прошу прощения Аида Дженибековна, я правда старался, но у меня возникли обстоятельства непреодолимой силы.", "ru": "Прошу прощения Аида Дженибековна, я правда старался, но у меня возникли обстоятельства непреодолимой силы.",
"en": "I apologize, Aida Dzhenibekovna, I really tried, but I encountered force majeure circumstances." "en": "I apologize, Aida Dzhanybekovna, I really tried, but I encountered force majeure circumstances."
}, },
{ {
"key": "Бекзат, я сейчас занята, не мешай мне!", "key": "Бекзат, я сейчас занята, не мешай мне!",
"ru": "Бекзат, я сейчас занята, не мешай мне!", "ru": "Бекзат, я сейчас занята, не мешай мне!",
"en": "Bekzat, I'm busy now, don't disturb me!" "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 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. Osho!"
},
{
"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, dep."
},
{
"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! Osho!"
} }
] ]
} }

View File

@ -134,7 +134,7 @@
"type": "Line", "type": "Line",
"speaker": "Бекзат", "speaker": "Бекзат",
"portrait": "resources/dialogue/portrait_hero_neutral.png", "portrait": "resources/dialogue/portrait_hero_neutral.png",
"text": "Я заказал такси до общаги, машина уже едет!", "text": "Я заказал такси до общаги, машина уже ждет!",
"next": "end_1" "next": "end_1"
}, },
{ {
@ -334,7 +334,7 @@
"type": "Line", "type": "Line",
"speaker": "Бекзат", "speaker": "Бекзат",
"portrait": "resources/dialogue/portrait_phone.png", "portrait": "resources/dialogue/portrait_phone.png",
"text": "Почему ты сама не можешь забрать нож у Аиды?", "text": "Почему ты сама не можешь забрать?",
"next": "line_9", "next": "line_9",
"chatBubble": "out" "chatBubble": "out"
}, },
@ -343,16 +343,7 @@
"type": "Line", "type": "Line",
"speaker": "Айпери", "speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png", "portrait": "resources/dialogue/portrait_phone.png",
"text": "Она опять будет скафнить.", "text": "Ты же знаешь, если я встречу Аиду, она 100% даст мне какое-нибудь сложное задание.",
"next": "line_12",
"chatBubble": "in"
},
{
"id": "line_12",
"type": "Line",
"speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png",
"text": "А может быть даже даст мне какое-нибудь сложное задание.",
"next": "line_10", "next": "line_10",
"chatBubble": "in" "chatBubble": "in"
}, },
@ -370,7 +361,7 @@
"type": "Line", "type": "Line",
"speaker": "Айпери", "speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png", "portrait": "resources/dialogue/portrait_phone.png",
"text": "Так что жду тебя в универе! Не вздумай прогулять! Ошо!", "text": "Так что жду тебя в универе! Не вздумай прогулять!",
"next": "setflag_1", "next": "setflag_1",
"chatBubble": "in", "chatBubble": "in",
"questUnlock": "aiperi_knife" "questUnlock": "aiperi_knife"
@ -412,70 +403,6 @@
} }
] ]
}, },
{
"id": "dialog_chat_aiperi003",
"start": "line_1",
"nodes": [
{
"id": "line_1",
"type": "Line",
"speaker": "Бекзат",
"portrait": "resources/dialogue/portrait_phone.png",
"text": "Айпери ты куда убежала?",
"next": "line_2",
"chatBubble": "out"
},
{
"id": "line_2",
"type": "Line",
"speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png",
"text": "Ты достал уже, я не могу тебя ждать вечно! Возишься как сонная муха.",
"next": "line_3",
"chatBubble": "in"
},
{
"id": "line_3",
"type": "Line",
"speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png",
"text": "У меня нет времени ждать тебя, мне уже пора на курсы ехать.",
"next": "line_4",
"chatBubble": "in"
},
{
"id": "line_4",
"type": "Line",
"speaker": "Бекзат",
"portrait": "resources/dialogue/portrait_phone.png",
"text": "Так когда я тебе нож верну?",
"next": "line_5",
"chatBubble": "out"
},
{
"id": "line_5",
"type": "Line",
"speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png",
"text": "Приходи завтра в универ! Я каждый день в унике с раннего утра.",
"next": "line_6",
"chatBubble": "in"
},
{
"id": "line_6",
"type": "Line",
"speaker": "Бекзат",
"portrait": "resources/dialogue/portrait_phone.png",
"text": "Хорошо, встретимся завтра.",
"next": "end_1",
"chatBubble": "out"
},
{
"id": "end_1",
"type": "End"
}
]
},
{ {
"id": "dialog_car001", "id": "dialog_car001",
"start": "line_1", "start": "line_1",

View File

@ -220,15 +220,7 @@
"type": "Line", "type": "Line",
"speaker": "Айпери", "speaker": "Айпери",
"portrait": "resources/dialogue/portrait_aiperi.png", "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" "next": "line_16"
}, },
{ {
@ -300,7 +292,7 @@
"type": "Line", "type": "Line",
"speaker": "Айпери", "speaker": "Айпери",
"portrait": "resources/dialogue/portrait_aiperi.png", "portrait": "resources/dialogue/portrait_aiperi.png",
"text": "Я уверена, она меня ищет. Она хочет мне какое-то задание дать.", "text": "Она меня ищет, я уверена, она хочет мне какое-то задание дать.",
"next": "line_26" "next": "line_26"
}, },
{ {
@ -346,7 +338,7 @@
"type": "Line", "type": "Line",
"speaker": "Айпери", "speaker": "Айпери",
"portrait": "resources/dialogue/portrait_aiperi.png", "portrait": "resources/dialogue/portrait_aiperi.png",
"text": "Пока ты не принесешь мне нож из учительской, никуда я тебя не выпущу. Ошо!", "text": "Пока ты не принесешь мне нож из учительской, никуда я тебя не выпущу.",
"next": "end_1" "next": "end_1"
}, },
{ {
@ -2393,15 +2385,7 @@
"type": "Line", "type": "Line",
"speaker": "Айпери", "speaker": "Айпери",
"portrait": "resources/dialogue/portrait_aiperi.png", "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" "next": "line_21"
}, },
{ {
@ -2587,7 +2571,7 @@
"type": "Line", "type": "Line",
"speaker": "Бекзат", "speaker": "Бекзат",
"portrait": "resources/dialogue/portrait_phone.png", "portrait": "resources/dialogue/portrait_phone.png",
"text": "Почему ты сама не можешь забрать нож у Аиды?", "text": "Почему ты сама не можешь забрать?",
"next": "line_9", "next": "line_9",
"chatBubble": "out" "chatBubble": "out"
}, },
@ -2596,16 +2580,7 @@
"type": "Line", "type": "Line",
"speaker": "Айпери", "speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png", "portrait": "resources/dialogue/portrait_phone.png",
"text": "Да она надоела, все время скафнит.", "text": "Ты же знаешь, если я встречу Аиду, она 100% даст мне какое-нибудь сложное задание.",
"next": "line_12",
"chatBubble": "in"
},
{
"id": "line_12",
"type": "Line",
"speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png",
"text": "Если я ее встречу, она на 100% даст мне какое-нибудь сложное задание.",
"next": "line_10", "next": "line_10",
"chatBubble": "in" "chatBubble": "in"
}, },
@ -2623,7 +2598,7 @@
"type": "Line", "type": "Line",
"speaker": "Айпери", "speaker": "Айпери",
"portrait": "resources/dialogue/portrait_phone.png", "portrait": "resources/dialogue/portrait_phone.png",
"text": "Так что жду тебя в универе! Не вздумай прогулять! Ошо!", "text": "Так что жду тебя в универе! Не вздумай прогулять!",
"next": "setflag_1", "next": "setflag_1",
"chatBubble": "in", "chatBubble": "in",
"questUnlock": "aiperi_knife" "questUnlock": "aiperi_knife"

Binary file not shown.

Binary file not shown.

View File

@ -1,20 +0,0 @@
precision highp float;
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uLightFromCamera;
void main()
{
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
texCoord = vTexCoord;
fragPosLightSpace = uLightFromCamera * ModelViewMatrix * vec4(vPosition, 1.0);
fragNormal = mat3(ModelViewMatrix) * vNormal;
}

View File

@ -1,23 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
varying float fogDistance;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uLightFromCamera;
uniform vec3 uPlayerEyePos;
void main()
{
vec4 eyePos = ModelViewMatrix * vec4(vPosition, 1.0);
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
texCoord = vTexCoord;
fragPosLightSpace = uLightFromCamera * eyePos;
fragNormal = mat3(ModelViewMatrix) * vNormal;
}

View File

@ -1,69 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
attribute vec4 aBoneIndices0;
attribute vec2 aBoneIndices1;
attribute vec4 aBoneWeights0;
attribute vec2 aBoneWeights1;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
varying float fogDistance;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uLightFromCamera;
uniform mat4 uBoneMatrices[58];
uniform vec3 uPlayerEyePos;
void main()
{
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
vec3 skinnedNormal = vec3(0.0, 0.0, 0.0);
vec4 originalPos = vec4(vPosition, 1.0);
float totalWeight = 0.0;
if (aBoneWeights0.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.x)]) * vNormal * aBoneWeights0.x;
totalWeight += aBoneWeights0.x;
}
if (aBoneWeights0.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.y)]) * vNormal * aBoneWeights0.y;
totalWeight += aBoneWeights0.y;
}
if (aBoneWeights0.z > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.z)]) * vNormal * aBoneWeights0.z;
totalWeight += aBoneWeights0.z;
}
if (aBoneWeights0.w > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.w)]) * vNormal * aBoneWeights0.w;
totalWeight += aBoneWeights0.w;
}
if (aBoneWeights1.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.x)]) * vNormal * aBoneWeights1.x;
totalWeight += aBoneWeights1.x;
}
if (aBoneWeights1.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.y)]) * vNormal * aBoneWeights1.y;
totalWeight += aBoneWeights1.y;
}
if (totalWeight < 0.001) {
skinnedPos = originalPos;
skinnedNormal = vNormal;
}
vec4 eyePos = ModelViewMatrix * skinnedPos;
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * skinnedPos;
texCoord = vTexCoord;
fragPosLightSpace = uLightFromCamera * eyePos;
fragNormal = mat3(ModelViewMatrix) * skinnedNormal;
}

View File

@ -1,55 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec4 aBoneIndices0;
attribute vec2 aBoneIndices1;
attribute vec4 aBoneWeights0;
attribute vec2 aBoneWeights1;
varying vec2 texCoord;
varying float fogDistance;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uBoneMatrices[58];
uniform vec3 uPlayerEyePos;
void main()
{
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
vec4 originalPos = vec4(vPosition, 1.0);
float totalWeight = 0.0;
if (aBoneWeights0.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
totalWeight += aBoneWeights0.x;
}
if (aBoneWeights0.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
totalWeight += aBoneWeights0.y;
}
if (aBoneWeights0.z > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
totalWeight += aBoneWeights0.z;
}
if (aBoneWeights0.w > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
totalWeight += aBoneWeights0.w;
}
if (aBoneWeights1.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
totalWeight += aBoneWeights1.x;
}
if (aBoneWeights1.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
totalWeight += aBoneWeights1.y;
}
if (totalWeight < 0.001) {
skinnedPos = originalPos;
}
vec4 eyePos = ModelViewMatrix * skinnedPos;
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * skinnedPos;
texCoord = vTexCoord;
}

View File

@ -1,4 +1,4 @@
precision highp float; precision mediump float;
uniform sampler2D Texture; uniform sampler2D Texture;
uniform sampler2D uShadowMap; uniform sampler2D uShadowMap;

View File

@ -1,28 +0,0 @@
precision highp float;
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
varying float fogDistance;
varying vec3 fragViewPos;
varying vec2 fragWorldXZ;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uLightFromCamera;
uniform vec3 uPlayerEyePos;
void main()
{
vec4 eyePos = ModelViewMatrix * vec4(vPosition, 1.0);
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
texCoord = vTexCoord;
fragViewPos = eyePos.xyz;
fragNormal = mat3(ModelViewMatrix) * vNormal;
fragPosLightSpace = uLightFromCamera * eyePos;
fragWorldXZ = vPosition.xz;
}

View File

@ -1,74 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
attribute vec4 aBoneIndices0;
attribute vec2 aBoneIndices1;
attribute vec4 aBoneWeights0;
attribute vec2 aBoneWeights1;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
varying float fogDistance;
varying vec3 fragViewPos;
varying vec2 fragWorldXZ;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uLightFromCamera;
uniform mat4 uViewInverse;
uniform mat4 uBoneMatrices[58];
uniform vec3 uPlayerEyePos;
void main()
{
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
vec3 skinnedNormal = vec3(0.0, 0.0, 0.0);
vec4 originalPos = vec4(vPosition, 1.0);
float totalWeight = 0.0;
if (aBoneWeights0.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.x)]) * vNormal * aBoneWeights0.x;
totalWeight += aBoneWeights0.x;
}
if (aBoneWeights0.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.y)]) * vNormal * aBoneWeights0.y;
totalWeight += aBoneWeights0.y;
}
if (aBoneWeights0.z > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.z)]) * vNormal * aBoneWeights0.z;
totalWeight += aBoneWeights0.z;
}
if (aBoneWeights0.w > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.w)]) * vNormal * aBoneWeights0.w;
totalWeight += aBoneWeights0.w;
}
if (aBoneWeights1.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.x)]) * vNormal * aBoneWeights1.x;
totalWeight += aBoneWeights1.x;
}
if (aBoneWeights1.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.y)]) * vNormal * aBoneWeights1.y;
totalWeight += aBoneWeights1.y;
}
if (totalWeight < 0.001) {
skinnedPos = originalPos;
skinnedNormal = vNormal;
}
vec4 eyePos = ModelViewMatrix * skinnedPos;
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * skinnedPos;
texCoord = vTexCoord;
fragViewPos = eyePos.xyz;
fragNormal = mat3(ModelViewMatrix) * skinnedNormal;
fragPosLightSpace = uLightFromCamera * eyePos;
fragWorldXZ = (uViewInverse * eyePos).xz;
}

View File

@ -1,71 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
attribute vec4 aBoneIndices0;
attribute vec2 aBoneIndices1;
attribute vec4 aBoneWeights0;
attribute vec2 aBoneWeights1;
varying vec2 texCoord;
varying float fogDistance;
varying vec3 fragViewPos;
varying vec3 fragNormal;
varying vec2 fragWorldXZ;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uViewInverse;
uniform mat4 uBoneMatrices[58];
uniform vec3 uPlayerEyePos;
void main()
{
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
vec3 skinnedNormal = vec3(0.0, 0.0, 0.0);
vec4 originalPos = vec4(vPosition, 1.0);
float totalWeight = 0.0;
if (aBoneWeights0.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.x)]) * vNormal * aBoneWeights0.x;
totalWeight += aBoneWeights0.x;
}
if (aBoneWeights0.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.y)]) * vNormal * aBoneWeights0.y;
totalWeight += aBoneWeights0.y;
}
if (aBoneWeights0.z > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.z)]) * vNormal * aBoneWeights0.z;
totalWeight += aBoneWeights0.z;
}
if (aBoneWeights0.w > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.w)]) * vNormal * aBoneWeights0.w;
totalWeight += aBoneWeights0.w;
}
if (aBoneWeights1.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.x)]) * vNormal * aBoneWeights1.x;
totalWeight += aBoneWeights1.x;
}
if (aBoneWeights1.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.y)]) * vNormal * aBoneWeights1.y;
totalWeight += aBoneWeights1.y;
}
if (totalWeight < 0.001) {
skinnedPos = originalPos;
skinnedNormal = vNormal;
}
vec4 eyePos = ModelViewMatrix * skinnedPos;
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * skinnedPos;
texCoord = vTexCoord;
fragViewPos = eyePos.xyz;
fragNormal = mat3(ModelViewMatrix) * skinnedNormal;
fragWorldXZ = (uViewInverse * eyePos).xz;
}

View File

@ -1,24 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
varying vec2 texCoord;
varying float fogDistance;
varying vec3 fragViewPos;
varying vec3 fragNormal;
varying vec2 fragWorldXZ;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform vec3 uPlayerEyePos;
void main()
{
vec4 eyePos = ModelViewMatrix * vec4(vPosition.xyz, 1.0);
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
texCoord = vTexCoord;
fragViewPos = eyePos.xyz;
fragNormal = mat3(ModelViewMatrix) * vNormal;
fragWorldXZ = vPosition.xz;
}

Some files were not shown because too many files have changed in this diff Show More