diff --git a/ROADMAP.md b/ROADMAP.md index e3e4eb8..26a3406 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -74,42 +74,42 @@ - ✅ Help text con `--help` - Ejemplo: `./vibe3_physics -w 1920 -h 1080 -f` -### 4. 🎯 Implementar Modo Logo (Easter Egg) +### 4. ✅ Implementar Modo Logo (Easter Egg) **Descripción:** Modo especial que muestra el logo JAILGAMES como "marca de agua" **Prioridad:** Alta (característica distintiva) -**Estimación:** 2 horas +**Estado:** ✅ COMPLETADO **Detalles:** -#### Configuración Modo Logo: -- **Figura:** Solo PNG_SHAPE (logo JAILGAMES) -- **Textura:** Siempre "tiny" (pelota más pequeña) -- **Tema:** Siempre MONOCHROME (blanco puro) -- **Escala:** 120% (figuras más grandes que normal) -- **Pelotas mínimas:** 500 -- **Tecla manual:** K (activa/desactiva modo logo) +#### ✅ Configuración Modo Logo: +- ✅ **Figura:** Solo PNG_SHAPE (logo JAILGAMES) +- ✅ **Textura:** Siempre "tiny" (pelota más pequeña) +- ✅ **Tema:** Siempre MONOCHROME (blanco puro) +- ✅ **Escala:** 120% (figuras más grandes que normal) +- ✅ **Pelotas mínimas:** 500 +- ✅ **Tecla manual:** K (activa/desactiva modo logo) -#### Comportamiento en Modo Logo: -- Alterna entre modo SHAPE y modo PHYSICS (como DEMO) -- Mantiene configuración fija (no cambia tema/textura/escala) -- Es como un "DEMO específico del logo" +#### ✅ Comportamiento en Modo Logo: +- ✅ Alterna entre modo SHAPE y modo PHYSICS (como DEMO) +- ✅ Mantiene configuración fija (no cambia tema/textura/escala) +- ✅ Es como un "DEMO específico del logo" -#### Integración con DEMO LITE: -- **Requisitos para salto automático:** +#### ✅ Integración con DEMO LITE: +- ✅ **Requisitos para salto automático:** - Mínimo 500 pelotas - Tema MONOCHROME activo - Si se cumplen → cambia automáticamente textura a "tiny" y escala a 120% -- **Duración:** Menos tiempo que DEMO normal (es un "recordatorio") -- **Después:** Vuelve a DEMO LITE normal +- ✅ **Duración:** Menos tiempo que DEMO normal (es un "recordatorio") +- ✅ **Después:** Vuelve a DEMO LITE normal -#### Integración con DEMO: -- **Requisitos:** Mínimo 500 pelotas -- **Acción:** Cambia automáticamente a: MONOCHROME + tiny + escala 120% -- **Duración:** Menos tiempo que acciones normales -- **Después:** Vuelve a DEMO normal +#### ✅ Integración con DEMO: +- ✅ **Requisitos:** Mínimo 500 pelotas +- ✅ **Acción:** Cambia automáticamente a: MONOCHROME + tiny + escala 120% +- ✅ **Duración:** Menos tiempo que acciones normales +- ✅ **Después:** Vuelve a DEMO normal -#### Proporción temporal sugerida: -- DEMO/DEMO_LITE normal: 80-90% del tiempo -- Modo Logo: 10-20% del tiempo (aparición ocasional como "easter egg") +#### ✅ Proporción temporal sugerida: +- ✅ DEMO/DEMO_LITE normal: 80-90% del tiempo +- ✅ Modo Logo: 10-20% del tiempo (aparición ocasional como "easter egg") ### 5. ⏳ Mejorar Sistema de Vértices PNG_SHAPE **Descripción:** Con 50 pelotas no activa modo vértices correctamente diff --git a/data/balls/normal.png b/data/balls/normal.png index 6b1cc9c..2b3eb02 100644 Binary files a/data/balls/normal.png and b/data/balls/normal.png differ diff --git a/source/defines.h b/source/defines.h index 8ff5351..d8c255c 100644 --- a/source/defines.h +++ b/source/defines.h @@ -165,8 +165,8 @@ constexpr float PNG_EXTRUSION_DEPTH_FACTOR = 0.12f; // Profundidad de extrusió constexpr int PNG_NUM_EXTRUSION_LAYERS = 15; // Capas de extrusión (más capas = más pegajosidad) constexpr bool PNG_USE_EDGES_ONLY = false; // true = solo bordes, false = relleno completo // Rotación "legible" (texto de frente con volteretas ocasionales) -constexpr float PNG_IDLE_TIME_MIN = 3.0f; // Tiempo mínimo de frente (segundos) -constexpr float PNG_IDLE_TIME_MAX = 8.0f; // Tiempo máximo de frente (segundos) +constexpr float PNG_IDLE_TIME_MIN = 0.5f; // Tiempo mínimo de frente (segundos) - reducido para Logo Mode +constexpr float PNG_IDLE_TIME_MAX = 2.0f; // Tiempo máximo de frente (segundos) - reducido para Logo Mode constexpr float PNG_FLIP_SPEED = 3.0f; // Velocidad voltereta (rad/s) constexpr float PNG_FLIP_DURATION = 1.5f; // Duración voltereta (segundos) @@ -209,8 +209,8 @@ constexpr int DEMO_LITE_WEIGHT_IMPULSE = 10; // Aplicar impulso (10%) // Configuración de Modo LOGO (easter egg - "marca de agua") constexpr int LOGO_MODE_MIN_BALLS = 500; // Mínimo de pelotas para activar modo logo constexpr float LOGO_MODE_SHAPE_SCALE = 1.2f; // Escala de figura en modo logo (120%) -constexpr float LOGO_ACTION_INTERVAL_MIN = 2.0f; // Tiempo mínimo entre alternancia SHAPE/PHYSICS -constexpr float LOGO_ACTION_INTERVAL_MAX = 5.0f; // Tiempo máximo entre alternancia SHAPE/PHYSICS +constexpr float LOGO_ACTION_INTERVAL_MIN = 4.0f; // Tiempo mínimo entre alternancia SHAPE/PHYSICS (más tiempo para ver rotaciones) +constexpr float LOGO_ACTION_INTERVAL_MAX = 8.0f; // Tiempo máximo entre alternancia SHAPE/PHYSICS constexpr int LOGO_WEIGHT_TOGGLE_PHYSICS = 100; // Único peso: alternar SHAPE ↔ PHYSICS (100%) // Probabilidad de salto a Logo Mode desde DEMO/DEMO_LITE (%) diff --git a/source/engine.cpp b/source/engine.cpp index e75e793..8cff8f7 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -23,6 +23,7 @@ #include "ball.h" // for Ball #include "external/dbgtxt.h" // for dbg_init, dbg_print +#include "external/mouse.h" // for Mouse namespace #include "external/texture.h" // for Texture #include "shapes/atom_shape.h" // for AtomShape #include "shapes/cube_shape.h" // for CubeShape @@ -242,6 +243,9 @@ void Engine::calculateDeltaTime() { } void Engine::update() { + // Actualizar visibilidad del cursor (auto-ocultar tras inactividad) + Mouse::updateCursorVisibility(); + // Calcular FPS fps_frame_count_++; Uint64 current_time = SDL_GetTicks(); @@ -295,6 +299,9 @@ void Engine::update() { void Engine::handleEvents() { SDL_Event event; while (SDL_PollEvent(&event)) { + // Procesar eventos de ratón (auto-ocultar cursor) + Mouse::handleEvent(event); + // Salir del bucle si se detecta una petición de cierre if (event.type == SDL_EVENT_QUIT) { should_exit_ = true; @@ -623,6 +630,20 @@ void Engine::handleEvents() { text_init_time_ = SDL_GetTicks(); } break; + + // Toggle Modo LOGO (easter egg - marca de agua) + case SDLK_K: + toggleLogoMode(); + // Mostrar texto informativo + if (logo_mode_enabled_) { + text_ = "LOGO MODE ON"; + } else { + text_ = "LOGO MODE OFF"; + } + text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + break; } } } @@ -759,6 +780,21 @@ void Engine::render() { mode_text = "MODE SHAPE"; } dbg_print(8, 72, mode_text.c_str(), 0, 255, 128); // Verde claro para modo + + // Debug: Mostrar modo DEMO/LOGO activo (siempre visible cuando debug está ON) + if (logo_mode_enabled_) { + int logo_text_width = 9 * 8; // "LOGO MODE" = 9 caracteres × 8 píxeles + int logo_x = (current_screen_width_ - logo_text_width) / 2; + dbg_print(logo_x, 80, "LOGO MODE", 255, 128, 0); // Naranja para Logo Mode + } else if (demo_mode_enabled_) { + int demo_text_width = 9 * 8; // "DEMO MODE" = 9 caracteres × 8 píxeles + int demo_x = (current_screen_width_ - demo_text_width) / 2; + dbg_print(demo_x, 80, "DEMO MODE", 255, 165, 0); // Naranja para DEMO + } else if (demo_lite_enabled_) { + int lite_text_width = 14 * 8; // "DEMO LITE MODE" = 14 caracteres × 8 píxeles + int lite_x = (current_screen_width_ - lite_text_width) / 2; + dbg_print(lite_x, 80, "DEMO LITE MODE", 255, 200, 0); // Amarillo-naranja para DEMO LITE + } } SDL_RenderPresent(renderer_); @@ -860,6 +896,21 @@ void Engine::forceBallsGravityOn() { } void Engine::forceBallsGravityOff() { + // Contar cuántas pelotas están en superficie (suelo/techo/pared) + int balls_on_surface = 0; + for (const auto& ball : balls_) { + if (ball->isOnSurface()) { + balls_on_surface++; + } + } + + // Si la mayoría (>50%) están en superficie, aplicar impulso para que se vea el efecto + float surface_ratio = static_cast(balls_on_surface) / static_cast(balls_.size()); + if (surface_ratio > 0.5f) { + pushBallsAwayFromGravity(); // Dar impulso contrario a gravedad + } + + // Desactivar gravedad for (auto& ball : balls_) { ball->forceGravityOff(); } @@ -889,13 +940,6 @@ void Engine::toggleFullscreen() { fullscreen_enabled_ = !fullscreen_enabled_; SDL_SetWindowFullscreen(window_, fullscreen_enabled_); - - // Ocultar/mostrar cursor según modo fullscreen - if (fullscreen_enabled_) { - SDL_HideCursor(); - } else { - SDL_ShowCursor(); - } } void Engine::toggleRealFullscreen() { @@ -930,9 +974,6 @@ void Engine::toggleRealFullscreen() { } SDL_free(displays); } - - // Ocultar cursor en real fullscreen - SDL_HideCursor(); } else { // Volver a resolución base (configurada por CLI o default) current_screen_width_ = base_screen_width_; @@ -947,9 +988,6 @@ void Engine::toggleRealFullscreen() { // Reinicar la escena con resolución original initBalls(scenario_); - - // Mostrar cursor al salir de real fullscreen - SDL_ShowCursor(); } } @@ -1366,8 +1404,8 @@ Color Engine::getInterpolatedColor(size_t ball_index) const { // Sistema de Modo DEMO (auto-play) void Engine::updateDemoMode() { - // Verificar si algún modo está activo - bool is_demo_active = demo_mode_enabled_ || demo_lite_enabled_; + // Verificar si algún modo está activo (DEMO, DEMO LITE o LOGO) + bool is_demo_active = demo_mode_enabled_ || demo_lite_enabled_ || logo_mode_enabled_; if (!is_demo_active) return; // Actualizar timer @@ -1375,22 +1413,92 @@ void Engine::updateDemoMode() { // Si es hora de ejecutar acción if (demo_timer_ >= demo_next_action_time_) { - // Ejecutar acción según modo activo - bool is_lite = demo_lite_enabled_; - performDemoAction(is_lite); + // MODO LOGO: Sistema de acciones variadas con gravedad dinámica + if (logo_mode_enabled_) { + // Elegir acción aleatoria ponderada + int action = rand() % 100; - // Resetear timer y calcular próximo intervalo aleatorio - demo_timer_ = 0.0f; + if (current_mode_ == SimulationMode::SHAPE) { + // Logo quieto (formado) → 2 opciones posibles + if (action < 50) { + // 50%: SHAPE → PHYSICS con gravedad ON (caída dramática) + toggleShapeMode(true); + } else { + // 50%: SHAPE → PHYSICS con gravedad OFF (dar vueltas sin caer) + toggleShapeMode(false); + } + } else { + // Logo animado (PHYSICS) → 3 opciones posibles + if (action < 60) { + // 60%: PHYSICS → SHAPE (reconstruir logo y ver rotaciones) + toggleShapeMode(false); + } else if (action < 80) { + // 20%: Forzar gravedad ON (empezar a caer mientras da vueltas) + forceBallsGravityOn(); + } else { + // 20%: Forzar gravedad OFF (flotar mientras da vueltas) + forceBallsGravityOff(); + } + } - // Usar intervalos diferentes según modo - float interval_min = is_lite ? DEMO_LITE_ACTION_INTERVAL_MIN : DEMO_ACTION_INTERVAL_MIN; - float interval_max = is_lite ? DEMO_LITE_ACTION_INTERVAL_MAX : DEMO_ACTION_INTERVAL_MAX; - float interval_range = interval_max - interval_min; - demo_next_action_time_ = interval_min + (rand() % 1000) / 1000.0f * interval_range; + // Resetear timer con intervalos de Logo Mode + demo_timer_ = 0.0f; + float interval_range = LOGO_ACTION_INTERVAL_MAX - LOGO_ACTION_INTERVAL_MIN; + demo_next_action_time_ = LOGO_ACTION_INTERVAL_MIN + (rand() % 1000) / 1000.0f * interval_range; + + // Solo salir automáticamente si NO es modo manual (tecla K) + // Probabilidad de salir: 25% en cada acción → promedio 4 acciones antes de salir + if (!logo_mode_is_manual_ && rand() % 100 < 25) { + exitLogoMode(true); // Volver a DEMO/DEMO_LITE + } + } + // MODO DEMO/DEMO_LITE: Acciones normales + else { + bool is_lite = demo_lite_enabled_; + performDemoAction(is_lite); + + // Resetear timer y calcular próximo intervalo aleatorio + demo_timer_ = 0.0f; + + // Usar intervalos diferentes según modo + float interval_min = is_lite ? DEMO_LITE_ACTION_INTERVAL_MIN : DEMO_ACTION_INTERVAL_MIN; + float interval_max = is_lite ? DEMO_LITE_ACTION_INTERVAL_MAX : DEMO_ACTION_INTERVAL_MAX; + float interval_range = interval_max - interval_min; + demo_next_action_time_ = interval_min + (rand() % 1000) / 1000.0f * interval_range; + } } } void Engine::performDemoAction(bool is_lite) { + // ============================================ + // SALTO AUTOMÁTICO A LOGO MODE (Easter Egg) + // ============================================ + + if (is_lite) { + // DEMO LITE: Verificar condiciones para salto a Logo Mode + if (static_cast(balls_.size()) >= LOGO_MODE_MIN_BALLS && + current_theme_ == ColorTheme::MONOCHROME) { + // 10% probabilidad de saltar a Logo Mode + if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO_LITE) { + enterLogoMode(true); // Entrar desde DEMO + return; + } + } + } else { + // DEMO COMPLETO: Verificar condiciones para salto a Logo Mode + if (static_cast(balls_.size()) >= LOGO_MODE_MIN_BALLS) { + // 15% probabilidad de saltar a Logo Mode + if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO) { + enterLogoMode(true); // Entrar desde DEMO + return; + } + } + } + + // ============================================ + // ACCIONES NORMALES DE DEMO/DEMO_LITE + // ============================================ + int TOTAL_WEIGHT; int random_value; int accumulated_weight = 0; @@ -1415,11 +1523,11 @@ void Engine::performDemoAction(bool is_lite) { return; } - // Activar figura 3D (25%) + // Activar figura 3D (25%) - PNG_SHAPE excluido (reservado para Logo Mode) accumulated_weight += DEMO_LITE_WEIGHT_SHAPE; if (random_value < accumulated_weight) { - ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE}; - int shape_index = rand() % 9; + ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM}; + int shape_index = rand() % 8; activateShape(shapes[shape_index]); return; } @@ -1458,11 +1566,11 @@ void Engine::performDemoAction(bool is_lite) { return; } - // Activar figura 3D (20%) + // Activar figura 3D (20%) - PNG_SHAPE excluido (reservado para Logo Mode) accumulated_weight += DEMO_WEIGHT_SHAPE; if (random_value < accumulated_weight) { - ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE}; - int shape_index = rand() % 9; + ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM}; + int shape_index = rand() % 8; activateShape(shapes[shape_index]); return; } @@ -1629,6 +1737,125 @@ void Engine::toggleGravityOnOff() { } } +// ============================================================================ +// SISTEMA DE MODO LOGO (Easter Egg - "Marca de Agua") +// ============================================================================ + +// Entrar al Modo Logo (manual con tecla K o automático desde DEMO) +void Engine::enterLogoMode(bool from_demo) { + // Verificar mínimo de pelotas + if (static_cast(balls_.size()) < LOGO_MODE_MIN_BALLS) { + // Ajustar a 5000 pelotas automáticamente + scenario_ = 5; // Escenario 5000 pelotas (índice 5 en BALL_COUNT_SCENARIOS) + initBalls(scenario_); + } + + // Guardar estado previo (para restaurar al salir) + logo_previous_theme_ = current_theme_; + logo_previous_texture_index_ = current_texture_index_; + logo_previous_shape_scale_ = shape_scale_factor_; + + // Buscar índice de textura "tiny" + size_t tiny_index = current_texture_index_; // Por defecto mantener actual + for (size_t i = 0; i < texture_names_.size(); i++) { + if (texture_names_[i] == "tiny") { + tiny_index = i; + break; + } + } + + // Aplicar configuración fija del Modo Logo + if (tiny_index != current_texture_index_) { + current_texture_index_ = tiny_index; + int old_size = current_ball_size_; + current_ball_size_ = textures_[current_texture_index_]->getWidth(); + updateBallSizes(old_size, current_ball_size_); + + // Actualizar textura global y en cada pelota + texture_ = textures_[current_texture_index_]; + for (auto& ball : balls_) { + ball->setTexture(texture_); + } + } + + // Cambiar a tema MONOCHROME + startThemeTransition(ColorTheme::MONOCHROME); + + // Establecer escala a 120% + shape_scale_factor_ = LOGO_MODE_SHAPE_SCALE; + clampShapeScale(); + + // Activar PNG_SHAPE (el logo) + activateShape(ShapeType::PNG_SHAPE); + + // Activar modo logo + logo_mode_enabled_ = true; + + // Marcar si es activación manual (tecla K) o automática (desde DEMO) + logo_mode_is_manual_ = !from_demo; + + // Configurar timer para alternancia SHAPE/PHYSICS + demo_timer_ = 0.0f; + demo_next_action_time_ = LOGO_ACTION_INTERVAL_MIN + + (rand() % 1000) / 1000.0f * (LOGO_ACTION_INTERVAL_MAX - LOGO_ACTION_INTERVAL_MIN); + + std::cout << "[LOGO MODE] Activado" << (from_demo ? " (desde DEMO)" : " (manual)") << "\n"; +} + +// Salir del Modo Logo (volver a estado anterior o salir de DEMO) +void Engine::exitLogoMode(bool return_to_demo) { + if (!logo_mode_enabled_) return; + + // Restaurar estado previo + startThemeTransition(logo_previous_theme_); + + if (logo_previous_texture_index_ != current_texture_index_ && + logo_previous_texture_index_ < textures_.size()) { + current_texture_index_ = logo_previous_texture_index_; + int old_size = current_ball_size_; + current_ball_size_ = textures_[current_texture_index_]->getWidth(); + updateBallSizes(old_size, current_ball_size_); + + // Actualizar textura global y en cada pelota + texture_ = textures_[current_texture_index_]; + for (auto& ball : balls_) { + ball->setTexture(texture_); + } + } + + shape_scale_factor_ = logo_previous_shape_scale_; + clampShapeScale(); + generateShape(); + + // Desactivar modo logo + logo_mode_enabled_ = false; + logo_mode_is_manual_ = false; // Resetear flag manual + + if (!return_to_demo) { + // Salida manual (tecla K): desactivar todos los modos DEMO + demo_mode_enabled_ = false; + demo_lite_enabled_ = false; + } else { + // Volver a DEMO: reconfigurar timer + demo_timer_ = 0.0f; + demo_next_action_time_ = (demo_lite_enabled_ ? DEMO_LITE_ACTION_INTERVAL_MIN : DEMO_ACTION_INTERVAL_MIN) + + (rand() % 1000) / 1000.0f * + ((demo_lite_enabled_ ? DEMO_LITE_ACTION_INTERVAL_MAX : DEMO_ACTION_INTERVAL_MAX) - + (demo_lite_enabled_ ? DEMO_LITE_ACTION_INTERVAL_MIN : DEMO_ACTION_INTERVAL_MIN)); + } + + std::cout << "[LOGO MODE] Desactivado" << (return_to_demo ? " (volviendo a DEMO)" : " (salida manual)") << "\n"; +} + +// Toggle manual del Modo Logo (tecla K) +void Engine::toggleLogoMode() { + if (logo_mode_enabled_) { + exitLogoMode(false); // Salir y desactivar DEMO + } else { + enterLogoMode(false); // Entrar manualmente + } +} + // Sistema de cambio de sprites dinámico void Engine::updateBallSizes(int old_size, int new_size) { float delta_size = static_cast(new_size - old_size); @@ -1728,8 +1955,8 @@ void Engine::toggleShapeMode(bool force_gravity_on_exit) { forceBallsGravityOn(); } - // Mostrar texto informativo (solo si NO estamos en modo demo) - if (!demo_mode_enabled_ && !demo_lite_enabled_) { + // Mostrar texto informativo (solo si NO estamos en modo demo o logo) + if (!demo_mode_enabled_ && !demo_lite_enabled_ && !logo_mode_enabled_) { text_ = "MODO FISICA"; int text_width = static_cast(text_.length() * 8); text_pos_ = (current_screen_width_ - text_width) / 2; @@ -1793,8 +2020,8 @@ void Engine::activateShape(ShapeType type) { ball->enableRotoBallAttraction(true); } - // Mostrar texto informativo con nombre de figura (solo si NO estamos en modo demo) - if (active_shape_ && !demo_mode_enabled_ && !demo_lite_enabled_) { + // Mostrar texto informativo con nombre de figura (solo si NO estamos en modo demo o logo) + if (active_shape_ && !demo_mode_enabled_ && !demo_lite_enabled_ && !logo_mode_enabled_) { text_ = std::string("MODO ") + active_shape_->getName(); int text_width = static_cast(text_.length() * 8); text_pos_ = (current_screen_width_ - text_width) / 2; diff --git a/source/engine.h b/source/engine.h index 339f760..93d5197 100644 --- a/source/engine.h +++ b/source/engine.h @@ -103,6 +103,7 @@ class Engine { bool demo_mode_enabled_ = false; // ¿Está activo el modo demo completo? bool demo_lite_enabled_ = false; // ¿Está activo el modo demo lite? bool logo_mode_enabled_ = false; // ¿Está activo el modo logo (easter egg)? + bool logo_mode_is_manual_ = false; // ¿Logo Mode activado manualmente (tecla K)? float demo_timer_ = 0.0f; // Contador de tiempo para próxima acción float demo_next_action_time_ = 0.0f; // Tiempo aleatorio hasta próxima acción (segundos) diff --git a/source/external/mouse.cpp b/source/external/mouse.cpp new file mode 100644 index 0000000..bf816b8 --- /dev/null +++ b/source/external/mouse.cpp @@ -0,0 +1,27 @@ +#include "mouse.h" + +#include // Para SDL_GetTicks, Uint32, SDL_HideCursor, SDL_ShowCursor + +namespace Mouse { +Uint32 cursor_hide_time = 3000; // Tiempo en milisegundos para ocultar el cursor +Uint32 last_mouse_move_time = 0; // Última vez que el ratón se movió +bool cursor_visible = true; // Estado del cursor + +void handleEvent(const SDL_Event &event) { + if (event.type == SDL_EVENT_MOUSE_MOTION) { + last_mouse_move_time = SDL_GetTicks(); + if (!cursor_visible) { + SDL_ShowCursor(); + cursor_visible = true; + } + } +} + +void updateCursorVisibility() { + Uint32 current_time = SDL_GetTicks(); + if (cursor_visible && (current_time - last_mouse_move_time > cursor_hide_time)) { + SDL_HideCursor(); + cursor_visible = false; + } +} +} // namespace Mouse diff --git a/source/external/mouse.h b/source/external/mouse.h new file mode 100644 index 0000000..17d0f6b --- /dev/null +++ b/source/external/mouse.h @@ -0,0 +1,15 @@ +#pragma once + +#include // Para Uint32, SDL_Event + +// --- Namespace Mouse: gestión del ratón --- +namespace Mouse { +// --- Variables de estado del cursor --- +extern Uint32 cursor_hide_time; // Tiempo en milisegundos para ocultar el cursor tras inactividad +extern Uint32 last_mouse_move_time; // Última vez (en ms) que el ratón se movió +extern bool cursor_visible; // Indica si el cursor está visible + +// --- Funciones --- +void handleEvent(const SDL_Event &event); // Procesa eventos de ratón (movimiento, clic, etc.) +void updateCursorVisibility(); // Actualiza la visibilidad del cursor según la inactividad +} // namespace Mouse