#include "state_manager.hpp" #include // for rand #include "defines.hpp" // for constantes DEMO/LOGO #include "engine.hpp" // for Engine (callbacks) #include "shapes/png_shape.hpp" // for PNGShape flip detection StateManager::StateManager() : engine_(nullptr) , current_app_mode_(AppMode::SANDBOX) , previous_app_mode_(AppMode::SANDBOX) , demo_timer_(0.0f) , demo_next_action_time_(0.0f) , logo_convergence_threshold_(0.90f) , logo_min_time_(3.0f) , logo_max_time_(5.0f) , logo_waiting_for_flip_(false) , logo_target_flip_number_(0) , logo_target_flip_percentage_(0.0f) , logo_current_flip_count_(0) , logo_entered_manually_(false) , logo_previous_theme_(0) , logo_previous_texture_index_(0) , logo_previous_shape_scale_(1.0f) { } StateManager::~StateManager() { } void StateManager::initialize(Engine* engine) { engine_ = engine; } void StateManager::setLogoPreviousState(int theme, size_t texture_index, float shape_scale) { logo_previous_theme_ = theme; logo_previous_texture_index_ = texture_index; logo_previous_shape_scale_ = shape_scale; } // =========================================================================== // ACTUALIZACIÓN DE ESTADOS - Migrado desde Engine::updateDemoMode() // =========================================================================== void StateManager::update(float delta_time, float shape_convergence, Shape* active_shape) { // Verificar si algún modo demo está activo (DEMO, DEMO_LITE o LOGO) if (current_app_mode_ == AppMode::SANDBOX) return; // Actualizar timer demo_timer_ += delta_time; // Determinar si es hora de ejecutar acción (depende del modo) bool should_trigger = false; if (current_app_mode_ == AppMode::LOGO) { // LOGO MODE: Dos caminos posibles if (logo_waiting_for_flip_) { // CAMINO B: Esperando a que ocurran flips // Obtener referencia a PNGShape si está activa PNGShape* png_shape = dynamic_cast(active_shape); if (png_shape) { int current_flip_count = png_shape->getFlipCount(); // Detectar nuevo flip completado if (current_flip_count > logo_current_flip_count_) { logo_current_flip_count_ = current_flip_count; } // Si estamos EN o DESPUÉS del flip objetivo // +1 porque queremos actuar DURANTE el flip N, no después de completarlo if (logo_current_flip_count_ + 1 >= logo_target_flip_number_) { // Monitorear progreso del flip actual if (png_shape->isFlipping()) { float flip_progress = png_shape->getFlipProgress(); if (flip_progress >= logo_target_flip_percentage_) { should_trigger = true; // ¡Trigger durante el flip! } } } } } else { // CAMINO A: Esperar convergencia + tiempo (comportamiento original) bool min_time_reached = demo_timer_ >= logo_min_time_; bool max_time_reached = demo_timer_ >= logo_max_time_; bool convergence_ok = shape_convergence >= logo_convergence_threshold_; should_trigger = (min_time_reached && convergence_ok) || max_time_reached; } } else { // DEMO/DEMO_LITE: Timer simple como antes should_trigger = demo_timer_ >= demo_next_action_time_; } // Si es hora de ejecutar acción if (should_trigger) { // MODO LOGO: Sistema de acciones variadas con gravedad dinámica if (current_app_mode_ == AppMode::LOGO) { // Llamar a Engine para ejecutar acciones de LOGO // TODO FASE 9: Mover lógica de acciones LOGO desde Engine a StateManager if (engine_) { engine_->performLogoAction(logo_waiting_for_flip_); } } // MODO DEMO/DEMO_LITE: Acciones normales else { bool is_lite = (current_app_mode_ == AppMode::DEMO_LITE); performDemoAction(is_lite); // Resetear timer y calcular próximo intervalo aleatorio demo_timer_ = 0.0f; // Usar intervalos diferentes según modo float interval_min = is_lite ? DEMO_LITE_ACTION_INTERVAL_MIN : DEMO_ACTION_INTERVAL_MIN; float interval_max = is_lite ? DEMO_LITE_ACTION_INTERVAL_MAX : DEMO_ACTION_INTERVAL_MAX; float interval_range = interval_max - interval_min; demo_next_action_time_ = interval_min + (rand() % 1000) / 1000.0f * interval_range; } } } void StateManager::setState(AppMode new_mode, int current_screen_width, int current_screen_height) { if (current_app_mode_ == new_mode) return; if (current_app_mode_ == AppMode::LOGO && new_mode != AppMode::LOGO) { previous_app_mode_ = new_mode; } if (new_mode == AppMode::LOGO) { previous_app_mode_ = current_app_mode_; } current_app_mode_ = new_mode; // Resetear timer al cambiar modo demo_timer_ = 0.0f; // Configurar timer de demo según el modo if (new_mode == AppMode::DEMO || new_mode == AppMode::DEMO_LITE || new_mode == AppMode::LOGO) { 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); } } void StateManager::toggleDemoMode(int current_screen_width, int current_screen_height) { if (current_app_mode_ == AppMode::DEMO) { setState(AppMode::SANDBOX, current_screen_width, current_screen_height); } else { setState(AppMode::DEMO, current_screen_width, current_screen_height); randomizeOnDemoStart(false); // Randomizar estado al entrar } } void StateManager::toggleDemoLiteMode(int current_screen_width, int current_screen_height) { if (current_app_mode_ == AppMode::DEMO_LITE) { setState(AppMode::SANDBOX, current_screen_width, current_screen_height); } else { setState(AppMode::DEMO_LITE, current_screen_width, current_screen_height); randomizeOnDemoStart(true); // Randomizar estado al entrar } } void StateManager::toggleLogoMode(int current_screen_width, int current_screen_height, size_t ball_count) { if (current_app_mode_ == AppMode::LOGO) { exitLogoMode(false); // Salir de LOGO manualmente } else { enterLogoMode(false, current_screen_width, current_screen_height, ball_count); // Entrar manualmente } } // =========================================================================== // ACCIONES DE DEMO - Migrado desde Engine::performDemoAction() // =========================================================================== void StateManager::performDemoAction(bool is_lite) { // ============================================ // SALTO AUTOMÁTICO A LOGO MODE (Easter Egg) // ============================================ // Obtener información necesaria desde Engine via callbacks // (En el futuro, se podría pasar como parámetros al método) if (!engine_) return; // TODO FASE 9: Eliminar callbacks a Engine y pasar parámetros necesarios // Por ahora, delegar las acciones DEMO completas a Engine // ya que necesitan acceso a múltiples componentes (SceneManager, ThemeManager, etc.) engine_->executeDemoAction(is_lite); } // =========================================================================== // RANDOMIZACIÓN AL INICIAR DEMO - Migrado desde Engine::randomizeOnDemoStart() // =========================================================================== void StateManager::randomizeOnDemoStart(bool is_lite) { // Delegar a Engine para randomización completa // TODO FASE 9: Implementar lógica completa aquí if (engine_) { engine_->executeRandomizeOnDemoStart(is_lite); } } // =========================================================================== // TOGGLE GRAVEDAD (para DEMO) - Migrado desde Engine::toggleGravityOnOff() // =========================================================================== void StateManager::toggleGravityOnOff() { // Delegar a Engine temporalmente if (engine_) { engine_->executeToggleGravityOnOff(); } } // =========================================================================== // ENTRAR AL MODO LOGO - Migrado desde Engine::enterLogoMode() // =========================================================================== void StateManager::enterLogoMode(bool from_demo, int current_screen_width, int current_screen_height, size_t ball_count) { // Guardar si entrada fue manual (tecla K) o automática (desde DEMO) logo_entered_manually_ = !from_demo; // Resetear variables de espera de flips logo_waiting_for_flip_ = false; logo_target_flip_number_ = 0; logo_target_flip_percentage_ = 0.0f; logo_current_flip_count_ = 0; // Cambiar a modo LOGO (guarda previous_app_mode_ automáticamente) setState(AppMode::LOGO, current_screen_width, current_screen_height); // Delegar configuración visual a Engine // TODO FASE 9: Mover configuración completa aquí if (engine_) { engine_->executeEnterLogoMode(ball_count); } } // =========================================================================== // SALIR DEL MODO LOGO - Migrado desde Engine::exitLogoMode() // =========================================================================== void StateManager::exitLogoMode(bool return_to_demo) { if (current_app_mode_ != AppMode::LOGO) return; // Resetear flag de entrada manual logo_entered_manually_ = false; // Delegar restauración visual a Engine // TODO FASE 9: Mover lógica completa aquí if (engine_) { engine_->executeExitLogoMode(); } if (!return_to_demo) { // Salida manual (tecla K): volver a SANDBOX setState(AppMode::SANDBOX, 0, 0); } else { // Volver al modo previo (DEMO o DEMO_LITE) current_app_mode_ = previous_app_mode_; } }