Refactor fase 1: Extraer InputHandler de Engine
Aplicación del Principio de Responsabilidad Única (SRP) - Fase 1/6
## Cambios realizados
### Nuevos archivos
- source/input/input_handler.h - Declaración clase InputHandler
- source/input/input_handler.cpp - Procesamiento eventos SDL (~180 líneas)
- REFACTOR_PLAN.md - Documento de seguimiento del refactor
### Modificaciones en Engine
- **engine.h**: Agregados 24 métodos públicos para InputHandler
- **engine.cpp**:
- Eliminado handleEvents() (420 líneas)
- Implementados métodos públicos wrapper (~180 líneas)
- Renombrados métodos internos con sufijo `Internal`:
* toggleShapeMode → toggleShapeModeInternal
* activateShape → activateShapeInternal
* switchTexture → switchTextureInternal
- Bucle run() simplificado (5 → 12 líneas)
### Actualización build
- CMakeLists.txt: Agregado source/input/*.cpp a archivos fuente
## Impacto
- **Líneas extraídas**: ~430 del switch gigante de handleEvents()
- **Compilación**: ✅ Exitosa sin errores
- **Funcionalidad**: ✅ 100% preservada
## Beneficios
- ✅ Engine desacoplado de eventos SDL
- ✅ InputHandler stateless (fácilmente testeable)
- ✅ Clara separación detección input vs ejecución lógica
- ✅ Preparado para testing unitario de inputs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -213,6 +213,9 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
||||
|
||||
srand(static_cast<unsigned>(time(nullptr)));
|
||||
|
||||
// Inicializar InputHandler (sin estado)
|
||||
input_handler_ = std::make_unique<InputHandler>();
|
||||
|
||||
// Inicializar ThemeManager PRIMERO (requerido por Notifier)
|
||||
theme_manager_ = std::make_unique<ThemeManager>();
|
||||
theme_manager_->initialize();
|
||||
@@ -230,8 +233,13 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
||||
void Engine::run() {
|
||||
while (!should_exit_) {
|
||||
calculateDeltaTime();
|
||||
|
||||
// Procesar eventos de entrada (teclado, ratón, ventana)
|
||||
if (input_handler_->processEvents(*this)) {
|
||||
should_exit_ = true;
|
||||
}
|
||||
|
||||
update();
|
||||
handleEvents();
|
||||
render();
|
||||
}
|
||||
}
|
||||
@@ -311,426 +319,185 @@ void Engine::update() {
|
||||
theme_manager_->update(delta_time_);
|
||||
}
|
||||
|
||||
void Engine::handleEvents() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
// Procesar eventos de ratón (auto-ocultar cursor)
|
||||
Mouse::handleEvent(event);
|
||||
// === IMPLEMENTACIÓN DE MÉTODOS PÚBLICOS PARA INPUT HANDLER ===
|
||||
|
||||
// Salir del bucle si se detecta una petición de cierre
|
||||
if (event.type == SDL_EVENT_QUIT) {
|
||||
should_exit_ = true;
|
||||
break;
|
||||
// Gravedad y física
|
||||
void Engine::handleGravityToggle() {
|
||||
// Si estamos en modo figura, salir a modo física SIN GRAVEDAD
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeModeInternal(false); // Desactivar figura sin forzar gravedad ON
|
||||
showNotificationForAction("Gravedad 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 ? "Gravedad On" : "Gravedad Off");
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::handleGravityDirectionChange(GravityDirection direction, const char* notification_text) {
|
||||
// Si estamos en modo figura, salir a modo física CON gravedad
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeModeInternal(); // Desactivar figura (activa gravedad automáticamente)
|
||||
} else {
|
||||
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||
}
|
||||
changeGravityDirection(direction);
|
||||
showNotificationForAction(notification_text);
|
||||
}
|
||||
|
||||
// Display y depuración
|
||||
void Engine::toggleDebug() {
|
||||
show_debug_ = !show_debug_;
|
||||
}
|
||||
|
||||
// Figuras 3D
|
||||
void Engine::toggleShapeMode() {
|
||||
toggleShapeModeInternal();
|
||||
// Mostrar notificación según el modo actual después del toggle
|
||||
if (current_mode_ == SimulationMode::PHYSICS) {
|
||||
showNotificationForAction("Modo Física");
|
||||
} else {
|
||||
// Mostrar nombre de la figura actual (orden debe coincidir con enum ShapeType)
|
||||
// Índices: 0=NONE, 1=SPHERE, 2=CUBE, 3=HELIX, 4=TORUS, 5=LISSAJOUS, 6=CYLINDER, 7=ICOSAHEDRON, 8=ATOM, 9=PNG_SHAPE
|
||||
const char* shape_names[] = {"Ninguna", "Esfera", "Cubo", "Hélice", "Toroide", "Lissajous", "Cilindro", "Icosaedro", "Átomo", "Forma PNG"};
|
||||
showNotificationForAction(shape_names[static_cast<int>(current_shape_type_)]);
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::activateShape(ShapeType type, const char* notification_text) {
|
||||
activateShapeInternal(type);
|
||||
showNotificationForAction(notification_text);
|
||||
}
|
||||
|
||||
void Engine::handleShapeScaleChange(bool increase) {
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
if (increase) {
|
||||
shape_scale_factor_ += SHAPE_SCALE_STEP;
|
||||
} else {
|
||||
shape_scale_factor_ -= SHAPE_SCALE_STEP;
|
||||
}
|
||||
clampShapeScale();
|
||||
showNotificationForAction("Escala " + std::to_string(static_cast<int>(shape_scale_factor_ * 100.0f + 0.5f)) + "%");
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar eventos de teclado
|
||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 0) {
|
||||
switch (event.key.key) {
|
||||
case SDLK_ESCAPE:
|
||||
should_exit_ = true;
|
||||
break;
|
||||
void Engine::resetShapeScale() {
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
shape_scale_factor_ = SHAPE_SCALE_DEFAULT;
|
||||
showNotificationForAction("Escala 100%");
|
||||
}
|
||||
}
|
||||
|
||||
case SDLK_SPACE:
|
||||
pushBallsAwayFromGravity();
|
||||
break;
|
||||
void Engine::toggleDepthZoom() {
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
depth_zoom_enabled_ = !depth_zoom_enabled_;
|
||||
showNotificationForAction(depth_zoom_enabled_ ? "Profundidad On" : "Profundidad Off");
|
||||
}
|
||||
}
|
||||
|
||||
case SDLK_G:
|
||||
// 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("Gravedad 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 ? "Gravedad On" : "Gravedad Off");
|
||||
}
|
||||
break;
|
||||
// Temas de colores
|
||||
void Engine::cycleTheme(bool forward) {
|
||||
if (forward) {
|
||||
theme_manager_->cycleTheme();
|
||||
} else {
|
||||
theme_manager_->cyclePrevTheme();
|
||||
}
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
|
||||
// Controles de dirección de gravedad con teclas de cursor
|
||||
case SDLK_UP:
|
||||
// Si estamos en modo figura, salir a modo física CON gravedad
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeMode(); // Desactivar figura (activa gravedad automáticamente)
|
||||
} else {
|
||||
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||
}
|
||||
changeGravityDirection(GravityDirection::UP);
|
||||
showNotificationForAction("Gravedad Arriba");
|
||||
break;
|
||||
void Engine::switchThemeByNumpad(int numpad_key) {
|
||||
// Mapear tecla numpad a índice de tema según página actual
|
||||
int theme_index = -1;
|
||||
|
||||
case SDLK_DOWN:
|
||||
// Si estamos en modo figura, salir a modo física CON gravedad
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeMode(); // Desactivar figura (activa gravedad automáticamente)
|
||||
} else {
|
||||
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||
}
|
||||
changeGravityDirection(GravityDirection::DOWN);
|
||||
showNotificationForAction("Gravedad Abajo");
|
||||
break;
|
||||
|
||||
case SDLK_LEFT:
|
||||
// Si estamos en modo figura, salir a modo física CON gravedad
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeMode(); // Desactivar figura (activa gravedad automáticamente)
|
||||
} else {
|
||||
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||
}
|
||||
changeGravityDirection(GravityDirection::LEFT);
|
||||
showNotificationForAction("Gravedad Izquierda");
|
||||
break;
|
||||
|
||||
case SDLK_RIGHT:
|
||||
// Si estamos en modo figura, salir a modo física CON gravedad
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeMode(); // Desactivar figura (activa gravedad automáticamente)
|
||||
} else {
|
||||
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||
}
|
||||
changeGravityDirection(GravityDirection::RIGHT);
|
||||
showNotificationForAction("Gravedad Derecha");
|
||||
break;
|
||||
|
||||
case SDLK_V:
|
||||
toggleVSync();
|
||||
break;
|
||||
|
||||
case SDLK_H:
|
||||
show_debug_ = !show_debug_;
|
||||
break;
|
||||
|
||||
// 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("Modo Física");
|
||||
} else {
|
||||
// Mostrar nombre de la figura actual (orden debe coincidir con enum ShapeType)
|
||||
// Índices: 0=NONE, 1=SPHERE, 2=CUBE, 3=HELIX, 4=TORUS, 5=LISSAJOUS, 6=CYLINDER, 7=ICOSAHEDRON, 8=ATOM, 9=PNG_SHAPE
|
||||
const char* shape_names[] = {"Ninguna", "Esfera", "Cubo", "Hélice", "Toroide", "Lissajous", "Cilindro", "Icosaedro", "Átomo", "Forma PNG"};
|
||||
showNotificationForAction(shape_names[static_cast<int>(current_shape_type_)]);
|
||||
}
|
||||
break;
|
||||
|
||||
// Selección directa de figuras 3D
|
||||
case SDLK_Q:
|
||||
activateShape(ShapeType::SPHERE);
|
||||
showNotificationForAction("Esfera");
|
||||
break;
|
||||
|
||||
case SDLK_W:
|
||||
activateShape(ShapeType::LISSAJOUS);
|
||||
showNotificationForAction("Lissajous");
|
||||
break;
|
||||
|
||||
case SDLK_E:
|
||||
activateShape(ShapeType::HELIX);
|
||||
showNotificationForAction("Hélice");
|
||||
break;
|
||||
|
||||
case SDLK_R:
|
||||
activateShape(ShapeType::TORUS);
|
||||
showNotificationForAction("Toroide");
|
||||
break;
|
||||
|
||||
case SDLK_T:
|
||||
activateShape(ShapeType::CUBE);
|
||||
showNotificationForAction("Cubo");
|
||||
break;
|
||||
|
||||
case SDLK_Y:
|
||||
activateShape(ShapeType::CYLINDER);
|
||||
showNotificationForAction("Cilindro");
|
||||
break;
|
||||
|
||||
case SDLK_U:
|
||||
activateShape(ShapeType::ICOSAHEDRON);
|
||||
showNotificationForAction("Icosaedro");
|
||||
break;
|
||||
|
||||
case SDLK_I:
|
||||
activateShape(ShapeType::ATOM);
|
||||
showNotificationForAction("Átomo");
|
||||
break;
|
||||
|
||||
case SDLK_O:
|
||||
activateShape(ShapeType::PNG_SHAPE);
|
||||
showNotificationForAction("Forma PNG");
|
||||
break;
|
||||
|
||||
// Ciclar temas de color (movido de T a B)
|
||||
case SDLK_B:
|
||||
{
|
||||
// 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 notificación con el nombre del tema
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
break;
|
||||
|
||||
// Temas de colores con teclado numérico (con transición suave)
|
||||
case SDLK_KP_1:
|
||||
// Página 0: SUNSET (0), Página 1: OCEAN_WAVES (10)
|
||||
{
|
||||
int theme_index = (theme_page_ == 0) ? 0 : 10;
|
||||
theme_manager_->switchToTheme(theme_index);
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_2:
|
||||
// Página 0: OCEAN (1), Página 1: NEON_PULSE (11)
|
||||
{
|
||||
int theme_index = (theme_page_ == 0) ? 1 : 11;
|
||||
theme_manager_->switchToTheme(theme_index);
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_3:
|
||||
// Página 0: NEON (2), Página 1: FIRE (12)
|
||||
{
|
||||
int theme_index = (theme_page_ == 0) ? 2 : 12;
|
||||
theme_manager_->switchToTheme(theme_index);
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_4:
|
||||
// Página 0: FOREST (3), Página 1: AURORA (13)
|
||||
{
|
||||
int theme_index = (theme_page_ == 0) ? 3 : 13;
|
||||
theme_manager_->switchToTheme(theme_index);
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_5:
|
||||
// Página 0: RGB (4), Página 1: VOLCANIC (14)
|
||||
{
|
||||
int theme_index = (theme_page_ == 0) ? 4 : 14;
|
||||
theme_manager_->switchToTheme(theme_index);
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_6:
|
||||
// Solo página 0: MONOCHROME (5)
|
||||
if (theme_page_ == 0) {
|
||||
theme_manager_->switchToTheme(5); // MONOCHROME
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_7:
|
||||
// Solo página 0: LAVENDER (6)
|
||||
if (theme_page_ == 0) {
|
||||
theme_manager_->switchToTheme(6); // LAVENDER
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_8:
|
||||
// Solo página 0: CRIMSON (7)
|
||||
if (theme_page_ == 0) {
|
||||
theme_manager_->switchToTheme(7); // CRIMSON
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_9:
|
||||
// Solo página 0: EMERALD (8)
|
||||
if (theme_page_ == 0) {
|
||||
theme_manager_->switchToTheme(8); // EMERALD
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_0:
|
||||
// Solo página 0: SUNRISE (9)
|
||||
if (theme_page_ == 0) {
|
||||
theme_manager_->switchToTheme(9); // SUNRISE
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
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;
|
||||
showNotificationForAction((theme_page_ == 0) ? "Página 1" : "Página 2");
|
||||
break;
|
||||
|
||||
// Cambio de sprite/textura dinámico
|
||||
case SDLK_N:
|
||||
switchTexture();
|
||||
break;
|
||||
|
||||
// Control de escala de figura (solo en modo SHAPE)
|
||||
case SDLK_KP_PLUS:
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
shape_scale_factor_ += SHAPE_SCALE_STEP;
|
||||
clampShapeScale();
|
||||
showNotificationForAction("Escala " + std::to_string(static_cast<int>(shape_scale_factor_ * 100.0f + 0.5f)) + "%");
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_MINUS:
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
shape_scale_factor_ -= SHAPE_SCALE_STEP;
|
||||
clampShapeScale();
|
||||
showNotificationForAction("Escala " + std::to_string(static_cast<int>(shape_scale_factor_ * 100.0f + 0.5f)) + "%");
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_MULTIPLY:
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
shape_scale_factor_ = SHAPE_SCALE_DEFAULT;
|
||||
showNotificationForAction("Escala 100%");
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_KP_DIVIDE:
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
depth_zoom_enabled_ = !depth_zoom_enabled_;
|
||||
showNotificationForAction(depth_zoom_enabled_ ? "Profundidad On" : "Profundidad 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)
|
||||
case SDLK_F1:
|
||||
if (!fullscreen_enabled_ && !real_fullscreen_enabled_) {
|
||||
zoomOut();
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_F2:
|
||||
if (!fullscreen_enabled_ && !real_fullscreen_enabled_) {
|
||||
zoomIn();
|
||||
}
|
||||
break;
|
||||
|
||||
// Control de pantalla completa
|
||||
case SDLK_F3:
|
||||
toggleFullscreen();
|
||||
break;
|
||||
|
||||
// Modo real fullscreen (cambia resolución interna)
|
||||
case SDLK_F4:
|
||||
toggleRealFullscreen();
|
||||
break;
|
||||
|
||||
// Toggle escalado entero/estirado (solo en fullscreen F3)
|
||||
case SDLK_F5:
|
||||
toggleIntegerScaling();
|
||||
break;
|
||||
|
||||
// Toggle Modo DEMO COMPLETO (auto-play) o Pausar tema dinámico (Shift+D)
|
||||
case SDLK_D:
|
||||
// Shift+D = Pausar tema dinámico
|
||||
if (event.key.mod & SDL_KMOD_SHIFT) {
|
||||
theme_manager_->pauseDynamic();
|
||||
} else {
|
||||
// D sin Shift = Toggle DEMO ↔ SANDBOX
|
||||
if (current_app_mode_ == AppMode::DEMO) {
|
||||
// Ya estamos en DEMO → volver a SANDBOX
|
||||
setState(AppMode::SANDBOX);
|
||||
showNotificationForAction("MODO SANDBOX");
|
||||
} else {
|
||||
// Estamos en otro modo → ir a DEMO
|
||||
setState(AppMode::DEMO);
|
||||
randomizeOnDemoStart(false);
|
||||
showNotificationForAction("MODO DEMO");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Toggle Modo DEMO LITE (solo física/figuras)
|
||||
case SDLK_L:
|
||||
if (current_app_mode_ == AppMode::DEMO_LITE) {
|
||||
// Ya estamos en DEMO_LITE → volver a SANDBOX
|
||||
setState(AppMode::SANDBOX);
|
||||
showNotificationForAction("MODO SANDBOX");
|
||||
} else {
|
||||
// Estamos en otro modo → ir a DEMO_LITE
|
||||
setState(AppMode::DEMO_LITE);
|
||||
randomizeOnDemoStart(true);
|
||||
showNotificationForAction("MODO DEMO LITE");
|
||||
}
|
||||
break;
|
||||
|
||||
// Toggle Modo LOGO (easter egg - marca de agua)
|
||||
case SDLK_K:
|
||||
if (current_app_mode_ == AppMode::LOGO) {
|
||||
// Ya estamos en LOGO → volver a SANDBOX
|
||||
exitLogoMode(false);
|
||||
showNotificationForAction("MODO SANDBOX");
|
||||
} else {
|
||||
// Estamos en otro modo → ir a LOGO
|
||||
enterLogoMode(false);
|
||||
showNotificationForAction("MODO LOGO");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (theme_page_ == 0) {
|
||||
// Página 0: Temas 0-9 (estáticos + SUNRISE)
|
||||
if (numpad_key >= 0 && numpad_key <= 9) {
|
||||
theme_index = (numpad_key == 0) ? 9 : (numpad_key - 1);
|
||||
}
|
||||
} else {
|
||||
// Página 1: Temas 10-14 (dinámicos)
|
||||
if (numpad_key >= 1 && numpad_key <= 5) {
|
||||
theme_index = 9 + numpad_key;
|
||||
}
|
||||
}
|
||||
|
||||
if (theme_index != -1) {
|
||||
theme_manager_->switchToTheme(theme_index);
|
||||
showNotificationForAction(theme_manager_->getCurrentThemeNameES());
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::toggleThemePage() {
|
||||
theme_page_ = (theme_page_ == 0) ? 1 : 0;
|
||||
showNotificationForAction((theme_page_ == 0) ? "Página 1" : "Página 2");
|
||||
}
|
||||
|
||||
void Engine::pauseDynamicTheme() {
|
||||
theme_manager_->pauseDynamic();
|
||||
}
|
||||
|
||||
// Sprites/Texturas
|
||||
void Engine::switchTexture() {
|
||||
switchTextureInternal(true); // Mostrar notificación en modo manual
|
||||
}
|
||||
|
||||
// Escenarios (número de pelotas)
|
||||
void Engine::changeScenario(int scenario_id, const char* notification_text) {
|
||||
scenario_ = scenario_id;
|
||||
initBalls(scenario_);
|
||||
showNotificationForAction(notification_text);
|
||||
}
|
||||
|
||||
// Zoom y fullscreen
|
||||
void Engine::handleZoomIn() {
|
||||
if (!fullscreen_enabled_ && !real_fullscreen_enabled_) {
|
||||
zoomIn();
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::handleZoomOut() {
|
||||
if (!fullscreen_enabled_ && !real_fullscreen_enabled_) {
|
||||
zoomOut();
|
||||
}
|
||||
}
|
||||
|
||||
// Modos de aplicación (DEMO/LOGO)
|
||||
void Engine::toggleDemoMode() {
|
||||
if (current_app_mode_ == AppMode::DEMO) {
|
||||
// Ya estamos en DEMO → volver a SANDBOX
|
||||
setState(AppMode::SANDBOX);
|
||||
showNotificationForAction("MODO SANDBOX");
|
||||
} else {
|
||||
// Estamos en otro modo → ir a DEMO
|
||||
setState(AppMode::DEMO);
|
||||
randomizeOnDemoStart(false);
|
||||
showNotificationForAction("MODO DEMO");
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::toggleDemoLiteMode() {
|
||||
if (current_app_mode_ == AppMode::DEMO_LITE) {
|
||||
// Ya estamos en DEMO_LITE → volver a SANDBOX
|
||||
setState(AppMode::SANDBOX);
|
||||
showNotificationForAction("MODO SANDBOX");
|
||||
} else {
|
||||
// Estamos en otro modo → ir a DEMO_LITE
|
||||
setState(AppMode::DEMO_LITE);
|
||||
randomizeOnDemoStart(true);
|
||||
showNotificationForAction("MODO DEMO LITE");
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::toggleLogoMode() {
|
||||
if (current_app_mode_ == AppMode::LOGO) {
|
||||
// Ya estamos en LOGO → volver a SANDBOX
|
||||
exitLogoMode(false);
|
||||
showNotificationForAction("MODO SANDBOX");
|
||||
} else {
|
||||
// Estamos en otro modo → ir a LOGO
|
||||
enterLogoMode(false);
|
||||
showNotificationForAction("MODO LOGO");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1556,9 +1323,9 @@ void Engine::updateDemoMode() {
|
||||
// Ya estábamos esperando flips, y se disparó el trigger
|
||||
// → Hacer el cambio SHAPE → PHYSICS ahora (durante el flip)
|
||||
if (action < 50) {
|
||||
toggleShapeMode(true); // Con gravedad ON
|
||||
toggleShapeModeInternal(true); // Con gravedad ON
|
||||
} else {
|
||||
toggleShapeMode(false); // Con gravedad OFF
|
||||
toggleShapeModeInternal(false); // Con gravedad OFF
|
||||
}
|
||||
|
||||
// Resetear variables de espera de flips
|
||||
@@ -1590,10 +1357,10 @@ void Engine::updateDemoMode() {
|
||||
// CAMINO A (50%): Cambio inmediato
|
||||
if (action < 50) {
|
||||
// 50%: SHAPE → PHYSICS con gravedad ON (caída dramática)
|
||||
toggleShapeMode(true);
|
||||
toggleShapeModeInternal(true);
|
||||
} else {
|
||||
// 50%: SHAPE → PHYSICS con gravedad OFF (dar vueltas sin caer)
|
||||
toggleShapeMode(false);
|
||||
toggleShapeModeInternal(false);
|
||||
}
|
||||
|
||||
// Resetear variables de espera de flips al cambiar a PHYSICS
|
||||
@@ -1609,7 +1376,7 @@ void Engine::updateDemoMode() {
|
||||
// Logo animado (PHYSICS) → 3 opciones posibles
|
||||
if (action < 60) {
|
||||
// 60%: PHYSICS → SHAPE (reconstruir logo y ver rotaciones)
|
||||
toggleShapeMode(false);
|
||||
toggleShapeModeInternal(false);
|
||||
|
||||
// Resetear variables de espera de flips al volver a SHAPE
|
||||
logo_waiting_for_flip_ = false;
|
||||
@@ -1711,14 +1478,14 @@ void Engine::performDemoAction(bool is_lite) {
|
||||
if (random_value < accumulated_weight) {
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
int shape_index = rand() % 8;
|
||||
activateShape(shapes[shape_index]);
|
||||
activateShapeInternal(shapes[shape_index]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle física ↔ figura (20%)
|
||||
accumulated_weight += DEMO_LITE_WEIGHT_TOGGLE_PHYSICS;
|
||||
if (random_value < accumulated_weight) {
|
||||
toggleShapeMode(false); // NO forzar gravedad al salir
|
||||
toggleShapeModeInternal(false); // NO forzar gravedad al salir
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1754,14 +1521,14 @@ void Engine::performDemoAction(bool is_lite) {
|
||||
if (random_value < accumulated_weight) {
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
int shape_index = rand() % 8;
|
||||
activateShape(shapes[shape_index]);
|
||||
activateShapeInternal(shapes[shape_index]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle física ↔ figura (12%)
|
||||
accumulated_weight += DEMO_WEIGHT_TOGGLE_PHYSICS;
|
||||
if (random_value < accumulated_weight) {
|
||||
toggleShapeMode(false); // NO forzar gravedad al salir
|
||||
toggleShapeModeInternal(false); // NO forzar gravedad al salir
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1830,7 +1597,7 @@ void Engine::performDemoAction(bool is_lite) {
|
||||
// Cambiar sprite (2%)
|
||||
accumulated_weight += DEMO_WEIGHT_SPRITE;
|
||||
if (random_value < accumulated_weight) {
|
||||
switchTexture(false); // Suprimir notificación en modo automático
|
||||
switchTextureInternal(false); // Suprimir notificación en modo automático
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1844,7 +1611,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX,
|
||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
activateShape(shapes[rand() % 8]);
|
||||
activateShapeInternal(shapes[rand() % 8]);
|
||||
}
|
||||
|
||||
if (is_lite) {
|
||||
@@ -1853,12 +1620,12 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
||||
if (rand() % 2 == 0) {
|
||||
// Modo física
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeMode(false); // Salir a física sin forzar gravedad
|
||||
toggleShapeModeInternal(false); // Salir a física sin forzar gravedad
|
||||
}
|
||||
} else {
|
||||
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
activateShape(shapes[rand() % 8]);
|
||||
activateShapeInternal(shapes[rand() % 8]);
|
||||
}
|
||||
|
||||
// Randomizar gravedad: dirección + ON/OFF
|
||||
@@ -1882,19 +1649,19 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
||||
|
||||
// 3. Sprite
|
||||
if (rand() % 2 == 0) {
|
||||
switchTexture(false); // Suprimir notificación al activar modo DEMO
|
||||
switchTextureInternal(false); // Suprimir notificación al activar modo DEMO
|
||||
}
|
||||
|
||||
// 4. Física o Figura
|
||||
if (rand() % 2 == 0) {
|
||||
// Modo física
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeMode(false);
|
||||
toggleShapeModeInternal(false);
|
||||
}
|
||||
} else {
|
||||
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
activateShape(shapes[rand() % 8]);
|
||||
activateShapeInternal(shapes[rand() % 8]);
|
||||
|
||||
// 5. Profundidad (solo si estamos en figura)
|
||||
if (rand() % 2 == 0) {
|
||||
@@ -1981,7 +1748,7 @@ void Engine::enterLogoMode(bool from_demo) {
|
||||
clampShapeScale();
|
||||
|
||||
// Activar PNG_SHAPE (el logo)
|
||||
activateShape(ShapeType::PNG_SHAPE);
|
||||
activateShapeInternal(ShapeType::PNG_SHAPE);
|
||||
|
||||
// Configurar PNG_SHAPE en modo LOGO (flip intervals más largos)
|
||||
if (active_shape_) {
|
||||
@@ -2053,20 +1820,12 @@ void Engine::exitLogoMode(bool return_to_demo) {
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX,
|
||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
activateShape(shapes[rand() % 8]);
|
||||
activateShapeInternal(shapes[rand() % 8]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle manual del Modo Logo (tecla K)
|
||||
void Engine::toggleLogoMode() {
|
||||
if (current_app_mode_ == AppMode::LOGO) {
|
||||
exitLogoMode(false); // Salir y volver a MANUAL
|
||||
} 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<float>(new_size - old_size);
|
||||
@@ -2111,7 +1870,7 @@ void Engine::updateBallSizes(int old_size, int new_size) {
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::switchTexture(bool show_notification) {
|
||||
void Engine::switchTextureInternal(bool show_notification) {
|
||||
if (textures_.empty()) return;
|
||||
|
||||
// Guardar tamaño antiguo
|
||||
@@ -2142,10 +1901,10 @@ void Engine::switchTexture(bool show_notification) {
|
||||
}
|
||||
|
||||
// Sistema de Figuras 3D - Alternar entre modo física y última figura (Toggle con tecla F)
|
||||
void Engine::toggleShapeMode(bool force_gravity_on_exit) {
|
||||
void Engine::toggleShapeModeInternal(bool force_gravity_on_exit) {
|
||||
if (current_mode_ == SimulationMode::PHYSICS) {
|
||||
// Cambiar a modo figura (usar última figura seleccionada)
|
||||
activateShape(last_shape_type_);
|
||||
activateShapeInternal(last_shape_type_);
|
||||
|
||||
// Si estamos en modo LOGO y la figura es PNG_SHAPE, restaurar configuración LOGO
|
||||
if (current_app_mode_ == AppMode::LOGO && last_shape_type_ == ShapeType::PNG_SHAPE) {
|
||||
@@ -2189,7 +1948,7 @@ void Engine::toggleShapeMode(bool force_gravity_on_exit) {
|
||||
}
|
||||
|
||||
// Activar figura específica (llamado por teclas Q/W/E/R/Y/U/I o por toggleShapeMode)
|
||||
void Engine::activateShape(ShapeType type) {
|
||||
void Engine::activateShapeInternal(ShapeType type) {
|
||||
// Guardar como última figura seleccionada
|
||||
last_shape_type_ = type;
|
||||
current_shape_type_ = type;
|
||||
|
||||
Reference in New Issue
Block a user