Compare commits

...

24 Commits

Author SHA1 Message Date
Vladislav Khorev
a11c4614ea Resize Android even more, fixing crash when changing languages on Android 2026-08-01 20:18:18 +03:00
Vladislav Khorev
5cbe7a345e Android version bug fix 2026-08-01 19:42:21 +03:00
Vladislav Khorev
2add42c1cb Minor changes for the Windows non-steam version 2026-07-25 17:27:21 +03:00
Vladislav Khorev
4039c24372 change permissions 2026-07-25 16:58:18 +03:00
Vladislav Khorev
7df68febca Fixing minor stuff, working on Web version 2026-07-25 16:56:02 +03:00
Vladislav Khorev
5ecf5fb9d7 Adapting music to web, fixing scripts, fixing bug in initial state 2026-07-24 23:58:29 +03:00
Vladislav Khorev
c3f449b999 Prepare a version for Steam and fixing bugs 2026-07-22 10:59:20 +03:00
Vladislav Khorev
8a0a8390b2 Windows, make portable version + NSIS installer 2026-07-20 21:54:43 +03:00
Vladislav Khorev
d39f5ca68b Working on adapting for Windows 2026-07-20 20:48:41 +03:00
Vladislav Khorev
1c20333f30 Fixing UI bugs, some clean up 2026-07-20 18:09:16 +03:00
Vladislav Khorev
1d8e103690 Android build is successfully uploaded to Google Play 2026-07-19 23:21:23 +03:00
Vladislav Khorev
4c9ecb31d8 Fixing shadows going apeshit in dorm on Android 2026-07-19 22:34:03 +03:00
Vladislav Khorev
2e749ed26e Fixing Android stuff 2026-07-19 21:38:22 +03:00
Vladislav Khorev
fc93eb6680 Fixing android bugs and more 2026-07-19 19:49:50 +03:00
Vladislav Khorev
5b21ba615e Added localization to Android 2026-07-19 13:58:26 +03:00
Vladislav Khorev
02ba86331b Working on shadow bug 2026-07-19 13:29:35 +03:00
Vladislav Khorev
6a79e8e41e Fixing screen resize bugs 2026-07-19 13:07:39 +03:00
Vladislav Khorev
6406c4f258 Fixing bug with save game, sounds, and some hints are out of screen on Android 2026-07-19 12:19:28 +03:00
Vladislav Khorev
9f812f0318 Android fixing logs 2026-07-18 20:44:54 +03:00
Vladislav Khorev
adf9871861 Remove accidentally added files 2026-07-18 20:22:49 +03:00
Vladislav Khorev
825a56a4a4 Added music and perfom bug fixes 2026-07-18 20:21:31 +03:00
Vladislav Khorev
e5e3d7bb28 Working on Android build 2026-07-18 17:46:05 +03:00
Vladislav Khorev
e773b51985 Working on Android version 2026-07-18 13:19:08 +03:00
Vladislav Khorev
0975f3b9cd Some clean up, web version prepare 2026-07-17 21:27:47 +03:00
409 changed files with 6590 additions and 2637 deletions

3
.gitattributes vendored
View File

@ -5,3 +5,6 @@
*.wav filter=lfs diff=lfs merge=lfs -text
*.ogg 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

7
.gitignore vendored
View File

@ -408,4 +408,9 @@ public
web_resources/
pc_resources/
resources_hd/
web_resources_x2/
web_resources_x2/
android_resources/
.artifacts/
*.zip

View File

@ -184,4 +184,45 @@ make -j$(nproc) -C 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

@ -0,0 +1,128 @@
#!/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,7 +27,18 @@ except ImportError:
# 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"
}
# Path prefixes (relative to resources root, forward-slash) that get 8x reduction.
@ -35,13 +46,28 @@ 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
return 4
for prefix in LOW_REDUCTION_POSTFIXES:
if norm.endswith(prefix):
return 2
return 8
def _next_pot(n: int) -> int:

View File

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

View File

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

View File

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

View File

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

View File

@ -33,62 +33,145 @@ endforeach()
# Создаем кастомную цель, которая будет запускать процесс копирования
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
SDL_android_main.c
../../../../src/BoneAnimatedModel.cpp
../../../../src/Environment.cpp
../../../../src/Game.cpp
SDL_android_main.c
../../../../src/main.cpp
../../../../src/Projectile.cpp
../../../../src/SparkEmitter.cpp
../../../../src/TextModel.cpp
../../../../src/UiManager.cpp
../../../../src/utils/Perlin.cpp
../../../../src/utils/TaskManager.cpp
../../../../src/utils/Utils.cpp
../../../../src/navigation/PathFinder.cpp
../../../../src/planet/PlanetData.cpp
../../../../src/planet/PlanetObject.cpp
../../../../src/planet/StoneObject.cpp
../../../../src/render/FrameBuffer.cpp
../../../../src/render/ShadowMap.cpp
../../../../src/Game.cpp
../../../../src/Game.h
../../../../src/Character.cpp
../../../../src/Character.h
../../../../src/CharacterState.cpp
../../../../src/CharacterState.h
../../../../src/Environment.cpp
../../../../src/Environment.h
../../../../src/Localization.cpp
../../../../src/Localization.h
../../../../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
)
# Подключаем заголовки
target_include_directories(main PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../SDL/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}/../libzip
)
# ВАЖНО: Линкуемся с png_static (статика) или png_shared (динамика)
# Так как мы установили PNG_STATIC=ON и PNG_SHARED=OFF,
# должна создаться цель png_static
target_link_libraries(main
png_static # ЭТО ПРАВИЛЬНОЕ ИМЯ ЦЕЛИ!
z
SDL2
../../../../src/render/OpenGlExtensions.h
../../../../src/utils/Utils.cpp
../../../../src/utils/Utils.h
../../../../src/SparkEmitter.cpp
../../../../src/SparkEmitter.h
../../../../src/TeleportZone.h
../../../../src/TeleportZone.cpp
../../../../src/utils/TaskManager.cpp
../../../../src/utils/TaskManager.h
../../../../src/render/FrameBuffer.cpp
../../../../src/render/FrameBuffer.h
../../../../src/render/ShadowMap.cpp
../../../../src/render/ShadowMap.h
../../../../src/UiManager.cpp
../../../../src/UiManager.h
../../../../src/render/TextRenderer.h
../../../../src/render/TextRenderer.cpp
../../../../src/MenuManager.h
../../../../src/MenuManager.cpp
../../../../src/Location.h
../../../../src/Location.cpp
../../../../src/LocationState.h
../../../../src/LocationState.cpp
../../../../src/LocationEditor.h
../../../../src/LocationEditor.cpp
../../../../src/NpcCar.h
../../../../src/NpcCar.cpp
../../../../src/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/items/GameObjectLoader.h
../../../../src/items/GameObjectLoader.cpp
../../../../src/items/Item.h
../../../../src/items/Item.cpp
../../../../src/items/ItemRegistry.h
../../../../src/items/ItemRegistry.cpp
../../../../src/items/InteractiveObject.h
../../../../src/items/InteractiveObject.cpp
../../../../src/items/InteractiveObjectState.h
../../../../src/dialogue/DialogueTypes.h
../../../../src/dialogue/DialogueDatabase.h
../../../../src/dialogue/DialogueDatabase.cpp
../../../../src/dialogue/DialogueRuntime.h
../../../../src/dialogue/DialogueRuntime.cpp
../../../../src/dialogue/DialogueOverlay.h
../../../../src/dialogue/DialogueOverlay.cpp
../../../../src/dialogue/DialogueSystem.h
../../../../src/dialogue/DialogueSystem.cpp
../../../../src/dialogue/TranslationDatabase.h
../../../../src/dialogue/TranslationDatabase.cpp
../../../../src/quest/QuestTypes.h
../../../../src/quest/QuestJournal.h
../../../../src/quest/QuestJournal.cpp
../../../../src/cutscene/CutsceneTypes.h
../../../../src/cutscene/CutsceneDatabase.h
../../../../src/cutscene/CutsceneDatabase.cpp
../../../../src/cutscene/CutsceneRuntime.h
../../../../src/cutscene/CutsceneRuntime.cpp
../../../../src/cutscene/CutsceneOverlay.h
../../../../src/cutscene/CutsceneOverlay.cpp
../../../../src/render/UiQuad.h
)
find_library(OPENGLES2_LIB GLESv2)
find_library(OPENGLES3_LIB GLESv3)
target_link_libraries(main
${OPENGLES2_LIB} # OpenGL ES 2.0/3.0
${OPENGLES3_LIB}
png_static
z
SDL2
SDL2_ttf
SDL2_mixer
log
android
OpenSLES
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)

View File

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

View File

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

View File

@ -0,0 +1,13 @@
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);
}
}

BIN
proj-android/app/src/main/res/drawable/splash_icon.png (Stored with Git LFS) Normal file

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

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

View File

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

View File

@ -4,4 +4,13 @@
<style name="AppTheme" parent="android:Theme.NoTitleBar.Fullscreen">
<!-- Customize your theme here. -->
</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>

View File

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

View File

@ -9,7 +9,18 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
android.builtInKotlin=false
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.
# This option should only be used with decoupled projects. More details, visit

View File

@ -0,0 +1,12 @@
#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
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -290,7 +290,7 @@ endif()
# ===========================================
set(RUNTIME_RESOURCE_DIRS
"resources"
"audio"
"music"
)
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)

View File

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

View File

@ -37,3 +37,14 @@ Run:
```
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

BIN
proj-web/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

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

View File

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

View File

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

View File

@ -104,6 +104,7 @@ add_executable(ShadowOverBishkekDemo WIN32
../src/cutscene/CutsceneOverlay.h
../src/cutscene/CutsceneOverlay.cpp
../src/render/UiQuad.h
app.rc
)
# Установка проекта по умолчанию для Visual Studio
@ -118,15 +119,10 @@ set_target_properties(ShadowOverBishkekDemo PROPERTIES
OUTPUT_NAME "ShadowOverBishkekDemo"
)
# Определения препроцессора:
# PNG_ENABLED включает код PNG в TextureManager
# SDL_MAIN_HANDLED отключает переопределение main -> SDL_main
# Определения препроцессора
target_compile_definitions(ShadowOverBishkekDemo PRIVATE
WIN32_LEAN_AND_MEAN
PNG_ENABLED
# SDL_MAIN_HANDLED
# DEBUG_LIGHT
# SHOW_PATH
)
# Линкуем с SDL2main, если он вообще установлен
@ -157,18 +153,15 @@ endif()
if(STEAMSDK)
message(STATUS "Steamworks SDK integration is ENABLED.")
# Макрос STEAMSDK для условной компиляции в C++ коде (#ifdef STEAMSDK)
target_compile_definitions(ShadowOverBishkekDemo PRIVATE STEAMSDK)
set(STEAM_SDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/steamworks_sdk_164/sdk")
# Подключение заголовочных файлов Steamworks
target_include_directories(ShadowOverBishkekDemo PRIVATE
"${STEAM_SDK_DIR}/public"
"${STEAM_SDK_DIR}/public/steam"
)
# Определение разрядности сборки (x86 vs x64) для выбора версий lib и dll
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(STEAM_LIB_DIR "${STEAM_SDK_DIR}/redistributable_bin/win64")
set(STEAM_LIB_NAME "steam_api64")
@ -179,7 +172,6 @@ if(STEAMSDK)
set(STEAM_DLL_NAME "steam_api.dll")
endif()
# Поиск библиотеки линковки (.lib)
find_library(STEAM_LIBRARY
NAMES ${STEAM_LIB_NAME}
PATHS ${STEAM_LIB_DIR}
@ -193,22 +185,18 @@ if(STEAMSDK)
message(FATAL_ERROR "Steam API library (${STEAM_LIB_NAME}) NOT found in ${STEAM_LIB_DIR}!")
endif()
# Генерация временного файла steam_appid.txt с ID демо-версии (4945840)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/steam_appid.txt" "4945840")
else()
message(STATUS "Steamworks SDK integration is DISABLED.")
endif()
# ===========================================
# Копирование SDL2d.dll и zlibd.dll рядом с exe
# Копирование DLL и ресурсов для локального запуска в Visual Studio
# ===========================================
if (WIN32)
# SDL2: в Debug - SDL2d.dll, в Release - SDL2.dll
set(SDL2_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2_INSTALL_DIR}/bin/SDL2d.dll,${SDL2_INSTALL_DIR}/bin/SDL2.dll>")
set(SDL2_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/SDL2d.dll,$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/SDL2.dll>")
set(LIBZIP_DLL_SRC "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/bin/zip.dll,${LIBZIP_BASE_DIR}-Release/bin/zip.dll>")
set(ZLIB_DLL_SRC "$<IF:$<CONFIG:Debug>,${ZLIB_INSTALL_DIR}/bin/zd.dll,${ZLIB_INSTALL_DIR}/bin/z.dll>")
@ -216,47 +204,26 @@ if (WIN32)
set(SDL2TTF_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/bin/SDL2_ttfd.dll,${SDL2TTF_BASE_DIR}-Release/bin/SDL2_ttf.dll>")
set(SDL2MIXER_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2MIXER_BASE_DIR}-Debug/bin/SDL2_mixerd.dll,${SDL2MIXER_BASE_DIR}-Release/bin/SDL2_mixer.dll>")
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
# Копируем SDL2
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${SDL2_DLL_SRC}"
"${SDL2_DLL_DST}"
# Копируем LIBZIP (целевое имя всегда zip.dll)
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${LIBZIP_DLL_SRC}"
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/zip.dll"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${ZLIB_DLL_SRC}"
"${ZLIB_DLL_DST}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${SDL2TTF_DLL_SRC}"
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>"
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"
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:ShadowOverBishkekDemo>"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/SDL_mixer-release-2.8.0/install-$<CONFIG>/bin/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
)
# Если включен Steam, добавляем шаги копирования DLL и appid в этот же POST_BUILD
if(STEAMSDK)
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying Steam SDK components..."
# Копирование dll к исполняемому файлу
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${STEAM_LIB_DIR}/${STEAM_DLL_NAME}"
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/${STEAM_DLL_NAME}"
# Копирование steam_appid.txt к исполняемому файлу
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_BINARY_DIR}/steam_appid.txt"
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/steam_appid.txt"
# Дублируем steam_appid.txt в корень сборки (полезно при отладке прямо из MSVC)
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_BINARY_DIR}/steam_appid.txt"
"${CMAKE_BINARY_DIR}/steam_appid.txt"
@ -264,27 +231,99 @@ if (WIN32)
endif()
endif()
# ===========================================
# Копирование ресурсов после сборки
# ===========================================
# Какие папки с ресурсами нужно копировать
set(RUNTIME_RESOURCE_DIRS
"resources"
"audio"
)
# Копируем ресурсы и шейдеры в папку exe и в корень build/
# Копирование ресурсов локально
set(RUNTIME_RESOURCE_DIRS "resources" "music")
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
add_custom_command(TARGET ShadowOverBishkekDemo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
# 1) туда, где лежит exe (build/Debug, build/Release и т.п.)
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/../${resdir}"
"$<TARGET_FILE_DIR:ShadowOverBishkekDemo>/${resdir}"
# 2) в корень build, если захочешь запускать из этой папки
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_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)

21
proj-windows/LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
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.

1
proj-windows/app.rc Normal file
View File

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

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

BIN
proj-windows/dist_dlls/msvcp140_atomic_wait.dll (Stored with Git LFS) Normal file

Binary file not shown.

BIN
proj-windows/dist_dlls/msvcp140_clr0400.dll (Stored with Git LFS) Normal file

Binary file not shown.

BIN
proj-windows/dist_dlls/msvcp140_codecvt_ids.dll (Stored with Git LFS) Normal file

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

BIN
proj-windows/dist_dlls/vcruntime140_1_clr0400.dll (Stored with Git LFS) Normal file

Binary file not shown.

BIN
proj-windows/dist_dlls/vcruntime140_threads.dll (Stored with Git LFS) Normal file

Binary file not shown.

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

Binary file not shown.

BIN
proj-windows/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

View File

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

View File

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

View File

@ -2276,14 +2276,14 @@
"en": "And maybe she will even give me some difficult assignment."
},
{
"key": даже уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"ru": даже уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"en": "I'm even sure she's looking for me. She wants to give me some kind of assignment."
"key": уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"ru": уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"en": "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."
"en": "Until you bring me a knife from the teachers' room, I won't let you go anywhere. Osho!"
},
{
"key": "Я слышала она не смогла сдать курсовую по манасоведению.",
@ -2293,7 +2293,7 @@
{
"key": "Она прыгнула с окна и разбилась насмерть деп.",
"ru": "Она прыгнула с окна и разбилась насмерть деп.",
"en": "She jumped from the window and fell to her death, they said."
"en": "She jumped from the window and fell to her death, dep."
},
{
"key": "Да она надоела, все время скафнит.",
@ -2308,7 +2308,7 @@
{
"key": "Так что жду тебя в универе! Не вздумай прогулять! Ошо!",
"ru": "Так что жду тебя в универе! Не вздумай прогулять! Ошо!",
"en": "So I'm waiting for you at the university! Don't even think about skipping!"
"en": "So I'm waiting for you at the university! Don't even think about skipping! Osho!"
}
]
}

View File

@ -412,6 +412,70 @@
}
]
},
{
"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",
"start": "line_1",

View File

@ -293,14 +293,6 @@
"speaker": "Бекзат",
"portrait": "resources/dialogue/portrait_hero_neutral.png",
"text": "Почему?",
"next": "line_30"
},
{
"id": "line_30",
"type": "Line",
"speaker": "Айпери",
"portrait": "resources/dialogue/portrait_aiperi.png",
"text": "Она опять будет скафнить.",
"next": "line_25"
},
{
@ -308,7 +300,7 @@
"type": "Line",
"speaker": "Айпери",
"portrait": "resources/dialogue/portrait_aiperi.png",
"text": даже уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"text": уверена, она меня ищет. Она хочет мне какое-то задание дать.",
"next": "line_26"
},
{

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,20 @@
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

@ -0,0 +1,23 @@
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

@ -0,0 +1,69 @@
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

@ -0,0 +1,55 @@
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 mediump float;
precision highp float;
uniform sampler2D Texture;
uniform sampler2D uShadowMap;

View File

@ -0,0 +1,28 @@
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

@ -0,0 +1,74 @@
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

@ -0,0 +1,71 @@
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

@ -0,0 +1,24 @@
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;
}

View File

@ -0,0 +1,65 @@
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;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uLightFromCamera;
uniform mat4 uBoneMatrices[58];
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;
}
gl_Position = ProjectionModelViewMatrix * skinnedPos;
texCoord = vTexCoord;
fragPosLightSpace = uLightFromCamera * ModelViewMatrix * skinnedPos;
fragNormal = mat3(ModelViewMatrix) * skinnedNormal;
}

View File

@ -106,13 +106,15 @@ end
function on_alik_door_click()
if (game_api.is_night()) then
game_api.start_dialogue("door_alik_dialog002")
game_api.player_stop()
game_api.player_rotate_to(-90.0)
else
if (alik_door_opened == false) then
game_api.start_dialogue("door_alik_dialog001")
game_api.player_stop()
game_api.player_rotate_to(-90.0)
end
end
game_api.player_stop()
game_api.player_rotate_to(-90.0)
end
function callback_deactivate_cover_alik_room()

View File

@ -155,8 +155,9 @@ function callback_aiperi_chat()
game_api.setIntValue("aiperi_chat_opened", 1)
local aiperi_knife_aware = game_api.getIntValue("aiperi_knife_aware")
local aiperi_talked_after_knife = game_api.getIntValue("aiperi_talked_after_knife")
if (player_hold_knife) and (aiperi_talked_after_knife == 0) then
local player_hold_knife = game_api.getIntValue("player_hold_knife")
if (player_hold_knife==1) and (aiperi_talked_after_knife == 0) then
game_api.start_dialogue("dialog_chat_aiperi003")
game_api.setIntValue("aiperi_talked_after_knife", 1)
else
@ -170,6 +171,36 @@ function callback_aiperi_chat()
end
end
end
--[[
local aiperi_chat_opened = game_api.getIntValue("aiperi_chat_opened")
local aiperi_knife_aware = game_api.getIntValue("aiperi_knife_aware")
local aiperi_talked_after_knife = game_api.getIntValue("aiperi_talked_after_knife")
local lection_is_over = game_api.getIntValue("lection_is_over")
local player_hold_knife = game_api.getIntValue("player_hold_knife")
local player_hold_key = game_api.getIntValue("player_hold_key")
if (player_hold_knife==1) and (aiperi_talked_after_knife == 0) then
game_api.start_dialogue("dialog_chat_aiperi003")
game_api.setIntValue("aiperi_talked_after_knife", 1)
else
print("dialogx- step 2")
if aiperi_chat_opened == 0 then
print("dialogx- step 3")
game_api.setIntValue("aiperi_chat_opened", 1)
if aiperi_knife_aware == 0 then
print("dialogx- step 4")
if (lection_is_over == 1) then
print("dialogx- step 5")
if (player_hold_key == 0) then
print("dialogx- step 6")
game_api.start_dialogue("dialog_chat_aiperi002")
end
else
game_api.start_dialogue("dialog_chat_aiperi001")
end
end
end
end]]
end
end

View File

@ -5,7 +5,6 @@ hall_door_opened = false
teacher_door_opened = false
player_hold_book = false
player_hold_knife = false
teacher_arrived_to_library = false
teacher_told_about_book = false
@ -60,9 +59,10 @@ game_api.set_trigger_zone_callbacks("lection_hall_zone001",
function knife_dialog_zone001_enter_callback()
local day = game_api.getIntValue("day")
local player_hold_key = game_api.getIntValue("player_hold_key")
local player_hold_knife = game_api.getIntValue("player_hold_knife")
if (day == 0) then
if (player_hold_knife == false) then
if (player_hold_knife == 0) then
local lection_is_over = game_api.getIntValue("lection_is_over")
if lection_is_over == 1 then
game_api.npc_stop_and_rotate_to_player(1)
@ -111,7 +111,8 @@ function on_knife_pickup()
game_api.pickup_item("knife")
game_api.deactivate_interactive_object("Knife001")
game_api.set_npc_enabled(1, false)
player_hold_knife = true
game_api.setIntValue("player_hold_knife", 1)
game_api.set_trigger_zone_enabled(2, true)
game_api.npc_walk_to(0, -4.57412, 0, 6.78495, on_teacher_arrived2)
game_api.quest_set_objective_completed("aiperi_knife", "aiperi_knife_take")
@ -1171,8 +1172,9 @@ function callback_aiperi_chat()
local aiperi_knife_aware = game_api.getIntValue("aiperi_knife_aware")
local aiperi_talked_after_knife = game_api.getIntValue("aiperi_talked_after_knife")
local lection_is_over = game_api.getIntValue("lection_is_over")
if (player_hold_knife) and (aiperi_talked_after_knife == 0) then
local player_hold_knife = game_api.getIntValue("player_hold_knife")
if (player_hold_knife==1) and (aiperi_talked_after_knife == 0) then
game_api.start_dialogue("dialog_chat_aiperi003")
game_api.setIntValue("aiperi_talked_after_knife", 1)
else

Binary file not shown.

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