Refactor fase 2: Extraer SceneManager de Engine
Migra toda la lógica de gestión de bolas y física a SceneManager siguiendo el principio de Single Responsibility (SRP). ## Archivos Nuevos **source/scene/scene_manager.h:** - Declaración de clase SceneManager - Gestión de bolas (creación, destrucción, actualización) - Control de gravedad direccional y estado - Métodos de acceso: getBalls(), getBallsMutable(), getFirstBall() - Constructor: SceneManager(screen_width, screen_height) **source/scene/scene_manager.cpp:** - Implementación de lógica de escena (~200 líneas) - changeScenario(): Crea N bolas según escenario - pushBallsAwayFromGravity(): Impulso direccional - switchBallsGravity(), forceBallsGravityOn/Off() - changeGravityDirection(): Cambio de dirección física - updateBallTexture(): Actualiza textura y tamaño - updateScreenSize(): Ajusta resolución de pantalla - updateBallSizes(): Reescala pelotas desde centro ## Archivos Modificados **source/engine.h:** - Agregado: #include "scene/scene_manager.h" - Agregado: std::unique_ptr<SceneManager> scene_manager_ - Removido: std::vector<std::unique_ptr<Ball>> balls_ - Removido: GravityDirection current_gravity_ - Removido: int scenario_ - Removidos métodos privados: initBalls(), switchBallsGravity(), enableBallsGravityIfDisabled(), forceBallsGravityOn/Off(), changeGravityDirection(), updateBallSizes() **source/engine.cpp:** - initialize(): Crea scene_manager_ con resolución - update(): Delega a scene_manager_->update() - render(): Usa scene_manager_->getBalls() - changeScenario(): Delega a scene_manager_ - pushBallsAwayFromGravity(): Delega a scene_manager_ - handleGravityToggle(): Usa scene_manager_->switchBallsGravity() - handleGravityDirectionChange(): Delega dirección - switchTextureInternal(): Usa updateBallTexture() - toggleShapeModeInternal(): Usa getBallsMutable() - activateShapeInternal(): Usa forceBallsGravityOff() - updateShape(): Usa getBallsMutable() para asignar targets - Debug HUD: Usa getFirstBall() para info - toggleRealFullscreen(): Usa updateScreenSize() + changeScenario() - performDemoAction(): Delega gravedad y escenarios - randomizeOnDemoStart(): Delega changeScenario() - toggleGravityOnOff(): Usa forceBallsGravity*() - enterLogoMode(): Usa getBallCount() y changeScenario() - exitLogoMode(): Usa updateBallTexture() - Removidos ~150 líneas de implementación movidas a SceneManager **CMakeLists.txt:** - Agregado source/scene/*.cpp a file(GLOB SOURCE_FILES ...) ## Resultado - Engine.cpp reducido de 2341 → ~2150 líneas (-191 líneas) - SceneManager: 202 líneas de lógica de física/escena - Separación clara: Engine coordina, SceneManager ejecuta física - 100% funcional: Compila sin errores ni warnings - Preparado para Fase 3 (UIManager) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,7 @@ if (NOT SDL3_ttf_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Archivos fuente (excluir main_old.cpp)
|
# Archivos fuente (excluir main_old.cpp)
|
||||||
file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp source/input/*.cpp source/shapes/*.cpp source/themes/*.cpp source/text/*.cpp source/ui/*.cpp)
|
file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp source/input/*.cpp source/scene/*.cpp source/shapes/*.cpp source/themes/*.cpp source/text/*.cpp source/ui/*.cpp)
|
||||||
list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp")
|
list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp")
|
||||||
|
|
||||||
# Comprobar si se encontraron archivos fuente
|
# Comprobar si se encontraron archivos fuente
|
||||||
|
|||||||
@@ -216,15 +216,17 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
|||||||
// Inicializar InputHandler (sin estado)
|
// Inicializar InputHandler (sin estado)
|
||||||
input_handler_ = std::make_unique<InputHandler>();
|
input_handler_ = std::make_unique<InputHandler>();
|
||||||
|
|
||||||
// Inicializar ThemeManager PRIMERO (requerido por Notifier)
|
// Inicializar ThemeManager PRIMERO (requerido por Notifier y SceneManager)
|
||||||
theme_manager_ = std::make_unique<ThemeManager>();
|
theme_manager_ = std::make_unique<ThemeManager>();
|
||||||
theme_manager_->initialize();
|
theme_manager_->initialize();
|
||||||
|
|
||||||
|
// Inicializar SceneManager (gestión de bolas y física)
|
||||||
|
scene_manager_ = std::make_unique<SceneManager>(current_screen_width_, current_screen_height_);
|
||||||
|
scene_manager_->initialize(0, texture_, theme_manager_.get()); // Escenario 0 (10 bolas) por defecto
|
||||||
|
|
||||||
// Calcular tamaño físico de ventana y tamaño de fuente absoluto
|
// Calcular tamaño físico de ventana y tamaño de fuente absoluto
|
||||||
// NOTA: Debe llamarse DESPUÉS de inicializar ThemeManager porque notifier_.init() lo necesita
|
// NOTA: Debe llamarse DESPUÉS de inicializar ThemeManager porque notifier_.init() lo necesita
|
||||||
updatePhysicalWindowSize();
|
updatePhysicalWindowSize();
|
||||||
|
|
||||||
initBalls(scenario_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
@@ -295,10 +297,8 @@ void Engine::update() {
|
|||||||
|
|
||||||
// Bifurcar actualización según modo activo
|
// Bifurcar actualización según modo activo
|
||||||
if (current_mode_ == SimulationMode::PHYSICS) {
|
if (current_mode_ == SimulationMode::PHYSICS) {
|
||||||
// Modo física normal: actualizar física de cada pelota
|
// Modo física normal: actualizar física de cada pelota (delegado a SceneManager)
|
||||||
for (auto& ball : balls_) {
|
scene_manager_->update(delta_time_);
|
||||||
ball->update(delta_time_); // Pasar delta time a cada pelota
|
|
||||||
}
|
|
||||||
} else if (current_mode_ == SimulationMode::SHAPE) {
|
} else if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
// Modo Figura 3D: actualizar figura polimórfica
|
// Modo Figura 3D: actualizar figura polimórfica
|
||||||
updateShape();
|
updateShape();
|
||||||
@@ -328,9 +328,10 @@ void Engine::handleGravityToggle() {
|
|||||||
toggleShapeModeInternal(false); // Desactivar figura sin forzar gravedad ON
|
toggleShapeModeInternal(false); // Desactivar figura sin forzar gravedad ON
|
||||||
showNotificationForAction("Gravedad Off");
|
showNotificationForAction("Gravedad Off");
|
||||||
} else {
|
} else {
|
||||||
switchBallsGravity(); // Toggle normal en modo física
|
scene_manager_->switchBallsGravity(); // Toggle normal en modo física
|
||||||
// Determinar estado actual de gravedad (gravity_force_ != 0.0f significa ON)
|
// Determinar estado actual de gravedad (gravity_force_ != 0.0f significa ON)
|
||||||
bool gravity_on = balls_.empty() ? true : (balls_[0]->getGravityForce() != 0.0f);
|
const Ball* first_ball = scene_manager_->getFirstBall();
|
||||||
|
bool gravity_on = (first_ball == nullptr) ? true : (first_ball->getGravityForce() != 0.0f);
|
||||||
showNotificationForAction(gravity_on ? "Gravedad On" : "Gravedad Off");
|
showNotificationForAction(gravity_on ? "Gravedad On" : "Gravedad Off");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,9 +341,9 @@ void Engine::handleGravityDirectionChange(GravityDirection direction, const char
|
|||||||
if (current_mode_ == SimulationMode::SHAPE) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
toggleShapeModeInternal(); // Desactivar figura (activa gravedad automáticamente)
|
toggleShapeModeInternal(); // Desactivar figura (activa gravedad automáticamente)
|
||||||
} else {
|
} else {
|
||||||
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
scene_manager_->enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||||
}
|
}
|
||||||
changeGravityDirection(direction);
|
scene_manager_->changeGravityDirection(direction);
|
||||||
showNotificationForAction(notification_text);
|
showNotificationForAction(notification_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,8 +445,13 @@ void Engine::switchTexture() {
|
|||||||
|
|
||||||
// Escenarios (número de pelotas)
|
// Escenarios (número de pelotas)
|
||||||
void Engine::changeScenario(int scenario_id, const char* notification_text) {
|
void Engine::changeScenario(int scenario_id, const char* notification_text) {
|
||||||
scenario_ = scenario_id;
|
// Resetear modo SHAPE si está activo
|
||||||
initBalls(scenario_);
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
|
current_mode_ = SimulationMode::PHYSICS;
|
||||||
|
active_shape_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
scene_manager_->changeScenario(scenario_id);
|
||||||
showNotificationForAction(notification_text);
|
showNotificationForAction(notification_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,28 +551,31 @@ void Engine::render() {
|
|||||||
batch_vertices_.clear();
|
batch_vertices_.clear();
|
||||||
batch_indices_.clear();
|
batch_indices_.clear();
|
||||||
|
|
||||||
|
// Obtener referencia a las bolas desde SceneManager
|
||||||
|
const auto& balls = scene_manager_->getBalls();
|
||||||
|
|
||||||
if (current_mode_ == SimulationMode::SHAPE) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
// MODO FIGURA 3D: Ordenar por profundidad Z (Painter's Algorithm)
|
// MODO FIGURA 3D: Ordenar por profundidad Z (Painter's Algorithm)
|
||||||
// Las pelotas con menor depth_brightness (más lejos/oscuras) se renderizan primero
|
// Las pelotas con menor depth_brightness (más lejos/oscuras) se renderizan primero
|
||||||
|
|
||||||
// Crear vector de índices para ordenamiento
|
// Crear vector de índices para ordenamiento
|
||||||
std::vector<size_t> render_order;
|
std::vector<size_t> render_order;
|
||||||
render_order.reserve(balls_.size());
|
render_order.reserve(balls.size());
|
||||||
for (size_t i = 0; i < balls_.size(); i++) {
|
for (size_t i = 0; i < balls.size(); i++) {
|
||||||
render_order.push_back(i);
|
render_order.push_back(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ordenar índices por profundidad Z (menor primero = fondo primero)
|
// Ordenar índices por profundidad Z (menor primero = fondo primero)
|
||||||
std::sort(render_order.begin(), render_order.end(), [this](size_t a, size_t b) {
|
std::sort(render_order.begin(), render_order.end(), [&balls](size_t a, size_t b) {
|
||||||
return balls_[a]->getDepthBrightness() < balls_[b]->getDepthBrightness();
|
return balls[a]->getDepthBrightness() < balls[b]->getDepthBrightness();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Renderizar en orden de profundidad (fondo → frente)
|
// Renderizar en orden de profundidad (fondo → frente)
|
||||||
for (size_t idx : render_order) {
|
for (size_t idx : render_order) {
|
||||||
SDL_FRect pos = balls_[idx]->getPosition();
|
SDL_FRect pos = balls[idx]->getPosition();
|
||||||
Color color = theme_manager_->getInterpolatedColor(idx); // Usar color interpolado (LERP)
|
Color color = theme_manager_->getInterpolatedColor(idx); // Usar color interpolado (LERP)
|
||||||
float brightness = balls_[idx]->getDepthBrightness();
|
float brightness = balls[idx]->getDepthBrightness();
|
||||||
float depth_scale = balls_[idx]->getDepthScale();
|
float depth_scale = balls[idx]->getDepthScale();
|
||||||
|
|
||||||
// Mapear brightness de 0-1 a rango MIN-MAX
|
// Mapear brightness de 0-1 a rango MIN-MAX
|
||||||
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * (ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f;
|
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * (ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f;
|
||||||
@@ -580,8 +589,9 @@ void Engine::render() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// MODO PHYSICS: Renderizar en orden normal del vector (sin escala de profundidad)
|
// MODO PHYSICS: Renderizar en orden normal del vector (sin escala de profundidad)
|
||||||
|
const auto& balls = scene_manager_->getBalls();
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
for (auto& ball : balls_) {
|
for (auto& ball : balls) {
|
||||||
SDL_FRect pos = ball->getPosition();
|
SDL_FRect pos = ball->getPosition();
|
||||||
Color color = theme_manager_->getInterpolatedColor(idx); // Usar color interpolado (LERP)
|
Color color = theme_manager_->getInterpolatedColor(idx); // Usar color interpolado (LERP)
|
||||||
addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, color.r, color.g, color.b, 1.0f);
|
addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, color.r, color.g, color.b, 1.0f);
|
||||||
@@ -641,32 +651,33 @@ void Engine::render() {
|
|||||||
current_y += line_height;
|
current_y += line_height;
|
||||||
|
|
||||||
// Debug: Mostrar valores de la primera pelota (si existe)
|
// Debug: Mostrar valores de la primera pelota (si existe)
|
||||||
if (!balls_.empty()) {
|
const Ball* first_ball = scene_manager_->getFirstBall();
|
||||||
|
if (first_ball != nullptr) {
|
||||||
// Línea 1: Gravedad
|
// Línea 1: Gravedad
|
||||||
int grav_int = static_cast<int>(balls_[0]->getGravityForce());
|
int grav_int = static_cast<int>(first_ball->getGravityForce());
|
||||||
std::string grav_text = "Gravedad: " + std::to_string(grav_int);
|
std::string grav_text = "Gravedad: " + std::to_string(grav_int);
|
||||||
text_renderer_debug_.printAbsolute(margin, current_y, grav_text.c_str(), {255, 0, 255, 255}); // Magenta
|
text_renderer_debug_.printAbsolute(margin, current_y, grav_text.c_str(), {255, 0, 255, 255}); // Magenta
|
||||||
current_y += line_height;
|
current_y += line_height;
|
||||||
|
|
||||||
// Línea 2: Velocidad Y
|
// Línea 2: Velocidad Y
|
||||||
int vy_int = static_cast<int>(balls_[0]->getVelocityY());
|
int vy_int = static_cast<int>(first_ball->getVelocityY());
|
||||||
std::string vy_text = "Velocidad Y: " + std::to_string(vy_int);
|
std::string vy_text = "Velocidad Y: " + std::to_string(vy_int);
|
||||||
text_renderer_debug_.printAbsolute(margin, current_y, vy_text.c_str(), {255, 0, 255, 255}); // Magenta
|
text_renderer_debug_.printAbsolute(margin, current_y, vy_text.c_str(), {255, 0, 255, 255}); // Magenta
|
||||||
current_y += line_height;
|
current_y += line_height;
|
||||||
|
|
||||||
// Línea 3: Estado superficie
|
// Línea 3: Estado superficie
|
||||||
std::string surface_text = balls_[0]->isOnSurface() ? "Superficie: Sí" : "Superficie: No";
|
std::string surface_text = first_ball->isOnSurface() ? "Superficie: Sí" : "Superficie: No";
|
||||||
text_renderer_debug_.printAbsolute(margin, current_y, surface_text.c_str(), {255, 0, 255, 255}); // Magenta
|
text_renderer_debug_.printAbsolute(margin, current_y, surface_text.c_str(), {255, 0, 255, 255}); // Magenta
|
||||||
current_y += line_height;
|
current_y += line_height;
|
||||||
|
|
||||||
// Línea 4: Coeficiente de rebote (loss)
|
// Línea 4: Coeficiente de rebote (loss)
|
||||||
float loss_val = balls_[0]->getLossCoefficient();
|
float loss_val = first_ball->getLossCoefficient();
|
||||||
std::string loss_text = "Rebote: " + std::to_string(loss_val).substr(0, 4);
|
std::string loss_text = "Rebote: " + std::to_string(loss_val).substr(0, 4);
|
||||||
text_renderer_debug_.printAbsolute(margin, current_y, loss_text.c_str(), {255, 0, 255, 255}); // Magenta
|
text_renderer_debug_.printAbsolute(margin, current_y, loss_text.c_str(), {255, 0, 255, 255}); // Magenta
|
||||||
current_y += line_height;
|
current_y += line_height;
|
||||||
|
|
||||||
// Línea 5: Dirección de gravedad
|
// Línea 5: Dirección de gravedad
|
||||||
std::string gravity_dir_text = "Dirección: " + gravityDirectionToString(current_gravity_);
|
std::string gravity_dir_text = "Dirección: " + gravityDirectionToString(scene_manager_->getCurrentGravity());
|
||||||
text_renderer_debug_.printAbsolute(margin, current_y, gravity_dir_text.c_str(), {255, 255, 0, 255}); // Amarillo
|
text_renderer_debug_.printAbsolute(margin, current_y, gravity_dir_text.c_str(), {255, 255, 0, 255}); // Amarillo
|
||||||
current_y += line_height;
|
current_y += line_height;
|
||||||
}
|
}
|
||||||
@@ -723,46 +734,12 @@ void Engine::render() {
|
|||||||
SDL_RenderPresent(renderer_);
|
SDL_RenderPresent(renderer_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::initBalls(int value) {
|
|
||||||
// Si estamos en modo figura 3D, desactivarlo antes de regenerar pelotas
|
|
||||||
if (current_mode_ == SimulationMode::SHAPE) {
|
|
||||||
current_mode_ = SimulationMode::PHYSICS;
|
|
||||||
active_shape_.reset(); // Liberar figura actual
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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[value]; ++i) {
|
|
||||||
const int SIGN = ((rand() % 2) * 2) - 1; // Genera un signo aleatorio (+ o -)
|
|
||||||
// Calcular spawn zone: margen a cada lado, zona central para spawn
|
|
||||||
const int margin = static_cast<int>(current_screen_width_ * BALL_SPAWN_MARGIN);
|
|
||||||
const int spawn_zone_width = current_screen_width_ - (2 * margin);
|
|
||||||
const float X = (rand() % spawn_zone_width) + margin; // Posición inicial en X
|
|
||||||
const float VX = (((rand() % 20) + 10) * 0.1f) * SIGN; // Velocidad en X
|
|
||||||
const float VY = ((rand() % 60) - 30) * 0.1f; // Velocidad en Y
|
|
||||||
|
|
||||||
// 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<Ball>(X, VX, VY, COLOR, texture_, current_screen_width_, current_screen_height_, current_ball_size_, current_gravity_, mass_factor));
|
|
||||||
}
|
|
||||||
// NOTA: setText() removido - las notificaciones ahora se llaman manualmente desde cada tecla
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::setText() {
|
void Engine::setText() {
|
||||||
// Suprimir textos durante modos demo
|
// Suprimir textos durante modos demo
|
||||||
if (current_app_mode_ != AppMode::SANDBOX) return;
|
if (current_app_mode_ != AppMode::SANDBOX) return;
|
||||||
|
|
||||||
// Generar texto de número de pelotas
|
// Generar texto de número de pelotas
|
||||||
int num_balls = BALL_COUNT_SCENARIOS[scenario_];
|
int num_balls = BALL_COUNT_SCENARIOS[scene_manager_->getCurrentScenario()];
|
||||||
std::string notification_text;
|
std::string notification_text;
|
||||||
if (num_balls == 1) {
|
if (num_balls == 1) {
|
||||||
notification_text = "1 Pelota";
|
notification_text = "1 Pelota";
|
||||||
@@ -796,79 +773,7 @@ void Engine::showNotificationForAction(const std::string& text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Engine::pushBallsAwayFromGravity() {
|
void Engine::pushBallsAwayFromGravity() {
|
||||||
for (auto& ball : balls_) {
|
scene_manager_->pushBallsAwayFromGravity();
|
||||||
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 Engine::switchBallsGravity() {
|
|
||||||
for (auto& ball : balls_) {
|
|
||||||
ball->switchGravity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::enableBallsGravityIfDisabled() {
|
|
||||||
for (auto& ball : balls_) {
|
|
||||||
ball->enableGravityIfDisabled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::forceBallsGravityOn() {
|
|
||||||
for (auto& ball : balls_) {
|
|
||||||
ball->forceGravityOn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::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<float>(balls_on_surface) / static_cast<float>(balls_.size());
|
|
||||||
if (surface_ratio > 0.5f) {
|
|
||||||
pushBallsAwayFromGravity(); // Dar impulso contrario a gravedad
|
|
||||||
}
|
|
||||||
|
|
||||||
// Desactivar gravedad
|
|
||||||
for (auto& ball : balls_) {
|
|
||||||
ball->forceGravityOff();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::changeGravityDirection(GravityDirection direction) {
|
|
||||||
current_gravity_ = direction;
|
|
||||||
for (auto& ball : balls_) {
|
|
||||||
ball->setGravityDirection(direction);
|
|
||||||
ball->applyRandomLateralPush(); // Aplicar empuje lateral aleatorio
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::toggleVSync() {
|
void Engine::toggleVSync() {
|
||||||
@@ -923,7 +828,8 @@ void Engine::toggleRealFullscreen() {
|
|||||||
updatePhysicalWindowSize();
|
updatePhysicalWindowSize();
|
||||||
|
|
||||||
// Reinicar la escena con nueva resolución
|
// Reinicar la escena con nueva resolución
|
||||||
initBalls(scenario_);
|
scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
|
||||||
|
scene_manager_->changeScenario(scene_manager_->getCurrentScenario());
|
||||||
}
|
}
|
||||||
SDL_free(displays);
|
SDL_free(displays);
|
||||||
}
|
}
|
||||||
@@ -944,7 +850,8 @@ void Engine::toggleRealFullscreen() {
|
|||||||
updatePhysicalWindowSize();
|
updatePhysicalWindowSize();
|
||||||
|
|
||||||
// Reinicar la escena con resolución original
|
// Reinicar la escena con resolución original
|
||||||
initBalls(scenario_);
|
scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
|
||||||
|
scene_manager_->changeScenario(scene_manager_->getCurrentScenario());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1383,10 +1290,10 @@ void Engine::updateDemoMode() {
|
|||||||
logo_current_flip_count_ = 0;
|
logo_current_flip_count_ = 0;
|
||||||
} else if (action < 80) {
|
} else if (action < 80) {
|
||||||
// 20%: Forzar gravedad ON (empezar a caer mientras da vueltas)
|
// 20%: Forzar gravedad ON (empezar a caer mientras da vueltas)
|
||||||
forceBallsGravityOn();
|
scene_manager_->forceBallsGravityOn();
|
||||||
} else {
|
} else {
|
||||||
// 20%: Forzar gravedad OFF (flotar mientras da vueltas)
|
// 20%: Forzar gravedad OFF (flotar mientras da vueltas)
|
||||||
forceBallsGravityOff();
|
scene_manager_->forceBallsGravityOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resetear timer con intervalos escalados
|
// Resetear timer con intervalos escalados
|
||||||
@@ -1426,7 +1333,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|||||||
|
|
||||||
if (is_lite) {
|
if (is_lite) {
|
||||||
// DEMO LITE: Verificar condiciones para salto a Logo Mode
|
// DEMO LITE: Verificar condiciones para salto a Logo Mode
|
||||||
if (static_cast<int>(balls_.size()) >= LOGO_MODE_MIN_BALLS &&
|
if (static_cast<int>(scene_manager_->getBallCount()) >= LOGO_MODE_MIN_BALLS &&
|
||||||
theme_manager_->getCurrentThemeIndex() == 5) { // MONOCHROME
|
theme_manager_->getCurrentThemeIndex() == 5) { // MONOCHROME
|
||||||
// 10% probabilidad de saltar a Logo Mode
|
// 10% probabilidad de saltar a Logo Mode
|
||||||
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO_LITE) {
|
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO_LITE) {
|
||||||
@@ -1436,7 +1343,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// DEMO COMPLETO: Verificar condiciones para salto a Logo Mode
|
// DEMO COMPLETO: Verificar condiciones para salto a Logo Mode
|
||||||
if (static_cast<int>(balls_.size()) >= LOGO_MODE_MIN_BALLS) {
|
if (static_cast<int>(scene_manager_->getBallCount()) >= LOGO_MODE_MIN_BALLS) {
|
||||||
// 15% probabilidad de saltar a Logo Mode
|
// 15% probabilidad de saltar a Logo Mode
|
||||||
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO) {
|
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO) {
|
||||||
enterLogoMode(true); // Entrar desde DEMO
|
enterLogoMode(true); // Entrar desde DEMO
|
||||||
@@ -1462,7 +1369,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|||||||
accumulated_weight += DEMO_LITE_WEIGHT_GRAVITY_DIR;
|
accumulated_weight += DEMO_LITE_WEIGHT_GRAVITY_DIR;
|
||||||
if (random_value < accumulated_weight) {
|
if (random_value < accumulated_weight) {
|
||||||
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
||||||
changeGravityDirection(new_direction);
|
scene_manager_->changeGravityDirection(new_direction);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1505,7 +1412,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|||||||
accumulated_weight += DEMO_WEIGHT_GRAVITY_DIR;
|
accumulated_weight += DEMO_WEIGHT_GRAVITY_DIR;
|
||||||
if (random_value < accumulated_weight) {
|
if (random_value < accumulated_weight) {
|
||||||
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
||||||
changeGravityDirection(new_direction);
|
scene_manager_->changeGravityDirection(new_direction);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1555,8 +1462,8 @@ void Engine::performDemoAction(bool is_lite) {
|
|||||||
if (random_value < accumulated_weight) {
|
if (random_value < accumulated_weight) {
|
||||||
// Escenarios válidos: índices 1, 2, 3, 4, 5 (10, 100, 500, 1000, 10000 pelotas)
|
// Escenarios válidos: índices 1, 2, 3, 4, 5 (10, 100, 500, 1000, 10000 pelotas)
|
||||||
int valid_scenarios[] = {1, 2, 3, 4, 5};
|
int valid_scenarios[] = {1, 2, 3, 4, 5};
|
||||||
scenario_ = valid_scenarios[rand() % 5];
|
int new_scenario = valid_scenarios[rand() % 5];
|
||||||
initBalls(scenario_);
|
scene_manager_->changeScenario(new_scenario);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1630,7 +1537,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
|||||||
|
|
||||||
// Randomizar gravedad: dirección + ON/OFF
|
// Randomizar gravedad: dirección + ON/OFF
|
||||||
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
||||||
changeGravityDirection(new_direction);
|
scene_manager_->changeGravityDirection(new_direction);
|
||||||
if (rand() % 2 == 0) {
|
if (rand() % 2 == 0) {
|
||||||
toggleGravityOnOff(); // 50% probabilidad de desactivar gravedad
|
toggleGravityOnOff(); // 50% probabilidad de desactivar gravedad
|
||||||
}
|
}
|
||||||
@@ -1640,8 +1547,8 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
|||||||
|
|
||||||
// 1. Escenario (excluir índices 0, 6, 7)
|
// 1. Escenario (excluir índices 0, 6, 7)
|
||||||
int valid_scenarios[] = {1, 2, 3, 4, 5};
|
int valid_scenarios[] = {1, 2, 3, 4, 5};
|
||||||
scenario_ = valid_scenarios[rand() % 5];
|
int new_scenario = valid_scenarios[rand() % 5];
|
||||||
initBalls(scenario_);
|
scene_manager_->changeScenario(new_scenario);
|
||||||
|
|
||||||
// 2. Tema (elegir entre TODOS los 15 temas)
|
// 2. Tema (elegir entre TODOS los 15 temas)
|
||||||
int random_theme_index = rand() % 15;
|
int random_theme_index = rand() % 15;
|
||||||
@@ -1676,7 +1583,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
|||||||
|
|
||||||
// 7. Gravedad: dirección + ON/OFF
|
// 7. Gravedad: dirección + ON/OFF
|
||||||
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
||||||
changeGravityDirection(new_direction);
|
scene_manager_->changeGravityDirection(new_direction);
|
||||||
if (rand() % 3 == 0) { // 33% probabilidad de desactivar gravedad
|
if (rand() % 3 == 0) { // 33% probabilidad de desactivar gravedad
|
||||||
toggleGravityOnOff();
|
toggleGravityOnOff();
|
||||||
}
|
}
|
||||||
@@ -1686,14 +1593,14 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
|||||||
// Toggle gravedad ON/OFF para todas las pelotas
|
// Toggle gravedad ON/OFF para todas las pelotas
|
||||||
void Engine::toggleGravityOnOff() {
|
void Engine::toggleGravityOnOff() {
|
||||||
// Alternar entre activar/desactivar gravedad
|
// Alternar entre activar/desactivar gravedad
|
||||||
bool first_ball_gravity_enabled = (balls_.empty() || balls_[0]->getGravityForce() > 0.0f);
|
bool first_ball_gravity_enabled = (!scene_manager_->hasBalls() || scene_manager_->getFirstBall()->getGravityForce() > 0.0f);
|
||||||
|
|
||||||
if (first_ball_gravity_enabled) {
|
if (first_ball_gravity_enabled) {
|
||||||
// Desactivar gravedad
|
// Desactivar gravedad
|
||||||
forceBallsGravityOff();
|
scene_manager_->forceBallsGravityOff();
|
||||||
} else {
|
} else {
|
||||||
// Activar gravedad
|
// Activar gravedad
|
||||||
forceBallsGravityOn();
|
scene_manager_->forceBallsGravityOn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1704,10 +1611,9 @@ void Engine::toggleGravityOnOff() {
|
|||||||
// Entrar al Modo Logo (manual con tecla K o automático desde DEMO)
|
// Entrar al Modo Logo (manual con tecla K o automático desde DEMO)
|
||||||
void Engine::enterLogoMode(bool from_demo) {
|
void Engine::enterLogoMode(bool from_demo) {
|
||||||
// Verificar mínimo de pelotas
|
// Verificar mínimo de pelotas
|
||||||
if (static_cast<int>(balls_.size()) < LOGO_MODE_MIN_BALLS) {
|
if (static_cast<int>(scene_manager_->getBallCount()) < LOGO_MODE_MIN_BALLS) {
|
||||||
// Ajustar a 5000 pelotas automáticamente
|
// Ajustar a 5000 pelotas automáticamente
|
||||||
scenario_ = 5; // Escenario 5000 pelotas (índice 5 en BALL_COUNT_SCENARIOS)
|
scene_manager_->changeScenario(5); // Escenario 5000 pelotas (índice 5 en BALL_COUNT_SCENARIOS)
|
||||||
initBalls(scenario_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guardar estado previo (para restaurar al salir)
|
// Guardar estado previo (para restaurar al salir)
|
||||||
@@ -1727,15 +1633,10 @@ void Engine::enterLogoMode(bool from_demo) {
|
|||||||
// Aplicar configuración fija del Modo Logo
|
// Aplicar configuración fija del Modo Logo
|
||||||
if (small_index != current_texture_index_) {
|
if (small_index != current_texture_index_) {
|
||||||
current_texture_index_ = small_index;
|
current_texture_index_ = small_index;
|
||||||
int old_size = current_ball_size_;
|
|
||||||
current_ball_size_ = textures_[current_texture_index_]->getWidth();
|
|
||||||
updateBallSizes(old_size, current_ball_size_);
|
|
||||||
|
|
||||||
// Actualizar textura global y en cada pelota
|
|
||||||
texture_ = textures_[current_texture_index_];
|
texture_ = textures_[current_texture_index_];
|
||||||
for (auto& ball : balls_) {
|
int new_size = texture_->getWidth();
|
||||||
ball->setTexture(texture_);
|
current_ball_size_ = new_size;
|
||||||
}
|
scene_manager_->updateBallTexture(texture_, new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cambiar a tema aleatorio entre: MONOCHROME, LAVENDER, CRIMSON, ESMERALDA
|
// Cambiar a tema aleatorio entre: MONOCHROME, LAVENDER, CRIMSON, ESMERALDA
|
||||||
@@ -1782,15 +1683,10 @@ void Engine::exitLogoMode(bool return_to_demo) {
|
|||||||
if (logo_previous_texture_index_ != current_texture_index_ &&
|
if (logo_previous_texture_index_ != current_texture_index_ &&
|
||||||
logo_previous_texture_index_ < textures_.size()) {
|
logo_previous_texture_index_ < textures_.size()) {
|
||||||
current_texture_index_ = logo_previous_texture_index_;
|
current_texture_index_ = logo_previous_texture_index_;
|
||||||
int old_size = current_ball_size_;
|
|
||||||
current_ball_size_ = textures_[current_texture_index_]->getWidth();
|
|
||||||
updateBallSizes(old_size, current_ball_size_);
|
|
||||||
|
|
||||||
// Actualizar textura global y en cada pelota
|
|
||||||
texture_ = textures_[current_texture_index_];
|
texture_ = textures_[current_texture_index_];
|
||||||
for (auto& ball : balls_) {
|
int new_size = texture_->getWidth();
|
||||||
ball->setTexture(texture_);
|
current_ball_size_ = new_size;
|
||||||
}
|
scene_manager_->updateBallTexture(texture_, new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
shape_scale_factor_ = logo_previous_shape_scale_;
|
shape_scale_factor_ = logo_previous_shape_scale_;
|
||||||
@@ -1827,55 +1723,9 @@ void Engine::exitLogoMode(bool return_to_demo) {
|
|||||||
|
|
||||||
// Toggle manual del Modo Logo (tecla K)
|
// Toggle manual del Modo Logo (tecla K)
|
||||||
// Sistema de cambio de sprites dinámico
|
// Sistema de cambio de sprites dinámico
|
||||||
void Engine::updateBallSizes(int old_size, int new_size) {
|
|
||||||
float delta_size = static_cast<float>(new_size - old_size);
|
|
||||||
|
|
||||||
for (auto& ball : balls_) {
|
|
||||||
SDL_FRect pos = ball->getPosition();
|
|
||||||
|
|
||||||
// Solo ajustar posición si la pelota está en superficie
|
|
||||||
if (ball->isOnSurface()) {
|
|
||||||
GravityDirection grav_dir = ball->getGravityDirection();
|
|
||||||
|
|
||||||
switch (grav_dir) {
|
|
||||||
case GravityDirection::DOWN:
|
|
||||||
// Superficie inferior: ajustar Y hacia abajo si crece
|
|
||||||
pos.y += delta_size;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GravityDirection::UP:
|
|
||||||
// Superficie superior: ajustar Y hacia arriba si crece
|
|
||||||
pos.y -= delta_size;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GravityDirection::LEFT:
|
|
||||||
// Superficie izquierda: ajustar X hacia izquierda si crece
|
|
||||||
pos.x -= delta_size;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GravityDirection::RIGHT:
|
|
||||||
// Superficie derecha: ajustar X hacia derecha si crece
|
|
||||||
pos.x += delta_size;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actualizar tamaño del hitbox
|
|
||||||
ball->updateSize(new_size);
|
|
||||||
|
|
||||||
// Si ajustamos posición, aplicarla ahora
|
|
||||||
if (ball->isOnSurface()) {
|
|
||||||
ball->setShapeScreenPosition(pos.x, pos.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::switchTextureInternal(bool show_notification) {
|
void Engine::switchTextureInternal(bool show_notification) {
|
||||||
if (textures_.empty()) return;
|
if (textures_.empty()) return;
|
||||||
|
|
||||||
// Guardar tamaño antiguo
|
|
||||||
int old_size = current_ball_size_;
|
|
||||||
|
|
||||||
// Cambiar a siguiente textura (ciclar)
|
// Cambiar a siguiente textura (ciclar)
|
||||||
current_texture_index_ = (current_texture_index_ + 1) % textures_.size();
|
current_texture_index_ = (current_texture_index_ + 1) % textures_.size();
|
||||||
texture_ = textures_[current_texture_index_];
|
texture_ = textures_[current_texture_index_];
|
||||||
@@ -1884,13 +1734,8 @@ void Engine::switchTextureInternal(bool show_notification) {
|
|||||||
int new_size = texture_->getWidth();
|
int new_size = texture_->getWidth();
|
||||||
current_ball_size_ = new_size;
|
current_ball_size_ = new_size;
|
||||||
|
|
||||||
// Actualizar texturas y tamaños de todas las pelotas
|
// Actualizar texturas y tamaños de todas las pelotas (delegado a SceneManager)
|
||||||
for (auto& ball : balls_) {
|
scene_manager_->updateBallTexture(texture_, new_size);
|
||||||
ball->setTexture(texture_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ajustar posiciones según el cambio de tamaño
|
|
||||||
updateBallSizes(old_size, new_size);
|
|
||||||
|
|
||||||
// Mostrar notificación con el nombre de la textura (solo si se solicita)
|
// Mostrar notificación con el nombre de la textura (solo si se solicita)
|
||||||
if (show_notification) {
|
if (show_notification) {
|
||||||
@@ -1927,14 +1772,15 @@ void Engine::toggleShapeModeInternal(bool force_gravity_on_exit) {
|
|||||||
current_mode_ = SimulationMode::PHYSICS;
|
current_mode_ = SimulationMode::PHYSICS;
|
||||||
|
|
||||||
// Desactivar atracción y resetear escala de profundidad
|
// Desactivar atracción y resetear escala de profundidad
|
||||||
for (auto& ball : balls_) {
|
auto& balls = scene_manager_->getBallsMutable();
|
||||||
|
for (auto& ball : balls) {
|
||||||
ball->enableShapeAttraction(false);
|
ball->enableShapeAttraction(false);
|
||||||
ball->setDepthScale(1.0f); // Reset escala a 100% (evita "pop" visual)
|
ball->setDepthScale(1.0f); // Reset escala a 100% (evita "pop" visual)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activar gravedad al salir (solo si se especifica)
|
// Activar gravedad al salir (solo si se especifica)
|
||||||
if (force_gravity_on_exit) {
|
if (force_gravity_on_exit) {
|
||||||
forceBallsGravityOn();
|
scene_manager_->forceBallsGravityOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mostrar texto informativo (solo si NO estamos en modo demo o logo)
|
// Mostrar texto informativo (solo si NO estamos en modo demo o logo)
|
||||||
@@ -1957,7 +1803,7 @@ void Engine::activateShapeInternal(ShapeType type) {
|
|||||||
current_mode_ = SimulationMode::SHAPE;
|
current_mode_ = SimulationMode::SHAPE;
|
||||||
|
|
||||||
// Desactivar gravedad al entrar en modo figura
|
// Desactivar gravedad al entrar en modo figura
|
||||||
forceBallsGravityOff();
|
scene_manager_->forceBallsGravityOff();
|
||||||
|
|
||||||
// Crear instancia polimórfica de la figura correspondiente
|
// Crear instancia polimórfica de la figura correspondiente
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -1997,7 +1843,8 @@ void Engine::activateShapeInternal(ShapeType type) {
|
|||||||
generateShape();
|
generateShape();
|
||||||
|
|
||||||
// Activar atracción física en todas las pelotas
|
// Activar atracción física en todas las pelotas
|
||||||
for (auto& ball : balls_) {
|
auto& balls = scene_manager_->getBallsMutable();
|
||||||
|
for (auto& ball : balls) {
|
||||||
ball->enableShapeAttraction(true);
|
ball->enableShapeAttraction(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2014,7 +1861,7 @@ void Engine::activateShapeInternal(ShapeType type) {
|
|||||||
void Engine::generateShape() {
|
void Engine::generateShape() {
|
||||||
if (!active_shape_) return;
|
if (!active_shape_) return;
|
||||||
|
|
||||||
int num_points = static_cast<int>(balls_.size());
|
int num_points = static_cast<int>(scene_manager_->getBallCount());
|
||||||
active_shape_->generatePoints(num_points, static_cast<float>(current_screen_width_), static_cast<float>(current_screen_height_));
|
active_shape_->generatePoints(num_points, static_cast<float>(current_screen_width_), static_cast<float>(current_screen_height_));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2032,8 +1879,11 @@ void Engine::updateShape() {
|
|||||||
float center_x = current_screen_width_ / 2.0f;
|
float center_x = current_screen_width_ / 2.0f;
|
||||||
float center_y = current_screen_height_ / 2.0f;
|
float center_y = current_screen_height_ / 2.0f;
|
||||||
|
|
||||||
|
// Obtener referencia mutable a las bolas desde SceneManager
|
||||||
|
auto& balls = scene_manager_->getBallsMutable();
|
||||||
|
|
||||||
// Actualizar cada pelota con física de atracción
|
// Actualizar cada pelota con física de atracción
|
||||||
for (size_t i = 0; i < balls_.size(); i++) {
|
for (size_t i = 0; i < balls.size(); i++) {
|
||||||
// Obtener posición 3D rotada del punto i
|
// Obtener posición 3D rotada del punto i
|
||||||
float x_3d, y_3d, z_3d;
|
float x_3d, y_3d, z_3d;
|
||||||
active_shape_->getPoint3D(static_cast<int>(i), x_3d, y_3d, z_3d);
|
active_shape_->getPoint3D(static_cast<int>(i), x_3d, y_3d, z_3d);
|
||||||
@@ -2048,23 +1898,23 @@ void Engine::updateShape() {
|
|||||||
float target_y = center_y + y_3d;
|
float target_y = center_y + y_3d;
|
||||||
|
|
||||||
// Actualizar target de la pelota para cálculo de convergencia
|
// Actualizar target de la pelota para cálculo de convergencia
|
||||||
balls_[i]->setShapeTarget2D(target_x, target_y);
|
balls[i]->setShapeTarget2D(target_x, target_y);
|
||||||
|
|
||||||
// Aplicar fuerza de atracción física hacia el punto rotado
|
// Aplicar fuerza de atracción física hacia el punto rotado
|
||||||
// Usar constantes SHAPE (mayor pegajosidad que ROTOBALL)
|
// Usar constantes SHAPE (mayor pegajosidad que ROTOBALL)
|
||||||
float shape_size = scale_factor * 80.0f; // 80px = radio base
|
float shape_size = scale_factor * 80.0f; // 80px = radio base
|
||||||
balls_[i]->applyShapeForce(target_x, target_y, shape_size, delta_time_, SHAPE_SPRING_K, SHAPE_DAMPING_BASE, SHAPE_DAMPING_NEAR, SHAPE_NEAR_THRESHOLD, SHAPE_MAX_FORCE);
|
balls[i]->applyShapeForce(target_x, target_y, shape_size, delta_time_, SHAPE_SPRING_K, SHAPE_DAMPING_BASE, SHAPE_DAMPING_NEAR, SHAPE_NEAR_THRESHOLD, SHAPE_MAX_FORCE);
|
||||||
|
|
||||||
// Calcular brillo según profundidad Z para renderizado
|
// Calcular brillo según profundidad Z para renderizado
|
||||||
// Normalizar Z al rango de la figura (asumiendo simetría ±shape_size)
|
// Normalizar Z al rango de la figura (asumiendo simetría ±shape_size)
|
||||||
float z_normalized = (z_3d + shape_size) / (2.0f * shape_size);
|
float z_normalized = (z_3d + shape_size) / (2.0f * shape_size);
|
||||||
z_normalized = std::max(0.0f, std::min(1.0f, z_normalized));
|
z_normalized = std::max(0.0f, std::min(1.0f, z_normalized));
|
||||||
balls_[i]->setDepthBrightness(z_normalized);
|
balls[i]->setDepthBrightness(z_normalized);
|
||||||
|
|
||||||
// Calcular escala según profundidad Z (perspectiva) - solo si está activado
|
// Calcular escala según profundidad Z (perspectiva) - solo si está activado
|
||||||
// 0.0 (fondo) → 0.5x, 0.5 (medio) → 1.0x, 1.0 (frente) → 1.5x
|
// 0.0 (fondo) → 0.5x, 0.5 (medio) → 1.0x, 1.0 (frente) → 1.5x
|
||||||
float depth_scale = depth_zoom_enabled_ ? (0.5f + z_normalized * 1.0f) : 1.0f;
|
float depth_scale = depth_zoom_enabled_ ? (0.5f + z_normalized * 1.0f) : 1.0f;
|
||||||
balls_[i]->setDepthScale(depth_scale);
|
balls[i]->setDepthScale(depth_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcular convergencia en LOGO MODE (% de pelotas cerca de su objetivo)
|
// Calcular convergencia en LOGO MODE (% de pelotas cerca de su objetivo)
|
||||||
@@ -2072,13 +1922,13 @@ void Engine::updateShape() {
|
|||||||
int balls_near = 0;
|
int balls_near = 0;
|
||||||
float distance_threshold = LOGO_CONVERGENCE_DISTANCE; // 20px fijo (más permisivo)
|
float distance_threshold = LOGO_CONVERGENCE_DISTANCE; // 20px fijo (más permisivo)
|
||||||
|
|
||||||
for (const auto& ball : balls_) {
|
for (const auto& ball : balls) {
|
||||||
if (ball->getDistanceToTarget() < distance_threshold) {
|
if (ball->getDistanceToTarget() < distance_threshold) {
|
||||||
balls_near++;
|
balls_near++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shape_convergence_ = static_cast<float>(balls_near) / balls_.size();
|
shape_convergence_ = static_cast<float>(balls_near) / scene_manager_->getBallCount();
|
||||||
|
|
||||||
// Notificar a la figura sobre el porcentaje de convergencia
|
// Notificar a la figura sobre el porcentaje de convergencia
|
||||||
// Esto permite que PNGShape decida cuándo empezar a contar para flips
|
// Esto permite que PNGShape decida cuándo empezar a contar para flips
|
||||||
|
|||||||
@@ -11,13 +11,14 @@
|
|||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "ball.h" // for Ball
|
#include "ball.h" // for Ball
|
||||||
#include "defines.h" // for GravityDirection, ColorTheme, ShapeType
|
#include "defines.h" // for GravityDirection, ColorTheme, ShapeType
|
||||||
#include "external/texture.h" // for Texture
|
#include "external/texture.h" // for Texture
|
||||||
#include "input/input_handler.h" // for InputHandler
|
#include "input/input_handler.h" // for InputHandler
|
||||||
#include "shapes/shape.h" // for Shape (interfaz polimórfica)
|
#include "scene/scene_manager.h" // for SceneManager
|
||||||
#include "text/textrenderer.h" // for TextRenderer
|
#include "shapes/shape.h" // for Shape (interfaz polimórfica)
|
||||||
#include "theme_manager.h" // for ThemeManager
|
#include "text/textrenderer.h" // for TextRenderer
|
||||||
#include "ui/notifier.h" // for Notifier
|
#include "theme_manager.h" // for ThemeManager
|
||||||
|
#include "ui/notifier.h" // for Notifier
|
||||||
|
|
||||||
// Modos de aplicación mutuamente excluyentes
|
// Modos de aplicación mutuamente excluyentes
|
||||||
enum class AppMode {
|
enum class AppMode {
|
||||||
@@ -79,6 +80,7 @@ class Engine {
|
|||||||
private:
|
private:
|
||||||
// === Componentes del sistema (Composición) ===
|
// === Componentes del sistema (Composición) ===
|
||||||
std::unique_ptr<InputHandler> input_handler_; // Manejo de entradas SDL
|
std::unique_ptr<InputHandler> input_handler_; // Manejo de entradas SDL
|
||||||
|
std::unique_ptr<SceneManager> scene_manager_; // Gestión de bolas y física
|
||||||
|
|
||||||
// Recursos SDL
|
// Recursos SDL
|
||||||
SDL_Window* window_ = nullptr;
|
SDL_Window* window_ = nullptr;
|
||||||
@@ -90,9 +92,6 @@ class Engine {
|
|||||||
int current_ball_size_ = 10; // Tamaño actual de pelotas (dinámico, se actualiza desde texture)
|
int current_ball_size_ = 10; // Tamaño actual de pelotas (dinámico, se actualiza desde texture)
|
||||||
|
|
||||||
// Estado del simulador
|
// Estado del simulador
|
||||||
std::vector<std::unique_ptr<Ball>> balls_;
|
|
||||||
GravityDirection current_gravity_ = GravityDirection::DOWN;
|
|
||||||
int scenario_ = 0;
|
|
||||||
bool should_exit_ = false;
|
bool should_exit_ = false;
|
||||||
|
|
||||||
// Sistema de timing
|
// Sistema de timing
|
||||||
@@ -189,14 +188,8 @@ class Engine {
|
|||||||
void render();
|
void render();
|
||||||
|
|
||||||
// Métodos auxiliares privados (llamados por la interfaz pública)
|
// Métodos auxiliares privados (llamados por la interfaz pública)
|
||||||
void initBalls(int value);
|
|
||||||
void setText(); // DEPRECATED - usar showNotificationForAction() en su lugar
|
void setText(); // DEPRECATED - usar showNotificationForAction() en su lugar
|
||||||
void showNotificationForAction(const std::string& text); // Mostrar notificación solo en modo MANUAL
|
void showNotificationForAction(const std::string& text); // Mostrar notificación solo en modo MANUAL
|
||||||
void switchBallsGravity();
|
|
||||||
void enableBallsGravityIfDisabled();
|
|
||||||
void forceBallsGravityOn();
|
|
||||||
void forceBallsGravityOff();
|
|
||||||
void changeGravityDirection(GravityDirection direction);
|
|
||||||
std::string gravityDirectionToString(GravityDirection direction) const;
|
std::string gravityDirectionToString(GravityDirection direction) const;
|
||||||
|
|
||||||
// Sistema de gestión de estados (MANUAL/DEMO/DEMO_LITE/LOGO)
|
// Sistema de gestión de estados (MANUAL/DEMO/DEMO_LITE/LOGO)
|
||||||
@@ -214,7 +207,6 @@ class Engine {
|
|||||||
|
|
||||||
// Sistema de cambio de sprites dinámico - Métodos privados
|
// Sistema de cambio de sprites dinámico - Métodos privados
|
||||||
void switchTextureInternal(bool show_notification); // Implementación interna del cambio de textura
|
void switchTextureInternal(bool show_notification); // Implementación interna del cambio de textura
|
||||||
void updateBallSizes(int old_size, int new_size); // Ajusta posiciones al cambiar tamaño
|
|
||||||
|
|
||||||
// Sistema de zoom dinámico - Métodos privados
|
// Sistema de zoom dinámico - Métodos privados
|
||||||
int calculateMaxWindowZoom() const;
|
int calculateMaxWindowZoom() const;
|
||||||
|
|||||||
199
source/scene/scene_manager.cpp
Normal file
199
source/scene/scene_manager.cpp
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
#include "scene_manager.h"
|
||||||
|
|
||||||
|
#include <cstdlib> // 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> texture, ThemeManager* theme_manager) {
|
||||||
|
scenario_ = scenario;
|
||||||
|
texture_ = texture;
|
||||||
|
theme_manager_ = theme_manager;
|
||||||
|
current_ball_size_ = texture_->getWidth();
|
||||||
|
|
||||||
|
// Crear bolas iniciales
|
||||||
|
changeScenario(scenario_);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// 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) {
|
||||||
|
const int SIGN = ((rand() % 2) * 2) - 1; // Genera un signo aleatorio (+ o -)
|
||||||
|
|
||||||
|
// Calcular spawn zone: margen a cada lado, zona central para spawn
|
||||||
|
const int margin = static_cast<int>(screen_width_ * BALL_SPAWN_MARGIN);
|
||||||
|
const int spawn_zone_width = screen_width_ - (2 * margin);
|
||||||
|
const float X = (rand() % spawn_zone_width) + margin; // Posición inicial en X
|
||||||
|
const float VX = (((rand() % 20) + 10) * 0.1f) * SIGN; // Velocidad en X
|
||||||
|
const float VY = ((rand() % 60) - 30) * 0.1f; // Velocidad en Y
|
||||||
|
|
||||||
|
// 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<Ball>(
|
||||||
|
X, VX, VY, COLOR, texture_,
|
||||||
|
screen_width_, screen_height_, current_ball_size_,
|
||||||
|
current_gravity_, mass_factor
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::updateBallTexture(std::shared_ptr<Texture> 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<float>(balls_on_surface) / static_cast<float>(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<float>(new_size) / static_cast<float>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
166
source/scene/scene_manager.h
Normal file
166
source/scene/scene_manager.h
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory> // for unique_ptr, shared_ptr
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
|
#include "../ball.h" // for Ball
|
||||||
|
#include "../defines.h" // for GravityDirection
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
class Texture;
|
||||||
|
class ThemeManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class SceneManager
|
||||||
|
* @brief Gestiona toda la lógica de creación, física y actualización de bolas
|
||||||
|
*
|
||||||
|
* Responsabilidad única: Manejo de la escena (bolas, gravedad, física)
|
||||||
|
*
|
||||||
|
* Características:
|
||||||
|
* - Crea y destruye bolas según escenario seleccionado
|
||||||
|
* - Controla la dirección y estado de la gravedad
|
||||||
|
* - Actualiza física de todas las bolas cada frame
|
||||||
|
* - Proporciona acceso controlado a las bolas para rendering
|
||||||
|
* - Mantiene el Engine desacoplado de la lógica de física
|
||||||
|
*/
|
||||||
|
class SceneManager {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
* @param screen_width Ancho lógico de la pantalla
|
||||||
|
* @param screen_height Alto lógico de la pantalla
|
||||||
|
*/
|
||||||
|
SceneManager(int screen_width, int screen_height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inicializa el manager con configuración inicial
|
||||||
|
* @param scenario Escenario inicial (índice de BALL_COUNT_SCENARIOS)
|
||||||
|
* @param texture Textura compartida para sprites de bolas
|
||||||
|
* @param theme_manager Puntero al gestor de temas (para colores)
|
||||||
|
*/
|
||||||
|
void initialize(int scenario, std::shared_ptr<Texture> texture, ThemeManager* theme_manager);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Actualiza física de todas las bolas
|
||||||
|
* @param delta_time Tiempo transcurrido desde último frame (segundos)
|
||||||
|
*/
|
||||||
|
void update(float delta_time);
|
||||||
|
|
||||||
|
// === Gestión de bolas ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cambia el número de bolas según escenario
|
||||||
|
* @param scenario_id Índice del escenario (0-7 para 10 a 50,000 bolas)
|
||||||
|
*/
|
||||||
|
void changeScenario(int scenario_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Actualiza textura y tamaño de todas las bolas
|
||||||
|
* @param new_texture Nueva textura compartida
|
||||||
|
* @param new_ball_size Nuevo tamaño de bolas (píxeles)
|
||||||
|
*/
|
||||||
|
void updateBallTexture(std::shared_ptr<Texture> new_texture, int new_ball_size);
|
||||||
|
|
||||||
|
// === Control de gravedad ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Aplica impulso a todas las bolas alejándolas de la superficie de gravedad
|
||||||
|
*/
|
||||||
|
void pushBallsAwayFromGravity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Alterna el estado de gravedad (ON/OFF) en todas las bolas
|
||||||
|
*/
|
||||||
|
void switchBallsGravity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reactiva gravedad solo si estaba desactivada
|
||||||
|
*/
|
||||||
|
void enableBallsGravityIfDisabled();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fuerza gravedad ON en todas las bolas
|
||||||
|
*/
|
||||||
|
void forceBallsGravityOn();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fuerza gravedad OFF en todas las bolas (con impulso si >50% en superficie)
|
||||||
|
*/
|
||||||
|
void forceBallsGravityOff();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cambia la dirección de la gravedad
|
||||||
|
* @param direction Nueva dirección (UP/DOWN/LEFT/RIGHT)
|
||||||
|
*/
|
||||||
|
void changeGravityDirection(GravityDirection direction);
|
||||||
|
|
||||||
|
// === Acceso a datos (read-only) ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Obtiene referencia constante al vector de bolas (para rendering)
|
||||||
|
*/
|
||||||
|
const std::vector<std::unique_ptr<Ball>>& getBalls() const { return balls_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Obtiene referencia mutable al vector de bolas (para ShapeManager)
|
||||||
|
* NOTA: Usar con cuidado, solo para sistemas que necesitan modificar estado de bolas
|
||||||
|
*/
|
||||||
|
std::vector<std::unique_ptr<Ball>>& getBallsMutable() { return balls_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Obtiene número total de bolas
|
||||||
|
*/
|
||||||
|
size_t getBallCount() const { return balls_.size(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verifica si hay al menos una bola
|
||||||
|
*/
|
||||||
|
bool hasBalls() const { return !balls_.empty(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Obtiene puntero a la primera bola (para debug info)
|
||||||
|
* @return Puntero constante o nullptr si no hay bolas
|
||||||
|
*/
|
||||||
|
const Ball* getFirstBall() const { return balls_.empty() ? nullptr : balls_[0].get(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Obtiene dirección actual de gravedad
|
||||||
|
*/
|
||||||
|
GravityDirection getCurrentGravity() const { return current_gravity_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Obtiene escenario actual
|
||||||
|
*/
|
||||||
|
int getCurrentScenario() const { return scenario_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Actualiza resolución de pantalla (para resize/fullscreen)
|
||||||
|
* @param width Nuevo ancho lógico
|
||||||
|
* @param height Nuevo alto lógico
|
||||||
|
*/
|
||||||
|
void updateScreenSize(int width, int height);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// === Datos de escena ===
|
||||||
|
std::vector<std::unique_ptr<Ball>> balls_;
|
||||||
|
GravityDirection current_gravity_;
|
||||||
|
int scenario_;
|
||||||
|
|
||||||
|
// === Configuración de pantalla ===
|
||||||
|
int screen_width_;
|
||||||
|
int screen_height_;
|
||||||
|
int current_ball_size_;
|
||||||
|
|
||||||
|
// === Referencias a otros sistemas (no owned) ===
|
||||||
|
std::shared_ptr<Texture> texture_;
|
||||||
|
ThemeManager* theme_manager_;
|
||||||
|
|
||||||
|
// === Métodos privados auxiliares ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ajusta posiciones de bolas al cambiar tamaño de sprite
|
||||||
|
* @param old_size Tamaño anterior
|
||||||
|
* @param new_size Tamaño nuevo
|
||||||
|
*/
|
||||||
|
void updateBallSizes(int old_size, int new_size);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user