Refactor fase 8: Migrar lógica DEMO/LOGO a StateManager

Implementación:
- StateManager::update() ahora maneja timers y triggers DEMO/LOGO
- Detección de flips de PNG_SHAPE migrada completamente
- Callbacks temporales en Engine para acciones complejas
- enterLogoMode() y exitLogoMode() públicos para transiciones automáticas
- Toggle methods en Engine delegados a StateManager

Callbacks implementados (temporal para Fase 9):
- Engine::performLogoAction()
- Engine::executeDemoAction()
- Engine::executeRandomizeOnDemoStart()
- Engine::executeToggleGravityOnOff()
- Engine::executeEnterLogoMode()
- Engine::executeExitLogoMode()

TODO Fase 9:
- Eliminar callbacks moviendo lógica completa a StateManager
- Limpiar duplicación de estado entre Engine y StateManager

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-11 21:19:14 +02:00
parent 83ea03fda3
commit 01d1ebd2a3
4 changed files with 310 additions and 129 deletions

View File

@@ -321,8 +321,8 @@ void Engine::update() {
updateShape();
}
// Actualizar Modo DEMO (auto-play)
updateDemoMode();
// Actualizar Modo DEMO/LOGO (delegado a StateManager)
state_manager_->update(delta_time_, shape_convergence_, active_shape_.get());
// Actualizar transiciones de temas (delegado a ThemeManager)
theme_manager_->update(delta_time_);
@@ -477,41 +477,42 @@ void Engine::handleZoomOut() {
}
}
// Modos de aplicación (DEMO/LOGO)
// Modos de aplicación (DEMO/LOGO) - Delegados a StateManager
void Engine::toggleDemoMode() {
if (state_manager_->getCurrentMode() == AppMode::DEMO) {
// Ya estamos en DEMO → volver a SANDBOX
setState(AppMode::SANDBOX);
AppMode prev_mode = state_manager_->getCurrentMode();
state_manager_->toggleDemoMode(current_screen_width_, current_screen_height_);
AppMode new_mode = state_manager_->getCurrentMode();
// Mostrar notificación según el modo resultante
if (new_mode == AppMode::SANDBOX && prev_mode != AppMode::SANDBOX) {
showNotificationForAction("MODO SANDBOX");
} else {
// Estamos en otro modo → ir a DEMO
setState(AppMode::DEMO);
randomizeOnDemoStart(false);
} else if (new_mode == AppMode::DEMO && prev_mode != AppMode::DEMO) {
showNotificationForAction("MODO DEMO");
}
}
void Engine::toggleDemoLiteMode() {
if (state_manager_->getCurrentMode() == AppMode::DEMO_LITE) {
// Ya estamos en DEMO_LITE → volver a SANDBOX
setState(AppMode::SANDBOX);
AppMode prev_mode = state_manager_->getCurrentMode();
state_manager_->toggleDemoLiteMode(current_screen_width_, current_screen_height_);
AppMode new_mode = state_manager_->getCurrentMode();
// Mostrar notificación según el modo resultante
if (new_mode == AppMode::SANDBOX && prev_mode != AppMode::SANDBOX) {
showNotificationForAction("MODO SANDBOX");
} else {
// Estamos en otro modo → ir a DEMO_LITE
setState(AppMode::DEMO_LITE);
randomizeOnDemoStart(true);
} else if (new_mode == AppMode::DEMO_LITE && prev_mode != AppMode::DEMO_LITE) {
showNotificationForAction("MODO DEMO LITE");
}
}
void Engine::toggleLogoMode() {
if (state_manager_->getCurrentMode() == AppMode::LOGO) {
// Ya estamos en LOGO → volver a SANDBOX
exitLogoMode(false);
AppMode prev_mode = state_manager_->getCurrentMode();
state_manager_->toggleLogoMode(current_screen_width_, current_screen_height_, scene_manager_->getBallCount());
AppMode new_mode = state_manager_->getCurrentMode();
// Mostrar notificación según el modo resultante
if (new_mode == AppMode::SANDBOX && prev_mode != AppMode::SANDBOX) {
showNotificationForAction("MODO SANDBOX");
} else {
// Estamos en otro modo → ir a LOGO
enterLogoMode(false);
} else if (new_mode == AppMode::LOGO && prev_mode != AppMode::LOGO) {
showNotificationForAction("MODO LOGO");
}
}
@@ -949,44 +950,14 @@ void Engine::updatePhysicalWindowSize() {
}
// ============================================================================
// Sistema de gestión de estados (MANUAL/DEMO/DEMO_LITE/LOGO)
// CALLBACKS PARA STATEMANAGER - FASE 8
// ============================================================================
// NOTA: Estos métodos son callbacks temporales para que StateManager pueda
// ejecutar acciones que requieren acceso a múltiples componentes del Engine.
// TODO FASE 9: Eliminar estos callbacks moviendo la lógica completa a StateManager
void Engine::setState(AppMode new_mode) {
// Delegar a StateManager pero mantener lógica de setup en Engine temporalmente
// TODO: Mover toda esta lógica a StateManager
// Aplicar el nuevo modo a través de StateManager
state_manager_->setState(new_mode, current_screen_width_, current_screen_height_);
// 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) {
// Escalar tiempos con resolución (720p como base)
float resolution_scale = current_screen_height_ / 720.0f;
logo_min_time_ = LOGO_ACTION_INTERVAL_MIN * resolution_scale;
logo_max_time_ = LOGO_ACTION_INTERVAL_MAX * resolution_scale;
min_interval = logo_min_time_;
max_interval = logo_max_time_;
} 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() {
// Callback para ejecutar acciones de LOGO MODE
void Engine::performLogoAction(bool logo_waiting_for_flip) {
// Verificar si algún modo demo está activo (DEMO, DEMO_LITE o LOGO)
if (state_manager_->getCurrentMode() == AppMode::SANDBOX) return;
@@ -1130,13 +1101,13 @@ void Engine::updateDemoMode() {
// No salir si el usuario entró manualmente con tecla K
// Probabilidad de salir: 60% en cada acción → sale rápido (relación DEMO:LOGO = 6:1)
if (!logo_entered_manually_ && rand() % 100 < 60) {
exitLogoMode(true); // Volver a DEMO/DEMO_LITE
state_manager_->exitLogoMode(true); // Volver a DEMO/DEMO_LITE
}
}
// MODO DEMO/DEMO_LITE: Acciones normales
else {
bool is_lite = (state_manager_->getCurrentMode() == AppMode::DEMO_LITE);
performDemoAction(is_lite);
executeDemoAction(is_lite);
// Resetear timer y calcular próximo intervalo aleatorio
demo_timer_ = 0.0f;
@@ -1150,7 +1121,8 @@ void Engine::updateDemoMode() {
}
}
void Engine::performDemoAction(bool is_lite) {
// Callback para StateManager - Ejecutar acción DEMO
void Engine::executeDemoAction(bool is_lite) {
// ============================================
// SALTO AUTOMÁTICO A LOGO MODE (Easter Egg)
// ============================================
@@ -1161,7 +1133,7 @@ void Engine::performDemoAction(bool is_lite) {
theme_manager_->getCurrentThemeIndex() == 5) { // MONOCHROME
// 10% probabilidad de saltar a Logo Mode
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO_LITE) {
enterLogoMode(true); // Entrar desde DEMO
state_manager_->enterLogoMode(true, current_screen_width_, current_screen_height_, scene_manager_->getBallCount());
return;
}
}
@@ -1170,7 +1142,7 @@ void Engine::performDemoAction(bool is_lite) {
if (static_cast<int>(scene_manager_->getBallCount()) >= LOGO_MODE_MIN_BALLS) {
// 15% probabilidad de saltar a Logo Mode
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO) {
enterLogoMode(true); // Entrar desde DEMO
state_manager_->enterLogoMode(true, current_screen_width_, current_screen_height_, scene_manager_->getBallCount());
return;
}
}
@@ -1200,7 +1172,7 @@ void Engine::performDemoAction(bool is_lite) {
// Toggle gravedad ON/OFF (20%)
accumulated_weight += DEMO_LITE_WEIGHT_GRAVITY_TOGGLE;
if (random_value < accumulated_weight) {
toggleGravityOnOff();
executeToggleGravityOnOff();
return;
}
@@ -1243,7 +1215,7 @@ void Engine::performDemoAction(bool is_lite) {
// Toggle gravedad ON/OFF (8%)
accumulated_weight += DEMO_WEIGHT_GRAVITY_TOGGLE;
if (random_value < accumulated_weight) {
toggleGravityOnOff();
executeToggleGravityOnOff();
return;
}
@@ -1334,8 +1306,8 @@ void Engine::performDemoAction(bool is_lite) {
}
}
// Randomizar todo al iniciar modo DEMO
void Engine::randomizeOnDemoStart(bool is_lite) {
// Callback para StateManager - Randomizar estado al iniciar DEMO
void Engine::executeRandomizeOnDemoStart(bool is_lite) {
// Si venimos de LOGO con PNG_SHAPE, cambiar figura obligatoriamente
// PNG_SHAPE es exclusivo del modo LOGO y no debe aparecer en DEMO/DEMO_LITE
if (current_shape_type_ == ShapeType::PNG_SHAPE) {
@@ -1363,7 +1335,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
scene_manager_->changeGravityDirection(new_direction);
if (rand() % 2 == 0) {
toggleGravityOnOff(); // 50% probabilidad de desactivar gravedad
executeToggleGravityOnOff(); // 50% probabilidad de desactivar gravedad
}
} else {
@@ -1409,13 +1381,13 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
scene_manager_->changeGravityDirection(new_direction);
if (rand() % 3 == 0) { // 33% probabilidad de desactivar gravedad
toggleGravityOnOff();
executeToggleGravityOnOff();
}
}
}
// Toggle gravedad ON/OFF para todas las pelotas
void Engine::toggleGravityOnOff() {
// Callback para StateManager - Toggle gravedad ON/OFF para todas las pelotas
void Engine::executeToggleGravityOnOff() {
// Alternar entre activar/desactivar gravedad
bool first_ball_gravity_enabled = (!scene_manager_->hasBalls() || scene_manager_->getFirstBall()->getGravityForce() > 0.0f);
@@ -1432,7 +1404,7 @@ void Engine::toggleGravityOnOff() {
// SISTEMA DE MODO LOGO (Easter Egg - "Marca de Agua")
// ============================================================================
// Entrar al Modo Logo (manual con tecla K o automático desde DEMO)
// Método antiguo mantenido para código legacy de LOGO (será eliminado en Fase 9)
void Engine::enterLogoMode(bool from_demo) {
// Verificar mínimo de pelotas
if (static_cast<int>(scene_manager_->getBallCount()) < LOGO_MODE_MIN_BALLS) {
@@ -1490,18 +1462,64 @@ void Engine::enterLogoMode(bool from_demo) {
logo_target_flip_percentage_ = 0.0f;
logo_current_flip_count_ = 0;
// Guardar si entrada fue manual (tecla K) o automática (desde DEMO)
logo_entered_manually_ = !from_demo;
// Cambiar a modo LOGO (guarda previous_app_mode_ automáticamente)
setState(AppMode::LOGO);
// La configuración de estado se maneja en StateManager
}
// Salir del Modo Logo (volver a estado anterior o salir de DEMO)
void Engine::exitLogoMode(bool return_to_demo) {
if (state_manager_->getCurrentMode() != AppMode::LOGO) return;
// Callbacks para StateManager - Solo configuración visual
void Engine::executeEnterLogoMode(size_t ball_count) {
// Verificar mínimo de pelotas
if (static_cast<int>(ball_count) < LOGO_MODE_MIN_BALLS) {
// Ajustar a 5000 pelotas automáticamente
scene_manager_->changeScenario(5); // Escenario 5000 pelotas (índice 5 en BALL_COUNT_SCENARIOS)
}
// Restaurar estado previo
// Guardar estado previo (para restaurar al salir)
logo_previous_theme_ = theme_manager_->getCurrentThemeIndex();
logo_previous_texture_index_ = current_texture_index_;
logo_previous_shape_scale_ = shape_scale_factor_;
// Buscar índice de textura "small"
size_t small_index = current_texture_index_; // Por defecto mantener actual
for (size_t i = 0; i < texture_names_.size(); i++) {
if (texture_names_[i] == "small") {
small_index = i;
break;
}
}
// Aplicar configuración fija del Modo Logo
if (small_index != current_texture_index_) {
current_texture_index_ = small_index;
texture_ = textures_[current_texture_index_];
int new_size = texture_->getWidth();
current_ball_size_ = new_size;
scene_manager_->updateBallTexture(texture_, new_size);
}
// Cambiar a tema aleatorio entre: MONOCHROME, LAVENDER, CRIMSON, ESMERALDA
int logo_themes[] = {5, 6, 7, 8}; // MONOCHROME, LAVENDER, CRIMSON, ESMERALDA
int random_theme = logo_themes[rand() % 4];
theme_manager_->switchToTheme(random_theme);
// Establecer escala a 120%
shape_scale_factor_ = LOGO_MODE_SHAPE_SCALE;
clampShapeScale();
// Activar PNG_SHAPE (el logo)
activateShapeInternal(ShapeType::PNG_SHAPE);
// 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);
png_shape->resetFlipCount(); // Resetear contador de flips
}
}
}
void Engine::executeExitLogoMode() {
// Restaurar estado visual previo
theme_manager_->switchToTheme(logo_previous_theme_);
if (logo_previous_texture_index_ != current_texture_index_ &&
@@ -1525,23 +1543,12 @@ void Engine::exitLogoMode(bool return_to_demo) {
}
}
// Resetear flag de entrada manual
logo_entered_manually_ = false;
if (!return_to_demo) {
// Salida manual (tecla K): volver a MANUAL
setState(AppMode::SANDBOX);
} else {
// Volver al modo previo (DEMO o DEMO_LITE)
setState(previous_app_mode_);
// Si la figura activa es PNG_SHAPE, cambiar a otra figura aleatoria
if (current_shape_type_ == ShapeType::PNG_SHAPE) {
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX,
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
activateShapeInternal(shapes[rand() % 8]);
}
// Si la figura activa es PNG_SHAPE, cambiar a otra figura aleatoria
if (current_shape_type_ == ShapeType::PNG_SHAPE) {
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX,
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
activateShapeInternal(shapes[rand() % 8]);
}
}