diff --git a/ROADMAP.md b/ROADMAP.md index ccd0644..755a308 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -68,9 +68,34 @@ - Pausable con cualquier tecla de usuario - Indicador visual "DEMO MODE" en pantalla -### 3. ⏳ Sistema de Release -**Descripción:** Empaquetado para distribución standalone +### 3. ⏳ Resolución Lógica Configurable +**Descripción:** Especificar resolución lógica por parámetros de línea de comandos **Prioridad:** Media +**Estimación:** 1 hora +**Detalles:** +- Parámetros `--width --height ` o `-w -h ` +- Parámetro `--fullscreen` o `-f` (opcional) +- Defaults: 1280x720 en ventana +- Ejemplo: `./vibe3_physics -w 1920 -h 1080 -f` +- Validación de valores (mínimo 640x480, máximo según SDL_DisplayMode) +- Help text con `-h` o `--help` + +### 4. 🐛 Corregir Escalado de Pelotas en Reposo +**Descripción:** Las pelotas cambian de tamaño cuando están quietas (bug visual) +**Prioridad:** Alta (bug visible) +**Estimación:** 30 minutos +**Detalles:** +- **Síntoma:** Pelotas en reposo (velocidad ≈ 0) se escalan incorrectamente +- **Posible causa:** + - Scale factor calculado desde velocidad o energía + - División por cero o valor muy pequeño + - Interpolación incorrecta en modo transición +- **Investigar:** Ball::render(), scale calculations, depth brightness +- **Solución esperada:** Tamaño constante independiente de velocidad + +### 5. ⏳ Sistema de Release +**Descripción:** Empaquetado para distribución standalone +**Prioridad:** Baja **Estimación:** 30 minutos **Detalles:** - Crear carpeta `release/` diff --git a/source/ball.cpp b/source/ball.cpp index 9de7ec6..d17ce8c 100644 --- a/source/ball.cpp +++ b/source/ball.cpp @@ -39,7 +39,6 @@ Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr te screen_width_ = screen_width; // Dimensiones del terreno de juego screen_height_ = screen_height; on_surface_ = false; - stopped_ = false; // Coeficiente base IGUAL para todas las pelotas (solo variación por rebote individual) loss_ = BASE_BOUNCE_COEFFICIENT; // Coeficiente fijo para todas las pelotas @@ -56,10 +55,6 @@ Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr te // Actualiza la lógica de la clase void Ball::update(float deltaTime) { - if (stopped_) { - return; - } - // Aplica la gravedad según la dirección (píxeles/segundo²) if (!on_surface_) { // Aplicar gravedad multiplicada por factor de masa individual @@ -190,7 +185,6 @@ void Ball::update(float deltaTime) { vx_ = vx_ * friction_factor; if (std::fabs(vx_) < 6.0f) { vx_ = 0.0f; - stopped_ = true; } break; case GravityDirection::LEFT: @@ -199,7 +193,6 @@ void Ball::update(float deltaTime) { vy_ = vy_ * friction_factor; if (std::fabs(vy_) < 6.0f) { vy_ = 0.0f; - stopped_ = true; } break; } @@ -220,7 +213,6 @@ void Ball::modVel(float vx, float vy) { vx_ = vx_ + (vx * 60.0f); // Convertir a píxeles/segundo vy_ = vy_ + (vy * 60.0f); // Convertir a píxeles/segundo on_surface_ = false; - stopped_ = false; } // Cambia la gravedad (usa la versión convertida) @@ -249,7 +241,6 @@ void Ball::forceGravityOff() { void Ball::setGravityDirection(GravityDirection direction) { gravity_direction_ = direction; on_surface_ = false; // Ya no está en superficie al cambiar dirección - stopped_ = false; // Reactivar movimiento } // Aplica un pequeño empuje lateral aleatorio @@ -309,7 +300,6 @@ void Ball::enableRotoBallAttraction(bool enable) { // Al activar atracción, resetear flags de superficie para permitir física completa if (enable) { on_surface_ = false; - stopped_ = false; } } diff --git a/source/ball.h b/source/ball.h index 8a77ab1..ac9689d 100644 --- a/source/ball.h +++ b/source/ball.h @@ -20,7 +20,6 @@ class Ball { int screen_height_; // Alto del terreno de juego Color color_; // Color de la pelota bool on_surface_; // Indica si la pelota est\u00e1 en la superficie (suelo/techo/pared) - bool stopped_; // Indica si la pelota ha terminado de moverse; float loss_; // Coeficiente de rebote. Pérdida de energía en cada rebote // Datos para modo RotoBall (esfera 3D) @@ -71,7 +70,6 @@ class Ball { float getLossCoefficient() const { return loss_; } GravityDirection getGravityDirection() const { return gravity_direction_; } bool isOnSurface() const { return on_surface_; } - bool isStopped() const { return stopped_; } // Getters/Setters para batch rendering SDL_FRect getPosition() const { return pos_; } diff --git a/source/defines.h b/source/defines.h index 1a7aa97..32e954d 100644 --- a/source/defines.h +++ b/source/defines.h @@ -153,4 +153,19 @@ constexpr float SHAPE_SCALE_MAX = 3.0f; // Escala máxima (300%) constexpr float SHAPE_SCALE_STEP = 0.1f; // Incremento por pulsación constexpr float SHAPE_SCALE_DEFAULT = 1.0f; // Escala por defecto (100%) +// Configuración de Modo DEMO (auto-play) +constexpr float DEMO_ACTION_INTERVAL_MIN = 3.0f; // Tiempo mínimo entre acciones (segundos) +constexpr float DEMO_ACTION_INTERVAL_MAX = 8.0f; // Tiempo máximo entre acciones (segundos) + +// Pesos de probabilidad para cada tipo de acción (valores relativos, se normalizan) +constexpr int DEMO_WEIGHT_GRAVITY = 15; // Cambiar dirección gravedad (15%) +constexpr int DEMO_WEIGHT_SHAPE = 25; // Activar figura 3D (25%) +constexpr int DEMO_WEIGHT_THEME = 20; // Cambiar tema de colores (20%) +constexpr int DEMO_WEIGHT_SCENARIO = 15; // Cambiar número de pelotas (15%) +constexpr int DEMO_WEIGHT_IMPULSE = 10; // Aplicar impulso (SPACE) (10%) +constexpr int DEMO_WEIGHT_DEPTH_ZOOM = 5; // Toggle profundidad (5%) +constexpr int DEMO_WEIGHT_SHAPE_SCALE = 5; // Cambiar escala figura (5%) +constexpr int DEMO_WEIGHT_SPRITE = 5; // Cambiar sprite (5%) +// TOTAL: 100 (se pueden ajustar para priorizar acciones) + constexpr float PI = 3.14159265358979323846f; // Constante PI \ No newline at end of file diff --git a/source/engine.cpp b/source/engine.cpp index 2d25fa3..aad084e 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -166,9 +166,6 @@ void Engine::update() { for (auto &ball : balls_) { ball->update(delta_time_); // Pasar delta time a cada pelota } - - // Verificar auto-reinicio cuando todas las pelotas están quietas (solo en modo física) - checkAutoRestart(); } else if (current_mode_ == SimulationMode::SHAPE) { // Modo Figura 3D: actualizar figura polimórfica updateShape(); @@ -179,6 +176,9 @@ void Engine::update() { show_text_ = !(SDL_GetTicks() - text_init_time_ > TEXT_DURATION); } + // Actualizar Modo DEMO (auto-play) + updateDemoMode(); + // Actualizar transición de tema (LERP) if (transitioning_) { transition_progress_ += delta_time_ / transition_duration_; @@ -468,6 +468,27 @@ void Engine::handleEvents() { case SDLK_F5: toggleIntegerScaling(); break; + + // Toggle Modo DEMO (auto-play) + case SDLK_D: + demo_mode_enabled_ = !demo_mode_enabled_; + if (demo_mode_enabled_) { + // Al activar: 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 + text_ = "DEMO MODE ON"; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } else { + // Al desactivar: mostrar texto + text_ = "DEMO MODE OFF"; + show_text_ = true; + text_init_time_ = SDL_GetTicks(); + } + break; } } } @@ -608,6 +629,15 @@ void Engine::render() { dbg_print(8, 72, mode_text.c_str(), 0, 255, 128); // Verde claro para modo } + // Mostrar indicador "DEMO MODE" permanente cuando está activo (independiente de show_debug_) + if (demo_mode_enabled_) { + // Centrar texto horizontalmente + const char* demo_text = "DEMO MODE"; + int demo_text_width = static_cast(strlen(demo_text) * 8); // 8 píxeles por carácter + int demo_x = (current_screen_width_ - demo_text_width) / 2; + dbg_print(demo_x, 8, demo_text, 255, 128, 0); // Naranja brillante + } + SDL_RenderPresent(renderer_); } @@ -1167,48 +1197,113 @@ Color Engine::getInterpolatedColor(size_t ball_index) const { }; } -void Engine::checkAutoRestart() { - // Verificar si TODAS las pelotas están paradas - bool all_stopped = true; - for (const auto &ball : balls_) { - if (!ball->isStopped()) { - all_stopped = false; - break; - } - } +// Sistema de Modo DEMO (auto-play) +void Engine::updateDemoMode() { + if (!demo_mode_enabled_) return; - if (all_stopped) { - if (!all_balls_were_stopped_) { - // Primera vez que se detecta que todas están paradas - all_balls_stopped_start_time_ = SDL_GetTicks(); - all_balls_were_stopped_ = true; - } else { - // Ya estaban paradas, verificar tiempo transcurrido - Uint64 current_time = SDL_GetTicks(); - if (current_time - all_balls_stopped_start_time_ >= AUTO_RESTART_DELAY) { - performRandomRestart(); - } - } - } else { - // Al menos una pelota se está moviendo - resetear temporizador - all_balls_were_stopped_ = false; - all_balls_stopped_start_time_ = 0; + // Actualizar timer + demo_timer_ += delta_time_; + + // Si es hora de ejecutar acción + if (demo_timer_ >= demo_next_action_time_) { + performDemoAction(); + + // Resetear timer y calcular próximo 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; } } -void Engine::performRandomRestart() { - // Escenario aleatorio usando tamaño del array - scenario_ = rand() % test_.size(); +void Engine::performDemoAction() { + // Calcular suma total de pesos + const int TOTAL_WEIGHT = DEMO_WEIGHT_GRAVITY + DEMO_WEIGHT_SHAPE + DEMO_WEIGHT_THEME + + DEMO_WEIGHT_SCENARIO + DEMO_WEIGHT_IMPULSE + DEMO_WEIGHT_DEPTH_ZOOM + + DEMO_WEIGHT_SHAPE_SCALE + DEMO_WEIGHT_SPRITE; - // Tema aleatorio usando tamaño del array de temas - current_theme_ = static_cast(rand() % (sizeof(themes_) / sizeof(themes_[0]))); + // Generar número aleatorio entre 0 y TOTAL_WEIGHT + int random_value = rand() % TOTAL_WEIGHT; - // Reinicializar pelotas con nuevo escenario y tema - initBalls(scenario_); + // Determinar acción según pesos acumulados + int accumulated_weight = 0; - // Resetear temporizador - all_balls_were_stopped_ = false; - all_balls_stopped_start_time_ = 0; + // Acción: Cambiar gravedad (15%) + accumulated_weight += DEMO_WEIGHT_GRAVITY; + if (random_value < accumulated_weight) { + GravityDirection new_direction = static_cast(rand() % 4); + changeGravityDirection(new_direction); + return; + } + + // Acción: Activar figura 3D (25%) + accumulated_weight += DEMO_WEIGHT_SHAPE; + if (random_value < accumulated_weight) { + // Elegir figura aleatoria (SPHERE a ATOM = 8 figuras) + ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, + ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, + ShapeType::ICOSAHEDRON, ShapeType::ATOM}; + int shape_index = rand() % 8; + activateShape(shapes[shape_index]); + return; + } + + // Acción: Cambiar tema (20%) + accumulated_weight += DEMO_WEIGHT_THEME; + if (random_value < accumulated_weight) { + ColorTheme new_theme = static_cast(rand() % 6); // 6 temas disponibles + startThemeTransition(new_theme); + return; + } + + // Acción: Cambiar escenario/número de pelotas (15%) + accumulated_weight += DEMO_WEIGHT_SCENARIO; + if (random_value < accumulated_weight) { + scenario_ = rand() % test_.size(); + initBalls(scenario_); + return; + } + + // Acción: Aplicar impulso (10%) + accumulated_weight += DEMO_WEIGHT_IMPULSE; + if (random_value < accumulated_weight) { + pushBallsAwayFromGravity(); + return; + } + + // Acción: Toggle profundidad (5%) + accumulated_weight += DEMO_WEIGHT_DEPTH_ZOOM; + if (random_value < accumulated_weight) { + if (current_mode_ == SimulationMode::SHAPE) { + depth_zoom_enabled_ = !depth_zoom_enabled_; + } + return; + } + + // Acción: Cambiar escala de figura (5%) + accumulated_weight += DEMO_WEIGHT_SHAPE_SCALE; + if (random_value < accumulated_weight) { + if (current_mode_ == SimulationMode::SHAPE) { + // Aleatorio: +1, -1, o reset + int scale_action = rand() % 3; + if (scale_action == 0) { + shape_scale_factor_ += SHAPE_SCALE_STEP; + } else if (scale_action == 1) { + shape_scale_factor_ -= SHAPE_SCALE_STEP; + } else { + shape_scale_factor_ = SHAPE_SCALE_DEFAULT; + } + clampShapeScale(); + generateShape(); + } + return; + } + + // Acción: Cambiar sprite (5%) + accumulated_weight += DEMO_WEIGHT_SPRITE; + if (random_value < accumulated_weight) { + switchTexture(); + return; + } } // Sistema de cambio de sprites dinámico diff --git a/source/engine.h b/source/engine.h index 0e74d6c..b3dc353 100644 --- a/source/engine.h +++ b/source/engine.h @@ -63,11 +63,6 @@ private: bool real_fullscreen_enabled_ = false; ScalingMode current_scaling_mode_ = ScalingMode::INTEGER; // Modo de escalado actual (F5) - // Auto-restart system - Uint64 all_balls_stopped_start_time_ = 0; // Momento cuando todas se pararon - bool all_balls_were_stopped_ = false; // Flag de estado anterior - static constexpr Uint64 AUTO_RESTART_DELAY = 5000; // 5 segundos en ms - // Resolución dinámica para modo real fullscreen int current_screen_width_ = SCREEN_WIDTH; int current_screen_height_ = SCREEN_HEIGHT; @@ -100,6 +95,11 @@ private: float shape_scale_factor_ = 1.0f; // Factor de escala manual (Numpad +/-) bool depth_zoom_enabled_ = true; // Zoom por profundidad Z activado + // Sistema de Modo DEMO (auto-play) + bool demo_mode_enabled_ = false; // ¿Está activo el modo demo? + float demo_timer_ = 0.0f; // Contador de tiempo para próxima acción + float demo_next_action_time_ = 0.0f; // Tiempo aleatorio hasta próxima acción (segundos) + // Batch rendering std::vector batch_vertices_; std::vector batch_indices_; @@ -125,8 +125,10 @@ private: void toggleIntegerScaling(); std::string gravityDirectionToString(GravityDirection direction) const; void initializeThemes(); - void checkAutoRestart(); - void performRandomRestart(); + + // Sistema de Modo DEMO + void updateDemoMode(); + void performDemoAction(); // Sistema de transiciones LERP float lerp(float a, float b, float t) const { return a + (b - a) * t; }