782 lines
25 KiB
C++
782 lines
25 KiB
C++
#include "Game.h"
|
|
#include "AnimatedModel.h"
|
|
#include "BoneAnimatedModel.h"
|
|
#include "utils/Utils.h"
|
|
#include "render/OpenGlExtensions.h"
|
|
#include <iostream>
|
|
#include "render/TextureManager.h"
|
|
#include "TextModel.h"
|
|
#include <random>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#ifdef __ANDROID__
|
|
#include <android/log.h>
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EMSCRIPTEN
|
|
#include <emscripten.h>
|
|
#endif
|
|
|
|
#include "GameConstants.h"
|
|
|
|
namespace ZL
|
|
{
|
|
#ifdef EMSCRIPTEN
|
|
const char* CONST_ZIP_FILE = "resources.zip";
|
|
#else
|
|
const char* CONST_ZIP_FILE = "";
|
|
#endif
|
|
|
|
float x = 0;
|
|
float y = 0;
|
|
float z = 0;
|
|
|
|
#ifdef EMSCRIPTEN
|
|
Game* Game::s_instance = nullptr;
|
|
|
|
void Game::onResourcesZipLoaded(const char* /*filename*/) {
|
|
std::cout << "Resources.zip loaded successfully" << std::endl;
|
|
if (s_instance) {
|
|
s_instance->mainThreadHandler.EnqueueMainThreadTask([&]() {
|
|
s_instance->setupPart2();
|
|
});
|
|
}
|
|
}
|
|
|
|
void Game::onResourcesZipError(const char* /*filename*/) {
|
|
std::cout << "Failed to download resources.zip" << std::endl;
|
|
}
|
|
#endif
|
|
|
|
Game::Game()
|
|
: newTickCount(0)
|
|
, lastTickCount(0)
|
|
, menuManager(renderer)
|
|
, audioPlayer(std::make_unique<AudioPlayerAsync>())
|
|
{
|
|
}
|
|
|
|
Game::~Game() {
|
|
#ifndef EMSCRIPTEN
|
|
// In Emscripten, SDL must stay alive across context loss/restore cycles
|
|
// so the window remains valid when the game object is re-created.
|
|
SDL_Quit();
|
|
#endif
|
|
}
|
|
|
|
void Game::setup() {
|
|
Environment::width = Environment::CONST_DEFAULT_WIDTH;
|
|
Environment::height = Environment::CONST_DEFAULT_HEIGHT;
|
|
Environment::computeProjectionDimensions();
|
|
|
|
ZL::BindOpenGlFunctions();
|
|
ZL::CheckGlError(__FILE__, __LINE__);
|
|
renderer.InitOpenGL();
|
|
|
|
#ifdef EMSCRIPTEN
|
|
// These shaders and loading.png are preloaded separately (not from zip),
|
|
// so they are available immediately without waiting for resources.zip.
|
|
renderer.shaderManager.AddShaderFromFiles("defaultColor", "resources/shaders/defaultColor.vertex", "resources/shaders/defaultColor_web.fragment", "");
|
|
renderer.shaderManager.AddShaderFromFiles("default", "resources/shaders/default.vertex", "resources/shaders/default_web.fragment", "");
|
|
#else
|
|
renderer.shaderManager.AddShaderFromFiles("defaultColor", "resources/shaders/defaultColor.vertex", "resources/shaders/defaultColor_desktop.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("default", "resources/shaders/default.vertex", "resources/shaders/default_desktop.fragment", CONST_ZIP_FILE);
|
|
#endif
|
|
loadingTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/loading.png", ""));
|
|
|
|
float minDimension;
|
|
float width = Environment::projectionWidth;
|
|
float height = Environment::projectionHeight;
|
|
|
|
if (width >= height)
|
|
{
|
|
minDimension = height;
|
|
}
|
|
else
|
|
{
|
|
minDimension = width;
|
|
}
|
|
|
|
|
|
//loadingMesh.data = CreateRect2D({ 0.0f, 0.0f }, { minDimension*0.5f, minDimension*0.5f }, 3);
|
|
loadingMesh.data = CreateRect2D({ 0.0f, 0.0f }, { minDimension * 0.5f, minDimension * 0.25f }, 3);
|
|
loadingMesh.RefreshVBO();
|
|
|
|
#ifdef EMSCRIPTEN
|
|
// Asynchronously download resources.zip; setupPart2() is called on completion.
|
|
// The loading screen stays visible until the download finishes.
|
|
s_instance = this;
|
|
std::cout << "Load resurces step 1" << std::endl;
|
|
emscripten_async_wget("resources.zip", "resources.zip", onResourcesZipLoaded, onResourcesZipError);
|
|
#else
|
|
mainThreadHandler.EnqueueMainThreadTask([this]() {
|
|
std::cout << "Load resurces step 2" << std::endl;
|
|
this->setupPart2();
|
|
std::cout << "Load resurces step 3" << std::endl;
|
|
});
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
void Game::setupPart2()
|
|
{
|
|
|
|
#ifdef EMSCRIPTEN
|
|
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_web.fragment", CONST_ZIP_FILE);
|
|
renderer.shcuaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_web.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_web.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("planetStone", "resources/shaders/planet_stone.vertex", "resources/shaders/planet_stone_web.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("planetLand", "resources/shaders/planet_land.vertex", "resources/shaders/planet_land_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);
|
|
|
|
#else
|
|
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("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("planetStone", "resources/shaders/planet_stone.vertex", "resources/shaders/planet_stone_desktop.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("planetLand", "resources/shaders/planet_land.vertex", "resources/shaders/planet_land_desktop.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("spark", "resources/shaders/spark.vertex", "resources/shaders/spark_desktop.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("skinning", "resources/shaders/skinning.vertex", "resources/shaders/default_desktop.fragment", CONST_ZIP_FILE);
|
|
#endif
|
|
|
|
std::cout << "Load resurces step 4" << std::endl;
|
|
|
|
forestLocation = std::make_shared<Location>(renderer, inventory, "forest");
|
|
forestLocation->setup();
|
|
forestLocation->onLocationChangeRequest = [this](const std::string& locId) {
|
|
this->changeLocation(locId);
|
|
};
|
|
|
|
|
|
defaultLocation = std::make_shared<Location>(renderer, inventory, "default");
|
|
defaultLocation->setup();
|
|
defaultLocation->onLocationChangeRequest = [this](const std::string& locId) {
|
|
this->changeLocation(locId);
|
|
};
|
|
|
|
currentLocation = defaultLocation;
|
|
//currentLocation = forestLocation;
|
|
|
|
std::cout << "Load resurces step 5" << std::endl;
|
|
|
|
std::cout << "Load resurces step 12" << std::endl;
|
|
|
|
// Shadow mapping shaders
|
|
#ifdef EMSCRIPTEN
|
|
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("default_shadow", "resources/shaders/default_shadow.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);
|
|
#else
|
|
renderer.shaderManager.AddShaderFromFiles("shadow_depth", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_desktop.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("shadow_depth_skinning", "resources/shaders/shadow_depth_skinning.vertex", "resources/shaders/shadow_depth_desktop.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("default_shadow", "resources/shaders/default_shadow.vertex", "resources/shaders/default_shadow_desktop.fragment", CONST_ZIP_FILE);
|
|
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_desktop.fragment", CONST_ZIP_FILE);
|
|
#endif
|
|
|
|
|
|
std::cout << "Load resurces step 13" << std::endl;
|
|
|
|
// Load UI with inventory button
|
|
/*try {
|
|
menuManager.uiManager.loadFromFile("resources/config2/ui_inventory.json", renderer, CONST_ZIP_FILE);
|
|
std::cout << "UI loaded successfully" << std::endl;
|
|
|
|
menuManager.uiManager.setNodeVisible("inventory_items_panel", false);
|
|
menuManager.uiManager.setNodeVisible("close_inventory_button", false);
|
|
|
|
menuManager.uiManager.setTextButtonCallback("inventory_button", [this](const std::string& name) {
|
|
std::cout << "[UI] Inventory button clicked" << std::endl;
|
|
this->menuManager.uiManager.setNodeVisible("inventory_items_panel", true);
|
|
this->menuManager.uiManager.setNodeVisible("close_inventory_button", true);
|
|
this->inventoryOpen = true;
|
|
|
|
// Update UI with current items
|
|
const auto& items = this->inventory.getItems();
|
|
std::string itemText;
|
|
|
|
if (items.empty()) {
|
|
itemText = "Inventory (Empty)";
|
|
}
|
|
else {
|
|
itemText = "Inventory (" + std::to_string(items.size()) + " items)\n\n";
|
|
for (size_t i = 0; i < items.size(); ++i) {
|
|
itemText += std::to_string(i + 1) + ". " + items[i].name + "\n";
|
|
}
|
|
}
|
|
|
|
this->menuManager.uiManager.setText("inventory_items_text", itemText);
|
|
});
|
|
|
|
menuManager.uiManager.setTextButtonCallback("close_inventory_button", [this](const std::string& name) {
|
|
std::cout << "[UI] Close button clicked" << std::endl;
|
|
menuManager.uiManager.setNodeVisible("inventory_items_panel", false);
|
|
menuManager.uiManager.setNodeVisible("close_inventory_button", false);
|
|
inventoryOpen = false;
|
|
});
|
|
}
|
|
catch (const std::exception& e) {
|
|
std::cerr << "Failed to load UI: " << e.what() << std::endl;
|
|
}*/
|
|
//menuManager.setupMainMenu();
|
|
loadingCompleted = true;
|
|
|
|
if (audioPlayer->init()) {
|
|
audioPlayer->setMusicVolume(100);
|
|
audioPlayer->setSoundVolume(80);
|
|
std::cout << "Audio initialized successfully" << std::endl;
|
|
}
|
|
else {
|
|
std::cout << "Audio initialization failed" << std::endl;
|
|
}
|
|
|
|
|
|
menuManager.onGameStateChanged = [this](GameState newState) {
|
|
this->updateMusicForGameState(newState);
|
|
};
|
|
|
|
//menuManager.setupMainMenu();
|
|
/*<<<<<<< HEAD
|
|
menuManager.currentState = GameState::Gameplay;
|
|
=======
|
|
>>>>>>> c9324bd30b923f7fae86013a96b0db8b9909728e
|
|
*/
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
}
|
|
|
|
void Game::drawUI()
|
|
{
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
|
|
renderer.shaderManager.PushShader(defaultShaderName);
|
|
renderer.RenderUniform1i(textureUniformName, 0);
|
|
glEnable(GL_BLEND);
|
|
|
|
if (menuManager.getState() == GameState::MainMenu ||
|
|
menuManager.getState() == GameState::HelpScreen ||
|
|
menuManager.getState() == GameState::AboutMenu)
|
|
{
|
|
// Normal UI: show cursor and release grab / relative mode
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
SDL_SetRelativeMouseMode(SDL_FALSE);
|
|
if (Environment::window) SDL_SetWindowGrab(Environment::window, SDL_FALSE);
|
|
}
|
|
else if (menuManager.getState() == GameState::Gameplay)
|
|
{
|
|
// Gameplay: hide cursor and enable relative mouse mode + window grab
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
if (Environment::window) SDL_SetWindowGrab(Environment::window, SDL_TRUE);
|
|
}
|
|
|
|
if (menuManager.getState() != GameState::Gameplay)
|
|
{
|
|
menuManager.uiManager.draw(renderer);
|
|
}
|
|
|
|
if (currentLocation)
|
|
{
|
|
currentLocation->dialogueSystem.draw(renderer);
|
|
}
|
|
|
|
glDisable(GL_BLEND);
|
|
renderer.shaderManager.PopShader();
|
|
|
|
glDepthMask(GL_TRUE);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
CheckGlError(__FILE__, __LINE__);
|
|
}
|
|
|
|
void Game::drawScene() {
|
|
glViewport(0, 0, Environment::width, Environment::height);
|
|
|
|
if (!loadingCompleted) {
|
|
drawLoading();
|
|
return;
|
|
}
|
|
|
|
if (menuManager.getState() == GameState::MainMenu ||
|
|
menuManager.getState() == GameState::HelpScreen ||
|
|
menuManager.getState() == GameState::AboutMenu)
|
|
{
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
drawUI();
|
|
return;
|
|
}
|
|
|
|
if (currentLocation)
|
|
{
|
|
currentLocation->drawGame();
|
|
CheckGlError(__FILE__, __LINE__);
|
|
}
|
|
|
|
drawUI();
|
|
CheckGlError(__FILE__, __LINE__);
|
|
}
|
|
|
|
void Game::drawLoading()
|
|
{
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
renderer.shaderManager.PushShader(defaultShaderName);
|
|
renderer.RenderUniform1i(textureUniformName, 0);
|
|
|
|
float width = Environment::projectionWidth;
|
|
float height = Environment::projectionHeight;
|
|
|
|
renderer.PushProjectionMatrix(
|
|
-width * 0.5f, width*0.5f,
|
|
-height * 0.5f, height * 0.5f,
|
|
-10, 10);
|
|
|
|
renderer.PushMatrix();
|
|
renderer.LoadIdentity();
|
|
|
|
glBindTexture(GL_TEXTURE_2D, loadingTexture->getTexID());
|
|
renderer.DrawVertexRenderStruct(loadingMesh);
|
|
|
|
renderer.PopMatrix();
|
|
renderer.PopProjectionMatrix();
|
|
renderer.shaderManager.PopShader();
|
|
CheckGlError(__FILE__, __LINE__);
|
|
}
|
|
|
|
|
|
int64_t Game::getSyncTimeMs() {
|
|
int64_t localNow = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
std::chrono::system_clock::now().time_since_epoch()).count();
|
|
return localNow;
|
|
}
|
|
|
|
|
|
void Game::processTickCount() {
|
|
|
|
if (lastTickCount == 0) {
|
|
lastTickCount = getSyncTimeMs();
|
|
|
|
lastTickCount = (lastTickCount / 50) * 50;
|
|
|
|
return;
|
|
}
|
|
|
|
newTickCount = getSyncTimeMs();
|
|
|
|
newTickCount = (newTickCount / 50) * 50;
|
|
|
|
if (newTickCount - lastTickCount > CONST_TIMER_INTERVAL) {
|
|
|
|
int64_t delta = newTickCount - lastTickCount;
|
|
|
|
lastTickCount = newTickCount;
|
|
|
|
if (currentLocation)
|
|
{
|
|
currentLocation->update(delta);
|
|
}
|
|
|
|
if (mainMenuTimeout >= 0)
|
|
{
|
|
mainMenuTimeout -= delta;
|
|
if (mainMenuTimeout < 0 && loadingCompleted)
|
|
{
|
|
menuManager.setupMainMenu();
|
|
/*if (audioPlayer) {
|
|
audioPlayer->stopMusicAsync();
|
|
audioPlayer->playMusicAsync("audio/ETM_Titles_track.ogg", -1);
|
|
}*/
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
void Game::render() {
|
|
ZL::CheckGlError(__FILE__, __LINE__);
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
drawScene();
|
|
processTickCount();
|
|
|
|
SDL_GL_SwapWindow(ZL::Environment::window);
|
|
}
|
|
|
|
void Game::update() {
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event)) {
|
|
if (event.type == SDL_QUIT) {
|
|
Environment::exitGameLoop = true;
|
|
}
|
|
|
|
|
|
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
|
// Обновляем размеры и сбрасываем кеш текстов, т.к. меши хранятся в пикселях
|
|
Environment::width = event.window.data1;
|
|
Environment::height = event.window.data2;
|
|
Environment::computeProjectionDimensions();
|
|
//menuManager.uiManager.updateAllLayouts();
|
|
std::cout << "Window resized: " << Environment::width << "x" << Environment::height << std::endl;
|
|
|
|
//space.clearTextRendererCache();
|
|
}
|
|
|
|
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
|
|
if (menuManager.getState() == GameState::Gameplay) {
|
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
if (Environment::window) SDL_SetWindowGrab(Environment::window, SDL_TRUE);
|
|
} else {
|
|
SDL_SetRelativeMouseMode(SDL_FALSE);
|
|
if (Environment::window) SDL_SetWindowGrab(Environment::window, SDL_FALSE);
|
|
}
|
|
//SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
}
|
|
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
|
|
SDL_SetRelativeMouseMode(SDL_FALSE);
|
|
if (Environment::window) SDL_SetWindowGrab(Environment::window, SDL_FALSE);
|
|
}
|
|
|
|
#ifdef __ANDROID__
|
|
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
|
|
Environment::exitGameLoop = true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef __ANDROID__
|
|
if (event.type == SDL_FINGERDOWN) {
|
|
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
|
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
|
handleDown(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
|
}
|
|
else if (event.type == SDL_FINGERUP) {
|
|
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
|
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
|
handleUp(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
|
}
|
|
else if (event.type == SDL_FINGERMOTION) {
|
|
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
|
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
|
handleMotion(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
|
}
|
|
#else
|
|
// Emscripten on mobile browser: handle real touch events with per-finger IDs.
|
|
// SDL_HINT_TOUCH_MOUSE_EVENTS="0" is set in main.cpp so these don't
|
|
// also fire SDL_MOUSEBUTTONDOWN, preventing double-processing.
|
|
#ifdef EMSCRIPTEN
|
|
if (event.type == SDL_FINGERDOWN) {
|
|
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
|
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
|
handleDown(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
|
}
|
|
else if (event.type == SDL_FINGERUP) {
|
|
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
|
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
|
handleUp(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
|
}
|
|
else if (event.type == SDL_FINGERMOTION) {
|
|
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
|
|
int my = static_cast<int>(event.tfinger.y * Environment::projectionHeight);
|
|
handleMotion(static_cast<int64_t>(event.tfinger.fingerId), mx, my);
|
|
}
|
|
#endif
|
|
|
|
if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
|
|
if (event.button.button == SDL_BUTTON_LEFT) {
|
|
int mx = static_cast<int>((float)event.button.x / Environment::width * Environment::projectionWidth);
|
|
int my = static_cast<int>((float)event.button.y / Environment::height * Environment::projectionHeight);
|
|
|
|
if (event.type == SDL_MOUSEBUTTONDOWN) {
|
|
std::cout << "\n========== MOUSE DOWN EVENT ==========" << std::endl;
|
|
handleDown(ZL::UiManager::MOUSE_FINGER_ID, mx, my);
|
|
|
|
|
|
if (menuManager.getState() != GameState::Gameplay ||
|
|
menuManager.uiManager.isUiInteractionForFinger(ZL::UiManager::MOUSE_FINGER_ID)) {
|
|
std::cout << "[CLICK] UI handled or in menu, skipping game logic" << std::endl;
|
|
continue;
|
|
}
|
|
|
|
if (currentLocation)
|
|
{
|
|
currentLocation->handleDown(ZL::UiManager::MOUSE_FINGER_ID, event.button.x, event.button.y, mx, my);
|
|
}
|
|
///.....
|
|
} else {
|
|
handleUp(ZL::UiManager::MOUSE_FINGER_ID, mx, my);
|
|
}
|
|
}
|
|
else if (event.button.button == SDL_BUTTON_RIGHT) {
|
|
if (menuManager.getState() == GameState::Gameplay)
|
|
{
|
|
if (event.type == SDL_MOUSEBUTTONDOWN) {
|
|
rightMouseDown = true;
|
|
lastMouseX = event.button.x;
|
|
lastMouseY = event.button.y;
|
|
if (currentLocation)
|
|
{
|
|
currentLocation->rightMouseDown = true;
|
|
currentLocation->lastMouseX = event.button.x;
|
|
currentLocation->lastMouseY = event.button.y;
|
|
}
|
|
}
|
|
else {
|
|
rightMouseDown = false;
|
|
if (currentLocation)
|
|
{
|
|
currentLocation->rightMouseDown = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (event.type == SDL_MOUSEMOTION) {
|
|
int mx = static_cast<int>((float)event.motion.x / Environment::width * Environment::projectionWidth);
|
|
int my = static_cast<int>((float)event.motion.y / Environment::height * Environment::projectionHeight);
|
|
handleMotion(ZL::UiManager::MOUSE_FINGER_ID, mx, my);
|
|
|
|
if (menuManager.getState() == GameState::Gameplay && currentLocation)
|
|
{
|
|
currentLocation->handleMotion(ZL::UiManager::MOUSE_FINGER_ID, event.motion.xrel, event.motion.yrel, mx, my);
|
|
}
|
|
}
|
|
|
|
if (event.type == SDL_MOUSEWHEEL) {
|
|
if (menuManager.getState() == GameState::Gameplay) {
|
|
static const float zoomstep = 2.0f;
|
|
if (event.wheel.y > 0) {
|
|
Environment::zoom -= zoomstep;
|
|
}
|
|
else if (event.wheel.y < 0) {
|
|
Environment::zoom += zoomstep;
|
|
}
|
|
if (Environment::zoom < zoomstep) {
|
|
Environment::zoom = zoomstep;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
|
switch (event.key.keysym.sym) {
|
|
/*case SDLK_1:
|
|
if (audioPlayer) audioPlayer->playSoundAsync("audio/background.wav");
|
|
break;
|
|
case SDLK_2:
|
|
if (audioPlayer) audioPlayer->playMusicAsync("audio/lullaby-music-vol20-186394--online-audio-convert.com.ogg");
|
|
break;
|
|
case SDLK_3:
|
|
if (audioPlayer) audioPlayer->stopMusicAsync();
|
|
break;*/
|
|
case SDLK_c:
|
|
{
|
|
/*if (menuManager.getState() == GameState::Gameplay) {
|
|
std::cout << "[GAME] Location change triggered by key press" << std::endl;
|
|
static size_t locIndex = 0;
|
|
static std::vector<std::string> locations = {"forest", "barn", "default"};
|
|
locIndex = (locIndex + 1) % locations.size();
|
|
changeLocation(locations[locIndex]);
|
|
}*/
|
|
break;
|
|
}
|
|
case SDLK_f:
|
|
if (currentLocation) currentLocation->dialogueSystem.startDialogue("dialogue_gas1");
|
|
break;
|
|
|
|
|
|
|
|
case SDLK_p:
|
|
x = x + 1;
|
|
break;
|
|
case SDLK_o:
|
|
|
|
x = x - 1;
|
|
break;
|
|
|
|
case SDLK_l:
|
|
case SDLK_w:
|
|
case SDLK_s:
|
|
case SDLK_a:
|
|
case SDLK_d:
|
|
case SDLK_u:
|
|
case SDLK_i:
|
|
case SDLK_e:
|
|
if (menuManager.getState() == GameState::Gameplay && currentLocation) {
|
|
currentLocation->handleKeyDown(event.key.keysym.sym);
|
|
}
|
|
break;
|
|
|
|
case SDLK_RETURN:
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Обработка ввода текста
|
|
if (event.type == SDL_KEYDOWN) {
|
|
if (event.key.keysym.sym == SDLK_BACKSPACE) {
|
|
//menuManager.uiManager.onKeyBackspace();
|
|
}
|
|
}
|
|
|
|
if (event.type == SDL_TEXTINPUT) {
|
|
// Пропускаем ctrl+c и другие команды
|
|
if ((event.text.text[0] & 0x80) == 0) { // ASCII символы
|
|
//menuManager.uiManager.onKeyPress((unsigned char)event.text.text[0]);
|
|
}
|
|
}
|
|
|
|
if (event.type == SDL_KEYUP) {
|
|
if (event.key.keysym.sym == SDLK_r) {
|
|
std::cout << "Camera position: x=" << x << " y=" << y << " z=" << z << std::endl;
|
|
}
|
|
}
|
|
|
|
if (event.type == SDL_KEYUP) {
|
|
switch (event.key.keysym.sym) {
|
|
case SDLK_w:
|
|
case SDLK_s:
|
|
case SDLK_a:
|
|
case SDLK_d:
|
|
case SDLK_i:
|
|
if (menuManager.getState() == GameState::Gameplay && currentLocation) {
|
|
currentLocation->handleKeyUp(event.key.keysym.sym);
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
render();
|
|
|
|
mainThreadHandler.processMainThreadTasks();
|
|
|
|
}
|
|
|
|
void Game::handleDown(int64_t fingerId, int mx, int my)
|
|
{
|
|
//if (!loadingCompleted) return;
|
|
if (!loadingCompleted) {
|
|
std::cout << "[LOADING] Input ignored - loadingCompleted=" << loadingCompleted << std::endl;
|
|
return;
|
|
}
|
|
if (menuManager.getState() == GameState::Gameplay) return;
|
|
int uiX = mx;
|
|
int uiY = Environment::projectionHeight - my;
|
|
|
|
|
|
menuManager.uiManager.onTouchDown(fingerId, uiX, uiY);
|
|
|
|
}
|
|
|
|
void Game::handleUp(int64_t fingerId, int mx, int my)
|
|
{
|
|
if (!loadingCompleted) return;
|
|
if (menuManager.getState() == GameState::Gameplay) return;
|
|
int uiX = mx;
|
|
int uiY = Environment::projectionHeight - my;
|
|
|
|
// Check BEFORE onTouchUp erases the finger from the map.
|
|
// If this finger started on a UI element, don't notify space —
|
|
// otherwise space would think the ship-control finger was released.
|
|
bool wasUiInteraction = menuManager.uiManager.isUiInteractionForFinger(fingerId);
|
|
menuManager.uiManager.onTouchUp(fingerId, uiX, uiY);
|
|
|
|
}
|
|
|
|
void Game::handleMotion(int64_t fingerId, int mx, int my)
|
|
{
|
|
if (!loadingCompleted) return;
|
|
if (menuManager.getState() == GameState::Gameplay) return;
|
|
int uiX = mx;
|
|
int uiY = Environment::projectionHeight - my;
|
|
|
|
// Check before onTouchMove so the "started on UI" state is preserved
|
|
// regardless of what onTouchMove does internally.
|
|
bool wasUiInteraction = menuManager.uiManager.isUiInteractionForFinger(fingerId);
|
|
menuManager.uiManager.onTouchMove(fingerId, uiX, uiY);
|
|
|
|
}
|
|
|
|
void Game::updateMusicForGameState(GameState newState)
|
|
{
|
|
std::string musicTrack;
|
|
|
|
switch (newState) {
|
|
case GameState::MainMenu:
|
|
case GameState::AboutMenu:
|
|
case GameState::HelpScreen:
|
|
musicTrack = "audio/ETM_Titles_track.ogg";
|
|
break;
|
|
case GameState::Gameplay:
|
|
musicTrack = "audio/naprtyag_loop.ogg";
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (currentMusicTrack == musicTrack) {
|
|
return;
|
|
}
|
|
|
|
currentMusicTrack = musicTrack;
|
|
if (audioPlayer) {
|
|
audioPlayer->stopMusicAsync();
|
|
audioPlayer->playMusicAsync(musicTrack, -1);
|
|
}
|
|
}
|
|
|
|
void Game::updateMusicForLocation(const std::string& locationId)
|
|
{
|
|
std::string musicTrack;
|
|
|
|
if (locationId == "default") {
|
|
musicTrack = "audio/naprtyag_loop.ogg";
|
|
}
|
|
else if (locationId == "forest") {
|
|
musicTrack = "audio/naprtyag_loop.ogg";
|
|
}
|
|
else {
|
|
musicTrack = "audio/naprtyag_loop.ogg";
|
|
}
|
|
|
|
if (currentMusicTrack == musicTrack) {
|
|
return;
|
|
}
|
|
|
|
currentMusicTrack = musicTrack;
|
|
if (audioPlayer) {
|
|
audioPlayer->stopMusicAsync();
|
|
audioPlayer->playMusicAsync(musicTrack, -1);
|
|
}
|
|
}
|
|
|
|
void Game::changeLocation(const std::string& locId)
|
|
{
|
|
if (locId == "forest") {
|
|
currentLocation = forestLocation;
|
|
}
|
|
else if (locId == "default") {
|
|
currentLocation = defaultLocation;
|
|
}
|
|
else {
|
|
std::cout << "[GAME] Unknown location id: " << locId << std::endl;
|
|
return;
|
|
}
|
|
|
|
if (menuManager.getState() == GameState::Gameplay) {
|
|
updateMusicForLocation(locId);
|
|
}
|
|
}
|
|
} // namespace ZL
|