Corregidos ~2570 issues automáticamente con clang-tidy --fix-errors más ajustes manuales posteriores: - modernize: designated-initializers, trailing-return-type, use-auto, avoid-c-arrays (→ std::array<>), use-ranges, use-emplace, deprecated-headers, use-equals-default, pass-by-value, return-braced-init-list, use-default-member-init - readability: math-missing-parentheses, implicit-bool-conversion, braces-around-statements, isolate-declaration, use-std-min-max, identifier-naming, else-after-return, redundant-casting, convert-member-functions-to-static, make-member-function-const, static-accessed-through-instance - performance: avoid-endl, unnecessary-value-param, type-promotion, inefficient-vector-operation - dead code: XOR_KEY (orphan tras eliminar encryptData/decryptData), dead stores en engine.cpp y png_shape.cpp - NOLINT justificado en 10 funciones con alta complejidad cognitiva (initialize, render, main, processEvents, update×3, performDemoAction, randomizeOnDemoStart, renderDebugHUD, AppLogo::update) Compilación: gcc -Wall sin warnings. clang-tidy: 0 issues. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
661 lines
26 KiB
C++
661 lines
26 KiB
C++
#include "state_manager.hpp"
|
|
|
|
#include <algorithm> // for std::min
|
|
#include <array> // for std::array
|
|
#include <cstdlib> // for rand
|
|
#include <vector> // for std::vector
|
|
|
|
#include "defines.hpp" // for constantes DEMO/LOGO
|
|
#include "engine.hpp" // for Engine (enter/exitShapeMode, texture)
|
|
#include "scene/scene_manager.hpp" // for SceneManager
|
|
#include "shapes/png_shape.hpp" // for PNGShape flip detection
|
|
#include "shapes_mgr/shape_manager.hpp" // for ShapeManager
|
|
#include "theme_manager.hpp" // for ThemeManager
|
|
|
|
StateManager::StateManager()
|
|
: engine_(nullptr),
|
|
scene_mgr_(nullptr),
|
|
theme_mgr_(nullptr),
|
|
shape_mgr_(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) {
|
|
}
|
|
|
|
void StateManager::initialize(Engine* engine, SceneManager* scene_mgr, ThemeManager* theme_mgr, ShapeManager* shape_mgr) {
|
|
engine_ = engine;
|
|
scene_mgr_ = scene_mgr;
|
|
theme_mgr_ = theme_mgr;
|
|
shape_mgr_ = shape_mgr;
|
|
}
|
|
|
|
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
|
|
// ===========================================================================
|
|
|
|
void StateManager::update(float delta_time, float shape_convergence, Shape* active_shape) { // NOLINT(readability-function-cognitive-complexity)
|
|
if (current_app_mode_ == AppMode::SANDBOX) {
|
|
return;
|
|
}
|
|
|
|
demo_timer_ += delta_time;
|
|
|
|
bool should_trigger = false;
|
|
|
|
if (current_app_mode_ == AppMode::LOGO) {
|
|
if (logo_waiting_for_flip_) {
|
|
// CAMINO B: Esperando a que ocurran flips
|
|
auto* png_shape = dynamic_cast<PNGShape*>(active_shape);
|
|
|
|
if (png_shape != nullptr) {
|
|
int current_flip_count = png_shape->getFlipCount();
|
|
|
|
logo_current_flip_count_ = std::max(current_flip_count, logo_current_flip_count_);
|
|
|
|
if (logo_current_flip_count_ + 1 >= logo_target_flip_number_) {
|
|
if (png_shape->isFlipping()) {
|
|
float flip_progress = png_shape->getFlipProgress();
|
|
if (flip_progress >= logo_target_flip_percentage_) {
|
|
should_trigger = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// CAMINO A: Esperar convergencia + tiempo
|
|
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 {
|
|
should_trigger = demo_timer_ >= demo_next_action_time_;
|
|
}
|
|
|
|
if (!should_trigger) {
|
|
return;
|
|
}
|
|
|
|
if (current_app_mode_ == AppMode::LOGO) {
|
|
// LOGO MODE: Sistema de acciones variadas con gravedad dinámica
|
|
int action = rand() % 100;
|
|
|
|
if (shape_mgr_->isShapeModeActive()) {
|
|
// Logo quieto (formado) → Decidir camino a seguir
|
|
if (logo_waiting_for_flip_) {
|
|
// Ya estábamos esperando flips → hacer el cambio SHAPE → PHYSICS
|
|
if (action < 50) {
|
|
engine_->exitShapeMode(true); // Con gravedad ON
|
|
} else {
|
|
engine_->exitShapeMode(false); // Con gravedad OFF
|
|
}
|
|
|
|
logo_waiting_for_flip_ = false;
|
|
logo_current_flip_count_ = 0;
|
|
|
|
demo_timer_ = 0.0f;
|
|
float interval_range = logo_max_time_ - logo_min_time_;
|
|
demo_next_action_time_ = logo_min_time_ + (rand() % 1000) / 1000.0f * interval_range;
|
|
} else if (rand() % 100 < LOGO_FLIP_WAIT_PROBABILITY) {
|
|
// CAMINO B (50%): Esperar flips
|
|
logo_waiting_for_flip_ = true;
|
|
logo_target_flip_number_ = LOGO_FLIP_WAIT_MIN + rand() % (LOGO_FLIP_WAIT_MAX - LOGO_FLIP_WAIT_MIN + 1);
|
|
logo_target_flip_percentage_ = LOGO_FLIP_TRIGGER_MIN + (rand() % 1000) / 1000.0f * (LOGO_FLIP_TRIGGER_MAX - LOGO_FLIP_TRIGGER_MIN);
|
|
logo_current_flip_count_ = 0;
|
|
|
|
auto* png_shape = dynamic_cast<PNGShape*>(shape_mgr_->getActiveShape());
|
|
if (png_shape != nullptr) {
|
|
png_shape->resetFlipCount();
|
|
}
|
|
// No hacer nada más — esperar a que ocurran los flips
|
|
} else {
|
|
// CAMINO A (50%): Cambio inmediato
|
|
if (action < 50) {
|
|
engine_->exitShapeMode(true); // SHAPE → PHYSICS con gravedad ON
|
|
} else {
|
|
engine_->exitShapeMode(false); // SHAPE → PHYSICS con gravedad OFF
|
|
}
|
|
|
|
logo_waiting_for_flip_ = false;
|
|
logo_current_flip_count_ = 0;
|
|
|
|
demo_timer_ = 0.0f;
|
|
float interval_range = logo_max_time_ - logo_min_time_;
|
|
demo_next_action_time_ = logo_min_time_ + (rand() % 1000) / 1000.0f * interval_range;
|
|
}
|
|
} else {
|
|
// Logo animado (PHYSICS) → 4 opciones
|
|
if (action < 50) {
|
|
// 50%: PHYSICS → SHAPE (reconstruir logo)
|
|
engine_->exitShapeMode(false); // toggleShapeMode: PHYSICS → SHAPE con last_type
|
|
|
|
logo_waiting_for_flip_ = false;
|
|
logo_current_flip_count_ = 0;
|
|
} else if (action < 68) {
|
|
// 18%: Forzar gravedad ON
|
|
scene_mgr_->forceBallsGravityOn();
|
|
} else if (action < 84) {
|
|
// 16%: Forzar gravedad OFF
|
|
scene_mgr_->forceBallsGravityOff();
|
|
} else {
|
|
// 16%: Cambiar dirección de gravedad
|
|
auto new_direction = static_cast<GravityDirection>(rand() % 4);
|
|
scene_mgr_->changeGravityDirection(new_direction);
|
|
scene_mgr_->forceBallsGravityOn();
|
|
}
|
|
|
|
demo_timer_ = 0.0f;
|
|
float interval_range = logo_max_time_ - logo_min_time_;
|
|
demo_next_action_time_ = logo_min_time_ + (rand() % 1000) / 1000.0f * interval_range;
|
|
}
|
|
|
|
// Salir automáticamente si la entrada fue automática (desde DEMO)
|
|
if (!logo_entered_manually_ && rand() % 100 < 60) {
|
|
exitLogoMode(true); // Volver a DEMO/DEMO_LITE
|
|
}
|
|
} else {
|
|
// DEMO/DEMO_LITE: Acciones normales
|
|
bool is_lite = (current_app_mode_ == AppMode::DEMO_LITE);
|
|
performDemoAction(is_lite);
|
|
|
|
demo_timer_ = 0.0f;
|
|
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;
|
|
|
|
demo_timer_ = 0.0f;
|
|
|
|
if (new_mode == AppMode::DEMO || new_mode == AppMode::DEMO_LITE || new_mode == AppMode::LOGO) {
|
|
float min_interval;
|
|
float max_interval;
|
|
|
|
if (new_mode == AppMode::LOGO) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void StateManager::toggleLogoMode(int current_screen_width, int current_screen_height, size_t ball_count) {
|
|
if (current_app_mode_ == AppMode::LOGO) {
|
|
exitLogoMode(false);
|
|
} else {
|
|
enterLogoMode(false, current_screen_width, current_screen_height, ball_count);
|
|
}
|
|
}
|
|
|
|
// ===========================================================================
|
|
// ACCIONES DE DEMO
|
|
// ===========================================================================
|
|
|
|
void StateManager::performDemoAction(bool is_lite) { // NOLINT(readability-function-cognitive-complexity)
|
|
if ((engine_ == nullptr) || (scene_mgr_ == nullptr) || (theme_mgr_ == nullptr) || (shape_mgr_ == nullptr)) {
|
|
return;
|
|
}
|
|
|
|
// ============================================
|
|
// SALTO AUTOMÁTICO A LOGO MODE (Easter Egg)
|
|
// ============================================
|
|
|
|
if (is_lite) {
|
|
if (static_cast<int>(scene_mgr_->getBallCount()) >= BALL_COUNT_SCENARIOS[LOGO_MIN_SCENARIO_IDX] &&
|
|
theme_mgr_->getCurrentThemeIndex() == 5) { // MONOCHROME
|
|
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO_LITE) {
|
|
enterLogoMode(true, 0, 0, scene_mgr_->getBallCount());
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
if (static_cast<int>(scene_mgr_->getBallCount()) >= BALL_COUNT_SCENARIOS[LOGO_MIN_SCENARIO_IDX]) {
|
|
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO) {
|
|
enterLogoMode(true, 0, 0, scene_mgr_->getBallCount());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// ACCIONES NORMALES DE DEMO/DEMO_LITE
|
|
// ============================================
|
|
|
|
int total_weight;
|
|
int random_value;
|
|
int accumulated_weight = 0;
|
|
|
|
if (is_lite) {
|
|
total_weight = DEMO_LITE_WEIGHT_GRAVITY_DIR + DEMO_LITE_WEIGHT_GRAVITY_TOGGLE + DEMO_LITE_WEIGHT_SHAPE + DEMO_LITE_WEIGHT_TOGGLE_PHYSICS + DEMO_LITE_WEIGHT_IMPULSE;
|
|
random_value = rand() % total_weight;
|
|
|
|
// Cambiar dirección gravedad (25%)
|
|
accumulated_weight += DEMO_LITE_WEIGHT_GRAVITY_DIR;
|
|
if (random_value < accumulated_weight) {
|
|
auto new_direction = static_cast<GravityDirection>(rand() % 4);
|
|
scene_mgr_->changeGravityDirection(new_direction);
|
|
return;
|
|
}
|
|
|
|
// Toggle gravedad ON/OFF (20%)
|
|
accumulated_weight += DEMO_LITE_WEIGHT_GRAVITY_TOGGLE;
|
|
if (random_value < accumulated_weight) {
|
|
toggleGravityOnOff();
|
|
return;
|
|
}
|
|
|
|
// Activar figura 3D (25%) - PNG_SHAPE excluido
|
|
accumulated_weight += DEMO_LITE_WEIGHT_SHAPE;
|
|
if (random_value < accumulated_weight) {
|
|
constexpr std::array<ShapeType, 8> SHAPES = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
engine_->enterShapeMode(SHAPES[rand() % 8]);
|
|
return;
|
|
}
|
|
|
|
// Toggle física ↔ figura (20%)
|
|
accumulated_weight += DEMO_LITE_WEIGHT_TOGGLE_PHYSICS;
|
|
if (random_value < accumulated_weight) {
|
|
engine_->exitShapeMode(false);
|
|
return;
|
|
}
|
|
|
|
// Aplicar impulso (10%)
|
|
accumulated_weight += DEMO_LITE_WEIGHT_IMPULSE;
|
|
if (random_value < accumulated_weight) {
|
|
scene_mgr_->pushBallsAwayFromGravity();
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
total_weight = DEMO_WEIGHT_GRAVITY_DIR + DEMO_WEIGHT_GRAVITY_TOGGLE + DEMO_WEIGHT_SHAPE + DEMO_WEIGHT_TOGGLE_PHYSICS + DEMO_WEIGHT_REGENERATE_SHAPE + DEMO_WEIGHT_THEME + DEMO_WEIGHT_SCENARIO + DEMO_WEIGHT_IMPULSE + DEMO_WEIGHT_DEPTH_ZOOM + DEMO_WEIGHT_SHAPE_SCALE + DEMO_WEIGHT_SPRITE;
|
|
random_value = rand() % total_weight;
|
|
|
|
// Cambiar dirección gravedad (10%)
|
|
accumulated_weight += DEMO_WEIGHT_GRAVITY_DIR;
|
|
if (random_value < accumulated_weight) {
|
|
auto new_direction = static_cast<GravityDirection>(rand() % 4);
|
|
scene_mgr_->changeGravityDirection(new_direction);
|
|
return;
|
|
}
|
|
|
|
// Toggle gravedad ON/OFF (8%)
|
|
accumulated_weight += DEMO_WEIGHT_GRAVITY_TOGGLE;
|
|
if (random_value < accumulated_weight) {
|
|
toggleGravityOnOff();
|
|
return;
|
|
}
|
|
|
|
// Activar figura 3D (20%) - PNG_SHAPE excluido
|
|
accumulated_weight += DEMO_WEIGHT_SHAPE;
|
|
if (random_value < accumulated_weight) {
|
|
constexpr std::array<ShapeType, 8> SHAPES = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
engine_->enterShapeMode(SHAPES[rand() % 8]);
|
|
return;
|
|
}
|
|
|
|
// Toggle física ↔ figura (12%)
|
|
accumulated_weight += DEMO_WEIGHT_TOGGLE_PHYSICS;
|
|
if (random_value < accumulated_weight) {
|
|
engine_->exitShapeMode(false);
|
|
return;
|
|
}
|
|
|
|
// Re-generar misma figura (8%)
|
|
accumulated_weight += DEMO_WEIGHT_REGENERATE_SHAPE;
|
|
if (random_value < accumulated_weight) {
|
|
if (shape_mgr_->isShapeModeActive()) {
|
|
shape_mgr_->generateShape();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Cambiar tema (15%)
|
|
accumulated_weight += DEMO_WEIGHT_THEME;
|
|
if (random_value < accumulated_weight) {
|
|
theme_mgr_->switchToTheme(rand() % 15);
|
|
return;
|
|
}
|
|
|
|
// Cambiar escenario (10%)
|
|
accumulated_weight += DEMO_WEIGHT_SCENARIO;
|
|
if (random_value < accumulated_weight) {
|
|
int auto_max = std::min(engine_->getMaxAutoScenario(), DEMO_AUTO_MAX_SCENARIO);
|
|
std::vector<int> candidates;
|
|
for (int i = DEMO_AUTO_MIN_SCENARIO; i <= auto_max; ++i) {
|
|
candidates.push_back(i);
|
|
}
|
|
if (engine_->isCustomScenarioEnabled() && engine_->isCustomAutoAvailable()) {
|
|
candidates.push_back(CUSTOM_SCENARIO_IDX);
|
|
}
|
|
int new_scenario = candidates[rand() % candidates.size()];
|
|
SimulationMode current_sim_mode = shape_mgr_->getCurrentMode();
|
|
scene_mgr_->changeScenario(new_scenario, current_sim_mode);
|
|
|
|
if (shape_mgr_->isShapeModeActive()) {
|
|
shape_mgr_->generateShape();
|
|
scene_mgr_->enableShapeAttractionAll(true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Aplicar impulso (10%)
|
|
accumulated_weight += DEMO_WEIGHT_IMPULSE;
|
|
if (random_value < accumulated_weight) {
|
|
scene_mgr_->pushBallsAwayFromGravity();
|
|
return;
|
|
}
|
|
|
|
// Toggle profundidad (3%)
|
|
accumulated_weight += DEMO_WEIGHT_DEPTH_ZOOM;
|
|
if (random_value < accumulated_weight) {
|
|
if (shape_mgr_->isShapeModeActive()) {
|
|
shape_mgr_->setDepthZoomEnabled(!shape_mgr_->isDepthZoomEnabled());
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Cambiar escala de figura (2%)
|
|
accumulated_weight += DEMO_WEIGHT_SHAPE_SCALE;
|
|
if (random_value < accumulated_weight) {
|
|
if (shape_mgr_->isShapeModeActive()) {
|
|
int scale_action = rand() % 3;
|
|
if (scale_action == 0) {
|
|
shape_mgr_->setShapeScaleFactor(shape_mgr_->getShapeScaleFactor() + SHAPE_SCALE_STEP);
|
|
} else if (scale_action == 1) {
|
|
shape_mgr_->setShapeScaleFactor(shape_mgr_->getShapeScaleFactor() - SHAPE_SCALE_STEP);
|
|
} else {
|
|
shape_mgr_->setShapeScaleFactor(SHAPE_SCALE_DEFAULT);
|
|
}
|
|
shape_mgr_->generateShape();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Cambiar sprite (2%)
|
|
accumulated_weight += DEMO_WEIGHT_SPRITE;
|
|
if (random_value < accumulated_weight) {
|
|
engine_->switchTextureSilent();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===========================================================================
|
|
// RANDOMIZACIÓN AL INICIAR DEMO
|
|
// ===========================================================================
|
|
|
|
void StateManager::randomizeOnDemoStart(bool is_lite) { // NOLINT(readability-function-cognitive-complexity)
|
|
if ((engine_ == nullptr) || (scene_mgr_ == nullptr) || (theme_mgr_ == nullptr) || (shape_mgr_ == nullptr)) {
|
|
return;
|
|
}
|
|
|
|
// Si venimos de LOGO con PNG_SHAPE, cambiar figura obligatoriamente
|
|
if (shape_mgr_->getCurrentShapeType() == ShapeType::PNG_SHAPE) {
|
|
constexpr std::array<ShapeType, 8> SHAPES = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
engine_->enterShapeMode(SHAPES[rand() % 8]);
|
|
}
|
|
|
|
if (is_lite) {
|
|
// DEMO LITE: Solo randomizar física/figura + gravedad
|
|
if (rand() % 2 == 0) {
|
|
if (shape_mgr_->isShapeModeActive()) {
|
|
engine_->exitShapeMode(false);
|
|
}
|
|
} else {
|
|
constexpr std::array<ShapeType, 8> SHAPES = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
engine_->enterShapeMode(SHAPES[rand() % 8]);
|
|
}
|
|
|
|
auto new_direction = static_cast<GravityDirection>(rand() % 4);
|
|
scene_mgr_->changeGravityDirection(new_direction);
|
|
if (rand() % 2 == 0) {
|
|
toggleGravityOnOff();
|
|
}
|
|
|
|
} else {
|
|
// DEMO COMPLETO: Randomizar TODO
|
|
|
|
// 1. Física o Figura
|
|
if (rand() % 2 == 0) {
|
|
if (shape_mgr_->isShapeModeActive()) {
|
|
engine_->exitShapeMode(false);
|
|
}
|
|
} else {
|
|
constexpr std::array<ShapeType, 8> SHAPES = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
ShapeType selected_shape = SHAPES[rand() % 8];
|
|
|
|
// Randomizar profundidad y escala ANTES de activar la figura
|
|
if (rand() % 2 == 0) {
|
|
shape_mgr_->setDepthZoomEnabled(!shape_mgr_->isDepthZoomEnabled());
|
|
}
|
|
shape_mgr_->setShapeScaleFactor(0.5f + ((rand() % 1500) / 1000.0f));
|
|
|
|
engine_->enterShapeMode(selected_shape);
|
|
}
|
|
|
|
// 2. Escenario
|
|
int auto_max = std::min(engine_->getMaxAutoScenario(), DEMO_AUTO_MAX_SCENARIO);
|
|
std::vector<int> candidates;
|
|
for (int i = DEMO_AUTO_MIN_SCENARIO; i <= auto_max; ++i) {
|
|
candidates.push_back(i);
|
|
}
|
|
if (engine_->isCustomScenarioEnabled() && engine_->isCustomAutoAvailable()) {
|
|
candidates.push_back(CUSTOM_SCENARIO_IDX);
|
|
}
|
|
int new_scenario = candidates[rand() % candidates.size()];
|
|
SimulationMode current_sim_mode = shape_mgr_->getCurrentMode();
|
|
scene_mgr_->changeScenario(new_scenario, current_sim_mode);
|
|
|
|
if (shape_mgr_->isShapeModeActive()) {
|
|
shape_mgr_->generateShape();
|
|
scene_mgr_->enableShapeAttractionAll(true);
|
|
}
|
|
|
|
// 3. Tema
|
|
theme_mgr_->switchToTheme(rand() % 15);
|
|
|
|
// 4. Sprite
|
|
if (rand() % 2 == 0) {
|
|
engine_->switchTextureSilent();
|
|
}
|
|
|
|
// 5. Gravedad
|
|
auto new_direction = static_cast<GravityDirection>(rand() % 4);
|
|
scene_mgr_->changeGravityDirection(new_direction);
|
|
if (rand() % 3 == 0) {
|
|
toggleGravityOnOff();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===========================================================================
|
|
// TOGGLE GRAVEDAD (para DEMO)
|
|
// ===========================================================================
|
|
|
|
void StateManager::toggleGravityOnOff() {
|
|
if (scene_mgr_ == nullptr) {
|
|
return;
|
|
}
|
|
|
|
bool gravity_enabled = scene_mgr_->hasBalls() &&
|
|
(scene_mgr_->getFirstBall()->getGravityForce() > 0.0f);
|
|
|
|
if (gravity_enabled) {
|
|
scene_mgr_->forceBallsGravityOff();
|
|
} else {
|
|
scene_mgr_->forceBallsGravityOn();
|
|
}
|
|
}
|
|
|
|
// ===========================================================================
|
|
// ENTRAR AL MODO LOGO
|
|
// ===========================================================================
|
|
|
|
void StateManager::enterLogoMode(bool from_demo, int current_screen_width, int current_screen_height, size_t ball_count) {
|
|
if ((engine_ == nullptr) || (scene_mgr_ == nullptr) || (theme_mgr_ == nullptr) || (shape_mgr_ == nullptr)) {
|
|
return;
|
|
}
|
|
|
|
logo_entered_manually_ = !from_demo;
|
|
|
|
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);
|
|
|
|
// Verificar mínimo de pelotas
|
|
if (scene_mgr_->getCurrentScenario() < LOGO_MIN_SCENARIO_IDX) {
|
|
scene_mgr_->changeScenario(LOGO_MIN_SCENARIO_IDX, shape_mgr_->getCurrentMode());
|
|
}
|
|
|
|
// Guardar estado previo (para restaurar al salir)
|
|
logo_previous_theme_ = theme_mgr_->getCurrentThemeIndex();
|
|
logo_previous_texture_index_ = engine_->getCurrentTextureIndex();
|
|
logo_previous_shape_scale_ = shape_mgr_->getShapeScaleFactor();
|
|
|
|
// Aplicar textura "small" si existe — buscar por nombre iterando índices
|
|
if (engine_->getCurrentTextureName() != "small") {
|
|
size_t original_idx = logo_previous_texture_index_;
|
|
bool found = false;
|
|
for (size_t i = 0; i < 20; ++i) {
|
|
engine_->setTextureByIndex(i);
|
|
if (engine_->getCurrentTextureName() == "small") {
|
|
found = true;
|
|
break;
|
|
}
|
|
if (engine_->getCurrentTextureName().empty()) {
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
engine_->setTextureByIndex(original_idx);
|
|
}
|
|
}
|
|
|
|
// Cambiar a tema aleatorio entre: MONOCHROME, LAVENDER, CRIMSON, ESMERALDA
|
|
constexpr std::array<int, 4> LOGO_THEMES = {5, 6, 7, 8};
|
|
theme_mgr_->switchToTheme(LOGO_THEMES[rand() % 4]);
|
|
|
|
// Establecer escala a 120%
|
|
shape_mgr_->setShapeScaleFactor(LOGO_MODE_SHAPE_SCALE);
|
|
|
|
// Activar PNG_SHAPE (el logo)
|
|
engine_->enterShapeMode(ShapeType::PNG_SHAPE);
|
|
|
|
// Configurar PNG_SHAPE en modo LOGO
|
|
auto* png_shape = dynamic_cast<PNGShape*>(shape_mgr_->getActiveShape());
|
|
if (png_shape != nullptr) {
|
|
png_shape->setLogoMode(true);
|
|
png_shape->resetFlipCount();
|
|
}
|
|
}
|
|
|
|
// ===========================================================================
|
|
// SALIR DEL MODO LOGO
|
|
// ===========================================================================
|
|
|
|
void StateManager::exitLogoMode(bool return_to_demo) {
|
|
if (current_app_mode_ != AppMode::LOGO) {
|
|
return;
|
|
}
|
|
if ((engine_ == nullptr) || (scene_mgr_ == nullptr) || (theme_mgr_ == nullptr) || (shape_mgr_ == nullptr)) {
|
|
return;
|
|
}
|
|
|
|
logo_entered_manually_ = false;
|
|
|
|
// Restaurar estado visual previo
|
|
theme_mgr_->switchToTheme(logo_previous_theme_);
|
|
engine_->setTextureByIndex(logo_previous_texture_index_);
|
|
shape_mgr_->setShapeScaleFactor(logo_previous_shape_scale_);
|
|
shape_mgr_->generateShape();
|
|
|
|
// Activar atracción física si estamos en modo SHAPE
|
|
if (shape_mgr_->isShapeModeActive()) {
|
|
scene_mgr_->enableShapeAttractionAll(true);
|
|
}
|
|
|
|
// Desactivar modo LOGO en PNG_SHAPE
|
|
auto* png_shape = dynamic_cast<PNGShape*>(shape_mgr_->getActiveShape());
|
|
if (png_shape != nullptr) {
|
|
png_shape->setLogoMode(false);
|
|
}
|
|
|
|
// Si la figura activa es PNG_SHAPE, cambiar a otra figura aleatoria
|
|
if (shape_mgr_->getCurrentShapeType() == ShapeType::PNG_SHAPE) {
|
|
constexpr std::array<ShapeType, 8> SHAPES = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
engine_->enterShapeMode(SHAPES[rand() % 8]);
|
|
}
|
|
|
|
if (!return_to_demo) {
|
|
setState(AppMode::SANDBOX, 0, 0);
|
|
} else {
|
|
current_app_mode_ = previous_app_mode_;
|
|
}
|
|
}
|