diff --git a/source/defines.h b/source/defines.h index e3cc884..5dbb31f 100644 --- a/source/defines.h +++ b/source/defines.h @@ -2,10 +2,16 @@ constexpr char WINDOW_CAPTION[] = "vibe3_physics"; -constexpr int SCREEN_WIDTH = 320; -constexpr int SCREEN_HEIGHT = 240; -constexpr int WINDOW_SIZE = 3; +constexpr int SCREEN_WIDTH = 640; +constexpr int SCREEN_HEIGHT = 480; +constexpr int WINDOW_ZOOM = 2; // Zoom inicial de la ventana constexpr int BALL_SIZE = 10; + +// Configuración de zoom dinámico de ventana +constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240) +constexpr int WINDOW_ZOOM_MAX = 10; // Zoom máximo teórico (3200x2400) +constexpr int WINDOW_DESKTOP_MARGIN = 10; // Margen mínimo con bordes del escritorio +constexpr int WINDOW_DECORATION_HEIGHT = 30; // Altura estimada de decoraciones del SO constexpr float GRAVITY_FORCE = 0.2f; // DEMO_SPEED eliminado - ya no se usa con delta time diff --git a/source/engine.cpp b/source/engine.cpp index 41379ec..801a8e5 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -6,17 +6,36 @@ #include // for SDL_Keycode #include // for SDL_SetRenderDrawColor, SDL_RenderPresent #include // for SDL_GetTicks -#include // for SDL_CreateWindow, SDL_DestroyWindow +#include // for SDL_CreateWindow, SDL_DestroyWindow, SDL_GetDisplayBounds +#include // for std::min, std::max #include // for rand, srand #include // for time #include // for cout #include // for string +#include // for path operations + +#ifdef _WIN32 +#include // for GetModuleFileName +#endif #include "ball.h" // for Ball #include "external/dbgtxt.h" // for dbg_init, dbg_print #include "external/texture.h" // for Texture +// Función auxiliar para obtener la ruta del directorio del ejecutable +std::string getExecutableDirectory() { +#ifdef _WIN32 + char buffer[MAX_PATH]; + GetModuleFileNameA(NULL, buffer, MAX_PATH); + std::filesystem::path exe_path(buffer); + return exe_path.parent_path().string(); +#else + // Para Linux/macOS se podría usar readlink("/proc/self/exe") o dladdr + return "."; // Fallback para otros sistemas +#endif +} + // Implementación de métodos públicos bool Engine::initialize() { bool success = true; @@ -26,7 +45,7 @@ bool Engine::initialize() { success = false; } else { // Crear ventana principal - window_ = SDL_CreateWindow(WINDOW_CAPTION, SCREEN_WIDTH * WINDOW_SIZE, SCREEN_HEIGHT * WINDOW_SIZE, SDL_WINDOW_OPENGL); + window_ = SDL_CreateWindow(WINDOW_CAPTION, SCREEN_WIDTH * WINDOW_ZOOM, SCREEN_HEIGHT * WINDOW_ZOOM, SDL_WINDOW_OPENGL); if (window_ == nullptr) { std::cout << "¡No se pudo crear la ventana! Error de SDL: " << SDL_GetError() << std::endl; success = false; @@ -51,7 +70,10 @@ bool Engine::initialize() { // Inicializar otros componentes si SDL se inicializó correctamente if (success) { - texture_ = std::make_shared(renderer_, "data/ball.png"); + // Construir ruta absoluta a la imagen + std::string exe_dir = getExecutableDirectory(); + std::string texture_path = exe_dir + "/data/ball.png"; + texture_ = std::make_shared(renderer_, texture_path); srand(static_cast(time(nullptr))); dbg_init(renderer_); initBalls(scenario_); @@ -181,22 +203,23 @@ void Engine::handleEvents() { initBalls(scenario_); // Regenerar bolas con nueva paleta break; - case SDLK_F1: + // Temas de colores con teclado numérico + case SDLK_KP_1: current_theme_ = ColorTheme::SUNSET; initBalls(scenario_); break; - case SDLK_F2: + case SDLK_KP_2: current_theme_ = ColorTheme::OCEAN; initBalls(scenario_); break; - case SDLK_F3: + case SDLK_KP_3: current_theme_ = ColorTheme::NEON; initBalls(scenario_); break; - case SDLK_F4: + case SDLK_KP_4: current_theme_ = ColorTheme::FOREST; initBalls(scenario_); break; @@ -240,6 +263,20 @@ void Engine::handleEvents() { scenario_ = 7; initBalls(scenario_); break; + + // Controles de zoom dinámico + case SDLK_F1: + zoomOut(); + break; + + case SDLK_F2: + zoomIn(); + break; + + // Control de pantalla completa + case SDLK_F3: + toggleFullscreen(); + break; } } } @@ -400,6 +437,11 @@ void Engine::toggleVSync() { SDL_SetRenderVSync(renderer_, vsync_enabled_ ? 1 : 0); } +void Engine::toggleFullscreen() { + fullscreen_enabled_ = !fullscreen_enabled_; + SDL_SetWindowFullscreen(window_, fullscreen_enabled_); +} + std::string Engine::gravityDirectionToString(GravityDirection direction) const { switch (direction) { case GravityDirection::DOWN: return "DOWN"; @@ -495,4 +537,80 @@ void Engine::addSpriteToBatch(float x, float y, float w, float h, int r, int g, batch_indices_.push_back(vertex_index + 2); batch_indices_.push_back(vertex_index + 3); batch_indices_.push_back(vertex_index + 0); +} + +// Sistema de zoom dinámico +int Engine::calculateMaxWindowZoom() const { + // Obtener información del display usando el método de Coffee Crisis + int num_displays = 0; + SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + if (displays == nullptr || num_displays == 0) { + return WINDOW_ZOOM_MIN; // Fallback si no se puede obtener + } + + // Obtener el modo de display actual + const auto *dm = SDL_GetCurrentDisplayMode(displays[0]); + if (dm == nullptr) { + SDL_free(displays); + return WINDOW_ZOOM_MIN; + } + + // Calcular zoom máximo usando la fórmula de Coffee Crisis + const int MAX_ZOOM = std::min(dm->w / SCREEN_WIDTH, (dm->h - WINDOW_DECORATION_HEIGHT) / SCREEN_HEIGHT); + + SDL_free(displays); + + // Aplicar límites + return std::max(WINDOW_ZOOM_MIN, std::min(MAX_ZOOM, WINDOW_ZOOM_MAX)); +} + +void Engine::setWindowZoom(int new_zoom) { + // Validar zoom + int max_zoom = calculateMaxWindowZoom(); + new_zoom = std::max(WINDOW_ZOOM_MIN, std::min(new_zoom, max_zoom)); + + if (new_zoom == current_window_zoom_) { + return; // No hay cambio + } + + // Obtener posición actual del centro de la ventana + int current_x, current_y; + SDL_GetWindowPosition(window_, ¤t_x, ¤t_y); + int current_center_x = current_x + (SCREEN_WIDTH * current_window_zoom_) / 2; + int current_center_y = current_y + (SCREEN_HEIGHT * current_window_zoom_) / 2; + + // Calcular nuevo tamaño + int new_width = SCREEN_WIDTH * new_zoom; + int new_height = SCREEN_HEIGHT * new_zoom; + + // Calcular nueva posición (centrada en el punto actual) + int new_x = current_center_x - new_width / 2; + int new_y = current_center_y - new_height / 2; + + // Obtener límites del escritorio para no salirse + SDL_Rect display_bounds; + if (SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &display_bounds) == 0) { + // Aplicar márgenes + int min_x = WINDOW_DESKTOP_MARGIN; + int min_y = WINDOW_DESKTOP_MARGIN; + int max_x = display_bounds.w - new_width - WINDOW_DESKTOP_MARGIN; + int max_y = display_bounds.h - new_height - WINDOW_DESKTOP_MARGIN - WINDOW_DECORATION_HEIGHT; + + // Limitar posición + new_x = std::max(min_x, std::min(new_x, max_x)); + new_y = std::max(min_y, std::min(new_y, max_y)); + } + + // Aplicar cambios + SDL_SetWindowSize(window_, new_width, new_height); + SDL_SetWindowPosition(window_, new_x, new_y); + current_window_zoom_ = new_zoom; +} + +void Engine::zoomIn() { + setWindowZoom(current_window_zoom_ + 1); +} + +void Engine::zoomOut() { + setWindowZoom(current_window_zoom_ - 1); } \ No newline at end of file diff --git a/source/engine.h b/source/engine.h index 7b2469f..2136e9c 100644 --- a/source/engine.h +++ b/source/engine.h @@ -41,6 +41,9 @@ private: // UI y debug bool show_debug_ = false; bool show_text_ = true; + + // Sistema de zoom dinámico + int current_window_zoom_ = WINDOW_ZOOM; std::string text_; int text_pos_ = 0; Uint64 text_init_time_ = 0; @@ -52,6 +55,7 @@ private: std::string fps_text_ = "FPS: 0"; bool vsync_enabled_ = true; std::string vsync_text_ = "VSYNC ON"; + bool fullscreen_enabled_ = false; // Sistema de temas ColorTheme current_theme_ = ColorTheme::SUNSET; @@ -100,8 +104,15 @@ private: void switchBallsGravity(); void changeGravityDirection(GravityDirection direction); void toggleVSync(); + void toggleFullscreen(); std::string gravityDirectionToString(GravityDirection direction) const; + // Sistema de zoom dinámico + int calculateMaxWindowZoom() const; + void setWindowZoom(int new_zoom); + void zoomIn(); + void zoomOut(); + // Rendering void renderGradientBackground(); void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b);