Implementar sistema de estados mutuamente excluyentes y fix PNG_SHAPE flip en LOGO
## 1. Sistema de Estados AppMode (MANUAL/DEMO/DEMO_LITE/LOGO) **engine.h:** - Creado enum AppMode con 4 estados mutuamente excluyentes - Reemplazadas 4 flags booleanas por 2 variables de estado: * current_app_mode_: Modo actual * previous_app_mode_: Para restaurar al salir de LOGO - Añadido método setState() para gestión centralizada **engine.cpp:** - Implementado setState() con configuración automática de timers - Actualizado updateDemoMode() para usar current_app_mode_ - Actualizado handleEvents() para teclas D/L/K con setState() - Actualizadas todas las referencias a flags antiguas (8 ubicaciones) - enterLogoMode/exitLogoMode usan setState() **Comportamiento:** - Teclas D/L/K ahora desactivan otros modos automáticamente - Al salir de LOGO vuelve al modo previo (DEMO/DEMO_LITE/MANUAL) ## 2. Ajuste Ratio DEMO:LOGO = 6:1 **defines.h:** - Probabilidad DEMO→LOGO: 15% → 5% (más raro) - Probabilidad DEMO_LITE→LOGO: 10% → 3% - Probabilidad salir de LOGO: 25% → 60% (sale rápido) - Intervalos LOGO: 4-8s → 3-5s (más corto que DEMO) **Resultado:** DEMO pasa 6x más tiempo activo que LOGO ## 3. Fix PNG_SHAPE no hace flip en modo LOGO **Bugs encontrados:** 1. next_idle_time_ inicializado a 5.0s (hardcoded) > intervalos LOGO (3-5s) 2. toggleShapeMode() recrea PNG_SHAPE → pierde is_logo_mode_=true **Soluciones:** **png_shape.cpp (constructor):** - Inicializa next_idle_time_ con PNG_IDLE_TIME_MIN/MAX (no hardcoded) **png_shape.h:** - Añadidos includes: defines.h, <cstdlib> - Flag is_logo_mode_ para distinguir MANUAL vs LOGO - Expandido setLogoMode() para recalcular next_idle_time_ con rangos apropiados - PNG_IDLE_TIME_MIN_LOGO/MAX_LOGO: 2.5-4.5s (ajustable en defines.h) **engine.cpp (toggleShapeMode):** - Detecta si vuelve a SHAPE en modo LOGO con PNG_SHAPE - Restaura setLogoMode(true) después de recrear instancia **defines.h:** - PNG_IDLE_TIME_MIN/MAX = 0.5-2.0s (modo MANUAL) - PNG_IDLE_TIME_MIN_LOGO/MAX_LOGO = 2.5-4.5s (modo LOGO) **Resultado:** PNG_SHAPE ahora hace flip cada 2.5-4.5s en modo LOGO (visible antes de toggles) ## 4. Nuevas Texturas **data/balls/big.png:** 16x16px (añadida) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -575,27 +575,18 @@ void Engine::handleEvents() {
|
||||
|
||||
// Toggle Modo DEMO COMPLETO (auto-play)
|
||||
case SDLK_D:
|
||||
demo_mode_enabled_ = !demo_mode_enabled_;
|
||||
if (demo_mode_enabled_) {
|
||||
// Desactivar demo lite si estaba activo (mutuamente excluyentes)
|
||||
demo_lite_enabled_ = false;
|
||||
|
||||
// Randomizar TODO al activar
|
||||
randomizeOnDemoStart(false);
|
||||
|
||||
// Inicializar timer con primer intervalo aleatorio
|
||||
demo_timer_ = 0.0f;
|
||||
float interval_range = DEMO_ACTION_INTERVAL_MAX - DEMO_ACTION_INTERVAL_MIN;
|
||||
demo_next_action_time_ = DEMO_ACTION_INTERVAL_MIN + (rand() % 1000) / 1000.0f * interval_range;
|
||||
|
||||
// Mostrar texto de activación (usa color del tema)
|
||||
text_ = "DEMO MODE ON";
|
||||
if (current_app_mode_ == AppMode::DEMO) {
|
||||
// Desactivar DEMO → MANUAL
|
||||
setState(AppMode::MANUAL);
|
||||
text_ = "DEMO MODE OFF";
|
||||
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
} else {
|
||||
// Al desactivar: mostrar texto
|
||||
text_ = "DEMO MODE OFF";
|
||||
// Activar DEMO (desde cualquier otro modo)
|
||||
setState(AppMode::DEMO);
|
||||
randomizeOnDemoStart(false);
|
||||
text_ = "DEMO MODE ON";
|
||||
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
@@ -604,27 +595,18 @@ void Engine::handleEvents() {
|
||||
|
||||
// Toggle Modo DEMO LITE (solo física/figuras)
|
||||
case SDLK_L:
|
||||
demo_lite_enabled_ = !demo_lite_enabled_;
|
||||
if (demo_lite_enabled_) {
|
||||
// Desactivar demo completo si estaba activo (mutuamente excluyentes)
|
||||
demo_mode_enabled_ = false;
|
||||
|
||||
// Randomizar solo física/figura (mantiene escenario y tema)
|
||||
randomizeOnDemoStart(true);
|
||||
|
||||
// Inicializar timer con primer intervalo aleatorio (más rápido)
|
||||
demo_timer_ = 0.0f;
|
||||
float interval_range = DEMO_LITE_ACTION_INTERVAL_MAX - DEMO_LITE_ACTION_INTERVAL_MIN;
|
||||
demo_next_action_time_ = DEMO_LITE_ACTION_INTERVAL_MIN + (rand() % 1000) / 1000.0f * interval_range;
|
||||
|
||||
// Mostrar texto de activación (usa color del tema)
|
||||
text_ = "DEMO LITE ON";
|
||||
if (current_app_mode_ == AppMode::DEMO_LITE) {
|
||||
// Desactivar DEMO_LITE → MANUAL
|
||||
setState(AppMode::MANUAL);
|
||||
text_ = "DEMO LITE OFF";
|
||||
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
} else {
|
||||
// Al desactivar: mostrar texto
|
||||
text_ = "DEMO LITE OFF";
|
||||
// Activar DEMO_LITE (desde cualquier otro modo)
|
||||
setState(AppMode::DEMO_LITE);
|
||||
randomizeOnDemoStart(true);
|
||||
text_ = "DEMO LITE ON";
|
||||
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
@@ -635,7 +617,7 @@ void Engine::handleEvents() {
|
||||
case SDLK_K:
|
||||
toggleLogoMode();
|
||||
// Mostrar texto informativo
|
||||
if (logo_mode_enabled_) {
|
||||
if (current_app_mode_ == AppMode::LOGO) {
|
||||
text_ = "LOGO MODE ON";
|
||||
} else {
|
||||
text_ = "LOGO MODE OFF";
|
||||
@@ -782,15 +764,15 @@ void Engine::render() {
|
||||
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_) {
|
||||
if (current_app_mode_ == AppMode::LOGO) {
|
||||
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_) {
|
||||
} else if (current_app_mode_ == AppMode::DEMO) {
|
||||
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_) {
|
||||
} else if (current_app_mode_ == AppMode::DEMO_LITE) {
|
||||
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
|
||||
@@ -835,7 +817,7 @@ void Engine::initBalls(int value) {
|
||||
|
||||
void Engine::setText() {
|
||||
// Suprimir textos durante modos demo
|
||||
if (demo_mode_enabled_ || demo_lite_enabled_) return;
|
||||
if (current_app_mode_ != AppMode::MANUAL) return;
|
||||
|
||||
int num_balls = BALL_COUNT_SCENARIOS[scenario_];
|
||||
if (num_balls == 1) {
|
||||
@@ -1372,7 +1354,7 @@ void Engine::startThemeTransition(ColorTheme new_theme) {
|
||||
transition_progress_ = 0.0f;
|
||||
|
||||
// Mostrar nombre del tema (solo si NO estamos en modo demo)
|
||||
if (!demo_mode_enabled_ && !demo_lite_enabled_) {
|
||||
if (current_app_mode_ == AppMode::MANUAL) {
|
||||
ThemeColors& theme = themes_[static_cast<int>(new_theme)];
|
||||
text_ = theme.name_es;
|
||||
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
|
||||
@@ -1402,11 +1384,52 @@ Color Engine::getInterpolatedColor(size_t ball_index) const {
|
||||
static_cast<Uint8>(lerp(static_cast<float>(current_color.b), static_cast<float>(target_color.b), transition_progress_))};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Sistema de gestión de estados (MANUAL/DEMO/DEMO_LITE/LOGO)
|
||||
// ============================================================================
|
||||
|
||||
void Engine::setState(AppMode new_mode) {
|
||||
// Si ya estamos en ese modo, no hacer nada
|
||||
if (current_app_mode_ == new_mode) return;
|
||||
|
||||
// Al salir de LOGO, guardar en previous_app_mode_ (para volver al modo correcto)
|
||||
if (current_app_mode_ == AppMode::LOGO && new_mode != AppMode::LOGO) {
|
||||
previous_app_mode_ = new_mode;
|
||||
}
|
||||
|
||||
// Al entrar a LOGO, guardar el modo previo
|
||||
if (new_mode == AppMode::LOGO) {
|
||||
previous_app_mode_ = current_app_mode_;
|
||||
}
|
||||
|
||||
// Aplicar el nuevo modo
|
||||
current_app_mode_ = new_mode;
|
||||
|
||||
// Configurar timer de demo según el modo
|
||||
if (new_mode == AppMode::DEMO || new_mode == AppMode::DEMO_LITE || new_mode == AppMode::LOGO) {
|
||||
demo_timer_ = 0.0f;
|
||||
float min_interval, max_interval;
|
||||
|
||||
if (new_mode == AppMode::LOGO) {
|
||||
min_interval = LOGO_ACTION_INTERVAL_MIN;
|
||||
max_interval = LOGO_ACTION_INTERVAL_MAX;
|
||||
} else {
|
||||
bool is_lite = (new_mode == AppMode::DEMO_LITE);
|
||||
min_interval = is_lite ? DEMO_LITE_ACTION_INTERVAL_MIN : DEMO_ACTION_INTERVAL_MIN;
|
||||
max_interval = is_lite ? DEMO_LITE_ACTION_INTERVAL_MAX : DEMO_ACTION_INTERVAL_MAX;
|
||||
}
|
||||
|
||||
demo_next_action_time_ = min_interval + (rand() % 1000) / 1000.0f * (max_interval - min_interval);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Sistema de Modo DEMO (auto-play)
|
||||
// ============================================================================
|
||||
|
||||
void Engine::updateDemoMode() {
|
||||
// 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;
|
||||
// Verificar si algún modo demo está activo (DEMO, DEMO_LITE o LOGO)
|
||||
if (current_app_mode_ == AppMode::MANUAL) return;
|
||||
|
||||
// Actualizar timer
|
||||
demo_timer_ += delta_time_;
|
||||
@@ -1414,7 +1437,7 @@ void Engine::updateDemoMode() {
|
||||
// Si es hora de ejecutar acción
|
||||
if (demo_timer_ >= demo_next_action_time_) {
|
||||
// MODO LOGO: Sistema de acciones variadas con gravedad dinámica
|
||||
if (logo_mode_enabled_) {
|
||||
if (current_app_mode_ == AppMode::LOGO) {
|
||||
// Elegir acción aleatoria ponderada
|
||||
int action = rand() % 100;
|
||||
|
||||
@@ -1446,15 +1469,15 @@ void Engine::updateDemoMode() {
|
||||
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) {
|
||||
// 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) {
|
||||
exitLogoMode(true); // Volver a DEMO/DEMO_LITE
|
||||
}
|
||||
}
|
||||
// MODO DEMO/DEMO_LITE: Acciones normales
|
||||
else {
|
||||
bool is_lite = demo_lite_enabled_;
|
||||
bool is_lite = (current_app_mode_ == AppMode::DEMO_LITE);
|
||||
performDemoAction(is_lite);
|
||||
|
||||
// Resetear timer y calcular próximo intervalo aleatorio
|
||||
@@ -1788,21 +1811,21 @@ void Engine::enterLogoMode(bool from_demo) {
|
||||
// Activar PNG_SHAPE (el logo)
|
||||
activateShape(ShapeType::PNG_SHAPE);
|
||||
|
||||
// Activar modo logo
|
||||
logo_mode_enabled_ = true;
|
||||
// Configurar PNG_SHAPE en modo LOGO (flip intervals más largos)
|
||||
if (active_shape_) {
|
||||
PNGShape* png_shape = dynamic_cast<PNGShape*>(active_shape_.get());
|
||||
if (png_shape) {
|
||||
png_shape->setLogoMode(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);
|
||||
// Cambiar a modo LOGO (guarda previous_app_mode_ automáticamente)
|
||||
setState(AppMode::LOGO);
|
||||
}
|
||||
|
||||
// Salir del Modo Logo (volver a estado anterior o salir de DEMO)
|
||||
void Engine::exitLogoMode(bool return_to_demo) {
|
||||
if (!logo_mode_enabled_) return;
|
||||
if (current_app_mode_ != AppMode::LOGO) return;
|
||||
|
||||
// Restaurar estado previo
|
||||
startThemeTransition(logo_previous_theme_);
|
||||
@@ -1825,28 +1848,27 @@ void Engine::exitLogoMode(bool return_to_demo) {
|
||||
clampShapeScale();
|
||||
generateShape();
|
||||
|
||||
// Desactivar modo logo
|
||||
logo_mode_enabled_ = false;
|
||||
logo_mode_is_manual_ = false; // Resetear flag manual
|
||||
// Desactivar modo LOGO en PNG_SHAPE (volver a flip intervals normales)
|
||||
if (active_shape_) {
|
||||
PNGShape* png_shape = dynamic_cast<PNGShape*>(active_shape_.get());
|
||||
if (png_shape) {
|
||||
png_shape->setLogoMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!return_to_demo) {
|
||||
// Salida manual (tecla K): desactivar todos los modos DEMO
|
||||
demo_mode_enabled_ = false;
|
||||
demo_lite_enabled_ = false;
|
||||
// Salida manual (tecla K): volver a MANUAL
|
||||
setState(AppMode::MANUAL);
|
||||
} 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));
|
||||
// Volver al modo previo (DEMO o DEMO_LITE)
|
||||
setState(previous_app_mode_);
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle manual del Modo Logo (tecla K)
|
||||
void Engine::toggleLogoMode() {
|
||||
if (logo_mode_enabled_) {
|
||||
exitLogoMode(false); // Salir y desactivar DEMO
|
||||
if (current_app_mode_ == AppMode::LOGO) {
|
||||
exitLogoMode(false); // Salir y volver a MANUAL
|
||||
} else {
|
||||
enterLogoMode(false); // Entrar manualmente
|
||||
}
|
||||
@@ -1919,7 +1941,7 @@ void Engine::switchTexture() {
|
||||
updateBallSizes(old_size, new_size);
|
||||
|
||||
// Mostrar texto informativo (solo si NO estamos en modo demo)
|
||||
if (!demo_mode_enabled_ && !demo_lite_enabled_) {
|
||||
if (current_app_mode_ == AppMode::MANUAL) {
|
||||
// Obtener nombre de textura (uppercase)
|
||||
std::string texture_name = texture_names_[current_texture_index_];
|
||||
std::transform(texture_name.begin(), texture_name.end(), texture_name.begin(), ::toupper);
|
||||
@@ -1936,6 +1958,16 @@ void Engine::toggleShapeMode(bool force_gravity_on_exit) {
|
||||
if (current_mode_ == SimulationMode::PHYSICS) {
|
||||
// Cambiar a modo figura (usar última figura seleccionada)
|
||||
activateShape(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) {
|
||||
if (active_shape_) {
|
||||
PNGShape* png_shape = dynamic_cast<PNGShape*>(active_shape_.get());
|
||||
if (png_shape) {
|
||||
png_shape->setLogoMode(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Volver a modo física normal
|
||||
current_mode_ = SimulationMode::PHYSICS;
|
||||
@@ -1952,7 +1984,7 @@ void Engine::toggleShapeMode(bool force_gravity_on_exit) {
|
||||
}
|
||||
|
||||
// Mostrar texto informativo (solo si NO estamos en modo demo o logo)
|
||||
if (!demo_mode_enabled_ && !demo_lite_enabled_ && !logo_mode_enabled_) {
|
||||
if (current_app_mode_ == AppMode::MANUAL) {
|
||||
text_ = "MODO FISICA";
|
||||
int text_width = static_cast<int>(text_.length() * 8);
|
||||
text_pos_ = (current_screen_width_ - text_width) / 2;
|
||||
@@ -2017,7 +2049,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_ && !demo_mode_enabled_ && !demo_lite_enabled_ && !logo_mode_enabled_) {
|
||||
if (active_shape_ && current_app_mode_ == AppMode::MANUAL) {
|
||||
text_ = std::string("MODO ") + active_shape_->getName();
|
||||
int text_width = static_cast<int>(text_.length() * 8);
|
||||
text_pos_ = (current_screen_width_ - text_width) / 2;
|
||||
|
||||
Reference in New Issue
Block a user