Some clean up, web version prepare

This commit is contained in:
Vladislav Khorev 2026-07-17 21:27:47 +03:00
parent e9c888e2bf
commit 0975f3b9cd
104 changed files with 779 additions and 1483 deletions

View File

@ -109,6 +109,8 @@ 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
@ -195,6 +197,7 @@ 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"

View File

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

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

@ -1,49 +0,0 @@
cmake_minimum_required(VERSION 3.15)
project(SpaceGameServer)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Подключаем зависимости нашего движка
include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/ThirdParty.cmake)
# Настройка флагов для тяжелых шаблонов Boost
if (MSVC)
add_compile_options(/bigobj)
endif()
# Добавляем скомпилированные компоненты Boost через относительные пути
# CMake сам создаст цели boost_system и др.
add_subdirectory("${BOOST_SRC_DIR}/libs/system" boost-system-build EXCLUDE_FROM_ALL)
add_subdirectory("${BOOST_SRC_DIR}/libs/assert" boost-assert-build EXCLUDE_FROM_ALL)
add_subdirectory("${BOOST_SRC_DIR}/libs/config" boost-config-build EXCLUDE_FROM_ALL)
add_subdirectory("${BOOST_SRC_DIR}/libs/throw_exception" boost-throw_exception-build EXCLUDE_FROM_ALL)
add_subdirectory("${BOOST_SRC_DIR}/libs/variant2" boost-variant2-build EXCLUDE_FROM_ALL)
add_subdirectory("${BOOST_SRC_DIR}/libs/mp11" boost-mp11-build EXCLUDE_FROM_ALL)
add_subdirectory("${BOOST_SRC_DIR}/libs/winapi" boost-winapi-build EXCLUDE_FROM_ALL)
add_subdirectory("${BOOST_SRC_DIR}/libs/predef" boost-predef-build EXCLUDE_FROM_ALL)
# EXCLUDE_FROM_ALL гарантирует, что мы собираем только то, что линкуем
# Исполняемый файл сервера
add_executable(Server
server.h
server.cpp
../src/network/ClientState.h
../src/network/ClientState.cpp
)
target_include_directories(Server PRIVATE ${BOOST_SRC_DIR})
# Линковка
target_link_libraries(Server
PRIVATE
boost_system # Скомпилированная часть для error_code
eigen_external_lib # Если планируешь использовать математику на сервере
)
if(WIN32)
target_link_libraries(Server PRIVATE ws2_32 mswsock)
endif()
# Дополнительный макрос, чтобы Asio знал, что мы работаем без устаревших функций
target_compile_definitions(Server PRIVATE BOOST_ASIO_NO_DEPRECATED)

View File

@ -1,957 +0,0 @@
#include "server.h"
#include <boost/beast/websocket.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <iostream>
#include <sstream>
#include <random>
#include <algorithm>
#include <chrono>
std::vector<std::string> split(const std::string& s, char delimiter) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
Session::Session(Server& server, tcp::socket&& socket, int id)
: server_(server)
, ws_(std::move(socket))
, id_(id)
, lastReceivedTime_(std::chrono::system_clock::now()) {
}
bool Session::is_timed_out(std::chrono::system_clock::time_point now) const {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastReceivedTime_).count();
return elapsed > PLAYER_TIMEOUT_MS;
}
void Session::force_disconnect() {
beast::error_code ec;
ws_.next_layer().socket().close(ec);
}
int Session::get_id() const { return id_; }
bool Session::hasSpawnReserved() const { return hasReservedSpawn_; }
const Eigen::Vector3f& Session::reservedSpawn() const { return reservedSpawn_; }
bool Session::fetchStateAtTime(std::chrono::system_clock::time_point targetTime, ClientState& outState) const {
if (timedClientStates.canFetchClientStateAtTime(targetTime)) {
outState = timedClientStates.fetchClientStateAtTime(targetTime);
return true;
}
return false;
}
void Session::send_message(const std::string& msg) {
auto ss = std::make_shared<std::string>(msg);
{
std::lock_guard<std::mutex> lock(writeMutex_);
writeQueue_.push(ss);
}
doWrite();
}
void Session::run() {
{
std::lock_guard<std::mutex> lock(server_.g_sessions_mutex);
server_.g_sessions.push_back(shared_from_this());
}
ws_.async_accept([self = shared_from_this()](beast::error_code ec) {
if (ec) return;
std::cout << "Client " << self->id_ << " connected\n";
self->init();
});
}
bool Session::IsMessageValid(const std::string& fullMessage) {
#ifdef ENABLE_NETWORK_CHECKSUM
size_t hashPos = fullMessage.find("#hash:");
if (hashPos == std::string::npos) {
return false; // Хеша нет, хотя он ожидался
}
std::string originalContent = fullMessage.substr(0, hashPos);
std::string receivedHashStr = fullMessage.substr(hashPos + 6); // 6 — длина "#hash:"
// Вычисляем ожидаемый хеш от контента
size_t expectedHash = fnv1a_hash(originalContent + NET_SECRET);
std::stringstream ss;
ss << std::hex << expectedHash;
return ss.str() == receivedHashStr;
#else
return true; // В режиме отладки пропускаем всё
#endif
}
void Session::sendBoxesToClient() {
std::lock_guard<std::mutex> lock(server_.g_boxes_mutex);
std::string boxMsg = "BOXES:";
bool first = true;
for (size_t i = 0; i < server_.g_serverBoxes.size(); ++i) {
const auto& box = server_.g_serverBoxes[i];
if (box.destroyed) continue;
Eigen::Quaternionf q(box.rotation);
if (!first) boxMsg += "|";
first = false;
boxMsg += std::to_string(i) + ":" +
std::to_string(box.position.x()) + ":" +
std::to_string(box.position.y()) + ":" +
std::to_string(box.position.z()) + ":" +
std::to_string(q.w()) + ":" +
std::to_string(q.x()) + ":" +
std::to_string(q.y()) + ":" +
std::to_string(q.z()) + ":" +
"0";
}
send_message(boxMsg);
}
void Session::init()
{
auto timer = std::make_shared<net::steady_timer>(ws_.get_executor());
timer->expires_after(std::chrono::milliseconds(100));
timer->async_wait([self = shared_from_this(), timer](const boost::system::error_code& ec) {
if (!ec) {
auto now_tp = std::chrono::system_clock::now();
uint64_t now_ms = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(now_tp.time_since_epoch()).count());
self->sendBoxesToClient();
self->send_message("ID:" + std::to_string(self->id_) + ":" + std::to_string(now_ms));
self->do_read();
}
});
}
ClientState Session::get_latest_state(std::chrono::system_clock::time_point now) {
if (timedClientStates.timedStates.empty()) {
return {};
}
ClientState latest = timedClientStates.timedStates.back();
latest.apply_lag_compensation(now);
return latest;
}
void Session::doWrite() {
std::lock_guard<std::mutex> lock(writeMutex_);
if (is_writing_ || writeQueue_.empty()) {
return;
}
is_writing_ = true;
auto message = writeQueue_.front();
ws_.async_write(net::buffer(*message),
[self = shared_from_this(), message](beast::error_code ec, std::size_t) {
if (ec) {
std::cerr << "Write error: " << ec.message() << std::endl;
return;
}
{
std::lock_guard<std::mutex> lock(self->writeMutex_);
self->writeQueue_.pop();
self->is_writing_ = false;
}
self->doWrite();
});
}
void Session::do_read() {
ws_.async_read(buffer_, [self = shared_from_this()](beast::error_code ec, std::size_t) {
if (ec) {
if (self->joined_) {
self->server_.broadcastToAllExceptId("PLAYER_LEFT:" + std::to_string(self->id_), self->id_);
std::cout << "Client " << self->id_ << " disconnected, broadcasting PLAYER_LEFT\n";
}
std::lock_guard<std::mutex> lock(self->server_.g_sessions_mutex);
self->server_.g_sessions.erase(std::remove_if(self->server_.g_sessions.begin(), self->server_.g_sessions.end(),
[self](const std::shared_ptr<Session>& session) {
return session.get() == self.get();
}), self->server_.g_sessions.end());
std::cout << "Client " << self->id_ << " removed from session list\n";
return;
}
std::string msg = beast::buffers_to_string(self->buffer_.data());
self->process_message(msg);
self->buffer_.consume(self->buffer_.size());
self->do_read();
});
}
void Session::process_message(const std::string& msg) {
if (!IsMessageValid(msg)) {
std::cout << "[Security] Invalid packet hash. Dropping message: " << msg << std::endl;
return;
}
lastReceivedTime_ = std::chrono::system_clock::now();
std::string cleanMessage = msg.substr(0, msg.find("#hash:"));
std::cout << "Received from player " << id_ << ": " << cleanMessage << std::endl;
auto parts = split(cleanMessage, ':');
if (parts.empty()) return;
std::string type = parts[0];
if (type == "JOIN") {
std::string nick = "Player";
int sType = 0;
if (parts.size() >= 2) nick = parts[1];
if (parts.size() >= 3) {
try { sType = std::stoi(parts[2]); }
catch (...) { sType = 0; }
}
this->nickname = nick;
this->shipType = sType;
this->joined_ = true;
auto now_tp = std::chrono::system_clock::now();
uint64_t now_ms = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(now_tp.time_since_epoch()).count());
Eigen::Vector3f spawnPos = server_.PickSafeSpawnPos(id_);
this->hasReservedSpawn_ = true;
this->reservedSpawn_ = spawnPos;
ClientState st;
st.id = id_;
st.position = spawnPos;
st.rotation = Eigen::Matrix3f::Identity();
st.currentAngularVelocity = Eigen::Vector3f::Zero();
st.velocity = 0.0f;
st.selectedVelocity = 0;
st.discreteMag = 0.0f;
st.discreteAngle = -1;
st.lastUpdateServerTime = now_tp;
st.nickname = this->nickname;
st.shipType = this->shipType;
timedClientStates.add_state(st);
this->send_message(
"SPAWN:" + std::to_string(id_) + ":" + std::to_string(now_ms) + ":" + st.formPingMessageContent()
);
std::string eventMsg =
"EVENT:" + std::to_string(id_) + ":UPD:" + std::to_string(now_ms) + ":" + st.formPingMessageContent();
server_.broadcastToAllExceptId(eventMsg, id_);
std::cout << "Server: Player " << id_ << " joined as [" << nick << "] shipType=" << sType << std::endl;
std::string info = "PLAYERINFO:" + std::to_string(id_) + ":" + nick + ":" + std::to_string(sType);
server_.broadcastToAllExceptId(info, id_);
{
std::lock_guard<std::mutex> lock(server_.g_sessions_mutex);
for (auto& session : server_.g_sessions) {
if (session->get_id() == this->id_) continue;
if (!session->joined_) continue;
std::string otherInfo = "PLAYERINFO:" + std::to_string(session->get_id()) + ":" + session->nickname + ":" + std::to_string(session->shipType);
this->send_message(otherInfo);
}
}
}
else if (type == "UPD") {
if (!joined_) {
std::cout << "Server: Ignoring UPD before JOIN from " << id_ << std::endl;
return;
}
{
std::lock_guard<std::mutex> gd(server_.g_dead_mutex);
if (server_.g_dead_players.find(id_) != server_.g_dead_players.end()) {
std::cout << "Server: Ignoring UPD from dead player " << id_ << std::endl;
return;
}
}
if (parts.size() < 16) return;
uint64_t clientTimestamp = std::stoull(parts[1]);
ClientState receivedState;
receivedState.id = id_;
std::chrono::system_clock::time_point uptime_timepoint{
std::chrono::milliseconds(clientTimestamp)
};
receivedState.lastUpdateServerTime = uptime_timepoint;
receivedState.handle_full_sync(parts, 2);
receivedState.nickname = this->nickname;
receivedState.shipType = this->shipType;
timedClientStates.add_state(receivedState);
}
else if (type == "RESPAWN") {
{
std::lock_guard<std::mutex> gd(server_.g_dead_mutex);
server_.g_dead_players.erase(id_);
}
{
auto now_tp = std::chrono::system_clock::now();
uint64_t now_ms = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(now_tp.time_since_epoch()).count());
ClientState st;
st.id = id_;
Eigen::Vector3f spawnPos = server_.PickSafeSpawnPos(id_);
st.position = spawnPos;
this->hasReservedSpawn_ = true;
this->reservedSpawn_ = spawnPos;
st.rotation = Eigen::Matrix3f::Identity();
st.currentAngularVelocity = Eigen::Vector3f::Zero();
st.velocity = 0.0f;
st.selectedVelocity = 0;
st.discreteMag = 0.0f;
st.discreteAngle = -1;
st.lastUpdateServerTime = now_tp;
st.nickname = this->nickname;
st.shipType = this->shipType;
timedClientStates.add_state(st);
this->send_message(
"SPAWN:" + std::to_string(id_) + ":" + std::to_string(now_ms) + ":" + st.formPingMessageContent()
);
std::string respawnMsg = "RESPAWN_ACK:" + std::to_string(id_);
server_.broadcastToAll(respawnMsg);
std::string playerInfo = "PLAYERINFO:" + std::to_string(id_) + ":" + st.nickname + ":" + std::to_string(st.shipType);
server_.broadcastToAll(playerInfo);
std::string eventMsg = "EVENT:" + std::to_string(id_) + ":UPD:" + std::to_string(now_ms) + ":" + st.formPingMessageContent();
server_.broadcastToAll(eventMsg);
std::cout << "Server: Player " << id_ << " respawned, broadcasted RESPAWN_ACK, PLAYERINFO and initial UPD\n";
}
}
else if (parts[0] == "BOX_PICKUP") {
if (parts.size() < 2) return;
if (this->shipType != 1) {
std::cout << "Server: Player " << id_ << " tried BOX_PICKUP but is not a cargo ship\n";
return;
}
int boxIdx = -1;
try { boxIdx = std::stoi(parts[1]); } catch (...) { return; }
std::lock_guard<std::mutex> bm(server_.g_boxes_mutex);
if (boxIdx < 0 || boxIdx >= (int)server_.g_serverBoxes.size()) return;
if (server_.g_serverBoxes[boxIdx].destroyed) return;
if (timedClientStates.timedStates.empty()) return;
const ClientState& playerState = timedClientStates.timedStates.back();
Eigen::Vector3f boxWorld = server_.g_serverBoxes[boxIdx].position + kWorldOffset;
float distSq = (playerState.position - boxWorld).squaredNorm();
if (distSq > BOX_PICKUP_RADIUS * BOX_PICKUP_RADIUS) {
std::cout << "Server: Player " << id_ << " too far to pick up box " << boxIdx << "\n";
return;
}
server_.g_serverBoxes[boxIdx].destroyed = true;
std::string pickedUpMsg = "BOX_PICKED_UP:" + std::to_string(boxIdx) + ":" + std::to_string(id_);
server_.broadcastToAll(pickedUpMsg);
std::cout << "Server: Box " << boxIdx << " picked up by player " << id_ << "\n";
// Respawn box
{
static thread_local std::mt19937 rng{ std::random_device{}() };
static thread_local std::uniform_real_distribution<float> angleDist(0.f, static_cast<float>(M_PI * 2.0));
Eigen::Vector3f newPos = server_.PickSafeBoxPos(boxIdx);
Eigen::Vector3f axis = Eigen::Vector3f::Random().normalized();
Eigen::Matrix3f newRot = Eigen::AngleAxisf(angleDist(rng), axis).toRotationMatrix();
server_.g_serverBoxes[boxIdx].position = newPos;
server_.g_serverBoxes[boxIdx].rotation = newRot;
server_.g_serverBoxes[boxIdx].destroyed = false;
Eigen::Quaternionf q(newRot);
std::string respawnMsg = "BOX_RESPAWN:" +
std::to_string(boxIdx) + ":" +
std::to_string(newPos.x()) + ":" +
std::to_string(newPos.y()) + ":" +
std::to_string(newPos.z()) + ":" +
std::to_string(q.w()) + ":" +
std::to_string(q.x()) + ":" +
std::to_string(q.y()) + ":" +
std::to_string(q.z());
server_.broadcastToAll(respawnMsg);
std::cout << "Server: Box " << boxIdx << " respawned after pickup\n";
}
}
else if (parts[0] == "FIRE") {
if (parts.size() < 10) return;
uint64_t clientTime = std::stoull(parts[1]);
Eigen::Vector3f pos{
std::stof(parts[2]), std::stof(parts[3]), std::stof(parts[4])
};
Eigen::Quaternionf dir(
std::stof(parts[5]), std::stof(parts[6]), std::stof(parts[7]), std::stof(parts[8])
);
float velocity = std::stof(parts[9]);
std::string broadcast = "PROJECTILE:" +
std::to_string(id_) + ":" +
std::to_string(clientTime) + ":" +
std::to_string(pos.x()) + ":" +
std::to_string(pos.y()) + ":" +
std::to_string(pos.z()) + ":" +
std::to_string(dir.w()) + ":" +
std::to_string(dir.x()) + ":" +
std::to_string(dir.y()) + ":" +
std::to_string(dir.z()) + ":" +
std::to_string(velocity);
server_.broadcastToAllExceptId(broadcast, id_);
server_.createProjectile(id_, pos, dir, velocity);
}
}
Eigen::Vector3f Server::PickSafeBoxPos(int skipIdx)
{
// Assumes g_boxes_mutex is already held by the caller
static thread_local std::mt19937 rng{ std::random_device{}() };
std::uniform_real_distribution<float> dist(-1000.f, 1000.f);
for (int attempt = 0; attempt < 500; ++attempt) {
Eigen::Vector3f cand(dist(rng), dist(rng), dist(rng));
bool safe = true;
for (int i = 0; i < (int)g_serverBoxes.size(); ++i) {
if (i == skipIdx) continue;
if (g_serverBoxes[i].destroyed) continue;
if ((cand - g_serverBoxes[i].position).squaredNorm() < 9.f) {
safe = false;
break;
}
}
if (safe) return cand;
}
return Eigen::Vector3f(dist(rng), dist(rng), dist(rng));
}
Eigen::Vector3f Server::PickSafeSpawnPos(int forPlayerId)
{
static thread_local std::mt19937 rng{ std::random_device{}() };
std::scoped_lock lock(g_boxes_mutex, g_sessions_mutex, g_dead_mutex);
auto isSafe = [&](const Eigen::Vector3f& pWorld) -> bool
{
for (const auto& box : g_serverBoxes) {
if (box.destroyed) continue;
Eigen::Vector3f boxWorld = box.position + kWorldOffset;
float minDist = kShipRadius + box.collisionRadius + kSpawnBoxMargin;
if ((pWorld - boxWorld).squaredNorm() < minDist * minDist)
return false;
}
for (const auto& s : g_sessions) {
int pid = s->get_id();
if (pid == forPlayerId) continue;
if (g_dead_players.count(pid)) continue;
Eigen::Vector3f otherPos;
if (!s->timedClientStates.timedStates.empty()) {
otherPos = s->timedClientStates.timedStates.back().position;
}
else if (s->hasSpawnReserved()) {
otherPos = s->reservedSpawn();
}
else {
continue;
}
float minDist = (kShipRadius * 2.0f) + kSpawnShipMargin;
if ((pWorld - otherPos).squaredNorm() < minDist * minDist)
return false;
}
return true;
};
const float radii[] = { 150.f, 250.f, 400.f, 650.f, 1000.f, 1600.f };
for (float r : radii) {
std::uniform_real_distribution<float> dxy(-r, r);
std::uniform_real_distribution<float> dz(-kSpawnZJitter, kSpawnZJitter);
for (int attempt = 0; attempt < 250; ++attempt) {
Eigen::Vector3f cand(
dxy(rng),
dxy(rng),
kWorldZOffset + dz(rng)
);
if (isSafe(cand))
return cand;
}
}
int a = (forPlayerId % 10);
int b = ((forPlayerId / 10) % 10);
return Eigen::Vector3f(600.0f + a * 100.0f, -600.0f + b * 100.0f, kWorldZOffset);
}
void Server::broadcastToAll(const std::string& message) {
std::lock_guard<std::mutex> lock(g_sessions_mutex);
for (const auto& session : g_sessions) {
session->send_message(message);
}
}
void Server::broadcastToAllExceptId(const std::string& message, int id)
{
std::lock_guard<std::mutex> lock(g_sessions_mutex);
for (auto& session : g_sessions) {
if (session->get_id() == id) continue;
session->send_message(message);
}
}
void Server::createProjectile(int id, Eigen::Vector3f pos, Eigen::Quaternionf dir, float velocity)
{
const std::vector<Eigen::Vector3f> localOffsets = {
Eigen::Vector3f(-1.5f, 0.9f - 6.f, 5.0f),
Eigen::Vector3f(1.5f, 0.9f - 6.f, 5.0f)
};
uint64_t now_ms = std::chrono::duration_cast<std::chrono::milliseconds>((
std::chrono::system_clock::now().time_since_epoch())).count();
std::lock_guard<std::mutex> pl(g_projectiles_mutex);
for (int i = 0; i < localOffsets.size(); ++i) {
Projectile pr;
pr.shooterId = id;
pr.spawnMs = now_ms;
Eigen::Vector3f shotPos = pos + dir.toRotationMatrix() * localOffsets[i];
pr.pos = shotPos;
Eigen::Vector3f localForward(0.0f, 0.0f, -1.0f);
Eigen::Vector3f worldForward = dir.toRotationMatrix() * localForward;
float len = worldForward.norm();
if (len > 1e-6f) worldForward /= len;
pr.vel = worldForward * velocity;
pr.lifeMs = 15000.0f;
g_projectiles.push_back(pr);
std::cout << "Server: Created projectile from player " << id
<< " at pos (" << shotPos.x() << ", " << shotPos.y() << ", " << shotPos.z()
<< ") vel (" << pr.vel.x() << ", " << pr.vel.y() << ", " << pr.vel.z() << ")" << std::endl;
}
}
void Server::update_world() {
static auto last_snapshot_time = std::chrono::system_clock::now();
auto now = std::chrono::system_clock::now();
uint64_t now_ms = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count());
// --- Detect and force-disconnect timed-out players ---
{
std::vector<std::shared_ptr<Session>> timedOut;
{
std::lock_guard<std::mutex> lock(g_sessions_mutex);
for (auto& session : g_sessions) {
if (session->is_timed_out(now)) {
timedOut.push_back(session);
}
}
}
for (auto& session : timedOut) {
std::cout << "Server: Player " << session->get_id()
<< " timed out after " << PLAYER_TIMEOUT_MS << "ms, forcing disconnect\n";
session->force_disconnect();
}
}
{
std::lock_guard<std::mutex> lock(g_sessions_mutex);
for (auto& sender : g_sessions) {
if (sender->timedClientStates.timedStates.empty()) continue;
const ClientState& st = sender->timedClientStates.timedStates.back();
uint64_t stateTime = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(
st.lastUpdateServerTime.time_since_epoch()).count());
std::string event_msg = "EVENT:" + std::to_string(sender->get_id()) +
":UPD:" + std::to_string(stateTime) + ":" + st.formPingMessageContent();
for (auto& receiver : g_sessions) {
if (receiver->get_id() != sender->get_id()) {
receiver->send_message(event_msg);
}
}
}
}
// --- Tick: projectile movement and hit detection ---
const float dt = 50.0f / 1000.0f;
std::vector<DeathInfo> deathEvents;
{
std::lock_guard<std::mutex> pl(g_projectiles_mutex);
std::vector<int> indicesToRemove;
for (size_t i = 0; i < g_projectiles.size(); ++i) {
auto& pr = g_projectiles[i];
pr.pos += pr.vel * dt;
if (now_ms > pr.spawnMs + static_cast<uint64_t>(pr.lifeMs)) {
indicesToRemove.push_back(static_cast<int>(i));
continue;
}
bool hitDetected = false;
{
std::lock_guard<std::mutex> lm(g_sessions_mutex);
std::lock_guard<std::mutex> gd(g_dead_mutex);
for (auto& session : g_sessions) {
int targetId = session->get_id();
if (targetId == pr.shooterId) continue;
if (g_dead_players.find(targetId) != g_dead_players.end()) continue;
ClientState targetState;
if (!session->fetchStateAtTime(now, targetState)) continue;
Eigen::Vector3f diff = pr.pos - targetState.position;
float combinedRadius = shipCollisionRadius + projectileHitRadius;
if (diff.squaredNorm() <= combinedRadius * combinedRadius) {
DeathInfo death;
death.targetId = targetId;
death.serverTime = now_ms;
death.position = pr.pos;
death.killerId = pr.shooterId;
deathEvents.push_back(death);
g_dead_players.insert(targetId);
indicesToRemove.push_back(static_cast<int>(i));
hitDetected = true;
std::cout << "Server: *** HIT DETECTED! ***" << std::endl;
std::cout << "Server: Projectile at ("
<< pr.pos.x() << ", " << pr.pos.y() << ", " << pr.pos.z()
<< ") hit player " << targetId << std::endl;
break;
}
}
}
if (hitDetected) continue;
}
if (!indicesToRemove.empty()) {
std::sort(indicesToRemove.rbegin(), indicesToRemove.rend());
for (int idx : indicesToRemove) {
if (idx >= 0 && idx < (int)g_projectiles.size()) {
g_projectiles.erase(g_projectiles.begin() + idx);
}
}
}
}
std::vector<int> boxesToRespawn;
// --- Tick: box-projectile collisions ---
{
std::lock_guard<std::mutex> bm(g_boxes_mutex);
std::vector<std::pair<size_t, size_t>> boxProjectileCollisions;
for (size_t bi = 0; bi < g_serverBoxes.size(); ++bi) {
if (g_serverBoxes[bi].destroyed) continue;
Eigen::Vector3f boxWorld = g_serverBoxes[bi].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
for (size_t pi = 0; pi < g_projectiles.size(); ++pi) {
const auto& pr = g_projectiles[pi];
Eigen::Vector3f diff = pr.pos - boxWorld;
float thresh = boxCollisionRadius + projectileHitRadius;
if (diff.squaredNorm() <= thresh * thresh) {
boxProjectileCollisions.push_back({ bi, pi });
}
}
}
for (const auto& [boxIdx, projIdx] : boxProjectileCollisions) {
if (g_serverBoxes[boxIdx].destroyed) continue;
g_serverBoxes[boxIdx].destroyed = true;
Eigen::Vector3f boxWorld = g_serverBoxes[boxIdx].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
BoxDestroyedInfo destruction;
destruction.boxIndex = static_cast<int>(boxIdx);
destruction.serverTime = now_ms;
destruction.position = boxWorld;
destruction.destroyedBy = g_projectiles[projIdx].shooterId;
{
std::lock_guard<std::mutex> dm(g_boxDestructions_mutex);
g_boxDestructions.push_back(destruction);
}
boxesToRespawn.push_back(static_cast<int>(boxIdx));
std::cout << "Server: Box " << boxIdx << " destroyed by projectile from player "
<< g_projectiles[projIdx].shooterId << std::endl;
}
}
// --- Tick: box-ship collisions ---
{
std::lock_guard<std::mutex> bm(g_boxes_mutex);
std::lock_guard<std::mutex> lm(g_sessions_mutex);
for (size_t bi = 0; bi < g_serverBoxes.size(); ++bi) {
if (g_serverBoxes[bi].destroyed) continue;
Eigen::Vector3f boxWorld = g_serverBoxes[bi].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
for (auto& session : g_sessions) {
{
std::lock_guard<std::mutex> gd(g_dead_mutex);
if (g_dead_players.find(session->get_id()) != g_dead_players.end()) {
continue;
}
}
ClientState shipState;
if (!session->fetchStateAtTime(now, shipState)) continue;
Eigen::Vector3f diff = shipState.position - boxWorld;
float thresh = shipCollisionRadius + boxCollisionRadius;
if (diff.squaredNorm() <= thresh * thresh) {
g_serverBoxes[bi].destroyed = true;
BoxDestroyedInfo destruction;
destruction.boxIndex = static_cast<int>(bi);
destruction.serverTime = now_ms;
destruction.position = boxWorld;
destruction.destroyedBy = session->get_id();
{
std::lock_guard<std::mutex> dm(g_boxDestructions_mutex);
g_boxDestructions.push_back(destruction);
}
boxesToRespawn.push_back(static_cast<int>(bi));
std::cout << "Server: Box " << bi << " destroyed by ship collision with player "
<< session->get_id() << std::endl;
break;
}
}
}
}
// --- Broadcast deaths ---
for (const auto& death : deathEvents) {
std::string deadMsg = "DEAD:" +
std::to_string(death.serverTime) + ":" +
std::to_string(death.targetId) + ":" +
std::to_string(death.position.x()) + ":" +
std::to_string(death.position.y()) + ":" +
std::to_string(death.position.z()) + ":" +
std::to_string(death.killerId);
broadcastToAll(deadMsg);
std::cout << "Server: Sent DEAD event - Player " << death.targetId
<< " killed by " << death.killerId << std::endl;
}
// --- Broadcast box destructions ---
{
std::lock_guard<std::mutex> dm(g_boxDestructions_mutex);
for (const auto& destruction : g_boxDestructions) {
std::string boxMsg = "BOX_DESTROYED:" +
std::to_string(destruction.boxIndex) + ":" +
std::to_string(destruction.serverTime) + ":" +
std::to_string(destruction.position.x()) + ":" +
std::to_string(destruction.position.y()) + ":" +
std::to_string(destruction.position.z()) + ":" +
std::to_string(destruction.destroyedBy);
broadcastToAll(boxMsg);
std::cout << "Server: Broadcasted BOX_DESTROYED for box " << destruction.boxIndex << std::endl;
}
g_boxDestructions.clear();
}
// --- Respawn destroyed boxes ---
if (!boxesToRespawn.empty()) {
static thread_local std::mt19937 rng{ std::random_device{}() };
static thread_local std::uniform_real_distribution<float> angleDist(0.f, static_cast<float>(M_PI * 2.0));
std::vector<std::string> respawnMsgs;
{
std::lock_guard<std::mutex> bm(g_boxes_mutex);
for (int idx : boxesToRespawn) {
if (idx < 0 || idx >= (int)g_serverBoxes.size()) continue;
Eigen::Vector3f newPos = PickSafeBoxPos(idx);
Eigen::Vector3f axis = Eigen::Vector3f::Random().normalized();
Eigen::Matrix3f newRot = Eigen::AngleAxisf(angleDist(rng), axis).toRotationMatrix();
g_serverBoxes[idx].position = newPos;
g_serverBoxes[idx].rotation = newRot;
g_serverBoxes[idx].destroyed = false;
Eigen::Quaternionf q(newRot);
std::string respawnMsg = "BOX_RESPAWN:" +
std::to_string(idx) + ":" +
std::to_string(newPos.x()) + ":" +
std::to_string(newPos.y()) + ":" +
std::to_string(newPos.z()) + ":" +
std::to_string(q.w()) + ":" +
std::to_string(q.x()) + ":" +
std::to_string(q.y()) + ":" +
std::to_string(q.z());
respawnMsgs.push_back(respawnMsg);
std::cout << "Server: Box " << idx << " respawned" << std::endl;
}
}
for (const auto& msg : respawnMsgs) {
broadcastToAll(msg);
}
}
// --- Schedule next tick in 50ms ---
timer.expires_after(std::chrono::milliseconds(50));
timer.async_wait([this](const boost::system::error_code& ec) {
if (ec) return;
update_world();
});
}
std::vector<ServerBox> Server::generateServerBoxes(int count) {
std::vector<ServerBox> boxes;
std::random_device rd;
std::mt19937 gen(rd());
const float MIN_COORD = -1000.0f;
const float MAX_COORD = 1000.0f;
const float MIN_DISTANCE = 3.0f;
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
const int MAX_ATTEMPTS = 1000;
std::uniform_real_distribution<> posDistrib(MIN_COORD, MAX_COORD);
std::uniform_real_distribution<> angleDistrib(0.0, M_PI * 2.0);
for (int i = 0; i < count; i++) {
bool accepted = false;
int attempts = 0;
while (!accepted && attempts < MAX_ATTEMPTS) {
ServerBox box;
box.position = Eigen::Vector3f(
(float)posDistrib(gen),
(float)posDistrib(gen),
(float)posDistrib(gen)
);
accepted = true;
for (const auto& existingBox : boxes) {
Eigen::Vector3f diff = box.position - existingBox.position;
if (diff.squaredNorm() < MIN_DISTANCE_SQUARED) {
accepted = false;
break;
}
}
if (accepted) {
float randomAngle = (float)angleDistrib(gen);
Eigen::Vector3f axis = Eigen::Vector3f::Random().normalized();
box.rotation = Eigen::AngleAxisf(randomAngle, axis).toRotationMatrix();
boxes.push_back(box);
}
attempts++;
}
}
return boxes;
}
Server::Server(tcp::acceptor& acceptor, net::io_context& ioc)
: acceptor_(acceptor)
, ioc_(ioc)
, timer(ioc_)
{
}
void Server::init()
{
std::lock_guard<std::mutex> lock(g_boxes_mutex);
g_serverBoxes = generateServerBoxes(50);
std::cout << "Generated " << g_serverBoxes.size() << " boxes on server\n";
}
void Server::accept()
{
acceptor_.async_accept([&](beast::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<Session>(*this, std::move(socket), next_id++)->run();
}
accept();
});
}
int main() {
try {
net::io_context ioc;
tcp::acceptor acceptor{ ioc, {tcp::v4(), 8081} };
Server server(acceptor, ioc);
server.init();
server.accept();
std::cout << "Server started on port 8081...\n";
server.update_world();
ioc.run();
}
catch (std::exception const& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}

View File

@ -1,142 +0,0 @@
#pragma once
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <string>
#include <memory>
#include <vector>
#include <mutex>
#include <queue>
#include <unordered_set>
#include <Eigen/Dense>
#include "../src/network/ClientState.h"
#define _USE_MATH_DEFINES
#include <math.h>
namespace beast = boost::beast;
namespace http = beast::http;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = net::ip::tcp;
static constexpr float kWorldZOffset = 45000.0f;
static const Eigen::Vector3f kWorldOffset(0.0f, 0.0f, kWorldZOffset);
static constexpr float kShipRadius = 15.0f;
static constexpr float kSpawnShipMargin = 25.0f;
static constexpr float kSpawnBoxMargin = 15.0f;
static constexpr float kSpawnZJitter = 60.0f;
std::vector<std::string> split(const std::string& s, char delimiter);
struct DeathInfo {
int targetId = -1;
uint64_t serverTime = 0;
Eigen::Vector3f position = Eigen::Vector3f::Zero();
int killerId = -1;
};
struct ServerBox {
Eigen::Vector3f position;
Eigen::Matrix3f rotation;
float collisionRadius = 2.0f;
bool destroyed = false;
};
struct Projectile {
int shooterId = -1;
uint64_t spawnMs = 0;
Eigen::Vector3f pos;
Eigen::Vector3f vel;
float lifeMs = PROJECTILE_LIFE;
};
struct BoxDestroyedInfo {
int boxIndex = -1;
uint64_t serverTime = 0;
Eigen::Vector3f position = Eigen::Vector3f::Zero();
int destroyedBy = -1;
};
class Server;
class Session : public std::enable_shared_from_this<Session> {
Server& server_;
websocket::stream<beast::tcp_stream> ws_;
beast::flat_buffer buffer_;
int id_;
bool is_writing_ = false;
std::queue<std::shared_ptr<std::string>> writeQueue_;
std::mutex writeMutex_;
public:
ClientStateInterval timedClientStates;
bool joined_ = false;
std::chrono::system_clock::time_point lastReceivedTime_;
bool hasReservedSpawn_ = false;
Eigen::Vector3f reservedSpawn_ = Eigen::Vector3f(0.0f, 0.0f, kWorldZOffset);
std::string nickname = "Player";
int shipType = 0;
Session(Server& server, tcp::socket&& socket, int id);
int get_id() const;
bool hasSpawnReserved() const;
const Eigen::Vector3f& reservedSpawn() const;
bool fetchStateAtTime(std::chrono::system_clock::time_point targetTime, ClientState& outState) const;
void send_message(const std::string& msg);
void run();
bool IsMessageValid(const std::string& fullMessage);
bool is_timed_out(std::chrono::system_clock::time_point now) const;
void force_disconnect();
private:
void sendBoxesToClient();
public:
void init();
ClientState get_latest_state(std::chrono::system_clock::time_point now);
void doWrite();
private:
void do_read();
void process_message(const std::string& msg);
};
class Server
{
public:
tcp::acceptor& acceptor_;
net::io_context& ioc_;
net::steady_timer timer;
std::vector<BoxDestroyedInfo> g_boxDestructions;
std::mutex g_boxDestructions_mutex;
std::vector<ServerBox> g_serverBoxes;
std::mutex g_boxes_mutex;
std::vector<std::shared_ptr<Session>> g_sessions;
std::mutex g_sessions_mutex;
std::vector<Projectile> g_projectiles;
std::mutex g_projectiles_mutex;
std::unordered_set<int> g_dead_players;
std::mutex g_dead_mutex;
int next_id = 1000;
std::vector<ServerBox> generateServerBoxes(int count);
public:
Server(tcp::acceptor& acceptor, net::io_context& ioc);
void broadcastToAll(const std::string& message);
void broadcastToAllExceptId(const std::string& message, int id);
void createProjectile(int id, Eigen::Vector3f pos, Eigen::Quaternionf dir, float velocity);
void update_world();
Eigen::Vector3f PickSafeSpawnPos(int forPlayerId);
// Caller must hold g_boxes_mutex
Eigen::Vector3f PickSafeBoxPos(int skipIdx);
void init();
void accept();
};

View File

@ -4,7 +4,7 @@
#include "render/Renderer.h" #include "render/Renderer.h"
#include "render/TextureManager.h" #include "render/TextureManager.h"
namespace ZL namespace FRG
{ {
struct MeshGroup struct MeshGroup

View File

@ -37,7 +37,7 @@ bool AudioPlayerAsync::init() {
Mix_AllocateChannels(16); Mix_AllocateChannels(16);
initialized = true; initialized = true;
ZL::logger() << "AudioPlayerAsync initialized with SDL2_mixer" << std::endl; FRG::logger() << "AudioPlayerAsync initialized with SDL2_mixer" << std::endl;
return true; return true;
} }
@ -61,7 +61,7 @@ void AudioPlayerAsync::shutdown() {
Mix_CloseAudio(); Mix_CloseAudio();
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
initialized = false; initialized = false;
ZL::logger() << "AudioPlayerAsync shutdown" << std::endl; FRG::logger() << "AudioPlayerAsync shutdown" << std::endl;
} }
void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, int channel) { void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, int channel) {

View File

@ -6,7 +6,7 @@
#include <sstream> #include <sstream>
#include <cstring> #include <cstring>
namespace ZL namespace FRG
{ {
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
using std::min; using std::min;

View File

@ -3,7 +3,7 @@
#include <unordered_map> #include <unordered_map>
namespace ZL namespace FRG
{ {
constexpr int MAX_BONE_COUNT = 6; constexpr int MAX_BONE_COUNT = 6;
constexpr int MAX_GPU_BONES = 64; constexpr int MAX_GPU_BONES = 64;

View File

@ -10,7 +10,7 @@
#include "utils/Utils.h" #include "utils/Utils.h"
#include "TextModel.h" #include "TextModel.h"
namespace ZL { namespace FRG {
const float ATTACK_COOLDOWN_TIME = 1.6f; const float ATTACK_COOLDOWN_TIME = 1.6f;
extern float x; extern float x;
@ -193,7 +193,7 @@ void Character::forceReplan() {
state.onArrivedCallbackName.clear(); state.onArrivedCallbackName.clear();
} }
void Character::setTexture(std::shared_ptr<ZL::Texture> texture) { void Character::setTexture(std::shared_ptr<FRG::Texture> texture) {
for (auto& animEntry : animations) { for (auto& animEntry : animations) {
for (const auto& name : animEntry.second.model.meshNamesOrdered) { for (const auto& name : animEntry.second.model.meshNamesOrdered) {
meshTextures[name] = texture; meshTextures[name] = texture;
@ -1354,4 +1354,4 @@ void Character::drawHealthBar(Renderer& renderer,
renderer.shaderManager.PopShader(); renderer.shaderManager.PopShader();
} }
} // namespace ZL } // namespace FRG

View File

@ -14,7 +14,7 @@
#include "dialogue/TranslationDatabase.h" #include "dialogue/TranslationDatabase.h"
#include "AudioPlayerAsync.h" #include "AudioPlayerAsync.h"
namespace ZL { namespace FRG {
class TextRenderer; class TextRenderer;
@ -40,7 +40,7 @@ public:
// Assigns a texture to a specific mesh by name. // Assigns a texture to a specific mesh by name.
void setTexture(const std::string& meshName, std::shared_ptr<Texture> texture); void setTexture(const std::string& meshName, std::shared_ptr<Texture> texture);
// Assigns one texture to every mesh in every loaded animation. Call AFTER loading animations. // Assigns one texture to every mesh in every loaded animation. Call AFTER loading animations.
void setTexture(std::shared_ptr<ZL::Texture> texture); void setTexture(std::shared_ptr<FRG::Texture> texture);
// Creates a fully-loaded Character from a CharacterState (including its creationInfo). // Creates a fully-loaded Character from a CharacterState (including its creationInfo).
// Loads all animations, textures, and weapon mesh/texture from the paths stored in creationInfo. // Loads all animations, textures, and weapon mesh/texture from the paths stored in creationInfo.
@ -158,4 +158,4 @@ private:
const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const float* ambientColor, const float* fogColor); const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const float* ambientColor, const float* fogColor);
}; };
} // namespace ZL } // namespace FRG

View File

@ -1,6 +1,6 @@
#include "CharacterState.h" #include "CharacterState.h"
namespace ZL { namespace FRG {
bool CharacterState::isMoving() const { bool CharacterState::isMoving() const {
Eigen::Vector3f toTarget = walkTarget - position; Eigen::Vector3f toTarget = walkTarget - position;
@ -92,4 +92,4 @@ void CharacterState::load(const nlohmann::json& in)
onArrivedCallbackName = in.value("onArrivedCallbackName", std::string()); onArrivedCallbackName = in.value("onArrivedCallbackName", std::string());
} }
} // namespace ZL } // namespace FRG

View File

@ -8,7 +8,7 @@
#include "external/nlohmann/json.hpp" #include "external/nlohmann/json.hpp"
#include "ISaveable.h" #include "ISaveable.h"
namespace ZL { namespace FRG {
constexpr float VISIBLE_RANGE = 6.f; constexpr float VISIBLE_RANGE = 6.f;
@ -141,4 +141,4 @@ public:
void load(const nlohmann::json& in) override; void load(const nlohmann::json& in) override;
}; };
} // namespace ZL } // namespace FRG

View File

@ -13,7 +13,7 @@
#include <emscripten/html5.h> #include <emscripten/html5.h>
#endif #endif
namespace ZL { namespace FRG {
@ -92,4 +92,4 @@ void Environment::setFullscreen(bool enable) {
#endif #endif
} }
} // namespace ZL } // namespace FRG

View File

@ -7,7 +7,7 @@
#endif #endif
#include <Eigen/Dense> #include <Eigen/Dense>
namespace ZL { namespace FRG {
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
@ -64,4 +64,4 @@ public:
static void computeProjectionDimensions(); static void computeProjectionDimensions();
}; };
} // namespace ZL } // namespace FRG

View File

@ -24,7 +24,7 @@
#include "GameConstants.h" #include "GameConstants.h"
namespace ZL namespace FRG
{ {
static const float zoomMin = 6.0f; static const float zoomMin = 6.0f;
static const float zoomMax = 20.0f; static const float zoomMax = 20.0f;
@ -85,8 +85,8 @@ namespace ZL
Environment::height = Environment::CONST_DEFAULT_HEIGHT; Environment::height = Environment::CONST_DEFAULT_HEIGHT;
Environment::computeProjectionDimensions(); Environment::computeProjectionDimensions();
ZL::BindOpenGlFunctions(); FRG::BindOpenGlFunctions();
ZL::CheckGlError(__FILE__, __LINE__); FRG::CheckGlError(__FILE__, __LINE__);
renderer.InitOpenGL(); renderer.InitOpenGL();
#if defined(EMSCRIPTEN) #if defined(EMSCRIPTEN)
@ -152,7 +152,7 @@ namespace ZL
// (e.g. resources/start_dorm.lua) start a dialogue as soon as they're // (e.g. resources/start_dorm.lua) start a dialogue as soon as they're
// loaded, so the language needs to be known before that first load. // loaded, so the language needs to be known before that first load.
loadSteps.push([this]() { loadSteps.push([this]() {
ZL::loadLanguageSetting(); FRG::loadLanguageSetting();
}); });
loadSteps.push([this]() { loadSteps.push([this]() {
@ -165,7 +165,7 @@ namespace ZL
renderer.shaderManager.AddShaderFromFiles("spark", "resources/shaders/spark.vertex", "resources/shaders/spark_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("spark", "resources/shaders/spark.vertex", "resources/shaders/spark_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("skinning", "resources/shaders/skinning.vertex", "resources/shaders/default_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("skinning", "resources/shaders/skinning.vertex", "resources/shaders/default_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog", "resources/shaders/fog.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("fog", "resources/shaders/fog.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_skinning", "resources/shaders/fog_skinning.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("fog_skinning", "resources/shaders/fog_skinning_web.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_fog", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("darklands_fog", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_fog_skinning", "resources/shaders/darklands_fog_skinning.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("darklands_fog_skinning", "resources/shaders/darklands_fog_skinning.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("darklands_flash", "resources/shaders/default.vertex", "resources/shaders/darklands_flash_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("darklands_flash", "resources/shaders/default.vertex", "resources/shaders/darklands_flash_web.fragment", CONST_ZIP_FILE);
@ -173,15 +173,15 @@ namespace ZL
renderer.shaderManager.AddShaderFromFiles("cutsceneBlack", "resources/shaders/default.vertex", "resources/shaders/cutscene_black_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("cutsceneBlack", "resources/shaders/default.vertex", "resources/shaders/cutscene_black_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("shadow_depth", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("shadow_depth", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("shadow_depth_skinning", "resources/shaders/shadow_depth_skinning.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("shadow_depth_skinning", "resources/shaders/shadow_depth_skinning.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("default_shadow", "resources/shaders/default_shadow.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("default_shadow", "resources/shaders/default_shadow_web.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow_web.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow_web.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow_web.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog", "resources/shaders/night_fog.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("night_fog", "resources/shaders/night_fog_web.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning_web.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow_web.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow_web.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
#elif defined(__linux__) /*#elif defined(__linux__)
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE);
@ -207,7 +207,7 @@ namespace ZL
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
*/
#else #else
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE);
@ -444,7 +444,7 @@ namespace ZL
void Game::performResetToInitialState() { void Game::performResetToInitialState() {
const std::string path = "resources/config/start_state.json"; const std::string path = "resources/config/start_state.json";
const std::string content = ZL::readTextFile(path); const std::string content = FRG::readTextFile(path);
if (!content.empty()) { if (!content.empty()) {
try { try {
nlohmann::json root = nlohmann::json::parse(content); nlohmann::json root = nlohmann::json::parse(content);
@ -1013,11 +1013,11 @@ namespace ZL
if (showLoadingProgressBar == false) // Loading language screen if (showLoadingProgressBar == false) // Loading language screen
{ {
if (ZL::g_currentLanguage == ZL::Language::English) if (FRG::g_currentLanguage == FRG::Language::English)
{ {
glBindTexture(GL_TEXTURE_2D, menuManager.languageLoadingEn->getTexID()); glBindTexture(GL_TEXTURE_2D, menuManager.languageLoadingEn->getTexID());
} }
else if (ZL::g_currentLanguage == ZL::Language::Russian) else if (FRG::g_currentLanguage == FRG::Language::Russian)
{ {
glBindTexture(GL_TEXTURE_2D, menuManager.languageLoadingRu->getTexID()); glBindTexture(GL_TEXTURE_2D, menuManager.languageLoadingRu->getTexID());
} }
@ -1156,7 +1156,7 @@ namespace ZL
} }
void Game::render() { void Game::render() {
ZL::CheckGlError(__FILE__, __LINE__); FRG::CheckGlError(__FILE__, __LINE__);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@ -1164,7 +1164,7 @@ namespace ZL
drawScene(); drawScene();
processTickCount(); processTickCount();
SDL_GL_SwapWindow(ZL::Environment::window); SDL_GL_SwapWindow(FRG::Environment::window);
} }
void Game::update() { void Game::update() {
@ -1178,7 +1178,7 @@ namespace ZL
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) { if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
const bool wasPortrait = Environment::width < Environment::height; const bool wasPortrait = Environment::width < Environment::height;
//ZL::logger() << "Window resized" << std::endl; //FRG::logger() << "Window resized" << std::endl;
if (Environment::isHighDPIEnabled) { if (Environment::isHighDPIEnabled) {
// Если High DPI включен, запрашиваем реальное физическое разрешение буфера // Если High DPI включен, запрашиваем реальное физическое разрешение буфера
int drawW, drawH; int drawW, drawH;
@ -1188,8 +1188,8 @@ namespace ZL
} }
else { else {
// Если High DPI выключен, используем логические размеры из события // Если High DPI выключен, используем логические размеры из события
Environment::width = event.window.data1 / ZL::Environment::customDpiScale; Environment::width = event.window.data1 / FRG::Environment::customDpiScale;
Environment::height = event.window.data2 / ZL::Environment::customDpiScale; Environment::height = event.window.data2 / FRG::Environment::customDpiScale;
} }
Environment::computeProjectionDimensions(); Environment::computeProjectionDimensions();
@ -1260,10 +1260,10 @@ namespace ZL
int my = static_cast<int>((float)eventY / Environment::height * Environment::projectionHeight); int my = static_cast<int>((float)eventY / Environment::height * Environment::projectionHeight);
if (event.type == SDL_MOUSEBUTTONDOWN) { if (event.type == SDL_MOUSEBUTTONDOWN) {
onPointerDown(ZL::UiManager::MOUSE_FINGER_ID, eventX, eventY, mx, my); onPointerDown(FRG::UiManager::MOUSE_FINGER_ID, eventX, eventY, mx, my);
} }
else { else {
onPointerUp(ZL::UiManager::MOUSE_FINGER_ID, eventX, eventY, mx, my); onPointerUp(FRG::UiManager::MOUSE_FINGER_ID, eventX, eventY, mx, my);
} }
} }
else if (event.button.button == SDL_BUTTON_RIGHT else if (event.button.button == SDL_BUTTON_RIGHT
@ -1278,7 +1278,7 @@ namespace ZL
int eventY = event.motion.y; int eventY = event.motion.y;
int mx = static_cast<int>((float)eventX / Environment::width * Environment::projectionWidth); int mx = static_cast<int>((float)eventX / Environment::width * Environment::projectionWidth);
int my = static_cast<int>((float)eventY / Environment::height * Environment::projectionHeight); int my = static_cast<int>((float)eventY / Environment::height * Environment::projectionHeight);
onPointerMotion(ZL::UiManager::MOUSE_FINGER_ID, eventX, eventY, mx, my); onPointerMotion(FRG::UiManager::MOUSE_FINGER_ID, eventX, eventY, mx, my);
} }
if (event.type == SDL_MOUSEWHEEL) { if (event.type == SDL_MOUSEWHEEL) {
@ -1851,7 +1851,7 @@ namespace ZL
SaveSlotInfo Game::readSlotInfo(int slot) const SaveSlotInfo Game::readSlotInfo(int slot) const
{ {
std::string path = "save_slot" + std::to_string(slot) + ".json"; std::string path = "save_slot" + std::to_string(slot) + ".json";
std::string content = ZL::readSavedTextFile(path); std::string content = FRG::readSavedTextFile(path);
if (content.empty()) return {}; if (content.empty()) return {};
try { try {
nlohmann::json root = nlohmann::json::parse(content); nlohmann::json root = nlohmann::json::parse(content);
@ -1883,7 +1883,7 @@ namespace ZL
void Game::loadGame(int slot) void Game::loadGame(int slot)
{ {
std::string path = "save_slot" + std::to_string(slot) + ".json"; std::string path = "save_slot" + std::to_string(slot) + ".json";
std::string content = ZL::readSavedTextFile(path); std::string content = FRG::readSavedTextFile(path);
if (content.empty()) { if (content.empty()) {
std::cerr << "[save] Save file not found or empty: " << path << std::endl; std::cerr << "[save] Save file not found or empty: " << path << std::endl;
return; return;
@ -1915,4 +1915,4 @@ namespace ZL
} }
} }
} // namespace ZL } // namespace FRG

View File

@ -22,7 +22,7 @@
#include "Location.h" #include "Location.h"
#include "AudioPlayerAsync.h" #include "AudioPlayerAsync.h"
#include "GameState.h" #include "GameState.h"
namespace ZL { namespace FRG {
struct SaveSlotInfo { struct SaveSlotInfo {
std::string locationName; std::string locationName;
@ -206,4 +206,4 @@ namespace ZL {
}; };
} // namespace ZL } // namespace FRG

View File

@ -1,6 +1,6 @@
#include "GameConstants.h" #include "GameConstants.h"
namespace ZL namespace FRG
{ {
const std::string defaultShaderName = "default"; const std::string defaultShaderName = "default";
const std::string envShaderName = "env"; const std::string envShaderName = "env";

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "render/Renderer.h" #include "render/Renderer.h"
namespace ZL namespace FRG
{ {
extern const std::string defaultShaderName; extern const std::string defaultShaderName;
extern const std::string envShaderName; extern const std::string envShaderName;

View File

@ -3,7 +3,7 @@
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
namespace ZL { namespace FRG {
void GameState::save(nlohmann::json& out) const void GameState::save(nlohmann::json& out) const
{ {
@ -158,4 +158,4 @@ void GameState::load(const nlohmann::json& in)
taxiIsCalled = in.value("taxiIsCalled", taxiIsCalled); taxiIsCalled = in.value("taxiIsCalled", taxiIsCalled);
} }
} // namespace ZL } // namespace FRG

View File

@ -9,7 +9,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
namespace ZL { namespace FRG {
enum class TutorialStep { enum class TutorialStep {
Step0, // Dialogue hint: "click to advance" Step0, // Dialogue hint: "click to advance"
@ -75,4 +75,4 @@ struct GameState : public ISaveable {
void load(const nlohmann::json& in) override; void load(const nlohmann::json& in) override;
}; };
} // namespace ZL } // namespace FRG

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "external/nlohmann/json.hpp" #include "external/nlohmann/json.hpp"
namespace ZL { namespace FRG {
struct ISaveable { struct ISaveable {
virtual void save(nlohmann::json& out) const = 0; virtual void save(nlohmann::json& out) const = 0;
@ -9,4 +9,4 @@ struct ISaveable {
virtual ~ISaveable() = default; virtual ~ISaveable() = default;
}; };
} // namespace ZL } // namespace FRG

View File

@ -11,7 +11,7 @@
#include <emscripten.h> #include <emscripten.h>
#endif #endif
namespace ZL { namespace FRG {
Language g_currentLanguage = Language::Russian; Language g_currentLanguage = Language::Russian;
@ -119,4 +119,4 @@ Language detectSystemLanguage() {
return Language::English; return Language::English;
} }
} // namespace ZL } // namespace FRG

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <string> #include <string>
namespace ZL { namespace FRG {
enum class Language { enum class Language {
Russian, Russian,
@ -31,4 +31,4 @@ void loadLanguageSetting();
Language detectSystemLanguage(); Language detectSystemLanguage();
} // namespace ZL } // namespace FRG

View File

@ -16,7 +16,7 @@
#include "external/nlohmann/json.hpp" #include "external/nlohmann/json.hpp"
#include <SDL.h> #include <SDL.h>
namespace ZL namespace FRG
{ {
extern const char* CONST_ZIP_FILE; extern const char* CONST_ZIP_FILE;
@ -2232,4 +2232,4 @@ namespace ZL
} }
} }
} // namespace ZL } // namespace FRG

View File

@ -20,7 +20,7 @@
#include <cstdint> #include <cstdint>
#include <unordered_map> #include <unordered_map>
namespace ZL namespace FRG
{ {
struct PointLight struct PointLight
@ -214,4 +214,4 @@ namespace ZL
std::unordered_map<int, float> npcBumpsPlayerCooldown; std::unordered_map<int, float> npcBumpsPlayerCooldown;
}; };
} // namespace ZL } // namespace FRG

View File

@ -12,7 +12,7 @@
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
namespace ZL namespace FRG
{ {
extern const char* CONST_ZIP_FILE; extern const char* CONST_ZIP_FILE;
@ -535,4 +535,4 @@ namespace ZL
saveJsonToFile(j, filename); saveJsonToFile(j, filename);
} }
} // namespace ZL } // namespace FRG

View File

@ -7,7 +7,7 @@
#include "items/GameObjectLoader.h" #include "items/GameObjectLoader.h"
#include <Eigen/Dense> #include <Eigen/Dense>
namespace ZL { namespace FRG {
class Location; // forward declaration — LocationEditor.cpp includes Location.h class Location; // forward declaration — LocationEditor.cpp includes Location.h
@ -67,4 +67,4 @@ private:
Location& loc; Location& loc;
}; };
} // namespace ZL } // namespace FRG

View File

@ -1,6 +1,6 @@
#include "LocationState.h" #include "LocationState.h"
namespace ZL { namespace FRG {
void LocationState::save(nlohmann::json& out) const void LocationState::save(nlohmann::json& out) const
{ {
@ -223,4 +223,4 @@ void LocationState::load(const nlohmann::json& in)
} }
} // namespace ZL } // namespace FRG

View File

@ -3,7 +3,7 @@
#include "ISaveable.h" #include "ISaveable.h"
#include "NpcCar.h" #include "NpcCar.h"
namespace ZL { namespace FRG {
struct LocationState : public ISaveable { struct LocationState : public ISaveable {
// ---- Camera ---- // ---- Camera ----
@ -41,4 +41,4 @@ struct LocationState : public ISaveable {
void load(const nlohmann::json& in) override; void load(const nlohmann::json& in) override;
}; };
} // namespace ZL } // namespace FRG

View File

@ -10,7 +10,7 @@
#include <string> #include <string>
#include <SDL.h> #include <SDL.h>
namespace ZL { namespace FRG {
// Localization // Localization
static const std::string EMPTY_LANGUAGE_RU = u8"(пусто)"; static const std::string EMPTY_LANGUAGE_RU = u8"(пусто)";
@ -669,13 +669,13 @@ namespace ZL {
uiManager.setTextButtonCallback("languageRussianButton", [this](const std::string&) { uiManager.setTextButtonCallback("languageRussianButton", [this](const std::string&) {
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg"); audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
ZL::g_currentLanguage = ZL::Language::Russian; FRG::g_currentLanguage = FRG::Language::Russian;
reloadLocalizedGameContent(); reloadLocalizedGameContent();
saveSettings(); saveSettings();
}); });
uiManager.setTextButtonCallback("languageEnglishButton", [this](const std::string&) { uiManager.setTextButtonCallback("languageEnglishButton", [this](const std::string&) {
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg"); audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
ZL::g_currentLanguage = ZL::Language::English; FRG::g_currentLanguage = FRG::Language::English;
reloadLocalizedGameContent(); reloadLocalizedGameContent();
saveSettings(); saveSettings();
}); });
@ -748,18 +748,18 @@ namespace ZL {
SaveSlotInfo info = getSlotInfoFunc(slot); SaveSlotInfo info = getSlotInfoFunc(slot);
std::string emptyText; std::string emptyText;
if (ZL::g_currentLanguage == ZL::Language::Russian) if (FRG::g_currentLanguage == FRG::Language::Russian)
{ {
emptyText = EMPTY_LANGUAGE_RU; emptyText = EMPTY_LANGUAGE_RU;
} }
else if (ZL::g_currentLanguage == ZL::Language::English) else if (FRG::g_currentLanguage == FRG::Language::English)
{ {
emptyText = EMPTY_LANGUAGE_EN; emptyText = EMPTY_LANGUAGE_EN;
} }
std::string label = info.empty std::string label = info.empty
? emptyText ? emptyText
: localizedLocationName[info.locationName][ZL::g_currentLanguage] + " " + info.savedAt; : localizedLocationName[info.locationName][FRG::g_currentLanguage] + " " + info.savedAt;
uiManager.setTextButtonText(kSlotButtons[i], label); uiManager.setTextButtonText(kSlotButtons[i], label);
} }
uiManager.setTextButtonCallback(kSlotButtons[i], [this, slot](const std::string&) { uiManager.setTextButtonCallback(kSlotButtons[i], [this, slot](const std::string&) {
@ -789,18 +789,18 @@ namespace ZL {
SaveSlotInfo info = getSlotInfoFunc(slot); SaveSlotInfo info = getSlotInfoFunc(slot);
std::string emptyText; std::string emptyText;
if (ZL::g_currentLanguage == ZL::Language::Russian) if (FRG::g_currentLanguage == FRG::Language::Russian)
{ {
emptyText = EMPTY_LANGUAGE_RU; emptyText = EMPTY_LANGUAGE_RU;
} }
else if (ZL::g_currentLanguage == ZL::Language::English) else if (FRG::g_currentLanguage == FRG::Language::English)
{ {
emptyText = EMPTY_LANGUAGE_EN; emptyText = EMPTY_LANGUAGE_EN;
} }
std::string label = info.empty std::string label = info.empty
? emptyText ? emptyText
: localizedLocationName[info.locationName][ZL::g_currentLanguage] + " " + info.savedAt; : localizedLocationName[info.locationName][FRG::g_currentLanguage] + " " + info.savedAt;
uiManager.setTextButtonText(buttonName, label); uiManager.setTextButtonText(buttonName, label);
} }
uiManager.setTextButtonCallback(buttonName, [this, slot, buttonName](const std::string&) { uiManager.setTextButtonCallback(buttonName, [this, slot, buttonName](const std::string&) {
@ -809,18 +809,18 @@ namespace ZL {
SaveSlotInfo info = getSlotInfoFunc(slot); SaveSlotInfo info = getSlotInfoFunc(slot);
std::string emptyText; std::string emptyText;
if (ZL::g_currentLanguage == ZL::Language::Russian) if (FRG::g_currentLanguage == FRG::Language::Russian)
{ {
emptyText = EMPTY_LANGUAGE_RU; emptyText = EMPTY_LANGUAGE_RU;
} }
else if (ZL::g_currentLanguage == ZL::Language::English) else if (FRG::g_currentLanguage == FRG::Language::English)
{ {
emptyText = EMPTY_LANGUAGE_EN; emptyText = EMPTY_LANGUAGE_EN;
} }
std::string label = info.empty std::string label = info.empty
? emptyText ? emptyText
: localizedLocationName[info.locationName][ZL::g_currentLanguage] + " " + info.savedAt; : localizedLocationName[info.locationName][FRG::g_currentLanguage] + " " + info.savedAt;
uiManager.setTextButtonText(buttonName, label); uiManager.setTextButtonText(buttonName, label);
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg"); audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
} }
@ -2271,7 +2271,7 @@ namespace ZL {
root["musicEnabled"] = audioPlayer_.isMusicEnabled(); root["musicEnabled"] = audioPlayer_.isMusicEnabled();
root["soundEnabled"] = audioPlayer_.isSoundEnabled(); root["soundEnabled"] = audioPlayer_.isSoundEnabled();
root["fullscreen"] = Environment::isFullscreen; root["fullscreen"] = Environment::isFullscreen;
root["language"] = ZL::languageToCode(ZL::g_currentLanguage); root["language"] = FRG::languageToCode(FRG::g_currentLanguage);
root["shadow"] = shadowsEnabled; root["shadow"] = shadowsEnabled;
saveJsonToFile(root, "settings.json"); saveJsonToFile(root, "settings.json");
@ -2282,12 +2282,12 @@ namespace ZL {
// directly in the right fullscreen state (see WIN32 main() in main.cpp) to avoid // directly in the right fullscreen state (see WIN32 main() in main.cpp) to avoid
// a windowed -> fullscreen flash. The block below just keeps Environment::isFullscreen // a windowed -> fullscreen flash. The block below just keeps Environment::isFullscreen
// in sync in case this is ever called from a path that didn't pre-apply it. // in sync in case this is ever called from a path that didn't pre-apply it.
// Language is loaded separately, even earlier (see ZL::loadLanguageSetting() // Language is loaded separately, even earlier (see FRG::loadLanguageSetting()
// callers), because some location scripts start a dialogue as soon as they're // callers), because some location scripts start a dialogue as soon as they're
// loaded, before this function ever runs. // loaded, before this function ever runs.
ZL::loadLanguageSetting(); FRG::loadLanguageSetting();
const std::string content = ZL::readSavedTextFile("settings.json"); const std::string content = FRG::readSavedTextFile("settings.json");
if (content.empty()) return; if (content.empty()) return;
try { try {
const nlohmann::json root = nlohmann::json::parse(content); const nlohmann::json root = nlohmann::json::parse(content);
@ -2427,4 +2427,4 @@ namespace ZL {
uiManager.updateAllLayouts(); uiManager.updateAllLayouts();
} }
} // namespace ZL } // namespace FRG

View File

@ -13,9 +13,9 @@
#include "render/FrameBuffer.h" #include "render/FrameBuffer.h"
// Forward-declared here to avoid pulling Game.h into MenuManager.h. // Forward-declared here to avoid pulling Game.h into MenuManager.h.
namespace ZL { struct SaveSlotInfo; } namespace FRG { struct SaveSlotInfo; }
namespace ZL { namespace FRG {
extern const char* CONST_ZIP_FILE; extern const char* CONST_ZIP_FILE;
@ -343,4 +343,4 @@ namespace ZL {
}; };
} // namespace ZL } // namespace FRG

View File

@ -1,6 +1,6 @@
#include "NpcCar.h" #include "NpcCar.h"
namespace ZL namespace FRG
{ {
const float NpcCar::acceleration = 10.0f; const float NpcCar::acceleration = 10.0f;
const float NpcCar::friction = 8.0f; const float NpcCar::friction = 8.0f;

View File

@ -2,7 +2,7 @@
#include "render/Renderer.h" #include "render/Renderer.h"
#include "Environment.h" #include "Environment.h"
namespace ZL { namespace FRG {
struct NpcCar struct NpcCar
{ {
enum class Mode { FOLLOW_WAYPOINTS, NONE }; enum class Mode { FOLLOW_WAYPOINTS, NONE };

View File

@ -10,7 +10,7 @@
#define SOL_ALL_SAFETIES_ON 1 #define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp> #include <sol/sol.hpp>
namespace ZL { namespace FRG {
namespace { namespace {
@ -1159,4 +1159,4 @@ namespace ZL {
outCallback = wrapLuaCallback(fn); outCallback = wrapLuaCallback(fn);
} }
} // namespace ZL } // namespace FRG

View File

@ -8,7 +8,7 @@
#include "ISaveable.h" #include "ISaveable.h"
#include "AudioPlayerAsync.h" #include "AudioPlayerAsync.h"
namespace ZL { namespace FRG {
class Location; class Location;
class Inventory; class Inventory;
@ -79,4 +79,4 @@ private:
std::unique_ptr<Impl> impl; std::unique_ptr<Impl> impl;
}; };
} // namespace ZL } // namespace FRG

View File

@ -10,7 +10,7 @@
#include "utils/Utils.h" #include "utils/Utils.h"
#include "GameConstants.h" #include "GameConstants.h"
namespace ZL { namespace FRG {
using json = nlohmann::json; using json = nlohmann::json;
@ -669,4 +669,4 @@ namespace ZL {
return true; return true;
} }
} // namespace ZL } // namespace FRG

View File

@ -6,7 +6,7 @@
#include <chrono> #include <chrono>
#include <string> #include <string>
namespace ZL { namespace FRG {
struct SparkParticle { struct SparkParticle {
Vector3f position; Vector3f position;
@ -126,4 +126,4 @@ namespace ZL {
Vector3f getRandomVelocity(int emitterIndex); Vector3f getRandomVelocity(int emitterIndex);
}; };
} // namespace ZL } // namespace FRG

View File

@ -2,7 +2,7 @@
#include "render/Renderer.h" #include "render/Renderer.h"
#include "render/TextureManager.h" #include "render/TextureManager.h"
namespace ZL { namespace FRG {
void TeleportZone::initSparks(std::shared_ptr<Texture> activeTex, std::shared_ptr<Texture> inactiveTex) void TeleportZone::initSparks(std::shared_ptr<Texture> activeTex, std::shared_ptr<Texture> inactiveTex)
{ {
@ -49,4 +49,4 @@ void TeleportZone::draw(Renderer& renderer, float zoom, int width, int height)
if (sparks) sparks->draw(renderer, zoom, width, height); if (sparks) sparks->draw(renderer, zoom, width, height);
} }
} // namespace ZL } // namespace FRG

View File

@ -4,7 +4,7 @@
#include <Eigen/Core> #include <Eigen/Core>
#include "SparkEmitter.h" #include "SparkEmitter.h"
namespace ZL { namespace FRG {
class Renderer; class Renderer;
class Texture; class Texture;
@ -33,4 +33,4 @@ struct TeleportZone {
void draw(Renderer& renderer, float zoom, int width, int height); void draw(Renderer& renderer, float zoom, int width, int height);
}; };
} // namespace ZL } // namespace FRG

View File

@ -7,7 +7,7 @@
#ifdef __ANDROID__ #ifdef __ANDROID__
#include <android/log.h> #include <android/log.h>
#endif #endif
namespace ZL namespace FRG
{ {
static std::unordered_map<std::string, VertexDataStruct> s_meshCache; static std::unordered_map<std::string, VertexDataStruct> s_meshCache;

View File

@ -4,7 +4,7 @@
#include <unordered_map> #include <unordered_map>
namespace ZL namespace FRG
{ {
VertexDataStruct LoadFromTextFile02(const std::string& fileName, const std::string& ZIPFileName = ""); VertexDataStruct LoadFromTextFile02(const std::string& fileName, const std::string& ZIPFileName = "");
VertexDataStruct LoadModelFromBinFile(const std::string& fileName, const std::string& ZIPFileName = ""); VertexDataStruct LoadModelFromBinFile(const std::string& fileName, const std::string& ZIPFileName = "");

View File

@ -7,7 +7,7 @@
#include <sstream> #include <sstream>
#include "GameConstants.h" #include "GameConstants.h"
namespace ZL { namespace FRG {
using json = nlohmann::json; using json = nlohmann::json;
@ -2307,4 +2307,4 @@ namespace ZL {
chatBubbles.clear(); chatBubbles.clear();
} }
} // namespace ZL } // namespace FRG

View File

@ -13,7 +13,7 @@
#include <variant> #include <variant>
#include <cstdint> #include <cstdint>
namespace ZL { namespace FRG {
using json = nlohmann::json; using json = nlohmann::json;
@ -565,4 +565,4 @@ namespace ZL {
}; };
} // namespace ZL } // namespace FRG

View File

@ -3,12 +3,12 @@
#include "utils/Utils.h" #include "utils/Utils.h"
#include <iostream> #include <iostream>
namespace ZL namespace FRG
{ {
extern const char* CONST_ZIP_FILE; extern const char* CONST_ZIP_FILE;
} }
namespace ZL::Cutscene { namespace FRG::Cutscene {
EasingType CutsceneDatabase::parseEasingType(const std::string& value) { EasingType CutsceneDatabase::parseEasingType(const std::string& value) {
if (value == "EaseInSine") return EasingType::EaseInSine; if (value == "EaseInSine") return EasingType::EaseInSine;
@ -92,11 +92,11 @@ bool CutsceneDatabase::loadFromFile(const std::string& path) {
std::string raw; std::string raw;
try { try {
if (strlen(ZL::CONST_ZIP_FILE) == 0) { if (strlen(FRG::CONST_ZIP_FILE) == 0) {
raw = readTextFile(path); raw = readTextFile(path);
} }
else { else {
auto buf = readFileFromZIP(path, ZL::CONST_ZIP_FILE); auto buf = readFileFromZIP(path, FRG::CONST_ZIP_FILE);
if (buf.empty()) { if (buf.empty()) {
std::cerr << "[cutscene] Failed to read " << path << " from zip\n"; std::cerr << "[cutscene] Failed to read " << path << " from zip\n";
throw std::runtime_error("Failed to load cutscene file: " + path); throw std::runtime_error("Failed to load cutscene file: " + path);
@ -135,4 +135,4 @@ const StaticCutsceneDefinition* CutsceneDatabase::findCutscene(const std::string
return (it != cutscenes.end()) ? &it->second : nullptr; return (it != cutscenes.end()) ? &it->second : nullptr;
} }
} // namespace ZL::Cutscene } // namespace FRG::Cutscene

View File

@ -5,7 +5,7 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
namespace ZL::Cutscene { namespace FRG::Cutscene {
class CutsceneDatabase { class CutsceneDatabase {
public: public:
@ -26,4 +26,4 @@ private:
static StaticCutsceneDefinition parseCutscene(const json& j); static StaticCutsceneDefinition parseCutscene(const json& j);
}; };
} // namespace ZL::Cutscene } // namespace FRG::Cutscene

View File

@ -6,7 +6,7 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
namespace ZL::Cutscene { namespace FRG::Cutscene {
bool CutsceneOverlay::init(Renderer& renderer, const std::string& zipFile) { bool CutsceneOverlay::init(Renderer& renderer, const std::string& zipFile) {
rendererRef = &renderer; rendererRef = &renderer;
@ -26,8 +26,8 @@ bool CutsceneOverlay::init(Renderer& renderer, const std::string& zipFile) {
choiceRenderer->init(renderer, "resources/fonts/DroidSans.ttf", 22, zipFile); choiceRenderer->init(renderer, "resources/fonts/DroidSans.ttf", 22, zipFile);
} }
void CutsceneOverlay::update(const ZL::Dialogue::PresentationModel& model, int deltaMs) { void CutsceneOverlay::update(const FRG::Dialogue::PresentationModel& model, int deltaMs) {
if (model.mode != ZL::Dialogue::PresentationMode::Cutscene || !model.cutsceneSkippable) { if (model.mode != FRG::Dialogue::PresentationMode::Cutscene || !model.cutsceneSkippable) {
cutsceneSkipHintVisible = false; cutsceneSkipHintVisible = false;
cutsceneSkipArmed = false; cutsceneSkipArmed = false;
cutsceneSkipHolding = false; cutsceneSkipHolding = false;
@ -106,8 +106,8 @@ void CutsceneOverlay::buildImageUV(
outBR = { (cx + halfW) / safeImgW, (cy - halfH) / safeImgH }; outBR = { (cx + halfW) / safeImgW, (cy - halfH) / safeImgH };
} }
void CutsceneOverlay::draw(Renderer& renderer, const ZL::Dialogue::PresentationModel& model) { void CutsceneOverlay::draw(Renderer& renderer, const FRG::Dialogue::PresentationModel& model) {
if (model.mode != ZL::Dialogue::PresentationMode::Cutscene) return; if (model.mode != FRG::Dialogue::PresentationMode::Cutscene) return;
const float W = Environment::projectionWidth; const float W = Environment::projectionWidth;
const float H = Environment::projectionHeight; const float H = Environment::projectionHeight;
@ -126,7 +126,7 @@ void CutsceneOverlay::draw(Renderer& renderer, const ZL::Dialogue::PresentationM
const UiRect screenRect{ 0.0f, 0.0f, W, H }; const UiRect screenRect{ 0.0f, 0.0f, W, H };
for (const ZL::Cutscene::PresentedCutsceneImage& layer : model.cutsceneImages) { for (const FRG::Cutscene::PresentedCutsceneImage& layer : model.cutsceneImages) {
const auto texture = loadTextureCached(layer.path); const auto texture = loadTextureCached(layer.path);
if (!texture) continue; if (!texture) continue;
@ -224,9 +224,9 @@ bool CutsceneOverlay::consumeSkipRequested() {
return result; return result;
} }
void CutsceneOverlay::handlePointerDown(float x, float y, const ZL::Dialogue::PresentationModel& model) { void CutsceneOverlay::handlePointerDown(float x, float y, const FRG::Dialogue::PresentationModel& model) {
(void)x; (void)y; (void)x; (void)y;
if (model.mode != ZL::Dialogue::PresentationMode::Cutscene || !model.cutsceneSkippable) return; if (model.mode != FRG::Dialogue::PresentationMode::Cutscene || !model.cutsceneSkippable) return;
if (!cutsceneSkipArmed) { if (!cutsceneSkipArmed) {
cutsceneSkipHintVisible = true; cutsceneSkipHintVisible = true;
@ -245,11 +245,11 @@ void CutsceneOverlay::handlePointerDown(float x, float y, const ZL::Dialogue::Pr
cutsceneSkipHoldElapsedMs = 0; cutsceneSkipHoldElapsedMs = 0;
} }
void CutsceneOverlay::handlePointerMoved(float /*x*/, float /*y*/, const ZL::Dialogue::PresentationModel& /*model*/) { void CutsceneOverlay::handlePointerMoved(float /*x*/, float /*y*/, const FRG::Dialogue::PresentationModel& /*model*/) {
} }
bool CutsceneOverlay::handlePointerReleased(float /*x*/, float /*y*/, const ZL::Dialogue::PresentationModel& model) { bool CutsceneOverlay::handlePointerReleased(float /*x*/, float /*y*/, const FRG::Dialogue::PresentationModel& model) {
if (model.mode != ZL::Dialogue::PresentationMode::Cutscene) return false; if (model.mode != FRG::Dialogue::PresentationMode::Cutscene) return false;
if (cutsceneSkipHolding && cutsceneSkipHoldElapsedMs < CutsceneSkipHoldDurationMs) { if (cutsceneSkipHolding && cutsceneSkipHoldElapsedMs < CutsceneSkipHoldDurationMs) {
cutsceneSkipHolding = false; cutsceneSkipHolding = false;
cutsceneSkipHoldElapsedMs = 0; cutsceneSkipHoldElapsedMs = 0;
@ -306,4 +306,4 @@ std::string CutsceneOverlay::wrapTextToWidth(
return output; return output;
} }
} // namespace ZL::Cutscene } // namespace FRG::Cutscene

View File

@ -11,17 +11,17 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace ZL::Cutscene { namespace FRG::Cutscene {
class CutsceneOverlay { class CutsceneOverlay {
public: public:
bool init(Renderer& renderer, const std::string& zipFile = ""); bool init(Renderer& renderer, const std::string& zipFile = "");
void update(const ZL::Dialogue::PresentationModel& model, int deltaMs); void update(const FRG::Dialogue::PresentationModel& model, int deltaMs);
void draw(Renderer& renderer, const ZL::Dialogue::PresentationModel& model); void draw(Renderer& renderer, const FRG::Dialogue::PresentationModel& model);
void handlePointerDown(float x, float y, const ZL::Dialogue::PresentationModel& model); void handlePointerDown(float x, float y, const FRG::Dialogue::PresentationModel& model);
void handlePointerMoved(float x, float y, const ZL::Dialogue::PresentationModel& model); void handlePointerMoved(float x, float y, const FRG::Dialogue::PresentationModel& model);
bool handlePointerReleased(float x, float y, const ZL::Dialogue::PresentationModel& model); bool handlePointerReleased(float x, float y, const FRG::Dialogue::PresentationModel& model);
bool consumeSkipRequested(); bool consumeSkipRequested();
private: private:
@ -69,4 +69,4 @@ private:
float maxWidthPx, float scale); float maxWidthPx, float scale);
}; };
} // namespace ZL::Cutscene } // namespace FRG::Cutscene

View File

@ -5,18 +5,18 @@
#include <iostream> #include <iostream>
#include "utils/Utils.h" #include "utils/Utils.h"
namespace ZL::Cutscene { namespace FRG::Cutscene {
void CutsceneRuntime::setDatabase(const CutsceneDatabase* value) { void CutsceneRuntime::setDatabase(const CutsceneDatabase* value) {
database = value; database = value;
} }
void CutsceneRuntime::setTranslationDatabase(const ZL::Dialogue::TranslationDatabase* value) { void CutsceneRuntime::setTranslationDatabase(const FRG::Dialogue::TranslationDatabase* value) {
translations = value; translations = value;
} }
std::string CutsceneRuntime::tr(const std::string& s) const { std::string CutsceneRuntime::tr(const std::string& s) const {
return translations ? translations->translate(s, ZL::g_currentLanguage) : s; return translations ? translations->translate(s, FRG::g_currentLanguage) : s;
} }
void CutsceneRuntime::setOnFinished(std::function<void(const std::string&)> cb) { void CutsceneRuntime::setOnFinished(std::function<void(const std::string&)> cb) {
@ -282,7 +282,7 @@ std::vector<PresentedCutsceneImage> CutsceneRuntime::evaluateImages() const {
void CutsceneRuntime::refreshPresentation() { void CutsceneRuntime::refreshPresentation() {
if (!activeCutscene) return; if (!activeCutscene) return;
presentation.mode = ZL::Dialogue::PresentationMode::Cutscene; presentation.mode = FRG::Dialogue::PresentationMode::Cutscene;
presentation.cutsceneSkippable = activeCutscene->skippable; presentation.cutsceneSkippable = activeCutscene->skippable;
presentation.cutsceneImages = evaluateImages(); presentation.cutsceneImages = evaluateImages();
@ -381,4 +381,4 @@ int CutsceneRuntime::computeFallbackDurationMs(const std::string& text) {
return std::max(minDuration, calculated + linger); return std::max(minDuration, calculated + linger);
} }
} // namespace ZL::Cutscene } // namespace FRG::Cutscene

View File

@ -7,12 +7,12 @@
#include <functional> #include <functional>
#include <string> #include <string>
namespace ZL::Cutscene { namespace FRG::Cutscene {
class CutsceneRuntime { class CutsceneRuntime {
public: public:
void setDatabase(const CutsceneDatabase* value); void setDatabase(const CutsceneDatabase* value);
void setTranslationDatabase(const ZL::Dialogue::TranslationDatabase* value); void setTranslationDatabase(const FRG::Dialogue::TranslationDatabase* value);
void setOnFinished(std::function<void(const std::string&)> cb); void setOnFinished(std::function<void(const std::string&)> cb);
void setOnLineStarted(std::function<void(const std::string&)> cb); void setOnLineStarted(std::function<void(const std::string&)> cb);
@ -26,11 +26,11 @@ public:
bool canSkip() const; bool canSkip() const;
void skip(); void skip();
const ZL::Dialogue::PresentationModel& getPresentation() const { return presentation; } const FRG::Dialogue::PresentationModel& getPresentation() const { return presentation; }
private: private:
const CutsceneDatabase* database = nullptr; const CutsceneDatabase* database = nullptr;
const ZL::Dialogue::TranslationDatabase* translations = nullptr; const FRG::Dialogue::TranslationDatabase* translations = nullptr;
const StaticCutsceneDefinition* activeCutscene = nullptr; const StaticCutsceneDefinition* activeCutscene = nullptr;
std::string activeCutsceneId; std::string activeCutsceneId;
@ -44,7 +44,7 @@ private:
int cutsceneTotalDurationMs = 0; int cutsceneTotalDurationMs = 0;
int cutsceneContentDurationMs = 0; int cutsceneContentDurationMs = 0;
ZL::Dialogue::PresentationModel presentation; FRG::Dialogue::PresentationModel presentation;
std::function<void(const std::string&)> onFinished; std::function<void(const std::string&)> onFinished;
std::function<void(const std::string&)> onLineStarted; std::function<void(const std::string&)> onLineStarted;
@ -62,4 +62,4 @@ private:
static int computeFallbackDurationMs(const std::string& text); static int computeFallbackDurationMs(const std::string& text);
}; };
} // namespace ZL::Cutscene } // namespace FRG::Cutscene

View File

@ -3,7 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace ZL::Cutscene { namespace FRG::Cutscene {
enum class EasingType { enum class EasingType {
Linear, Linear,
@ -75,4 +75,4 @@ struct PresentedCutsceneImage {
int height = 0; int height = 0;
}; };
} // namespace ZL::Cutscene } // namespace FRG::Cutscene

View File

@ -3,12 +3,12 @@
#include "utils/Utils.h" #include "utils/Utils.h"
#include <iostream> #include <iostream>
namespace ZL namespace FRG
{ {
extern const char* CONST_ZIP_FILE; extern const char* CONST_ZIP_FILE;
} }
namespace ZL::Dialogue { namespace FRG::Dialogue {
NodeType DialogueDatabase::parseNodeType(const std::string& value) { NodeType DialogueDatabase::parseNodeType(const std::string& value) {
if (value == "Choice") return NodeType::Choice; if (value == "Choice") return NodeType::Choice;
@ -174,4 +174,4 @@ const DialogueDefinition* DialogueDatabase::findDialogue(const std::string& id)
return (it != dialogues.end()) ? &it->second : nullptr; return (it != dialogues.end()) ? &it->second : nullptr;
} }
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -5,7 +5,7 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
namespace ZL::Dialogue { namespace FRG::Dialogue {
class DialogueDatabase { class DialogueDatabase {
public: public:
@ -29,4 +29,4 @@ private:
static DialogueDefinition parseDialogue(const json& j); static DialogueDefinition parseDialogue(const json& j);
}; };
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -6,13 +6,13 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
namespace ZL namespace FRG
{ {
extern float x; extern float x;
extern float y; extern float y;
} }
namespace ZL::Dialogue { namespace FRG::Dialogue {
bool DialogueOverlay::init(Renderer& renderer, const std::string& zipFile) { bool DialogueOverlay::init(Renderer& renderer, const std::string& zipFile) {
rendererRef = &renderer; rendererRef = &renderer;
@ -447,4 +447,4 @@ bool DialogueOverlay::rectContains(const UiRect& rect, float x, float y) {
return x >= rect.x && x <= rect.x + rect.w && y >= rect.y && y <= rect.y + rect.h; return x >= rect.x && x <= rect.x + rect.w && y >= rect.y && y <= rect.y + rect.h;
} }
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -10,7 +10,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace ZL::Dialogue { namespace FRG::Dialogue {
class DialogueOverlay { class DialogueOverlay {
public: public:
@ -62,4 +62,4 @@ private:
void drawPortrait(Renderer& renderer, const PresentationModel& model); void drawPortrait(Renderer& renderer, const PresentationModel& model);
}; };
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -3,7 +3,7 @@
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
namespace ZL::Dialogue { namespace FRG::Dialogue {
static std::pair<std::string, std::string> splitDot(const std::string& s) { static std::pair<std::string, std::string> splitDot(const std::string& s) {
const auto dot = s.find('.'); const auto dot = s.find('.');
@ -414,4 +414,4 @@ void DialogueRuntime::load(const nlohmann::json& in)
revealCharacters = static_cast<float>(presentation.fullText.size()); revealCharacters = static_cast<float>(presentation.fullText.size());
} }
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -11,9 +11,9 @@
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
namespace ZL::Dialogue { namespace FRG::Dialogue {
class DialogueRuntime : public ZL::ISaveable { class DialogueRuntime : public FRG::ISaveable {
public: public:
void setDatabase(const DialogueDatabase* value); void setDatabase(const DialogueDatabase* value);
void setTranslationDatabase(const TranslationDatabase* value); void setTranslationDatabase(const TranslationDatabase* value);
@ -92,4 +92,4 @@ private:
std::string tr(const std::string& s) const; std::string tr(const std::string& s) const;
}; };
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -1,6 +1,6 @@
#include "dialogue/DialogueSystem.h" #include "dialogue/DialogueSystem.h"
namespace ZL::Dialogue { namespace FRG::Dialogue {
bool DialogueSystem::init(Renderer& renderer, const std::string& zipFile) { bool DialogueSystem::init(Renderer& renderer, const std::string& zipFile) {
dialogueRuntime.setDatabase(&database); dialogueRuntime.setDatabase(&database);
@ -221,4 +221,4 @@ void DialogueSystem::setOnDialogueAdvanced(std::function<void()> cb) {
onDialogueAdvancedCallback = std::move(cb); onDialogueAdvancedCallback = std::move(cb);
} }
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -12,9 +12,9 @@
#include <functional> #include <functional>
#include <string> #include <string>
namespace ZL::Dialogue { namespace FRG::Dialogue {
class DialogueSystem : public ZL::ISaveable { class DialogueSystem : public FRG::ISaveable {
public: public:
bool init(Renderer& renderer, const std::string& zipFile = ""); bool init(Renderer& renderer, const std::string& zipFile = "");
@ -71,10 +71,10 @@ private:
DialogueRuntime dialogueRuntime; DialogueRuntime dialogueRuntime;
DialogueOverlay dialogueOverlay; DialogueOverlay dialogueOverlay;
ZL::Cutscene::CutsceneDatabase cutsceneDatabase; FRG::Cutscene::CutsceneDatabase cutsceneDatabase;
TranslationDatabase cutsceneTranslationDatabase; TranslationDatabase cutsceneTranslationDatabase;
ZL::Cutscene::CutsceneRuntime cutsceneRuntime; FRG::Cutscene::CutsceneRuntime cutsceneRuntime;
ZL::Cutscene::CutsceneOverlay cutsceneOverlay; FRG::Cutscene::CutsceneOverlay cutsceneOverlay;
std::function<void()> onDialogueAdvancedCallback; std::function<void()> onDialogueAdvancedCallback;
std::function<void()> onCutsceneStartedCallback; std::function<void()> onCutsceneStartedCallback;
@ -87,4 +87,4 @@ private:
void onCutsceneFinishedInternal(const std::string& id); void onCutsceneFinishedInternal(const std::string& id);
}; };
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -6,7 +6,7 @@
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
namespace ZL::Dialogue { namespace FRG::Dialogue {
enum class NodeType { enum class NodeType {
Line, Line,
@ -121,9 +121,9 @@ struct PresentationModel {
bool showCutsceneSubtitle = false; bool showCutsceneSubtitle = false;
bool cutsceneSkippable = false; bool cutsceneSkippable = false;
std::vector<ZL::Cutscene::PresentedCutsceneImage> cutsceneImages; std::vector<FRG::Cutscene::PresentedCutsceneImage> cutsceneImages;
float cutsceneGlobalFadeAlpha = 1.0f; float cutsceneGlobalFadeAlpha = 1.0f;
float cutsceneBlackAlpha = 0.0f; float cutsceneBlackAlpha = 0.0f;
}; };
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -3,12 +3,12 @@
#include "utils/Utils.h" #include "utils/Utils.h"
#include <iostream> #include <iostream>
namespace ZL namespace FRG
{ {
extern const char* CONST_ZIP_FILE; extern const char* CONST_ZIP_FILE;
} }
namespace ZL::Dialogue { namespace FRG::Dialogue {
bool TranslationDatabase::loadFromFile(const std::string& path) { bool TranslationDatabase::loadFromFile(const std::string& path) {
entries.clear(); entries.clear();
@ -94,4 +94,4 @@ const std::string& TranslationDatabase::translateRef(const std::string& key, Lan
return key; return key;
} }
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -5,7 +5,7 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
namespace ZL::Dialogue { namespace FRG::Dialogue {
// Loads a shared translation file mapping dialogue "key" strings (the literal // Loads a shared translation file mapping dialogue "key" strings (the literal
// speaker/text/choice text found in dialogue config files) to per-language text. // speaker/text/choice text found in dialogue config files) to per-language text.
@ -24,4 +24,4 @@ private:
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> entries; std::unordered_map<std::string, std::unordered_map<std::string, std::string>> entries;
}; };
} // namespace ZL::Dialogue } // namespace FRG::Dialogue

View File

@ -10,7 +10,7 @@
#include <SDL.h> #include <SDL.h>
namespace ZL { namespace FRG {
using json = nlohmann::json; using json = nlohmann::json;
@ -389,4 +389,4 @@ namespace ZL {
return npcs; return npcs;
} }
} // namespace ZL } // namespace FRG

View File

@ -10,7 +10,7 @@
#include "InteractiveObjectState.h" #include "InteractiveObjectState.h"
#include "../CharacterState.h" #include "../CharacterState.h"
namespace ZL { namespace FRG {
class Character; class Character;
@ -98,7 +98,7 @@ namespace ZL {
const std::string& zipPath = "" const std::string& zipPath = ""
); );
static std::vector<std::unique_ptr<ZL::Character>> loadAndCreate_Npcs( static std::vector<std::unique_ptr<FRG::Character>> loadAndCreate_Npcs(
const std::string& jsonPath, const std::string& jsonPath,
Renderer& renderer, Renderer& renderer,
const std::string& zipPath = "" const std::string& zipPath = ""
@ -114,4 +114,4 @@ namespace ZL {
static std::vector<InteractiveObjectData> loadInteractiveFromJson(const std::string& jsonPath, const std::string& zipPath); static std::vector<InteractiveObjectData> loadInteractiveFromJson(const std::string& jsonPath, const std::string& zipPath);
}; };
} // namespace ZL } // namespace FRG

View File

@ -7,7 +7,7 @@
#include <algorithm> #include <algorithm>
#include <Eigen/Geometry> #include <Eigen/Geometry>
namespace ZL { namespace FRG {
extern const std::string textureUniformName; extern const std::string textureUniformName;
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
@ -300,4 +300,4 @@ namespace ZL {
} }
} }
} // namespace ZL } // namespace FRG

View File

@ -8,7 +8,7 @@
#include "render/Renderer.h" #include "render/Renderer.h"
#include "InteractiveObjectState.h" #include "InteractiveObjectState.h"
namespace ZL { namespace FRG {
class Renderer; class Renderer;
@ -42,4 +42,4 @@ namespace ZL {
void drawDarklands(Renderer& renderer) const; void drawDarklands(Renderer& renderer) const;
}; };
} // namespace ZL } // namespace FRG

View File

@ -7,7 +7,7 @@
#include "external/nlohmann/json.hpp" #include "external/nlohmann/json.hpp"
#include "ISaveable.h" #include "ISaveable.h"
namespace ZL { namespace FRG {
// Animation task for timed position/rotation/scale/alpha transitions. // Animation task for timed position/rotation/scale/alpha transitions.
// Moved here from InteractiveObject so InteractiveObjectState can own it. // Moved here from InteractiveObject so InteractiveObjectState can own it.
@ -75,4 +75,4 @@ public:
void load(const nlohmann::json& in) override; void load(const nlohmann::json& in) override;
}; };
} // namespace ZL } // namespace FRG

View File

@ -5,7 +5,7 @@
#include <iostream> #include <iostream>
#include "../utils/Utils.h" #include "../utils/Utils.h"
namespace ZL { namespace FRG {
void Inventory::addItem(const std::string& itemId) { void Inventory::addItem(const std::string& itemId) {
itemIds.push_back(itemId); itemIds.push_back(itemId);
@ -54,4 +54,4 @@ namespace ZL {
} }
} }
} // namespace ZL } // namespace FRG

View File

@ -5,7 +5,7 @@
#include <functional> #include <functional>
#include "ISaveable.h" #include "ISaveable.h"
namespace ZL { namespace FRG {
struct Item { struct Item {
std::string id; std::string id;
@ -42,4 +42,4 @@ namespace ZL {
std::vector<std::string> itemIds; std::vector<std::string> itemIds;
}; };
} // namespace ZL } // namespace FRG

View File

@ -4,7 +4,7 @@
#include "external/nlohmann/json.hpp" #include "external/nlohmann/json.hpp"
#include <iostream> #include <iostream>
namespace ZL { namespace FRG {
ItemRegistry& ItemRegistry::instance() { ItemRegistry& ItemRegistry::instance() {
static ItemRegistry reg; static ItemRegistry reg;
@ -56,4 +56,4 @@ const Item* ItemRegistry::findById(const std::string& id) const {
return it != items_.end() ? &it->second : nullptr; return it != items_.end() ? &it->second : nullptr;
} }
} // namespace ZL } // namespace FRG

View File

@ -3,7 +3,7 @@
#include <unordered_map> #include <unordered_map>
#include "Item.h" #include "Item.h"
namespace ZL { namespace FRG {
class ItemRegistry { class ItemRegistry {
public: public:
@ -20,4 +20,4 @@ private:
std::unordered_map<std::string, Item> items_; std::unordered_map<std::string, Item> items_;
}; };
} // namespace ZL } // namespace FRG

View File

@ -16,20 +16,41 @@
#endif #endif
// Единый указатель на объект игры для всех платформ
FRG::Game* g_game = nullptr;
#if defined(EMSCRIPTEN) #if defined(EMSCRIPTEN)
static SDL_GLContext glContext_x; static SDL_GLContext glContext_x;
static bool g_isFsLoaded = false; // Флаг готовности
// Макрос EMSCRIPTEN_KEEPALIVE экспортирует функцию в глобальный объект Module
extern "C" void EMSCRIPTEN_KEEPALIVE onFileSystemLoaded() {
// 1. Файлы загружены. Теперь можно читать настройки и логи
// Если функции readSettings() нет для Emscripten, форсируем логгер:
FRG::Environment::enableLogging = true;
FRG::initLogger();
FRG::logger() << "[boot] File system is ready. Setting up game..." << std::endl;
// 2. Инициализируем игру ТОЛЬКО когда файлы доступны
g_game = new FRG::Game();
g_game->setup();
// 3. Даем отмашку главному циклу
g_isFsLoaded = true;
}
#endif #endif
// Единый указатель на объект игры для всех платформ
ZL::Game* g_game = nullptr;
void MainLoop() { void MainLoop() {
#if defined(EMSCRIPTEN)
// Крутим цикл вхолостую, пока JS читает IndexedDB
if (!g_isFsLoaded) return;
#endif
g_game->update(); g_game->update();
} }
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
EM_BOOL onWebGLContextLost(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) { EM_BOOL onWebGLContextLost(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) {
@ -55,8 +76,8 @@ static void applyResize(int logicalW, int logicalH) {
// Сообщаем SDL о новом размере. // Сообщаем SDL о новом размере.
// ВАЖНО: SDL2 в Emscripten ожидает здесь именно физические пиксели // ВАЖНО: SDL2 в Emscripten ожидает здесь именно физические пиксели
// для корректной работы последующих вызовов glViewport. // для корректной работы последующих вызовов glViewport.
if (ZL::Environment::window) { if (FRG::Environment::window) {
SDL_SetWindowSize(ZL::Environment::window, physicalW, physicalH); SDL_SetWindowSize(FRG::Environment::window, physicalW, physicalH);
} }
// Пушим событие, чтобы движок пересчитал матрицы проекции // Пушим событие, чтобы движок пересчитал матрицы проекции
@ -67,7 +88,7 @@ static void applyResize(int logicalW, int logicalH) {
e.window.data2 = physicalH; e.window.data2 = physicalH;
SDL_PushEvent(&e); SDL_PushEvent(&e);
logger() << "Resized, new size: " << logicalW << "x" << logicalH FRG::logger() << "Resized, new size: " << logicalW << "x" << logicalH
<< " (physical: " << physicalW << "x" << physicalH << " (physical: " << physicalW << "x" << physicalH
<< ", DPR: " << dpr << ")" << std::endl; << ", DPR: " << dpr << ")" << std::endl;
} }
@ -80,7 +101,7 @@ EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*u
} }
EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) { EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeEvent* e, void* /*userData*/) {
ZL::Environment::isFullscreen = e->isFullscreen; FRG::Environment::isFullscreen = e->isFullscreen;
// Вместо window.innerWidth, попробуйте запросить размер целевого элемента // Вместо window.innerWidth, попробуйте запросить размер целевого элемента
// так как после перехода в FS именно он растягивается на весь экран. // так как после перехода в FS именно он растягивается на весь экран.
@ -94,43 +115,32 @@ int main(int argc, char* argv[]) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); // Для WebGL 2.0 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
ZL::Environment::window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); FRG::Environment::window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
glContext_x = SDL_GL_CreateContext(ZL::Environment::window); glContext_x = SDL_GL_CreateContext(FRG::Environment::window);
SDL_GL_MakeCurrent(ZL::Environment::window, glContext_x); SDL_GL_MakeCurrent(FRG::Environment::window, glContext_x);
g_game = new ZL::Game();
g_game->setup();
emscripten_set_webglcontextlost_callback("#canvas", nullptr, EM_TRUE, onWebGLContextLost); emscripten_set_webglcontextlost_callback("#canvas", nullptr, EM_TRUE, onWebGLContextLost);
emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored); emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored);
// Keep Environment::width/height in sync when the canvas is resized.
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized); emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized);
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged); emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged);
// 2. ИНИЦИАЛИЗАЦИЯ РАЗМЕРОВ:
// Получаем реальные размеры окна браузера на момент запуска
int canvasW = EM_ASM_INT({ return window.innerWidth; }); int canvasW = EM_ASM_INT({ return window.innerWidth; });
int canvasH = EM_ASM_INT({ return window.innerHeight; }); int canvasH = EM_ASM_INT({ return window.innerHeight; });
// Вызываем вашу функцию — она сама применит DPR, выставит физический размер
// канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции.
applyResize(canvasW, canvasH); applyResize(canvasW, canvasH);
// Prevent mouse clicks from generating fake SDL_FINGERDOWN events (desktop browser)
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0"); SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
// Prevent touch events from generating fake SDL_MOUSEBUTTONDOWN events (mobile browser),
// since we now handle SDL_FINGERDOWN directly for multi-touch support.
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
ZL::emscriptenInitFileSystem(); // Запускаем асинхронное монтирование ФС
FRG::emscriptenInitFileSystem();
// Начинаем крутить пустой цикл. Он активируется, когда отработает onFileSystemLoaded
emscripten_set_main_loop(MainLoop, 0, 1); emscripten_set_main_loop(MainLoop, 0, 1);
return 0; return 0;
} }
@ -152,11 +162,11 @@ extern "C" int SDL_main(int argc, char* argv[]) {
SDL_Quit(); SDL_Quit();
return 1; return 1;
} }
ZL::Environment::width = displayMode.w; FRG::Environment::width = displayMode.w;
ZL::Environment::height = displayMode.h; FRG::Environment::height = displayMode.h;
__android_log_print(ANDROID_LOG_INFO, "Game", "Display resolution: %dx%d", __android_log_print(ANDROID_LOG_INFO, "Game", "Display resolution: %dx%d",
ZL::Environment::width, ZL::Environment::height); FRG::Environment::width, FRG::Environment::height);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
@ -169,31 +179,31 @@ extern "C" int SDL_main(int argc, char* argv[]) {
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
ZL::Environment::window = SDL_CreateWindow( FRG::Environment::window = SDL_CreateWindow(
"Shadow Over Bishkek", "Shadow Over Bishkek",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
ZL::Environment::width, ZL::Environment::height, FRG::Environment::width, FRG::Environment::height,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
); );
if (!ZL::Environment::window) { if (!FRG::Environment::window) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "Failed to create window: %s", SDL_GetError()); __android_log_print(ANDROID_LOG_ERROR, "Game", "Failed to create window: %s", SDL_GetError());
SDL_Quit(); SDL_Quit();
return 1; return 1;
} }
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window); SDL_GLContext ctx = SDL_GL_CreateContext(FRG::Environment::window);
if (!ctx) { if (!ctx) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_CreateContext failed: %s", SDL_GetError()); __android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_CreateContext failed: %s", SDL_GetError());
SDL_DestroyWindow(ZL::Environment::window); SDL_DestroyWindow(FRG::Environment::window);
SDL_Quit(); SDL_Quit();
return 1; return 1;
} }
if (SDL_GL_MakeCurrent(ZL::Environment::window, ctx) != 0) { if (SDL_GL_MakeCurrent(FRG::Environment::window, ctx) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_MakeCurrent failed: %s", SDL_GetError()); __android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_MakeCurrent failed: %s", SDL_GetError());
SDL_GL_DeleteContext(ctx); SDL_GL_DeleteContext(ctx);
SDL_DestroyWindow(ZL::Environment::window); SDL_DestroyWindow(FRG::Environment::window);
SDL_Quit(); SDL_Quit();
return 1; return 1;
} }
@ -232,20 +242,20 @@ extern "C" int SDL_main(int argc, char* argv[]) {
void readSettings() void readSettings()
{ {
const std::string settingsContent = ZL::readSavedTextFile("settings.json"); const std::string settingsContent = FRG::readSavedTextFile("settings.json");
if (!settingsContent.empty()) { if (!settingsContent.empty()) {
try { try {
const nlohmann::json settingsRoot = nlohmann::json::parse(settingsContent); const nlohmann::json settingsRoot = nlohmann::json::parse(settingsContent);
if (settingsRoot.contains("fullscreen")) { if (settingsRoot.contains("fullscreen")) {
ZL::Environment::isFullscreen = settingsRoot["fullscreen"].get<bool>(); FRG::Environment::isFullscreen = settingsRoot["fullscreen"].get<bool>();
} }
if (settingsRoot.contains("isHighDPIEnabled")) { if (settingsRoot.contains("isHighDPIEnabled")) {
ZL::Environment::isHighDPIEnabled = settingsRoot["isHighDPIEnabled"].get<bool>(); FRG::Environment::isHighDPIEnabled = settingsRoot["isHighDPIEnabled"].get<bool>();
} }
if (settingsRoot.contains("enableLogging")) { if (settingsRoot.contains("enableLogging")) {
ZL::Environment::enableLogging = settingsRoot["enableLogging"].get<bool>(); FRG::Environment::enableLogging = settingsRoot["enableLogging"].get<bool>();
} }
} }
@ -262,12 +272,12 @@ int main(int argc, char* argv[]) {
try try
{ {
readSettings(); readSettings();
if (ZL::Environment::enableLogging) if (FRG::Environment::enableLogging)
{ {
ZL::initLogger(); FRG::initLogger();
} }
ZL::logger() << "Log started!" << std::endl; FRG::logger() << "Log started!" << std::endl;
#ifdef STEAMSDK #ifdef STEAMSDK
// Передаем явный App ID демо-версии // Передаем явный App ID демо-версии
@ -283,11 +293,11 @@ int main(int argc, char* argv[]) {
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2"); SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2");
constexpr int CONST_WIDTH = ZL::Environment::CONST_DEFAULT_WIDTH; constexpr int CONST_WIDTH = FRG::Environment::CONST_DEFAULT_WIDTH;
constexpr int CONST_HEIGHT = ZL::Environment::CONST_DEFAULT_HEIGHT; constexpr int CONST_HEIGHT = FRG::Environment::CONST_DEFAULT_HEIGHT;
ZL::Environment::width = CONST_WIDTH; FRG::Environment::width = CONST_WIDTH;
ZL::Environment::height = CONST_HEIGHT; FRG::Environment::height = CONST_HEIGHT;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
SDL_Log("SDL init failed: %s", SDL_GetError()); SDL_Log("SDL init failed: %s", SDL_GetError());
return 1; return 1;
@ -298,53 +308,53 @@ int main(int argc, char* argv[]) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE; Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
if (ZL::Environment::isFullscreen) { if (FRG::Environment::isFullscreen) {
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} }
SDL_DisplayMode dm; SDL_DisplayMode dm;
if (SDL_GetCurrentDisplayMode(0, &dm) == 0) { if (SDL_GetCurrentDisplayMode(0, &dm) == 0) {
ZL::logger() << "Desktop resolution: " << dm.w << "x" << dm.h << " @ " << dm.refresh_rate << "Hz\n"; FRG::logger() << "Desktop resolution: " << dm.w << "x" << dm.h << " @ " << dm.refresh_rate << "Hz\n";
} }
else { else {
SDL_Log("SDL_GetCurrentDisplayMode failed: %s", SDL_GetError()); SDL_Log("SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
} }
int windowWidth = ZL::Environment::width; int windowWidth = FRG::Environment::width;
int windowHeight = ZL::Environment::height; int windowHeight = FRG::Environment::height;
if (dm.w <= 1280 && dm.h <= 720) { if (dm.w <= 1280 && dm.h <= 720) {
windowWidth = round(dm.w / 1.5); windowWidth = round(dm.w / 1.5);
windowHeight = round(dm.h / 1.5); windowHeight = round(dm.h / 1.5);
} }
ZL::Environment::window = SDL_CreateWindow( FRG::Environment::window = SDL_CreateWindow(
"Shadow Over Bishkek Demo", "Shadow Over Bishkek Demo",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
windowWidth, windowHeight, windowWidth, windowHeight,
windowFlags windowFlags
); );
ctx = SDL_GL_CreateContext(ZL::Environment::window); ctx = SDL_GL_CreateContext(FRG::Environment::window);
SDL_GL_MakeCurrent(ZL::Environment::window, ctx); SDL_GL_MakeCurrent(FRG::Environment::window, ctx);
if (ZL::Environment::isHighDPIEnabled) { if (FRG::Environment::isHighDPIEnabled) {
int drawW, drawH; int drawW, drawH;
SDL_GL_GetDrawableSize(ZL::Environment::window, &drawW, &drawH); SDL_GL_GetDrawableSize(FRG::Environment::window, &drawW, &drawH);
ZL::Environment::width = drawW; FRG::Environment::width = drawW;
ZL::Environment::height = drawH; FRG::Environment::height = drawH;
ZL::logger() << "HiDPI enabled, drawable size: " << drawW << "x" << drawH << std::endl; FRG::logger() << "HiDPI enabled, drawable size: " << drawW << "x" << drawH << std::endl;
} }
else { else {
int winW, winH; int winW, winH;
SDL_GetWindowSize(ZL::Environment::window, &winW, &winH); SDL_GetWindowSize(FRG::Environment::window, &winW, &winH);
ZL::Environment::width = winW; FRG::Environment::width = winW;
ZL::Environment::height = winH; FRG::Environment::height = winH;
ZL::logger() << "HiDPI disabled, window size: " << winW << "x" << winH << std::endl; FRG::logger() << "HiDPI disabled, window size: " << winW << "x" << winH << std::endl;
} }
// Динамическое создание объекта игры // Динамическое создание объекта игры
g_game = new ZL::Game(); g_game = new FRG::Game();
g_game->setup(); g_game->setup();
while (!g_game->shouldExit()) { while (!g_game->shouldExit()) {
@ -356,10 +366,10 @@ int main(int argc, char* argv[]) {
#endif #endif
} }
//ZL::logger() << "Quitting step 1: " << std::endl; //FRG::logger() << "Quitting step 1: " << std::endl;
// Явное уничтожение объекта игры ДО завершения работы Steamworks // Явное уничтожение объекта игры ДО завершения работы Steamworks
delete g_game; delete g_game;
//ZL::logger() << "Quitting step 2: " << std::endl; //FRG::logger() << "Quitting step 2: " << std::endl;
g_game = nullptr; g_game = nullptr;
} }
catch (const std::exception& e) catch (const std::exception& e)
@ -379,31 +389,31 @@ int main(int argc, char* argv[]) {
} }
} }
//ZL::logger() << "Quitting step 3: " << std::endl; //FRG::logger() << "Quitting step 3: " << std::endl;
#ifdef STEAMSDK #ifdef STEAMSDK
// Деинициализация Steamworks происходит только тогда, когда все объекты игры гарантированно уничтожены // Деинициализация Steamworks происходит только тогда, когда все объекты игры гарантированно уничтожены
SteamAPI_Shutdown(); SteamAPI_Shutdown();
#endif #endif
//ZL::logger() << "Quitting step 4: " << std::endl; //FRG::logger() << "Quitting step 4: " << std::endl;
// Очистка ресурсов SDL // Очистка ресурсов SDL
if (ctx) { if (ctx) {
//ZL::logger() << "Quitting step 5: " << std::endl; //FRG::logger() << "Quitting step 5: " << std::endl;
SDL_GL_DeleteContext(ctx); SDL_GL_DeleteContext(ctx);
} }
//ZL::logger() << "Quitting step 6: " << std::endl; //FRG::logger() << "Quitting step 6: " << std::endl;
if (ZL::Environment::window) { if (FRG::Environment::window) {
//ZL::logger() << "Quitting step 7: " << std::endl; //FRG::logger() << "Quitting step 7: " << std::endl;
SDL_DestroyWindow(ZL::Environment::window); SDL_DestroyWindow(FRG::Environment::window);
} }
//ZL::logger() << "Quitting step 8: " << std::endl; //FRG::logger() << "Quitting step 8: " << std::endl;
#ifndef EMSCRIPTEN #ifndef EMSCRIPTEN
// In Emscripten, SDL must stay alive across context loss/restore cycles // In Emscripten, SDL must stay alive across context loss/restore cycles
// so the window remains valid when the game object is re-created. // so the window remains valid when the game object is re-created.
SDL_Quit(); SDL_Quit();
#endif #endif
//ZL::logger() << "Quitting step 9: " << std::endl; //FRG::logger() << "Quitting step 9: " << std::endl;
return 0; return 0;
} }
#endif #endif

View File

@ -9,7 +9,7 @@
#include <limits> #include <limits>
#include <queue> #include <queue>
namespace ZL { namespace FRG {
using json = nlohmann::json; using json = nlohmann::json;
@ -1075,4 +1075,4 @@ bool PathFinder::pointInPolygon(float x, float z, const std::vector<Eigen::Vecto
return inside; return inside;
} }
} // namespace ZL } // namespace FRG

View File

@ -5,7 +5,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace ZL { namespace FRG {
class PathFinder { class PathFinder {
public: public:
@ -111,4 +111,4 @@ private:
const std::vector<unsigned char>& walkableGrid) const; const std::vector<unsigned char>& walkableGrid) const;
}; };
} // namespace ZL } // namespace FRG

View File

@ -5,7 +5,7 @@
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
namespace ZL::Quest { namespace FRG::Quest {
using json = nlohmann::json; using json = nlohmann::json;
@ -27,7 +27,7 @@ static QuestStatus parseQuestStatus(const std::string& value) {
} }
bool QuestJournal::loadFromFile(const std::string& path, const std::string& zipFile) { bool QuestJournal::loadFromFile(const std::string& path, const std::string& zipFile) {
const std::string content = ZL::readLocalizedConfigFile(path, zipFile); const std::string content = FRG::readLocalizedConfigFile(path, zipFile);
if (content.empty()) { if (content.empty()) {
std::cerr << "[quest] Failed to read " << path << std::endl; std::cerr << "[quest] Failed to read " << path << std::endl;
return false; return false;
@ -260,4 +260,4 @@ void QuestJournal::load(const nlohmann::json& in) {
} }
} }
} // namespace ZL::Quest } // namespace FRG::Quest

View File

@ -8,9 +8,9 @@
#include <vector> #include <vector>
#include <functional> #include <functional>
namespace ZL::Quest { namespace FRG::Quest {
class QuestJournal : public ZL::ISaveable { class QuestJournal : public FRG::ISaveable {
public: public:
bool loadFromFile(const std::string& path, const std::string& zipFile = ""); bool loadFromFile(const std::string& path, const std::string& zipFile = "");
@ -49,4 +49,4 @@ private:
bool setStatus(const std::string& questId, QuestStatus status); bool setStatus(const std::string& questId, QuestStatus status);
}; };
} // namespace ZL::Quest } // namespace FRG::Quest

View File

@ -3,7 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace ZL::Quest { namespace FRG::Quest {
enum class QuestStatus { enum class QuestStatus {
Hidden, Hidden,
@ -37,4 +37,4 @@ struct QuestState {
const char* toString(QuestStatus status); const char* toString(QuestStatus status);
} // namespace ZL::Quest } // namespace FRG::Quest

View File

@ -2,7 +2,7 @@
#include <iostream> #include <iostream>
#include "Environment.h" #include "Environment.h"
namespace ZL { namespace FRG {
FrameBuffer::FrameBuffer(int w, int h, bool useMipmaps) FrameBuffer::FrameBuffer(int w, int h, bool useMipmaps)
: width(w), height(h), useMipmaps(useMipmaps) { : width(w), height(h), useMipmaps(useMipmaps) {
@ -59,4 +59,4 @@ namespace ZL {
glViewport(0, 0, Environment::width, Environment::height); glViewport(0, 0, Environment::width, Environment::height);
} }
} // namespace ZL } // namespace FRG

View File

@ -2,7 +2,7 @@
#include "render/OpenGlExtensions.h" #include "render/OpenGlExtensions.h"
#include <memory> #include <memory>
namespace ZL { namespace FRG {
class FrameBuffer { class FrameBuffer {
private: private:
@ -28,4 +28,4 @@ namespace ZL {
int getHeight() const { return height; } int getHeight() const { return height; }
}; };
} // namespace ZL } // namespace FRG

View File

@ -117,7 +117,7 @@ PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArray = NULL;
#endif #endif
namespace ZL { namespace FRG {
bool BindOpenGlFunctions() bool BindOpenGlFunctions()
{ {

View File

@ -154,7 +154,7 @@ extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArray;
#else #else
#endif #endif
namespace ZL { namespace FRG {

View File

@ -1,7 +1,7 @@
#include "render/Renderer.h" #include "render/Renderer.h"
#include <cmath> #include <cmath>
namespace ZL { namespace FRG {
Matrix4f MakeOrthoMatrix(float width, float height, float zNear, float zFar) Matrix4f MakeOrthoMatrix(float width, float height, float zNear, float zFar)
{ {

View File

@ -8,7 +8,7 @@
#include "TextureManager.h" #include "TextureManager.h"
#include <Eigen/Dense> #include <Eigen/Dense>
namespace ZL { namespace FRG {
using Eigen::Vector2f; using Eigen::Vector2f;
using Eigen::Vector3f; using Eigen::Vector3f;

View File

@ -6,7 +6,7 @@
#include <android/log.h> #include <android/log.h>
#endif #endif
namespace ZL { namespace FRG {
ShaderResource::ShaderResource(const std::string &vertexCode, const std::string &fragmentCode) { ShaderResource::ShaderResource(const std::string &vertexCode, const std::string &fragmentCode) {

View File

@ -3,7 +3,7 @@
#include "render/OpenGlExtensions.h" #include "render/OpenGlExtensions.h"
#include "utils/Utils.h" #include "utils/Utils.h"
namespace ZL { namespace FRG {
constexpr size_t CONST_MAX_SHADER_STACK_SIZE = 16; constexpr size_t CONST_MAX_SHADER_STACK_SIZE = 16;

View File

@ -3,7 +3,7 @@
#include <iostream> #include <iostream>
#include <cmath> #include <cmath>
namespace ZL { namespace FRG {
// Build a look-at view matrix (column-major, same convention as the engine). // Build a look-at view matrix (column-major, same convention as the engine).
static Eigen::Matrix4f lookAt(const Eigen::Vector3f& eye, static Eigen::Matrix4f lookAt(const Eigen::Vector3f& eye,
@ -167,4 +167,4 @@ namespace ZL {
glViewport(0, 0, Environment::width, Environment::height); glViewport(0, 0, Environment::width, Environment::height);
} }
} // namespace ZL } // namespace FRG

View File

@ -2,7 +2,7 @@
#include "render/OpenGlExtensions.h" #include "render/OpenGlExtensions.h"
#include <Eigen/Dense> #include <Eigen/Dense>
namespace ZL { namespace FRG {
class ShadowMap { class ShadowMap {
private: private:
@ -44,4 +44,4 @@ namespace ZL {
const Eigen::Vector3f& getLightDirection() const { return lightDirection; } const Eigen::Vector3f& getLightDirection() const { return lightDirection; }
}; };
} // namespace ZL } // namespace FRG

View File

@ -10,7 +10,7 @@
#include <cmath> #include <cmath>
#include <unordered_set> #include <unordered_set>
namespace ZL { namespace FRG {
struct GlyphAtlasData { struct GlyphAtlasData {
std::unordered_map<uint32_t, GlyphInfo> glyphs; std::unordered_map<uint32_t, GlyphInfo> glyphs;
@ -71,13 +71,13 @@ bool TextRenderer::init(Renderer& renderer, const std::string& ttfPath, int pixe
#endif #endif
} }
ZL::CheckGlError(__FILE__, __LINE__); FRG::CheckGlError(__FILE__, __LINE__);
if (!loadGlyphs(ttfPath, pixelSize, zipfilename)) return false; if (!loadGlyphs(ttfPath, pixelSize, zipfilename)) return false;
ZL::CheckGlError(__FILE__, __LINE__); FRG::CheckGlError(__FILE__, __LINE__);
textMesh.data.PositionData.resize(6, Eigen::Vector3f(0, 0, 0)); textMesh.data.PositionData.resize(6, Eigen::Vector3f(0, 0, 0));
textMesh.RefreshVBO(); textMesh.RefreshVBO();
ZL::CheckGlError(__FILE__, __LINE__); FRG::CheckGlError(__FILE__, __LINE__);
return true; return true;
} }
@ -515,4 +515,4 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
// Сброс бинда текстуры не обязателен, но можно для чистоты // Сброс бинда текстуры не обязателен, но можно для чистоты
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
} // namespace ZL } // namespace FRG

View File

@ -9,7 +9,7 @@
#include <array> #include <array>
namespace ZL { namespace FRG {
struct GlyphInfo { struct GlyphInfo {
Eigen::Vector2f uv; // u,v координата левого верхнего угла в атласе (0..1) Eigen::Vector2f uv; // u,v координата левого верхнего угла в атласе (0..1)
@ -62,4 +62,4 @@ private:
std::unordered_map<std::string, CachedText> cache; std::unordered_map<std::string, CachedText> cache;
}; };
} // namespace ZL } // namespace FRG

View File

@ -5,7 +5,7 @@
#endif #endif
#include <iostream> #include <iostream>
namespace ZL namespace FRG
{ {
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
using std::min; using std::min;

View File

@ -9,7 +9,7 @@
#define PNG_ENABLED #define PNG_ENABLED
#endif #endif
namespace ZL namespace FRG
{ {
struct TextureDataStruct { struct TextureDataStruct {

View File

@ -4,7 +4,7 @@
#include "UiManager.h" #include "UiManager.h"
#include <Eigen/Core> #include <Eigen/Core>
namespace ZL namespace FRG
{ {
// Axis-aligned textured quad with cached mesh. Rebuild only when rect changes. // Axis-aligned textured quad with cached mesh. Rebuild only when rect changes.
struct UiQuad { struct UiQuad {

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