From 10a4234d4921380c2eee3131f827346654c872d2 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 10 Oct 2025 07:44:57 +0200 Subject: [PATCH] Refactor: Sistema de modos y notificaciones mejorado MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cambios principales: - Renombrado AppMode::MANUAL → AppMode::SANDBOX (nomenclatura más clara) - Notificaciones ahora funcionan en TODAS las transiciones de modo - Lógica de teclas D/L/K simplificada: toggle exclusivo modo ↔ SANDBOX - Mensajes simplificados: "MODO DEMO", "MODO SANDBOX", etc. (sin ON/OFF) - Eliminado check restrictivo en showNotificationForAction() Comportamiento nuevo: - Tecla D: Toggle DEMO ↔ SANDBOX - Tecla L: Toggle DEMO_LITE ↔ SANDBOX - Tecla K: Toggle LOGO ↔ SANDBOX - Cada tecla activa su modo o vuelve a SANDBOX si ya está activo - Notificaciones visibles tanto al activar como desactivar modos 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- source/engine.cpp | 251 ++++++++++++++++++++-------------------------- source/engine.h | 13 +-- 2 files changed, 115 insertions(+), 149 deletions(-) diff --git a/source/engine.cpp b/source/engine.cpp index 3d3b2c4..5f2a414 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -329,8 +329,12 @@ void Engine::handleEvents() { // Si estamos en modo figura, salir a modo física SIN GRAVEDAD if (current_mode_ == SimulationMode::SHAPE) { toggleShapeMode(false); // Desactivar figura sin forzar gravedad ON + showNotificationForAction("Gravity Off"); } else { switchBallsGravity(); // Toggle normal en modo física + // Determinar estado actual de gravedad (gravity_force_ != 0.0f significa ON) + bool gravity_on = balls_.empty() ? true : (balls_[0]->getGravityForce() != 0.0f); + showNotificationForAction(gravity_on ? "Gravity On" : "Gravity Off"); } break; @@ -343,6 +347,7 @@ void Engine::handleEvents() { enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF } changeGravityDirection(GravityDirection::UP); + showNotificationForAction("Gravity Up"); break; case SDLK_DOWN: @@ -353,6 +358,7 @@ void Engine::handleEvents() { enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF } changeGravityDirection(GravityDirection::DOWN); + showNotificationForAction("Gravity Down"); break; case SDLK_LEFT: @@ -363,6 +369,7 @@ void Engine::handleEvents() { enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF } changeGravityDirection(GravityDirection::LEFT); + showNotificationForAction("Gravity Left"); break; case SDLK_RIGHT: @@ -373,6 +380,7 @@ void Engine::handleEvents() { enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF } changeGravityDirection(GravityDirection::RIGHT); + showNotificationForAction("Gravity Right"); break; case SDLK_V: @@ -386,43 +394,60 @@ void Engine::handleEvents() { // Toggle Física ↔ Última Figura (antes era C) case SDLK_F: toggleShapeMode(); + // Mostrar notificación según el modo actual después del toggle + if (current_mode_ == SimulationMode::PHYSICS) { + showNotificationForAction("Physics Mode"); + } else { + // Mostrar nombre de la figura actual + const char* shape_names[] = {"Sphere", "Lissajous", "Helix", "Torus", "Cube", "Cylinder", "Icosahedron", "Atom", "PNG Shape"}; + showNotificationForAction(shape_names[static_cast(current_shape_type_)]); + } break; // Selección directa de figuras 3D case SDLK_Q: activateShape(ShapeType::SPHERE); + showNotificationForAction("Sphere"); break; case SDLK_W: activateShape(ShapeType::LISSAJOUS); + showNotificationForAction("Lissajous"); break; case SDLK_E: activateShape(ShapeType::HELIX); + showNotificationForAction("Helix"); break; case SDLK_R: activateShape(ShapeType::TORUS); + showNotificationForAction("Torus"); break; case SDLK_T: activateShape(ShapeType::CUBE); + showNotificationForAction("Cube"); break; case SDLK_Y: activateShape(ShapeType::CYLINDER); + showNotificationForAction("Cylinder"); break; case SDLK_U: activateShape(ShapeType::ICOSAHEDRON); + showNotificationForAction("Icosahedron"); break; case SDLK_I: activateShape(ShapeType::ATOM); + showNotificationForAction("Atom"); break; case SDLK_O: activateShape(ShapeType::PNG_SHAPE); + showNotificationForAction("PNG Shape"); break; // Ciclar temas de color (movido de T a B) @@ -438,13 +463,8 @@ void Engine::handleEvents() { theme_manager_->cycleTheme(); } - // Mostrar nombre del tema (solo si NO estamos en modo demo) - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + // Mostrar notificación con el nombre del tema + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -454,12 +474,7 @@ void Engine::handleEvents() { { int theme_index = (theme_page_ == 0) ? 0 : 10; theme_manager_->switchToTheme(theme_index); - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -468,12 +483,7 @@ void Engine::handleEvents() { { int theme_index = (theme_page_ == 0) ? 1 : 11; theme_manager_->switchToTheme(theme_index); - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -482,12 +492,7 @@ void Engine::handleEvents() { { int theme_index = (theme_page_ == 0) ? 2 : 12; theme_manager_->switchToTheme(theme_index); - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -496,12 +501,7 @@ void Engine::handleEvents() { { int theme_index = (theme_page_ == 0) ? 3 : 13; theme_manager_->switchToTheme(theme_index); - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -510,12 +510,7 @@ void Engine::handleEvents() { { int theme_index = (theme_page_ == 0) ? 4 : 14; theme_manager_->switchToTheme(theme_index); - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -523,12 +518,7 @@ void Engine::handleEvents() { // Solo página 0: MONOCHROME (5) if (theme_page_ == 0) { theme_manager_->switchToTheme(5); // MONOCHROME - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -536,12 +526,7 @@ void Engine::handleEvents() { // Solo página 0: LAVENDER (6) if (theme_page_ == 0) { theme_manager_->switchToTheme(6); // LAVENDER - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -549,12 +534,7 @@ void Engine::handleEvents() { // Solo página 0: CRIMSON (7) if (theme_page_ == 0) { theme_manager_->switchToTheme(7); // CRIMSON - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -562,12 +542,7 @@ void Engine::handleEvents() { // Solo página 0: EMERALD (8) if (theme_page_ == 0) { theme_manager_->switchToTheme(8); // EMERALD - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -575,12 +550,7 @@ void Engine::handleEvents() { // Solo página 0: SUNRISE (9) if (theme_page_ == 0) { theme_manager_->switchToTheme(9); // SUNRISE - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction(theme_manager_->getCurrentThemeNameES()); } break; @@ -588,14 +558,7 @@ void Engine::handleEvents() { case SDLK_KP_ENTER: // Alternar entre página 0 y página 1 theme_page_ = (theme_page_ == 0) ? 1 : 0; - - // Mostrar feedback visual (solo si NO estamos en modo demo) - if (current_app_mode_ == AppMode::MANUAL) { - text_ = (theme_page_ == 0) ? "Página 1" : "Página 2"; - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); - } + showNotificationForAction((theme_page_ == 0) ? "Página 1" : "Página 2"); break; // Cambio de sprite/textura dinámico @@ -608,11 +571,7 @@ void Engine::handleEvents() { if (current_mode_ == SimulationMode::SHAPE) { shape_scale_factor_ += SHAPE_SCALE_STEP; clampShapeScale(); - text_ = "Escala " + std::to_string(static_cast(shape_scale_factor_ * 100.0f + 0.5f)) + "%"; - int text_width = static_cast(text_.length() * 8); - text_pos_ = (current_screen_width_ - text_width) / 2; - text_init_time_ = SDL_GetTicks(); - show_text_ = true; + showNotificationForAction("Escala " + std::to_string(static_cast(shape_scale_factor_ * 100.0f + 0.5f)) + "%"); } break; @@ -620,74 +579,70 @@ void Engine::handleEvents() { if (current_mode_ == SimulationMode::SHAPE) { shape_scale_factor_ -= SHAPE_SCALE_STEP; clampShapeScale(); - text_ = "Escala " + std::to_string(static_cast(shape_scale_factor_ * 100.0f + 0.5f)) + "%"; - int text_width = static_cast(text_.length() * 8); - text_pos_ = (current_screen_width_ - text_width) / 2; - text_init_time_ = SDL_GetTicks(); - show_text_ = true; + showNotificationForAction("Escala " + std::to_string(static_cast(shape_scale_factor_ * 100.0f + 0.5f)) + "%"); } break; case SDLK_KP_MULTIPLY: if (current_mode_ == SimulationMode::SHAPE) { shape_scale_factor_ = SHAPE_SCALE_DEFAULT; - text_ = "Escala reiniciada (100%)"; - int text_width = static_cast(text_.length() * 8); - text_pos_ = (current_screen_width_ - text_width) / 2; - text_init_time_ = SDL_GetTicks(); - show_text_ = true; + showNotificationForAction("Escala 100%"); } break; case SDLK_KP_DIVIDE: if (current_mode_ == SimulationMode::SHAPE) { depth_zoom_enabled_ = !depth_zoom_enabled_; - text_ = depth_zoom_enabled_ ? "Zoom profundidad: On" : "Zoom profundidad: Off"; - int text_width = static_cast(text_.length() * 8); - text_pos_ = (current_screen_width_ - text_width) / 2; - text_init_time_ = SDL_GetTicks(); - show_text_ = true; + showNotificationForAction(depth_zoom_enabled_ ? "Depth Zoom On" : "Depth Zoom Off"); } break; case SDLK_1: scenario_ = 0; initBalls(scenario_); + showNotificationForAction("10 Pelotas"); break; case SDLK_2: scenario_ = 1; initBalls(scenario_); + showNotificationForAction("50 Pelotas"); break; case SDLK_3: scenario_ = 2; initBalls(scenario_); + showNotificationForAction("100 Pelotas"); break; case SDLK_4: scenario_ = 3; initBalls(scenario_); + showNotificationForAction("500 Pelotas"); break; case SDLK_5: scenario_ = 4; initBalls(scenario_); + showNotificationForAction("1,000 Pelotas"); break; case SDLK_6: scenario_ = 5; initBalls(scenario_); + showNotificationForAction("5,000 Pelotas"); break; case SDLK_7: scenario_ = 6; initBalls(scenario_); + showNotificationForAction("10,000 Pelotas"); break; case SDLK_8: scenario_ = 7; initBalls(scenario_); + showNotificationForAction("50,000 Pelotas"); break; // Controles de zoom dinámico (solo si no estamos en fullscreen) @@ -724,22 +679,16 @@ void Engine::handleEvents() { if (event.key.mod & SDL_KMOD_SHIFT) { theme_manager_->pauseDynamic(); } else { - // D sin Shift = Toggle modo DEMO + // D sin Shift = Toggle DEMO ↔ SANDBOX if (current_app_mode_ == AppMode::DEMO) { - // Desactivar DEMO → MANUAL - setState(AppMode::MANUAL); - text_ = "Modo Demo: Off"; - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // Ya estamos en DEMO → volver a SANDBOX + setState(AppMode::SANDBOX); + showNotificationForAction("MODO SANDBOX"); } else { - // Activar DEMO (desde cualquier otro modo) + // Estamos en otro modo → ir a DEMO setState(AppMode::DEMO); randomizeOnDemoStart(false); - text_ = "Modo Demo: On"; - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + showNotificationForAction("MODO DEMO"); } } break; @@ -747,35 +696,28 @@ void Engine::handleEvents() { // Toggle Modo DEMO LITE (solo física/figuras) case SDLK_L: if (current_app_mode_ == AppMode::DEMO_LITE) { - // Desactivar DEMO_LITE → MANUAL - setState(AppMode::MANUAL); - text_ = "Modo Demo Lite: Off"; - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // Ya estamos en DEMO_LITE → volver a SANDBOX + setState(AppMode::SANDBOX); + showNotificationForAction("MODO SANDBOX"); } else { - // Activar DEMO_LITE (desde cualquier otro modo) + // Estamos en otro modo → ir a DEMO_LITE setState(AppMode::DEMO_LITE); randomizeOnDemoStart(true); - text_ = "Modo Demo Lite: On"; - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + showNotificationForAction("MODO DEMO LITE"); } break; // Toggle Modo LOGO (easter egg - marca de agua) case SDLK_K: - toggleLogoMode(); - // Mostrar texto informativo if (current_app_mode_ == AppMode::LOGO) { - text_ = "Modo Logo: On"; + // Ya estamos en LOGO → volver a SANDBOX + exitLogoMode(false); + showNotificationForAction("MODO SANDBOX"); } else { - text_ = "Modo Logo: Off"; + // Estamos en otro modo → ir a LOGO + enterLogoMode(false); + showNotificationForAction("MODO LOGO"); } - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); break; } } @@ -1039,12 +981,12 @@ void Engine::initBalls(int value) { float mass_factor = GRAVITY_MASS_MIN + (rand() % 1000) / 1000.0f * (GRAVITY_MASS_MAX - GRAVITY_MASS_MIN); balls_.emplace_back(std::make_unique(X, VX, VY, COLOR, texture_, current_screen_width_, current_screen_height_, current_ball_size_, current_gravity_, mass_factor)); } - setText(); // Actualiza el texto + // NOTA: setText() removido - las notificaciones ahora se llaman manualmente desde cada tecla } void Engine::setText() { // Suprimir textos durante modos demo - if (current_app_mode_ != AppMode::MANUAL) return; + if (current_app_mode_ != AppMode::SANDBOX) return; // Generar texto de número de pelotas int num_balls = BALL_COUNT_SCENARIOS[scenario_]; @@ -1091,6 +1033,34 @@ void Engine::setText() { text_init_time_ = SDL_GetTicks(); } +void Engine::showNotificationForAction(const std::string& text) { + // IMPORTANTE: Esta función solo se llama desde handlers de teclado (acciones manuales) + // NUNCA se llama desde código automático (DEMO/LOGO), por lo tanto siempre mostramos notificación + + // Obtener color del tema actual para el texto + int text_r, text_g, text_b; + theme_manager_->getCurrentThemeTextColor(text_r, text_g, text_b); + SDL_Color notification_color = { + static_cast(text_r), + static_cast(text_g), + static_cast(text_b), + 255 + }; + + // Obtener color de fondo de la notificación desde el tema + int bg_r, bg_g, bg_b; + theme_manager_->getCurrentNotificationBackgroundColor(bg_r, bg_g, bg_b); + SDL_Color notification_bg_color = { + static_cast(bg_r), + static_cast(bg_g), + static_cast(bg_b), + 255 + }; + + // Mostrar notificación + notifier_.show(text, NOTIFICATION_DURATION, notification_color, notification_bg_color); +} + void Engine::pushBallsAwayFromGravity() { for (auto& ball : balls_) { const int SIGNO = ((rand() % 2) * 2) - 1; @@ -1552,7 +1522,7 @@ void Engine::setState(AppMode new_mode) { void Engine::updateDemoMode() { // Verificar si algún modo demo está activo (DEMO, DEMO_LITE o LOGO) - if (current_app_mode_ == AppMode::MANUAL) return; + if (current_app_mode_ == AppMode::SANDBOX) return; // Actualizar timer demo_timer_ += delta_time_; @@ -1692,7 +1662,7 @@ void Engine::updateDemoMode() { // Solo salir automáticamente si NO llegamos desde MANUAL // Probabilidad de salir: 60% en cada acción → sale rápido (relación DEMO:LOGO = 6:1) - if (previous_app_mode_ != AppMode::MANUAL && rand() % 100 < 60) { + if (previous_app_mode_ != AppMode::SANDBOX && rand() % 100 < 60) { exitLogoMode(true); // Volver a DEMO/DEMO_LITE } } @@ -1891,7 +1861,7 @@ void Engine::performDemoAction(bool is_lite) { // Cambiar sprite (2%) accumulated_weight += DEMO_WEIGHT_SPRITE; if (random_value < accumulated_weight) { - switchTexture(); + switchTexture(false); // Suprimir notificación en modo automático return; } } @@ -1934,7 +1904,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) { // 3. Sprite if (rand() % 2 == 0) { - switchTexture(); + switchTexture(false); // Suprimir notificación al activar modo DEMO } // 4. Física o Figura @@ -2087,7 +2057,7 @@ void Engine::exitLogoMode(bool return_to_demo) { if (!return_to_demo) { // Salida manual (tecla K): volver a MANUAL - setState(AppMode::MANUAL); + setState(AppMode::SANDBOX); } else { // Volver al modo previo (DEMO o DEMO_LITE) setState(previous_app_mode_); @@ -2147,7 +2117,7 @@ void Engine::updateBallSizes(int old_size, int new_size) { } } -void Engine::switchTexture() { +void Engine::switchTexture(bool show_notification) { if (textures_.empty()) return; // Guardar tamaño antiguo @@ -2169,16 +2139,11 @@ void Engine::switchTexture() { // Ajustar posiciones según el cambio de tamaño updateBallSizes(old_size, new_size); - // Mostrar texto informativo (solo si NO estamos en modo demo) - if (current_app_mode_ == AppMode::MANUAL) { - // Obtener nombre de textura (uppercase) + // Mostrar notificación con el nombre de la textura (solo si se solicita) + if (show_notification) { std::string texture_name = texture_names_[current_texture_index_]; std::transform(texture_name.begin(), texture_name.end(), texture_name.begin(), ::toupper); - - text_ = "Sprite: " + texture_name; - text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + showNotificationForAction("Sprite: " + texture_name); } } @@ -2220,7 +2185,7 @@ void Engine::toggleShapeMode(bool force_gravity_on_exit) { } // Mostrar texto informativo (solo si NO estamos en modo demo o logo) - if (current_app_mode_ == AppMode::MANUAL) { + if (current_app_mode_ == AppMode::SANDBOX) { text_ = "Modo Física"; text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; text_init_time_ = SDL_GetTicks(); @@ -2284,7 +2249,7 @@ void Engine::activateShape(ShapeType type) { } // Mostrar texto informativo con nombre de figura (solo si NO estamos en modo demo o logo) - if (active_shape_ && current_app_mode_ == AppMode::MANUAL) { + if (active_shape_ && current_app_mode_ == AppMode::SANDBOX) { text_ = std::string("Modo ") + active_shape_->getName(); text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; text_init_time_ = SDL_GetTicks(); diff --git a/source/engine.h b/source/engine.h index 7d6be8b..b998123 100644 --- a/source/engine.h +++ b/source/engine.h @@ -20,7 +20,7 @@ // Modos de aplicación mutuamente excluyentes enum class AppMode { - MANUAL, // Control manual del usuario + SANDBOX, // Control manual del usuario (modo sandbox) DEMO, // Modo demo completo (auto-play) DEMO_LITE, // Modo demo lite (solo física/figuras) LOGO // Modo logo (easter egg) @@ -103,8 +103,8 @@ class Engine { bool depth_zoom_enabled_ = true; // Zoom por profundidad Z activado // Sistema de Modo DEMO (auto-play) - AppMode current_app_mode_ = AppMode::MANUAL; // Modo actual (mutuamente excluyente) - AppMode previous_app_mode_ = AppMode::MANUAL; // Modo previo antes de entrar a LOGO + AppMode current_app_mode_ = AppMode::SANDBOX; // Modo actual (mutuamente excluyente) + AppMode previous_app_mode_ = AppMode::SANDBOX; // Modo previo antes de entrar a LOGO 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) @@ -142,7 +142,8 @@ class Engine { // Métodos auxiliares void initBalls(int value); - void setText(); + void setText(); // DEPRECATED - usar showNotificationForAction() en su lugar + void showNotificationForAction(const std::string& text); // Mostrar notificación solo en modo MANUAL void pushBallsAwayFromGravity(); void switchBallsGravity(); void enableBallsGravityIfDisabled(); @@ -170,8 +171,8 @@ class Engine { void exitLogoMode(bool return_to_demo = false); // Salir del modo logo // Sistema de cambio de sprites dinámico - void switchTexture(); // Cambia a siguiente textura disponible - void updateBallSizes(int old_size, int new_size); // Ajusta posiciones al cambiar tamaño + void switchTexture(bool show_notification = true); // Cambia a siguiente textura disponible + void updateBallSizes(int old_size, int new_size); // Ajusta posiciones al cambiar tamaño // Sistema de zoom dinámico int calculateMaxWindowZoom() const;