#include "scene_manager.h" #include // for rand #include "../defines.h" // for BALL_COUNT_SCENARIOS, GRAVITY_MASS_MIN, etc #include "../external/texture.h" // for Texture #include "../theme_manager.h" // for ThemeManager SceneManager::SceneManager(int screen_width, int screen_height) : current_gravity_(GravityDirection::DOWN) , scenario_(0) , screen_width_(screen_width) , screen_height_(screen_height) , current_ball_size_(10) , texture_(nullptr) , theme_manager_(nullptr) { } void SceneManager::initialize(int scenario, std::shared_ptr texture, ThemeManager* theme_manager) { scenario_ = scenario; texture_ = texture; theme_manager_ = theme_manager; current_ball_size_ = texture_->getWidth(); // Crear bolas iniciales (siempre en modo PHYSICS al inicio) changeScenario(scenario_, SimulationMode::PHYSICS); } void SceneManager::update(float delta_time) { // Actualizar física de todas las bolas for (auto& ball : balls_) { ball->update(delta_time); } } void SceneManager::changeScenario(int scenario_id, SimulationMode mode) { // Guardar escenario scenario_ = scenario_id; // Limpiar las bolas actuales balls_.clear(); // Resetear gravedad al estado por defecto (DOWN) al cambiar escenario changeGravityDirection(GravityDirection::DOWN); // Crear las bolas según el escenario for (int i = 0; i < BALL_COUNT_SCENARIOS[scenario_id]; ++i) { float X, Y, VX, VY; // Inicialización según SimulationMode (RULES.md líneas 23-26) switch (mode) { case SimulationMode::PHYSICS: { // PHYSICS: Parte superior, 75% distribución central en X const int SIGN = ((rand() % 2) * 2) - 1; const int margin = static_cast(screen_width_ * BALL_SPAWN_MARGIN); const int spawn_zone_width = screen_width_ - (2 * margin); X = (rand() % spawn_zone_width) + margin; Y = 0.0f; // Parte superior VX = (((rand() % 20) + 10) * 0.1f) * SIGN; VY = ((rand() % 60) - 30) * 0.1f; break; } case SimulationMode::SHAPE: { // SHAPE: Centro de pantalla, sin velocidad inicial X = screen_width_ / 2.0f; Y = screen_height_ / 2.0f; // Centro vertical VX = 0.0f; VY = 0.0f; break; } case SimulationMode::BOIDS: { // BOIDS: Posiciones aleatorias, velocidades aleatorias const int SIGN_X = ((rand() % 2) * 2) - 1; const int SIGN_Y = ((rand() % 2) * 2) - 1; X = static_cast(rand() % screen_width_); Y = static_cast(rand() % screen_height_); // Posición Y aleatoria VX = (((rand() % 40) + 10) * 0.1f) * SIGN_X; // 1.0 - 5.0 px/frame VY = (((rand() % 40) + 10) * 0.1f) * SIGN_Y; break; } default: // Fallback a PHYSICS por seguridad const int SIGN = ((rand() % 2) * 2) - 1; const int margin = static_cast(screen_width_ * BALL_SPAWN_MARGIN); const int spawn_zone_width = screen_width_ - (2 * margin); X = (rand() % spawn_zone_width) + margin; Y = 0.0f; // Parte superior VX = (((rand() % 20) + 10) * 0.1f) * SIGN; VY = ((rand() % 60) - 30) * 0.1f; break; } // Seleccionar color de la paleta del tema actual (delegado a ThemeManager) int random_index = rand(); Color COLOR = theme_manager_->getInitialBallColor(random_index); // Generar factor de masa aleatorio (0.7 = ligera, 1.3 = pesada) float mass_factor = GRAVITY_MASS_MIN + (rand() % 1000) / 1000.0f * (GRAVITY_MASS_MAX - GRAVITY_MASS_MIN); balls_.emplace_back(std::make_unique( X, Y, VX, VY, COLOR, texture_, screen_width_, screen_height_, current_ball_size_, current_gravity_, mass_factor )); } } void SceneManager::updateBallTexture(std::shared_ptr new_texture, int new_ball_size) { if (balls_.empty()) return; // Guardar tamaño antiguo int old_size = current_ball_size_; // Actualizar textura y tamaño texture_ = new_texture; current_ball_size_ = new_ball_size; // Actualizar texturas de todas las pelotas for (auto& ball : balls_) { ball->setTexture(texture_); } // Ajustar posiciones según el cambio de tamaño updateBallSizes(old_size, new_ball_size); } void SceneManager::pushBallsAwayFromGravity() { for (auto& ball : balls_) { const int SIGNO = ((rand() % 2) * 2) - 1; const float LATERAL = (((rand() % 20) + 10) * 0.1f) * SIGNO; const float MAIN = ((rand() % 40) * 0.1f) + 5; float vx = 0, vy = 0; switch (current_gravity_) { case GravityDirection::DOWN: // Impulsar ARRIBA vx = LATERAL; vy = -MAIN; break; case GravityDirection::UP: // Impulsar ABAJO vx = LATERAL; vy = MAIN; break; case GravityDirection::LEFT: // Impulsar DERECHA vx = MAIN; vy = LATERAL; break; case GravityDirection::RIGHT: // Impulsar IZQUIERDA vx = -MAIN; vy = LATERAL; break; } ball->modVel(vx, vy); // Modifica la velocidad según dirección de gravedad } } void SceneManager::switchBallsGravity() { for (auto& ball : balls_) { ball->switchGravity(); } } void SceneManager::enableBallsGravityIfDisabled() { for (auto& ball : balls_) { ball->enableGravityIfDisabled(); } } void SceneManager::forceBallsGravityOn() { for (auto& ball : balls_) { ball->forceGravityOn(); } } void SceneManager::forceBallsGravityOff() { // Contar cuántas pelotas están en superficie (suelo/techo/pared) int balls_on_surface = 0; for (const auto& ball : balls_) { if (ball->isOnSurface()) { balls_on_surface++; } } // Si la mayoría (>50%) están en superficie, aplicar impulso para que se vea el efecto float surface_ratio = static_cast(balls_on_surface) / static_cast(balls_.size()); if (surface_ratio > 0.5f) { pushBallsAwayFromGravity(); // Dar impulso contrario a gravedad } // Desactivar gravedad for (auto& ball : balls_) { ball->forceGravityOff(); } } void SceneManager::changeGravityDirection(GravityDirection direction) { current_gravity_ = direction; for (auto& ball : balls_) { ball->setGravityDirection(direction); ball->applyRandomLateralPush(); // Aplicar empuje lateral aleatorio } } void SceneManager::updateScreenSize(int width, int height) { screen_width_ = width; screen_height_ = height; // NOTA: No actualizamos las bolas existentes, solo afecta a futuras creaciones // Si se requiere reposicionar bolas existentes, implementar aquí } // === Métodos privados === void SceneManager::updateBallSizes(int old_size, int new_size) { for (auto& ball : balls_) { SDL_FRect pos = ball->getPosition(); // Ajustar posición para compensar cambio de tamaño // Si aumenta tamaño, mover hacia centro; si disminuye, alejar del centro float center_x = screen_width_ / 2.0f; float center_y = screen_height_ / 2.0f; float dx = pos.x - center_x; float dy = pos.y - center_y; // Ajustar proporcionalmente (evitar divisiones por cero) if (old_size > 0) { float scale_factor = static_cast(new_size) / static_cast(old_size); pos.x = center_x + dx * scale_factor; pos.y = center_y + dy * scale_factor; } // Actualizar tamaño del hitbox ball->updateSize(new_size); } }