From c50ecbc02a3b9f889d60c6ac5a4d0d1825c2dc2c Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Thu, 9 Oct 2025 18:04:13 +0200 Subject: [PATCH] =?UTF-8?q?Add:=20Sistema=20de=20p=C3=A1ginas=20para=20sel?= =?UTF-8?q?ecci=C3=B3n=20de=20temas=20+=205=20nuevos=20temas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementación: - 5 nuevos temas (2 estáticos: CRIMSON, EMERALD / 3 dinámicos: FIRE, AURORA, VOLCANIC) - Sistema de páginas con Numpad Enter (Página 1 ↔ Página 2) - Shift+B para ciclar temas hacia atrás - Página 1: 9 temas estáticos + SUNRISE (Numpad 1-9, 0) - Página 2: 5 temas dinámicos animados (Numpad 1-5) Motivo: - Shift+Numpad no funciona en Windows (limitación hardware/OS) - Solución: Toggle de página con Numpad Enter Archivos modificados: - defines.h: Añadidos 5 nuevos ColorTheme enum values - theme_manager.h: Añadido cyclePrevTheme() + actualizada doc 10→15 temas - theme_manager.cpp: Implementados 5 nuevos temas + cyclePrevTheme() - engine.h: Añadida variable theme_page_ (0 o 1) - engine.cpp: Handlers Numpad Enter, KP_1-9,0 con sistema de páginas, SDLK_B con Shift detection - CLAUDE.md: Documentación actualizada con tablas de 2 páginas 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CLAUDE.md | 36 ++++++- source/defines.h | 13 ++- source/engine.cpp | 195 ++++++++++++++++++++++++------------- source/engine.h | 1 + source/theme_manager.cpp | 202 +++++++++++++++++++++++++++++++++++++-- source/theme_manager.h | 29 +++--- 6 files changed, 380 insertions(+), 96 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a6cf572..c4aafa7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,7 +22,7 @@ - ✅ Contador FPS en tiempo real (esquina superior derecha, amarillo) - ✅ Control V-Sync dinámico con tecla "V" (ON/OFF) - ✅ Display V-Sync (esquina superior izquierda, cian) - - ✅ **Sistema de temas visuales** - 4 temas (SUNSET/OCEAN/NEON/FOREST) + - ✅ **Sistema de temas visuales** - 15 temas (9 estáticos + 6 dinámicos con animación) - ✅ **Batch rendering optimizado** - Maneja hasta 100,000 sprites #### 3. **NUEVA CARACTERÍSTICA: Gravedad Direccional** 🎯 @@ -63,13 +63,43 @@ | **C** | **🌐 MODO ROTOBALL - Toggle esfera 3D rotante** | | V | Alternar V-Sync ON/OFF | | H | **Toggle debug display (FPS, V-Sync, física, gravedad, modo)** | -| Num 1-5 | Selección directa de tema (1-Atardecer/2-Océano/3-Neón/4-Bosque/5-RGB) | -| T | Ciclar entre temas de colores | +| **Numpad Enter** | **Toggle página de temas (Página 1 ↔ Página 2)** | +| **Numpad 1-9, 0** | **Acceso directo a temas según página activa** (ver tablas abajo) | +| B | Ciclar entre TODOS los temas de colores (15 temas) - Adelante | +| Shift+B | Ciclar entre TODOS los temas de colores - Atrás | | 1-8 | Cambiar número de pelotas (1 a 100,000) | | ESPACIO | Impulsar pelotas hacia arriba | | G | Alternar gravedad ON/OFF (mantiene dirección) | | ESC | Salir | +### 🎨 Temas de Colores (15 Temas Disponibles - Sistema de 2 Páginas) + +**IMPORTANTE:** Usa **Numpad Enter** para cambiar entre Página 1 y Página 2 + +#### **Página 1** (Temas Estáticos + 1 Dinámico) +| Tecla | Tema | Tipo | Descripción | +|-------|------|------|-------------| +| Numpad 1 | ATARDECER | Estático | Naranjas, rojos, amarillos, rosas | +| Numpad 2 | OCÉANO | Estático | Azules, turquesas, blancos | +| Numpad 3 | NEÓN | Estático | Cian, magenta, verde lima, amarillo vibrante | +| Numpad 4 | BOSQUE | Estático | Verdes, marrones, amarillos otoño | +| Numpad 5 | RGB | Estático | Círculo cromático 24 colores (fondo blanco) | +| Numpad 6 | MONOCROMO | Estático | Fondo negro degradado, sprites blancos | +| Numpad 7 | LAVANDA | Estático | Degradado violeta-azul, pelotas amarillo dorado | +| Numpad 8 | CARMESÍ | Estático | Fondo negro-rojo, pelotas rojas uniformes | +| Numpad 9 | ESMERALDA | Estático | Fondo negro-verde, pelotas verdes uniformes | +| Numpad 0 | AMANECER | **Dinámico** | Noche → Alba → Día (loop 12s) | + +#### **Página 2** (Temas Dinámicos Animados) +| Tecla | Tema | Tipo | Descripción | +|-------|------|------|-------------| +| Numpad 1 | OLAS OCEÁNICAS | **Dinámico** | Azul oscuro ↔ Turquesa (loop 8s) | +| Numpad 2 | PULSO NEÓN | **Dinámico** | Negro ↔ Neón brillante (ping-pong 3s) | +| Numpad 3 | FUEGO | **Dinámico** | Brasas → Llamas → Inferno (loop 10s) | +| Numpad 4 | AURORA | **Dinámico** | Verde → Violeta → Cian (loop 14s) | +| Numpad 5 | VOLCÁN | **Dinámico** | Ceniza → Erupción → Lava (loop 12s) | +| Numpad 6-9, 0 | (sin asignar) | - | Sin función en Página 2 | + ### 🎯 Debug Display (Tecla H) Cuando está activado muestra: diff --git a/source/defines.h b/source/defines.h index 8fcf7af..f8208dc 100644 --- a/source/defines.h +++ b/source/defines.h @@ -84,7 +84,7 @@ enum class GravityDirection { RIGHT // → Gravedad hacia la derecha }; -// Enum para temas de colores (seleccionables con teclado numérico) +// Enum para temas de colores (seleccionables con teclado numérico y Shift+Numpad) // Todos los temas usan ahora sistema dinámico de keyframes enum class ColorTheme { SUNSET = 0, // Naranjas, rojos, amarillos, rosas (estático: 1 keyframe) @@ -94,9 +94,14 @@ enum class ColorTheme { RGB = 4, // RGB puros y subdivisiones matemáticas - fondo blanco (estático: 1 keyframe) MONOCHROME = 5, // Fondo negro degradado, sprites blancos monocromáticos (estático: 1 keyframe) LAVENDER = 6, // Degradado violeta-azul, pelotas amarillo dorado (estático: 1 keyframe) - SUNRISE = 7, // Amanecer: Noche → Alba → Día (animado: 4 keyframes, 12s ciclo) - OCEAN_WAVES = 8, // Olas oceánicas: Azul oscuro ↔ Turquesa (animado: 3 keyframes, 8s ciclo) - NEON_PULSE = 9 // Pulso neón: Negro ↔ Neón vibrante (animado: 3 keyframes, 3s ping-pong) + CRIMSON = 7, // Fondo negro-rojo, pelotas rojas uniformes (estático: 1 keyframe) + EMERALD = 8, // Fondo negro-verde, pelotas verdes uniformes (estático: 1 keyframe) + SUNRISE = 9, // Amanecer: Noche → Alba → Día (animado: 4 keyframes, 12s ciclo) + OCEAN_WAVES = 10, // Olas oceánicas: Azul oscuro ↔ Turquesa (animado: 3 keyframes, 8s ciclo) + NEON_PULSE = 11, // Pulso neón: Negro ↔ Neón vibrante (animado: 3 keyframes, 3s ping-pong) + FIRE = 12, // Fuego vivo: Brasas → Llamas → Inferno (animado: 4 keyframes, 10s ciclo) + AURORA = 13, // Aurora boreal: Verde → Violeta → Cian (animado: 4 keyframes, 14s ciclo) + VOLCANIC = 14 // Erupción volcánica: Ceniza → Erupción → Lava (animado: 4 keyframes, 12s ciclo) }; // Enum para tipo de figura 3D diff --git a/source/engine.cpp b/source/engine.cpp index 1fd69ac..6713b40 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -421,114 +421,171 @@ void Engine::handleEvents() { // Ciclar temas de color (movido de T a B) case SDLK_B: - // Ciclar al siguiente tema con transición suave - theme_manager_->cycleTheme(); + { + // Detectar si Shift está presionado + SDL_Keymod modstate = SDL_GetModState(); + if (modstate & SDL_KMOD_SHIFT) { + // Shift+B: Ciclar hacia atrás (tema anterior) + theme_manager_->cyclePrevTheme(); + } else { + // B solo: Ciclar hacia adelante (tema siguiente) + 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_ - static_cast(text_.length() * 8)) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } } break; // Temas de colores con teclado numérico (con transición suave) case SDLK_KP_1: - theme_manager_->switchToTheme(0); // SUNSET - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // Página 0: SUNSET (0), Página 1: OCEAN_WAVES (10) + { + 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } } break; case SDLK_KP_2: - theme_manager_->switchToTheme(1); // OCEAN - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // Página 0: OCEAN (1), Página 1: NEON_PULSE (11) + { + 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } } break; case SDLK_KP_3: - theme_manager_->switchToTheme(2); // NEON - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // Página 0: NEON (2), Página 1: FIRE (12) + { + 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } } break; case SDLK_KP_4: - theme_manager_->switchToTheme(3); // FOREST - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // Página 0: FOREST (3), Página 1: AURORA (13) + { + 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } } break; case SDLK_KP_5: - theme_manager_->switchToTheme(4); // RGB - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // Página 0: RGB (4), Página 1: VOLCANIC (14) + { + 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } } break; case SDLK_KP_6: - theme_manager_->switchToTheme(5); // MONOCHROME - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } } break; case SDLK_KP_7: - theme_manager_->switchToTheme(6); // LAVENDER - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } } break; - // Temas dinámicos (animados) - Solo Numpad 8/9/0 (teclas normales usadas para escenarios) case SDLK_KP_8: - theme_manager_->switchToTheme(7); // SUNRISE - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } } break; case SDLK_KP_9: - theme_manager_->switchToTheme(8); // OCEAN WAVES - if (current_app_mode_ == AppMode::MANUAL) { - text_ = theme_manager_->getCurrentThemeNameES(); - text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; - show_text_ = true; - text_init_time_ = SDL_GetTicks(); + // 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } } break; case SDLK_KP_0: - theme_manager_->switchToTheme(9); // NEON PULSE + // 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_ - static_cast(text_.length() * 8)) / 2; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } + } + break; + + // Toggle de página de temas (Numpad Enter) + 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_manager_->getCurrentThemeNameES(); + text_ = (theme_page_ == 0) ? "PAGINA 1" : "PAGINA 2"; text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; show_text_ = true; text_init_time_ = SDL_GetTicks(); @@ -1627,8 +1684,8 @@ void Engine::performDemoAction(bool is_lite) { // Cambiar tema (15%) accumulated_weight += DEMO_WEIGHT_THEME; if (random_value < accumulated_weight) { - // Elegir entre TODOS los 10 temas (estáticos + dinámicos) - int random_theme_index = rand() % 10; + // Elegir entre TODOS los 15 temas (9 estáticos + 6 dinámicos) + int random_theme_index = rand() % 15; theme_manager_->switchToTheme(random_theme_index); return; } @@ -1717,8 +1774,8 @@ void Engine::randomizeOnDemoStart(bool is_lite) { scenario_ = valid_scenarios[rand() % 5]; initBalls(scenario_); - // 2. Tema (elegir entre TODOS los 10 temas) - int random_theme_index = rand() % 10; + // 2. Tema (elegir entre TODOS los 15 temas) + int random_theme_index = rand() % 15; theme_manager_->switchToTheme(random_theme_index); // 3. Sprite diff --git a/source/engine.h b/source/engine.h index 0998c95..831378b 100644 --- a/source/engine.h +++ b/source/engine.h @@ -82,6 +82,7 @@ class Engine { // Sistema de temas (delegado a ThemeManager) std::unique_ptr theme_manager_; + int theme_page_ = 0; // Página actual de temas (0 o 1) para acceso por Numpad // Sistema de Figuras 3D (polimórfico) SimulationMode current_mode_ = SimulationMode::PHYSICS; diff --git a/source/theme_manager.cpp b/source/theme_manager.cpp index 534e8cc..7d7086c 100644 --- a/source/theme_manager.cpp +++ b/source/theme_manager.cpp @@ -9,10 +9,10 @@ void ThemeManager::initialize() { themes_.clear(); - themes_.reserve(10); // 7 estáticos + 3 dinámicos + themes_.reserve(15); // 9 estáticos + 6 dinámicos // ======================================== - // TEMAS ESTÁTICOS (índices 0-6) + // TEMAS ESTÁTICOS (índices 0-8) // ======================================== // 0: SUNSET (Atardecer) - Naranjas, rojos, amarillos, rosas @@ -128,11 +128,37 @@ void ThemeManager::initialize() { } )); + // 7: CRIMSON (Carmesí) - Fondo negro-rojo oscuro, pelotas rojas uniformes + themes_.push_back(std::make_unique( + "CRIMSON", + "CARMESI", + 255, 100, 100, // Color texto: rojo claro + 40.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo superior: rojo muy oscuro + 0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro puro + std::vector{ + {220, 20, 60}, {220, 20, 60}, {220, 20, 60}, {220, 20, 60}, + {220, 20, 60}, {220, 20, 60}, {220, 20, 60}, {220, 20, 60} + } + )); + + // 8: EMERALD (Esmeralda) - Fondo negro-verde oscuro, pelotas verdes uniformes + themes_.push_back(std::make_unique( + "EMERALD", + "ESMERALDA", + 100, 255, 100, // Color texto: verde claro + 0.0f / 255.0f, 40.0f / 255.0f, 0.0f / 255.0f, // Fondo superior: verde muy oscuro + 0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro puro + std::vector{ + {50, 205, 50}, {50, 205, 50}, {50, 205, 50}, {50, 205, 50}, + {50, 205, 50}, {50, 205, 50}, {50, 205, 50}, {50, 205, 50} + } + )); + // ======================================== - // TEMAS DINÁMICOS (índices 7-9) + // TEMAS DINÁMICOS (índices 9-14) // ======================================== - // 7: SUNRISE (Amanecer) - Noche → Alba → Día (loop) + // 9: SUNRISE (Amanecer) - Noche → Alba → Día (loop) themes_.push_back(std::make_unique( "SUNRISE", "AMANECER", @@ -173,7 +199,7 @@ void ThemeManager::initialize() { true // Loop = true )); - // 8: OCEAN WAVES (Olas Oceánicas) - Azul oscuro ↔ Turquesa (loop) + // 10: OCEAN WAVES (Olas Oceánicas) - Azul oscuro ↔ Turquesa (loop) themes_.push_back(std::make_unique( "OCEAN WAVES", "OLAS OCEANICAS", @@ -204,7 +230,7 @@ void ThemeManager::initialize() { true // Loop = true )); - // 9: NEON PULSE (Pulso Neón) - Negro → Neón brillante (rápido ping-pong) + // 11: NEON PULSE (Pulso Neón) - Negro → Neón brillante (rápido ping-pong) themes_.push_back(std::make_unique( "NEON PULSE", "PULSO NEON", @@ -234,6 +260,159 @@ void ThemeManager::initialize() { }, true // Loop = true )); + + // 12: FIRE (Fuego Vivo) - Brasas → Llamas → Inferno (loop) + themes_.push_back(std::make_unique( + "FIRE", + "FUEGO", + 255, 150, 80, // Color texto: naranja cálido + std::vector{ + // Keyframe 0: Brasas oscuras (estado inicial) + { + 60.0f / 255.0f, 20.0f / 255.0f, 10.0f / 255.0f, // Fondo superior: rojo muy oscuro + 20.0f / 255.0f, 10.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: casi negro + std::vector{ + {120, 40, 20}, {140, 35, 15}, {130, 38, 18}, {125, 42, 22}, + {135, 37, 16}, {128, 40, 20}, {132, 39, 19}, {138, 36, 17} + }, + 0.0f // Estado inicial + }, + // Keyframe 1: Llamas naranjas (transición) + { + 180.0f / 255.0f, 80.0f / 255.0f, 20.0f / 255.0f, // Fondo superior: naranja fuerte + 100.0f / 255.0f, 30.0f / 255.0f, 10.0f / 255.0f, // Fondo inferior: rojo oscuro + std::vector{ + {255, 140, 0}, {255, 120, 10}, {255, 160, 20}, {255, 130, 5}, + {255, 150, 15}, {255, 125, 8}, {255, 145, 12}, {255, 135, 18} + }, + 3.5f // 3.5 segundos para llegar + }, + // Keyframe 2: Inferno brillante (clímax) + { + 255.0f / 255.0f, 180.0f / 255.0f, 80.0f / 255.0f, // Fondo superior: amarillo-naranja brillante + 220.0f / 255.0f, 100.0f / 255.0f, 30.0f / 255.0f, // Fondo inferior: naranja intenso + std::vector{ + {255, 220, 100}, {255, 200, 80}, {255, 240, 120}, {255, 210, 90}, + {255, 230, 110}, {255, 205, 85}, {255, 225, 105}, {255, 215, 95} + }, + 3.0f // 3 segundos para llegar + }, + // Keyframe 3: Vuelta a llamas (antes de reiniciar loop) + { + 180.0f / 255.0f, 80.0f / 255.0f, 20.0f / 255.0f, // Fondo superior: naranja fuerte + 100.0f / 255.0f, 30.0f / 255.0f, 10.0f / 255.0f, // Fondo inferior: rojo oscuro + std::vector{ + {255, 140, 0}, {255, 120, 10}, {255, 160, 20}, {255, 130, 5}, + {255, 150, 15}, {255, 125, 8}, {255, 145, 12}, {255, 135, 18} + }, + 3.5f // 3.5 segundos para volver + } + // Loop = true hará transición automática de keyframe 3 → keyframe 0 + }, + true // Loop = true (ciclo completo: brasas→llamas→inferno→llamas→brasas...) + )); + + // 13: AURORA (Aurora Boreal) - Verde → Violeta → Cian (loop) + themes_.push_back(std::make_unique( + "AURORA", + "AURORA", + 150, 255, 200, // Color texto: verde claro + std::vector{ + // Keyframe 0: Verde aurora (estado inicial) + { + 30.0f / 255.0f, 80.0f / 255.0f, 60.0f / 255.0f, // Fondo superior: verde oscuro + 10.0f / 255.0f, 20.0f / 255.0f, 30.0f / 255.0f, // Fondo inferior: azul muy oscuro + std::vector{ + {100, 255, 180}, {80, 240, 160}, {120, 255, 200}, {90, 245, 170}, + {110, 255, 190}, {85, 242, 165}, {105, 252, 185}, {95, 248, 175} + }, + 0.0f // Estado inicial + }, + // Keyframe 1: Violeta aurora (transición) + { + 120.0f / 255.0f, 60.0f / 255.0f, 180.0f / 255.0f, // Fondo superior: violeta + 40.0f / 255.0f, 20.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior: violeta oscuro + std::vector{ + {200, 100, 255}, {180, 80, 240}, {220, 120, 255}, {190, 90, 245}, + {210, 110, 255}, {185, 85, 242}, {205, 105, 252}, {195, 95, 248} + }, + 5.0f // 5 segundos para llegar (transición lenta) + }, + // Keyframe 2: Cian aurora (clímax) + { + 60.0f / 255.0f, 180.0f / 255.0f, 220.0f / 255.0f, // Fondo superior: cian brillante + 20.0f / 255.0f, 80.0f / 255.0f, 120.0f / 255.0f, // Fondo inferior: azul oscuro + std::vector{ + {100, 220, 255}, {80, 200, 240}, {120, 240, 255}, {90, 210, 245}, + {110, 230, 255}, {85, 205, 242}, {105, 225, 252}, {95, 215, 248} + }, + 4.5f // 4.5 segundos para llegar + }, + // Keyframe 3: Vuelta a violeta (antes de reiniciar) + { + 120.0f / 255.0f, 60.0f / 255.0f, 180.0f / 255.0f, // Fondo superior: violeta + 40.0f / 255.0f, 20.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior: violeta oscuro + std::vector{ + {200, 100, 255}, {180, 80, 240}, {220, 120, 255}, {190, 90, 245}, + {210, 110, 255}, {185, 85, 242}, {205, 105, 252}, {195, 95, 248} + }, + 4.5f // 4.5 segundos para volver + } + // Loop = true hará transición automática de keyframe 3 → keyframe 0 + }, + true // Loop = true (ciclo: verde→violeta→cian→violeta→verde...) + )); + + // 14: VOLCANIC (Erupción Volcánica) - Ceniza → Erupción → Lava (loop) + themes_.push_back(std::make_unique( + "VOLCANIC", + "VOLCAN", + 200, 120, 80, // Color texto: naranja apagado + std::vector{ + // Keyframe 0: Ceniza oscura (pre-erupción) + { + 40.0f / 255.0f, 40.0f / 255.0f, 45.0f / 255.0f, // Fondo superior: gris oscuro + 20.0f / 255.0f, 15.0f / 255.0f, 15.0f / 255.0f, // Fondo inferior: casi negro + std::vector{ + {80, 80, 90}, {75, 75, 85}, {85, 85, 95}, {78, 78, 88}, + {82, 82, 92}, {76, 76, 86}, {84, 84, 94}, {79, 79, 89} + }, + 0.0f // Estado inicial + }, + // Keyframe 1: Erupción naranja-roja (explosión) + { + 180.0f / 255.0f, 60.0f / 255.0f, 30.0f / 255.0f, // Fondo superior: naranja-rojo + 80.0f / 255.0f, 20.0f / 255.0f, 10.0f / 255.0f, // Fondo inferior: rojo oscuro + std::vector{ + {255, 80, 40}, {255, 100, 50}, {255, 70, 35}, {255, 90, 45}, + {255, 75, 38}, {255, 95, 48}, {255, 85, 42}, {255, 78, 40} + }, + 3.0f // 3 segundos para erupción (rápido) + }, + // Keyframe 2: Lava brillante (clímax) + { + 220.0f / 255.0f, 120.0f / 255.0f, 40.0f / 255.0f, // Fondo superior: naranja brillante + 180.0f / 255.0f, 60.0f / 255.0f, 20.0f / 255.0f, // Fondo inferior: naranja-rojo + std::vector{ + {255, 180, 80}, {255, 200, 100}, {255, 170, 70}, {255, 190, 90}, + {255, 175, 75}, {255, 195, 95}, {255, 185, 85}, {255, 178, 78} + }, + 3.5f // 3.5 segundos para lava máxima + }, + // Keyframe 3: Enfriamiento (vuelta a ceniza gradual) + { + 100.0f / 255.0f, 80.0f / 255.0f, 70.0f / 255.0f, // Fondo superior: gris-naranja + 50.0f / 255.0f, 40.0f / 255.0f, 35.0f / 255.0f, // Fondo inferior: gris oscuro + std::vector{ + {150, 120, 100}, {140, 110, 90}, {160, 130, 110}, {145, 115, 95}, + {155, 125, 105}, {142, 112, 92}, {158, 128, 108}, {148, 118, 98} + }, + 5.5f // 5.5 segundos para enfriamiento (lento) + } + // Loop = true hará transición automática de keyframe 3 → keyframe 0 + }, + true // Loop = true (ciclo: ceniza→erupción→lava→enfriamiento→ceniza...) + )); } // ============================================================================ @@ -296,6 +475,17 @@ void ThemeManager::cycleTheme() { switchToTheme(next_theme_index); } +void ThemeManager::cyclePrevTheme() { + // Calcular tema anterior con wraparound (14→13→...→1→0→14) + int prev_theme_index = (current_theme_index_ - 1); + if (prev_theme_index < 0) { + prev_theme_index = static_cast(themes_.size()) - 1; // Wrap to último tema + } + + // Usar switchToTheme() para obtener transición LERP automáticamente + switchToTheme(prev_theme_index); +} + void ThemeManager::pauseDynamic() { // Solo funciona si el tema actual es dinámico if (themes_[current_theme_index_]->needsUpdate()) { diff --git a/source/theme_manager.h b/source/theme_manager.h index be34a4a..82748bf 100644 --- a/source/theme_manager.h +++ b/source/theme_manager.h @@ -12,9 +12,9 @@ * ThemeManager: Gestiona el sistema de temas visuales (unificado, estáticos y dinámicos) * * PHASE 2 - Sistema Unificado: - * - Vector unificado de 10 temas (7 estáticos + 3 dinámicos) - * - Índices 0-9 mapeados a ColorTheme enum (SUNSET=0, OCEAN=1, ..., NEON_PULSE=9) - * - API simplificada: switchToTheme(0-9) para cualquier tema + * - Vector unificado de 15 temas (9 estáticos + 6 dinámicos) + * - Índices 0-14 mapeados a ColorTheme enum (SUNSET=0, OCEAN=1, ..., VOLCANIC=14) + * - API simplificada: switchToTheme(0-14) para cualquier tema * - Sin lógica dual (eliminados if(dynamic_theme_active_) scattered) * * PHASE 3 - LERP Universal: @@ -24,7 +24,7 @@ * - Duración configurable (THEME_TRANSITION_DURATION = 0.5s por defecto) * * Responsabilidades: - * - Mantener 10 temas polimórficos (StaticTheme / DynamicTheme) + * - Mantener 15 temas polimórficos (StaticTheme / DynamicTheme) * - Actualizar animación de tema activo si es dinámico * - Gestionar transiciones LERP suaves entre temas * - Proporcionar colores interpolados para renderizado (con LERP si hay transición activa) @@ -32,7 +32,7 @@ * Uso desde Engine: * - initialize() al inicio * - update(delta_time) cada frame (actualiza tema activo + transición LERP) - * - switchToTheme(0-9) para cambiar tema con transición suave (Numpad 1-0, Tecla B) + * - switchToTheme(0-14) para cambiar tema con transición suave (Numpad/Shift+Numpad, Tecla B) * - getInterpolatedColor(index) en render loop (retorna color con LERP si transitioning) */ class ThemeManager { @@ -42,12 +42,13 @@ class ThemeManager { ~ThemeManager() = default; // Inicialización - void initialize(); // Inicializa 10 temas unificados (7 estáticos + 3 dinámicos) + void initialize(); // Inicializa 15 temas unificados (9 estáticos + 6 dinámicos) // Interfaz unificada (PHASE 2 + PHASE 3) - void switchToTheme(int theme_index); // Cambia a tema 0-9 con transición LERP suave (PHASE 3) + void switchToTheme(int theme_index); // Cambia a tema 0-14 con transición LERP suave (PHASE 3) void update(float delta_time); // Actualiza transición LERP + tema activo si es dinámico - void cycleTheme(); // Cicla al siguiente tema (0→1→...→9→0) - Tecla B + void cycleTheme(); // Cicla al siguiente tema (0→1→...→14→0) - Tecla B + void cyclePrevTheme(); // Cicla al tema anterior (14→...→1→0) - Shift+B void pauseDynamic(); // Toggle pausa de animación (Shift+D, solo dinámicos) // Queries de colores (usado en rendering) @@ -73,12 +74,12 @@ class ThemeManager { // DATOS UNIFICADOS (PHASE 2) // ======================================== - // Vector unificado de 10 temas (índices 0-9) - // 0-6: Estáticos (SUNSET, OCEAN, NEON, FOREST, RGB, MONOCHROME, LAVENDER) - // 7-9: Dinámicos (SUNRISE, OCEAN_WAVES, NEON_PULSE) + // Vector unificado de 15 temas (índices 0-14) + // 0-8: Estáticos (SUNSET, OCEAN, NEON, FOREST, RGB, MONOCHROME, LAVENDER, CRIMSON, EMERALD) + // 9-14: Dinámicos (SUNRISE, OCEAN_WAVES, NEON_PULSE, FIRE, AURORA, VOLCANIC) std::vector> themes_; - // Índice de tema activo actual (0-9) + // Índice de tema activo actual (0-14) int current_theme_index_ = 0; // Por defecto SUNSET // ======================================== @@ -102,8 +103,8 @@ class ThemeManager { // ======================================== // Inicialización - void initializeStaticThemes(); // Crea 7 temas estáticos (índices 0-6) - void initializeDynamicThemes(); // Crea 3 temas dinámicos (índices 7-9) + void initializeStaticThemes(); // Crea 9 temas estáticos (índices 0-8) + void initializeDynamicThemes(); // Crea 6 temas dinámicos (índices 9-14) // Sistema de transición LERP (PHASE 3) std::unique_ptr captureCurrentSnapshot() const; // Captura snapshot del tema actual