diff --git a/source/background.cpp b/source/background.cpp index 4dc0f23..fc03f6d 100644 --- a/source/background.cpp +++ b/source/background.cpp @@ -14,8 +14,11 @@ #include "texture.h" // Para Texture // Constructor -Background::Background() +Background::Background(float total_progress_to_complete) : renderer_(Screen::get()->getRenderer()), + total_progress_to_complete_(total_progress_to_complete), + progress_per_stage_(total_progress_to_complete_ / STAGES), + sun_completion_progress_(total_progress_to_complete_ * SUN_COMPLETION_FACTOR), buildings_texture_(Resource::get()->getTexture("game_buildings.png")), top_clouds_texture_(Resource::get()->getTexture("game_clouds1.png")), @@ -30,8 +33,8 @@ Background::Background() dst_rect_({0, 0, 320, 240}), base_(rect_.h), attenuate_color_(Color(param.background.attenuate_color.r, param.background.attenuate_color.g, param.background.attenuate_color.b)), - alpha_color_text_(param.background.attenuate_color.a), - alpha_color_text_temp_(param.background.attenuate_color.a) + alpha_color_texture_(param.background.attenuate_color.a), + previous_alpha_color_texture_(param.background.attenuate_color.a) { // Precalcula rutas @@ -102,7 +105,7 @@ Background::Background() color_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h); SDL_SetTextureBlendMode(color_texture_, SDL_BLENDMODE_BLEND); setColor(attenuate_color_); - SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_); + SDL_SetTextureAlphaMod(color_texture_, alpha_color_texture_); } // Destructor @@ -113,6 +116,11 @@ Background::~Background() { // Actualiza la lógica del objeto void Background::update() { + // Actualiza la progresión y calcula transiciones (solo si no está en modo manual) + if (!manual_mode_) { + updateProgression(); + } + // Actualiza el valor de alpha_ updateAlphaColorTexture(); @@ -125,7 +133,7 @@ void Background::update() { // Calcula el valor de alpha_ alpha_ = std::max((255 - (int)(255 * transition_)), 0); - // Mueve el sol + // Mueve el sol y la luna según la progresión sun_sprite_->setPosition(sun_path_.at(sun_index_)); moon_sprite_->setPosition(moon_path_.at(moon_index_)); @@ -136,6 +144,112 @@ void Background::update() { fillCanvas(); } +// Incrementa la progresión interna +void Background::incrementProgress(float amount) { + if (state_ == State::NORMAL) { + progress_ += amount; + progress_ = std::min(progress_, total_progress_to_complete_); + } +} + +// Cambia el estado del fondo +void Background::setState(State new_state) { + state_ = new_state; +} + +// Reinicia la progresión +void Background::reset() { + progress_ = 0.0f; + state_ = State::NORMAL; + manual_mode_ = false; + gradient_number_ = 0; + transition_ = 0.0f; + sun_index_ = 0; + moon_index_ = 0; +} + +// Activa/desactiva el modo manual +void Background::setManualMode(bool manual) { + manual_mode_ = manual; +} + +// Ajusta la velocidad de las nubes +void Background::setCloudsSpeed(float value) { + clouds_speed_ = value; +} + +// Establece el degradado de fondo +void Background::setGradientNumber(int value) { + gradient_number_ = value % STAGES; +} + +// Ajusta la transición entre texturas +void Background::setTransition(float value) { + transition_ = std::clamp(value, 0.0F, 1.0F); +} + +// Establece la posición del sol +void Background::setSunProgression(float progress) { + progress = std::clamp(progress, 0.0F, 1.0F); + sun_index_ = static_cast(progress * (sun_path_.size() - 1)); +} + +// Establece la posición de la luna +void Background::setMoonProgression(float progress) { + progress = std::clamp(progress, 0.0F, 1.0F); + moon_index_ = static_cast(progress * (moon_path_.size() - 1)); +} + +// Actualiza la progresión y calcula las transiciones +void Background::updateProgression() { + // Si el juego está completado, reduce la progresión gradualmente + if (state_ == State::COMPLETED) { + if (progress_ > MINIMUM_COMPLETED_PROGRESS) { + progress_ -= COMPLETED_REDUCTION_RATE; + } else { + progress_ = MINIMUM_COMPLETED_PROGRESS; + } + } + + // Calcula la transición de los diferentes fondos + const float gradient_float = std::min(progress_ / progress_per_stage_, static_cast(STAGES - 1)); + const float percent = gradient_float - static_cast(gradient_float); + + gradient_number_ = static_cast(gradient_float); + transition_ = percent; + + // Calcula la posición del sol + const float sun_progression = std::min(progress_ / sun_completion_progress_, 1.0f); + sun_index_ = static_cast(sun_progression * (sun_path_.size() - 1)); + + // Calcula la posición de la luna + const float moon_progression = std::min(progress_ / total_progress_to_complete_, 1.0f); + moon_index_ = static_cast(moon_progression * (moon_path_.size() - 1)); + + // Actualiza la velocidad de las nubes + updateCloudsSpeed(); +} + +// Actualiza la velocidad de las nubes según el estado y progresión +void Background::updateCloudsSpeed() { + // Velocidades base + constexpr float BASE_TOP_CLOUDS_SPEED = -0.1F; + constexpr float BASE_BOTTOM_CLOUDS_SPEED = -0.05F; + + // Factor de velocidad basado en la progresión (más rápido durante el día) + float speed_factor = 1.0f; + + // En estado completado, las nubes se ralentizan gradualmente + if (state_ == State::COMPLETED) { + // Factor de ralentización basado en qué tan cerca está de MINIMUM_COMPLETED_PROGRESS + float completion_factor = (progress_ - MINIMUM_COMPLETED_PROGRESS) / + (total_progress_to_complete_ - MINIMUM_COMPLETED_PROGRESS); + speed_factor = std::max(0.1f, completion_factor); + } + + clouds_speed_ = BASE_TOP_CLOUDS_SPEED * speed_factor; +} + // Dibuja el gradiente de fondo void Background::renderGradient() { // Dibuja el gradiente de detras @@ -221,21 +335,6 @@ void Background::render() { SDL_RenderTexture(renderer_, color_texture_, &src_rect_, &dst_rect_); } -// Ajusta el valor de la variable -void Background::setCloudsSpeed(float value) { - clouds_speed_ = value; -} - -// Ajusta el valor de la variable -void Background::setGradientNumber(int value) { - gradient_number_ = value % 4; -} - -// Ajusta el valor de la variable -void Background::setTransition(float value) { - transition_ = std::clamp(value, 0.0F, 1.0F); -} - // Establece la posición del objeto void Background::setPos(SDL_FRect pos) { dst_rect_ = pos; @@ -267,17 +366,17 @@ void Background::setAlpha(int alpha) { alpha_ = std::clamp(alpha, 0, 255); // Guarda el valor actual y establece el nuevo valor - alpha_color_text_temp_ = alpha_color_text_; - alpha_color_text_ = alpha_; + previous_alpha_color_texture_ = alpha_color_texture_; + alpha_color_texture_ = alpha_; } // Actualiza el valor de alpha_ void Background::updateAlphaColorTexture() { - if (alpha_color_text_ == alpha_color_text_temp_) { + if (alpha_color_texture_ == previous_alpha_color_texture_) { return; } - alpha_color_text_ > alpha_color_text_temp_ ? ++alpha_color_text_temp_ : --alpha_color_text_temp_; - SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_temp_); + alpha_color_texture_ > previous_alpha_color_texture_ ? ++previous_alpha_color_texture_ : --previous_alpha_color_texture_; + SDL_SetTextureAlphaMod(color_texture_, previous_alpha_color_texture_); } // Actualiza las nubes @@ -353,16 +452,4 @@ void Background::createMoonPath() { float y = CENTER_Y - (RADIUS * sin(theta)); moon_path_.push_back({x, y}); } -} - -// Establece la posición del sol -void Background::setSunProgression(float progress) { - progress = std::clamp(progress, 0.0F, 1.0F); - sun_index_ = static_cast(progress * (sun_path_.size() - 1)); -} - -// Establece la posición de la luna -void Background::setMoonProgression(float progress) { - progress = std::clamp(progress, 0.0F, 1.0F); - moon_index_ = static_cast(progress * (moon_path_.size() - 1)); } \ No newline at end of file diff --git a/source/background.h b/source/background.h index 6b5d886..c1b7fbf 100644 --- a/source/background.h +++ b/source/background.h @@ -16,21 +16,29 @@ class Texture; /* Esta clase gestiona el fondo que aparece en la sección jugable. - Usa una textura compuesta y una capa superior con un color sólido cuya opacidad es ajustable. - Su área total está definida por "rect", pero solo se pinta la región "srcRect" en la pantalla en "dstRect". + Maneja internamente su progresión a través de diferentes estados del día/noche, + controlando las transiciones entre gradientes, posiciones del sol/luna y velocidad de nubes. + + Estados: + - NORMAL: Progresión normal del día + - COMPLETED: Reducción gradual de la actividad (nubes más lentas) Métodos clave: - - setCloudsSpeed(float value) -> Define la velocidad de las nubes - - setGradientNumber(int value) -> Ajusta el índice del color de cielo - - setTransition(float value) -> Configura la transición entre texturas - - setColor(Color color) -> Aplica un color de atenuación - - setAlpha(int alpha) -> Ajusta la transparencia de la capa de atenuación + - incrementProgress() -> Avanza la progresión del fondo + - setState() -> Cambia el estado del fondo + - setColor/setAlpha -> Efectos de atenuación */ class Background { public: + // Enumeración de estados + enum class State { + NORMAL, + COMPLETED + }; + // Constructor y Destructor - Background(); + Background(float total_progress_to_complete = 6100.0f); ~Background(); // Actualización y renderizado @@ -40,20 +48,40 @@ class Background { // Configuración de posición void setPos(SDL_FRect pos); // Establece la posición del objeto - // Configuración de animaciones y efectos - void setCloudsSpeed(float value); // Ajusta la velocidad de desplazamiento de las nubes - void setGradientNumber(int value); // Establece el degradado de fondo a usar - void setTransition(float value); // Ajusta la transición entre texturas de fondo + // Control de progresión + void incrementProgress(float amount = 1.0f); // Incrementa la progresión interna + void setState(State new_state); // Cambia el estado del fondo + void reset(); // Reinicia la progresión + + // Configuración manual (para uso fuera del juego principal) + void setManualMode(bool manual); // Activa/desactiva el modo manual + void setCloudsSpeed(float value); // Ajusta la velocidad de las nubes + void setGradientNumber(int value); // Establece el degradado de fondo + void setTransition(float value); // Ajusta la transición entre texturas + void setSunProgression(float progress); // Establece la posición del sol + void setMoonProgression(float progress); // Establece la posición de la luna // Configuración de efectos visuales void setColor(Color color); // Establece el color de atenuación void setAlpha(int alpha); // Ajusta la transparencia del fondo - // Configuración del sol y la luna - void setSunProgression(float progress); // Establece la posición del sol - void setMoonProgression(float progress); // Establece la posición de la luna + // Getters para información del estado + float getProgress() const { return progress_; } + State getState() const { return state_; } + int getCurrentGradient() const { return static_cast(gradient_number_); } private: + // Constantes de configuración + static constexpr size_t STAGES = 4; + static constexpr float COMPLETED_REDUCTION_RATE = 25.0f; + static constexpr float MINIMUM_COMPLETED_PROGRESS = 200.0f; + static constexpr float SUN_COMPLETION_FACTOR = 0.5f; // El sol completa su recorrido a la mitad del progreso total + + // Configuración paramétrica + const float total_progress_to_complete_; + const float progress_per_stage_; + const float sun_completion_progress_; + // Objetos y punteros SDL_Renderer *renderer_; // Renderizador de la ventana @@ -81,28 +109,39 @@ class Background { SDL_Texture *canvas_; // Textura para componer el fondo SDL_Texture *color_texture_; // Textura para atenuar el fondo - // Variables de control - std::array gradient_rect_; - std::array top_clouds_rect_; - std::array bottom_clouds_rect_; - int gradient_number_ = 0; - int alpha_ = 0; - float clouds_speed_ = 0; - float transition_ = 0; - int counter_ = 0; - SDL_FRect rect_; - SDL_FRect src_rect_; - SDL_FRect dst_rect_; - int base_; - Color attenuate_color_; - int alpha_color_text_; - int alpha_color_text_temp_; - std::vector sun_path_; - std::vector moon_path_; - size_t sun_index_ = 0; - size_t moon_index_ = 0; + // Variables de estado y progresión + State state_ = State::NORMAL; + float progress_ = 0.0f; // Progresión interna (0 a total_progress_to_complete_) + bool manual_mode_ = false; // Si está en modo manual, no actualiza automáticamente + + // Variables de renderizado + SDL_FRect rect_; // Tamaño del objeto + SDL_FRect src_rect_; // Parte del objeto para copiar en pantalla + SDL_FRect dst_rect_; // Posición en pantalla donde se copia el objeto + std::array gradient_rect_; // Fondos degradados + std::array top_clouds_rect_; // Nubes superiores + std::array bottom_clouds_rect_; // Nubes inferiores + Color attenuate_color_; // Color de atenuación + + std::vector sun_path_; // Recorrido del sol + std::vector moon_path_; // Recorrido de la luna + + float clouds_speed_ = 0; // Velocidad de las nubes + float transition_ = 0; // Porcentaje de transición + + size_t gradient_number_ = 0; // Índice de fondo degradado + size_t counter_ = 0; // Contador interno + size_t alpha_color_texture_ = 0; // Transparencia de atenuación + size_t previous_alpha_color_texture_ = 0; // Transparencia anterior + size_t sun_index_ = 0; // Índice del recorrido del sol + size_t moon_index_ = 0; // Índice del recorrido de la luna + int base_ = 0; // Posición base del fondo + + Uint8 alpha_ = 0; // Transparencia entre fases // Métodos internos + void updateProgression(); // Actualiza la progresión y calcula transiciones (solo si no está en modo manual) + void updateCloudsSpeed(); // Actualiza la velocidad de las nubes según el estado void renderGradient(); // Dibuja el gradiente de fondo void renderTopClouds(); // Dibuja las nubes superiores void renderBottomClouds(); // Dibuja las nubes inferiores @@ -111,4 +150,4 @@ class Background { void updateClouds(); // Actualiza el movimiento de las nubes void createSunPath(); // Precalcula el recorrido del sol void createMoonPath(); // Precalcula el recorrido de la luna -}; +}; \ No newline at end of file diff --git a/source/balloon_manager.cpp b/source/balloon_manager.cpp index 08245e3..a4d98c9 100644 --- a/source/balloon_manager.cpp +++ b/source/balloon_manager.cpp @@ -12,13 +12,14 @@ #include "param.h" // Para Param, ParamGame, param #include "resource.h" // Para Resource #include "screen.h" // Para Screen -#include "stage.h" // Para addPower +#include "stage_interface.h" // Para IStageInfo #include "utils.h" // Constructor -BalloonManager::BalloonManager() +BalloonManager::BalloonManager(IStageInfo *stage_info) : explosions_(std::make_unique()), - balloon_formations_(std::make_unique()) { init(); } + balloon_formations_(std::make_unique()), + stage_info_(stage_info) { init(); } // Inicializa void BalloonManager::init() { @@ -227,7 +228,7 @@ void BalloonManager::setBalloonSpeed(float speed) { // Explosiona un globo. Lo destruye y crea otros dos si es el caso auto BalloonManager::popBalloon(std::shared_ptr balloon) -> int { - Stage::addPower(1); + stage_info_->addPower(1); int score = 0; if (balloon->getType() == Balloon::Type::POWERBALL) { @@ -274,7 +275,7 @@ auto BalloonManager::destroyBalloon(std::shared_ptr &balloon) -> int { } // Aumenta el poder de la fase - Stage::addPower(balloon->getPower()); + stage_info_->addPower(balloon->getPower()); // Destruye el globo explosions_->add(balloon->getPosX(), balloon->getPosY(), static_cast(balloon->getSize())); diff --git a/source/balloon_manager.h b/source/balloon_manager.h index 0f351bb..b92fd3d 100644 --- a/source/balloon_manager.h +++ b/source/balloon_manager.h @@ -12,6 +12,7 @@ #include "balloon_formations.h" // Para BalloonFormations #include "explosions.h" // Para Explosions #include "param.h" // Para Param, ParamGame, param +#include "stage_interface.h" // Para IStageInfo #include "utils.h" // Para Zone class Texture; @@ -22,7 +23,7 @@ using Balloons = std::vector>; class BalloonManager { public: // Constructor y Destructor - BalloonManager(); + BalloonManager(IStageInfo *stage_info); ~BalloonManager() = default; // Actualización y Renderizado @@ -103,6 +104,7 @@ class BalloonManager { bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido + IStageInfo *stage_info_; // Informacion de la pantalla actual // Metodos privados void init(); diff --git a/source/player.cpp b/source/player.cpp index e5f1118..a288fb6 100644 --- a/source/player.cpp +++ b/source/player.cpp @@ -14,6 +14,7 @@ #include "param.h" // Para Param, ParamGame, param #include "scoreboard.h" // Para Scoreboard #include "stage.h" // Para power_can_be_added +#include "stage_interface.h" // Para IStageInfo #include "texture.h" // Para Texture #ifdef _DEBUG #include @@ -26,6 +27,7 @@ Player::Player(const Config &config) enter_name_(std::make_unique()), hi_score_table_(config.hi_score_table), glowing_entry_(config.glowing_entry), + stage_info_(config.stage_info), play_area_(*config.play_area), id_(config.id), default_pos_x_(config.x), @@ -635,7 +637,7 @@ void Player::setPlayingState(State state) { init(); setInvulnerable(true); setScoreboardMode(Scoreboard::Mode::SCORE); - Stage::power_can_be_added = true; + stage_info_->canCollectPower(); break; } case State::CONTINUE: { diff --git a/source/player.h b/source/player.h index 1c2abf7..871b33f 100644 --- a/source/player.h +++ b/source/player.h @@ -12,6 +12,7 @@ #include "input.h" // Para Input #include "manage_hiscore_table.h" // Para Table #include "scoreboard.h" // Para Scoreboard +#include "stage_interface.h" // Para IStageInfo #include "utils.h" // Para Circle class Texture; @@ -82,8 +83,9 @@ class Player { SDL_FRect *play_area; // Usamos puntero para mantener la referencia std::vector> texture; std::vector> animations; - Table *hi_score_table; // También como puntero para referencia - int *glowing_entry; // Puntero para mantener la referencia + Table *hi_score_table; // También como puntero para referencia + int *glowing_entry; // Puntero para mantener la referencia + IStageInfo *stage_info; // Puntero para el gestor de pantallas }; // --- Constructor y destructor --- @@ -204,6 +206,7 @@ class Player { std::shared_ptr gamepad_ = nullptr; // Dispositivo asociado Table *hi_score_table_ = nullptr; // Tabla de máximas puntuaciones int *glowing_entry_ = nullptr; // Entrada de la tabla de puntuaciones para hacerla brillar + IStageInfo *stage_info_; // Informacion de la pantalla actual std::string name_; // Nombre del jugador std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones diff --git a/source/sections/credits.cpp b/source/sections/credits.cpp index f1d3a44..bb48bbb 100644 --- a/source/sections/credits.cpp +++ b/source/sections/credits.cpp @@ -35,7 +35,7 @@ constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner"; // Constructor Credits::Credits() - : balloon_manager_(std::make_unique()), + : balloon_manager_(std::make_unique(nullptr)), tiled_bg_(std::make_unique(param.game.game_area.rect, TiledBGMode::DIAGONAL)), fade_in_(std::make_unique()), fade_out_(std::make_unique()), diff --git a/source/sections/game.cpp b/source/sections/game.cpp index f18c813..51c26ad 100644 --- a/source/sections/game.cpp +++ b/source/sections/game.cpp @@ -42,7 +42,7 @@ #include "texture.h" // Para Texture #include "ui/service_menu.h" // Para ServiceMenu #ifdef _DEBUG -#include // Para Notifier +#include // Para std::cout #include "ui/notifier.h" // Para Notifier #endif @@ -55,9 +55,10 @@ Game::Game(Player::Id player_id, int current_stage, bool demo) background_(std::make_unique()), canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)), pause_manager_(std::make_unique([this](bool is_paused) { onPauseStateChanged(is_paused); })), + stage_manager_(std::make_unique()), fade_in_(std::make_unique()), fade_out_(std::make_unique()), - balloon_manager_(std::make_unique()), + balloon_manager_(std::make_unique(stage_manager_.get())), tabe_(std::make_unique()), hit_(Hit(Resource::get()->getTexture("hit.png"))) { // Pasa variables @@ -66,8 +67,8 @@ Game::Game(Player::Id player_id, int current_stage, bool demo) // Otras variables Section::name = Section::Name::GAME; Section::options = Section::Options::NONE; - Stage::init(); - Stage::number = current_stage; + stage_manager_->initialize(); + stage_manager_->jumpToStage(current_stage); // Asigna texturas y animaciones setResources(); @@ -99,7 +100,6 @@ Game::Game(Player::Id player_id, int current_stage, bool demo) initDifficultyVars(); initDemo(player_id); initPaths(); - setTotalPower(); // Registra callbacks ServiceMenu::get()->setStateChangeCallback([this](bool is_active) { @@ -109,14 +109,14 @@ Game::Game(Player::Id player_id, int current_stage, bool demo) } }); -#ifdef _DEBUG - // Si se empieza en una fase que no es la primera - if (!demo_.enabled) { - for (int i = 0; i < Stage::number; ++i) { - Stage::total_power += Stage::get(i).power_to_complete; + /* #ifdef _DEBUG + // Si se empieza en una fase que no es la primera + if (!demo_.enabled) { + for (int i = 0; i < Stage::number; ++i) { + Stage::total_power += Stage::get(i).power_to_complete; + } } - } -#endif + #endif */ } Game::~Game() { @@ -260,7 +260,7 @@ void Game::renderPlayers() { } } -// Comprueba si hay cambio de fase y actualiza las variables +/* // Comprueba si hay cambio de fase y actualiza las variables void Game::updateStage() { if (Stage::power >= Stage::get(Stage::number).power_to_complete) { // Cambio de fase @@ -289,6 +289,64 @@ void Game::updateStage() { background_->setAlpha(96); } } +} */ + +// Comprueba si hay cambio de fase y actualiza las variables +void Game::updateStage() { + if (!stage_manager_->isCurrentStageCompleted()) { + return; // No hay cambio de fase + } + + // Calcular poder sobrante antes de avanzar + int power_overflow = 0; + auto current_stage = stage_manager_->getCurrentStage(); + if (current_stage.has_value()) { + int current_power = stage_manager_->getCurrentPower(); + int power_needed = current_stage->getPowerToComplete(); + power_overflow = current_power - power_needed; // Poder que sobra + } + + // Intentar avanzar a la siguiente fase + if (!stage_manager_->advanceToNextStage()) { + // No se pudo avanzar (probablemente juego completado) + return; + } + + // Aplicar el poder sobrante a la nueva fase + if (power_overflow > 0) { + stage_manager_->addPower(power_overflow); + } + + // Efectos de cambio de fase + playSound("stage_change.wav"); + balloon_manager_->resetBalloonSpeed(); + screen_->flash(FLASH_COLOR, 3); + screen_->shake(); + + // Obtener datos de la nueva fase + size_t current_stage_index = stage_manager_->getCurrentStageIndex(); + size_t total_stages = stage_manager_->getTotalStages(); + + // Escribir texto por pantalla + if (current_stage_index < total_stages - 1) { // No es la última fase + std::vector paths = {paths_.at(2), paths_.at(3)}; + + if (current_stage_index == total_stages - 2) { // Penúltima fase (será la última) + createMessage(paths, Resource::get()->getTexture("game_text_last_stage")); + } else { + auto text = Resource::get()->getText("04b_25_2x"); + const std::string CAPTION = Lang::getText("[GAME_TEXT] 2") + + std::to_string(total_stages - current_stage_index) + + Lang::getText("[GAME_TEXT] 2A"); + createMessage(paths, text->writeToTexture(CAPTION, 1, -4)); + } + } + + // Modificar color de fondo en la última fase + if (current_stage_index == total_stages - 1) { // Última fase + background_->setColor(Color(0xdd, 0x19, 0x1d).DARKEN()); + background_->setAlpha(96); + } } // Actualiza el estado de fin de la partida @@ -363,11 +421,11 @@ void Game::updateGameStateCompleted() { // Para la música y elimina todos los globos e items if (game_completed_counter_ == 0) { stopMusic(); - Stage::number = 9; // Deja el valor dentro de los limites + // Stage::number = 9; // Deja el valor dentro de los limites balloon_manager_->destroyAllBalloons(); // Destruye a todos los globos playSound("power_ball_explosion.wav"); - destroyAllItems(); // Destruye todos los items - Stage::power = 0; // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos los globos + destroyAllItems(); // Destruye todos los items + // Stage::power = 0; // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos los globos background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas } @@ -409,8 +467,10 @@ void Game::updateGameStateCompleted() { // Comprueba el estado del juego void Game::checkState() { - if (state_ != State::COMPLETED && Stage::number == 10) { + // if (state_ != State::COMPLETED && Stage::number == 10) { + if (state_ != State::COMPLETED && stage_manager_->isGameCompleted()) { setState(State::COMPLETED); + background_->setState(Background::State::COMPLETED); } if (state_ != State::GAME_OVER && allPlayersAreGameOver()) { @@ -876,7 +936,8 @@ void Game::handlePlayerCollision(std::shared_ptr &player, std::shared_pt player->setPlayingState(Player::State::ROLLING); sendPlayerToTheBack(player); if (allPlayersAreNotPlaying()) { - Stage::power_can_be_added = false; // No se puede subir poder de fase si no hay nadie jugando + // Stage::power_can_be_added = false; // No se puede subir poder de fase si no hay nadie jugando + stage_manager_->disablePowerCollection(); } } } @@ -961,6 +1022,8 @@ void Game::updateGameStates() { // Actualiza el fondo void Game::updateBackground() { + /* static const auto total_power_to_complete_game = stage_manager_->getTotalPowerNeededToCompleteGame(); + // Si el juego está completado, se reduce la velocidad de las nubes if (state_ == State::COMPLETED) { Stage::total_power = (Stage::total_power > 200) ? (Stage::total_power - 25) : 200; @@ -969,7 +1032,7 @@ void Game::updateBackground() { // Calcula la velocidad en función de los globos explotados y el total de globos a explotar para acabar el juego constexpr float CLOUDS_INITIAL_SPEED = 0.05F; constexpr float CLOUDS_FINAL_SPEED = 2.00F - CLOUDS_INITIAL_SPEED; - const float CLOUDS_SPEED = (-CLOUDS_INITIAL_SPEED) + (-CLOUDS_FINAL_SPEED * (static_cast(Stage::total_power) / total_power_to_complete_game_)); + const float CLOUDS_SPEED = (-CLOUDS_INITIAL_SPEED) + (-CLOUDS_FINAL_SPEED * (static_cast(Stage::total_power) / total_power_to_complete_game)); background_->setCloudsSpeed(CLOUDS_SPEED); // Calcula la transición de los diferentes fondos @@ -982,7 +1045,7 @@ void Game::updateBackground() { // Calcula la posición del sol constexpr float SUN_FINAL_POWER = NUM * 2; background_->setSunProgression(Stage::total_power / SUN_FINAL_POWER); - background_->setMoonProgression(Stage::total_power / static_cast(total_power_to_complete_game_)); + background_->setMoonProgression(Stage::total_power / static_cast(total_power_to_complete_game)); */ // Actualiza el objeto background_->update(); @@ -1166,12 +1229,11 @@ void Game::updateScoreboard() { } // Resto de marcador - scoreboard_->setStage(Stage::number + 1); - scoreboard_->setPower((float)Stage::power / (float)Stage::get(Stage::number).power_to_complete); + scoreboard_->setStage(stage_manager_->getCurrentStageIndex() + 1); + scoreboard_->setPower(stage_manager_->getCurrentStageProgressFraction()); scoreboard_->setHiScore(hi_score_.score); scoreboard_->setHiScoreName(hi_score_.name); - // Lógica del marcador scoreboard_->update(); } @@ -1488,13 +1550,14 @@ void Game::initDemo(Player::Id player_id) { constexpr auto NUM_DEMOS = 3; const auto DEMO = rand() % NUM_DEMOS; constexpr std::array STAGES = {0, 3, 5}; - Stage::number = STAGES[DEMO]; + // Stage::number = STAGES[DEMO]; + stage_manager_->jumpToStage(STAGES.at(DEMO)); } // Actualiza el numero de globos explotados según la fase del modo demostración - for (int i = 0; i < Stage::number; ++i) { + /* for (int i = 0; i < Stage::number; ++i) { Stage::total_power += Stage::get(i).power_to_complete; - } + } */ // Activa o no al otro jugador if (rand() % 3 != 0) { @@ -1529,14 +1592,6 @@ void Game::initDemo(Player::Id player_id) { demo_.counter = 0; } -// Calcula el poder total necesario para completar el juego -void Game::setTotalPower() { - total_power_to_complete_game_ = 0; - for (const auto &stage : Stage::stages) { - total_power_to_complete_game_ += stage.power_to_complete; - } -} - // Inicializa el marcador void Game::initScoreboard() { scoreboard_->setPos(param.scoreboard.rect); @@ -1596,6 +1651,7 @@ void Game::initPlayers(Player::Id player_id) { config_player1.animations = player_animations_; config_player1.hi_score_table = &Options::settings.hi_score_table; config_player1.glowing_entry = &Options::settings.glowing_entries.at(static_cast(Player::Id::PLAYER1) - 1); + config_player1.stage_info = stage_manager_.get(); auto player1 = std::make_unique(config_player1); player1->setScoreBoardPanel(Scoreboard::Id::LEFT); @@ -1616,6 +1672,7 @@ void Game::initPlayers(Player::Id player_id) { config_player2.animations = player_animations_; config_player2.hi_score_table = &Options::settings.hi_score_table; config_player2.glowing_entry = &Options::settings.glowing_entries.at(static_cast(Player::Id::PLAYER2) - 1); + config_player2.stage_info = stage_manager_.get(); auto player2 = std::make_unique(config_player2); player2->setScoreBoardPanel(Scoreboard::Id::RIGHT); @@ -1755,7 +1812,8 @@ void Game::updateGameStateShowingGetReadyMessage() { void Game::updateGameStatePlaying() { #ifdef _DEBUG if (auto_pop_balloons_) { - Stage::addPower(5); + // Stage::addPower(5); + stage_manager_->addPower(5); } #endif updatePlayers(); @@ -1787,7 +1845,7 @@ void Game::cleanVectors() { freePathSprites(); } -// Gestiona el nivel de amenaza +/* // Gestiona el nivel de amenaza void Game::updateMenace() { if (state_ == State::PLAYING) { const auto STAGE = Stage::get(Stage::number); @@ -1803,6 +1861,30 @@ void Game::updateMenace() { evaluateAndSetMenace(); // Recalcula el nivel de amenaza con el nuevo globo } } +} */ + +// Gestiona el nivel de amenaza +void Game::updateMenace() { + if (state_ != State::PLAYING) { + return; + } + + auto current_stage = stage_manager_->getCurrentStage(); + if (!current_stage.has_value()) { + return; + } + + const auto &stage = current_stage.value(); + const double fraction = stage_manager_->getCurrentStageProgressFraction(); + const int difference = stage.getMaxMenace() - stage.getMinMenace(); + + // Aumenta el nivel de amenaza en función del progreso de la fase + menace_threshold_ = stage.getMinMenace() + (difference * fraction); + + if (menace_current_ < menace_threshold_) { + balloon_manager_->deployRandomFormation(stage_manager_->getCurrentStageIndex()); + evaluateAndSetMenace(); + } } // Calcula y establece el valor de amenaza en funcion de los globos activos @@ -1816,7 +1898,8 @@ void Game::checkAndUpdateBalloonSpeed() { return; } - const float PERCENT = static_cast(Stage::power) / Stage::get(Stage::number).power_to_complete; + // const float PERCENT = static_cast(Stage::power) / Stage::get(Stage::number).power_to_complete; + const float PERCENT = stage_manager_->getCurrentStageProgressPercentage(); constexpr std::array THRESHOLDS = {0.2F, 0.4F, 0.6F, 0.8F}; for (size_t i = 0; i < std::size(THRESHOLDS); ++i) { diff --git a/source/sections/game.h b/source/sections/game.h index cd04376..4307763 100644 --- a/source/sections/game.h +++ b/source/sections/game.h @@ -14,6 +14,7 @@ #include "player.h" // Para Player #include "smart_sprite.h" // Para SmartSprite #include "utils.h" // Para Demo +#include "stage.h" // Para StageManager class Background; class Balloon; @@ -119,6 +120,7 @@ class Game { std::vector> player_animations_; // Vector con las animaciones del jugador std::unique_ptr pause_manager_; // Objeto para gestionar la pausa + std::unique_ptr stage_manager_; // Objeto para gestionar las fases std::unique_ptr fade_in_; // Objeto para renderizar fades std::unique_ptr fade_out_; // Objeto para renderizar fades std::unique_ptr balloon_manager_; // Objeto para gestionar los globos @@ -141,7 +143,6 @@ class Game { int game_completed_counter_ = 0; // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más globos int game_over_counter_ = GAME_OVER_COUNTER; // Contador para el estado de fin de partida int time_stopped_counter_ = 0; // Temporizador para llevar la cuenta del tiempo detenido - int total_power_to_complete_game_; // La suma del poder necesario para completar todas las fases int menace_current_ = 0; // Nivel de amenaza actual int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos State state_ = State::FADE_IN; // Estado diff --git a/source/stage.cpp b/source/stage.cpp index 64aa167..0c303a0 100644 --- a/source/stage.cpp +++ b/source/stage.cpp @@ -1,43 +1,210 @@ #include "stage.h" -#include // Para min -#include // Para vector +#include -namespace Stage { +// ===== IMPLEMENTACIÓN DE StageData ===== +StageData::StageData(int power_to_complete, int min_menace, int max_menace, const std::string& name) + : power_to_complete_(power_to_complete), min_menace_(min_menace), max_menace_(max_menace), name_(name), status_(StageStatus::LOCKED) {} -std::vector stages; // Variable con los datos de cada pantalla -int power = 0; // Poder acumulado en la fase -int total_power = 0; // Poder total necesario para completar el juego -int number = 0; // Fase actual -bool power_can_be_added = true; // Habilita la recolecta de poder - -// Devuelve una fase -auto get(int index) -> Stage { return stages.at(std::min(9, index)); } - -// Inicializa las variables del namespace Stage -void init() { - stages.clear(); - stages.emplace_back(200, 7 + (4 * 1), 7 + (4 * 3)); - stages.emplace_back(300, 7 + (4 * 2), 7 + (4 * 4)); - stages.emplace_back(600, 7 + (4 * 3), 7 + (4 * 5)); - stages.emplace_back(600, 7 + (4 * 3), 7 + (4 * 5)); - stages.emplace_back(600, 7 + (4 * 4), 7 + (4 * 6)); - stages.emplace_back(600, 7 + (4 * 4), 7 + (4 * 6)); - stages.emplace_back(650, 7 + (4 * 5), 7 + (4 * 7)); - stages.emplace_back(750, 7 + (4 * 5), 7 + (4 * 7)); - stages.emplace_back(850, 7 + (4 * 6), 7 + (4 * 8)); - stages.emplace_back(950, 7 + (4 * 7), 7 + (4 * 10)); - - power = 0; - total_power = 0; - number = 0; +// ===== IMPLEMENTACIÓN DE StageManager ===== +StageManager::StageManager() + : current_power_(0), total_power_(0), current_stage_index_(0), power_collection_state_(PowerCollectionState::ENABLED) { + initialize(); } -// Añade poder -void addPower(int amount) { - if (power_can_be_added) { - power += amount; - total_power += amount; +void StageManager::initialize() { + stages_.clear(); + createDefaultStages(); + reset(); +} + +void StageManager::reset() { + current_power_ = 0; + total_power_ = 0; + current_stage_index_ = 0; + power_collection_state_ = PowerCollectionState::ENABLED; + updateStageStatuses(); +} + +void StageManager::createDefaultStages() { + // Crear las 10 fases como en tu código original + stages_.emplace_back(200, 7 + (4 * 1), 7 + (4 * 3), "Tutorial"); + stages_.emplace_back(300, 7 + (4 * 2), 7 + (4 * 4), "Primeros pasos"); + stages_.emplace_back(600, 7 + (4 * 3), 7 + (4 * 5), "Intensificación"); + stages_.emplace_back(600, 7 + (4 * 3), 7 + (4 * 5), "Persistencia"); + stages_.emplace_back(600, 7 + (4 * 4), 7 + (4 * 6), "Desafío medio"); + stages_.emplace_back(600, 7 + (4 * 4), 7 + (4 * 6), "Resistencia"); + stages_.emplace_back(650, 7 + (4 * 5), 7 + (4 * 7), "Aproximación final"); + stages_.emplace_back(750, 7 + (4 * 5), 7 + (4 * 7), "Penúltimo obstáculo"); + stages_.emplace_back(850, 7 + (4 * 6), 7 + (4 * 8), "Clímax"); + stages_.emplace_back(950, 7 + (4 * 7), 7 + (4 * 10), "Maestría"); +} + +bool StageManager::advanceToNextStage() { + if (!isCurrentStageCompleted() || current_stage_index_ >= stages_.size() - 1) { + return false; } + + current_stage_index_++; + current_power_ = 0; // Reiniciar poder para la nueva fase + updateStageStatuses(); + return true; } -} // namespace Stage \ No newline at end of file + +bool StageManager::jumpToStage(size_t target_stage_index) { + if (!validateStageIndex(target_stage_index)) { + return false; + } + + // Calcular el poder total acumulado hasta la fase objetivo + int accumulated_power = 0; + for (size_t i = 0; i < target_stage_index; ++i) { + accumulated_power += stages_[i].getPowerToComplete(); + } + + // Actualizar estado + current_stage_index_ = target_stage_index; + current_power_ = 0; // Empezar la fase objetivo sin poder + total_power_ = accumulated_power; // Poder total como si hubiéramos completado las anteriores + + updateStageStatuses(); + return true; +} + +bool StageManager::subtractPower(int amount) { + if (amount <= 0 || current_power_ < amount) { + return false; + } + + current_power_ -= amount; + updateStageStatuses(); + return true; +} + +void StageManager::enablePowerCollection() { + power_collection_state_ = PowerCollectionState::ENABLED; +} + +void StageManager::disablePowerCollection() { + power_collection_state_ = PowerCollectionState::DISABLED; +} + +std::optional StageManager::getCurrentStage() const { + return getStage(current_stage_index_); +} + +std::optional StageManager::getStage(size_t index) const { + if (!validateStageIndex(index)) { + return std::nullopt; + } + return stages_[index]; +} + +bool StageManager::isCurrentStageCompleted() const { + auto current_stage = getCurrentStage(); + if (!current_stage.has_value()) { + return false; + } + + return current_power_ >= current_stage->getPowerToComplete(); +} + +bool StageManager::isGameCompleted() const { + return current_stage_index_ >= stages_.size() - 1 && isCurrentStageCompleted(); +} + +double StageManager::getProgressPercentage() const { + if (stages_.empty()) return 0.0; + + int total_power_needed = getTotalPowerNeededToCompleteGame(); + if (total_power_needed == 0) return 100.0; + + return (static_cast(total_power_) / total_power_needed) * 100.0; +} + +double StageManager::getCurrentStageProgressPercentage() const { + return getCurrentStageProgressFraction() * 100.0; +} + +double StageManager::getCurrentStageProgressFraction() const { + auto current_stage = getCurrentStage(); + if (!current_stage.has_value()) { + return 0.0; + } + + int power_needed = current_stage->getPowerToComplete(); + if (power_needed == 0) { + return 1.0; + } + + // Retorna fracción entre 0.0 y 1.0 + double fraction = static_cast(current_power_) / power_needed; + return std::min(fraction, 1.0); +} + +int StageManager::getPowerNeededForCurrentStage() const { + auto current_stage = getCurrentStage(); + if (!current_stage.has_value()) { + return 0; + } + + return std::max(0, current_stage->getPowerToComplete() - current_power_); +} + +int StageManager::getTotalPowerNeededToCompleteGame() const { + int total_power_needed = 0; + for (const auto& stage : stages_) { + total_power_needed += stage.getPowerToComplete(); + } + return total_power_needed; +} + +// ===== IMPLEMENTACIÓN DE IStageInfo (lo que ven Player y Balloon) ===== +bool StageManager::canCollectPower() const { + return power_collection_state_ == PowerCollectionState::ENABLED; +} + +void StageManager::addPower(int amount) { + if (amount <= 0 || !canCollectPower()) { + return; + } + + current_power_ += amount; + total_power_ += amount; + + // Verificar si se completó la fase actual + if (isCurrentStageCompleted()) { + auto current_stage = getCurrentStage(); + if (current_stage.has_value()) { + stages_[current_stage_index_].setStatus(StageStatus::COMPLETED); + } + } + + updateStageStatuses(); +} + +int StageManager::getCurrentMenaceLevel() const { + auto current_stage = getCurrentStage(); + if (!current_stage.has_value()) { + return 0; + } + + return current_stage->getMinMenace(); +} + +// ===== MÉTODOS PRIVADOS ===== +bool StageManager::validateStageIndex(size_t index) const { + return index < stages_.size(); +} + +void StageManager::updateStageStatuses() { + for (size_t i = 0; i < stages_.size(); ++i) { + if (i < current_stage_index_) { + stages_[i].setStatus(StageStatus::COMPLETED); + } else if (i == current_stage_index_) { + stages_[i].setStatus(StageStatus::IN_PROGRESS); + } else { + stages_[i].setStatus(StageStatus::LOCKED); + } + } +} \ No newline at end of file diff --git a/source/stage.h b/source/stage.h index ed17fd7..984cb16 100644 --- a/source/stage.h +++ b/source/stage.h @@ -1,33 +1,96 @@ #pragma once -#include // Para vector +#include +#include +#include -/* - Namespace Stage: gestiona los datos y operaciones de las fases del juego. - Permite consultar y modificar el poder necesario, la amenaza y el estado de cada fase. -*/ +#include "stage_interface.h" -namespace Stage { -// --- Estructura con los datos de una fase --- -struct Stage { - int power_to_complete; // Cantidad de poder que se necesita para completar la fase - int min_menace; // Umbral mínimo de amenaza de la fase - int max_menace; // Umbral máximo de amenaza de la fase - - // Constructor - Stage(int power_to_complete, int min_menace, int max_menace) - : power_to_complete(power_to_complete), min_menace(min_menace), max_menace(max_menace) {} +enum class PowerCollectionState { + ENABLED, + DISABLED }; -// --- Variables globales del estado de las fases --- -extern std::vector stages; // Vector con los datos de cada pantalla -extern int power; // Poder acumulado en la fase actual -extern int total_power; // Poder total necesario para completar el juego -extern int number; // Índice de la fase actual -extern bool power_can_be_added; // Indica si se puede añadir poder a la fase +enum class StageStatus { + LOCKED, + IN_PROGRESS, + COMPLETED +}; -// --- Funciones principales --- -auto get(int index) -> Stage; // Devuelve una fase por índice -void init(); // Inicializa las variables del namespace Stage -void addPower(int amount); // Añade poder a la fase actual -} // namespace Stage \ No newline at end of file +class StageData { + private: + int power_to_complete_; + int min_menace_; + int max_menace_; + std::string name_; + StageStatus status_; + + public: + StageData(int power_to_complete, int min_menace, int max_menace, const std::string& name = ""); + + // Getters + int getPowerToComplete() const { return power_to_complete_; } + int getMinMenace() const { return min_menace_; } + int getMaxMenace() const { return max_menace_; } + const std::string& getName() const { return name_; } + StageStatus getStatus() const { return status_; } + + // Setters + void setStatus(StageStatus status) { status_ = status; } + + // Utilidades + bool isCompleted() const { return status_ == StageStatus::COMPLETED; } +}; + +class StageManager : public IStageInfo { // Hereda de la interfaz + private: + std::vector stages_; + int current_power_; + int total_power_; + size_t current_stage_index_; + PowerCollectionState power_collection_state_; + + public: + StageManager(); + + // Métodos principales para Game + void initialize(); + void reset(); + bool advanceToNextStage(); + + // Gestión de poder + bool subtractPower(int amount); + void enablePowerCollection(); + void disablePowerCollection(); + + // Navegación avanzada + bool jumpToStage(size_t target_stage_index); + + // Consultas de estado + std::optional getCurrentStage() const; + std::optional getStage(size_t index) const; + size_t getCurrentStageIndex() const { return current_stage_index_; } + int getCurrentPower() const { return current_power_; } + int getTotalPower() const { return total_power_; } + int getTotalPowerNeededToCompleteGame() const; + size_t getTotalStages() const { return stages_.size(); } + + // Progreso + bool isCurrentStageCompleted() const; + bool isGameCompleted() const; + double getProgressPercentage() const; // Progreso total del juego + double getCurrentStageProgressPercentage() const; // Progreso de la fase actual (0-100%) + double getCurrentStageProgressFraction() const; // Progreso de la fase actual (0.0-1.0) + int getPowerNeededForCurrentStage() const; + + // ===== IMPLEMENTACIÓN DE IStageInfo ===== + // (Esto es lo que ven Player y Balloon) + bool canCollectPower() const override; + void addPower(int amount) override; + int getCurrentMenaceLevel() const override; + + private: + void createDefaultStages(); + bool validateStageIndex(size_t index) const; + void updateStageStatuses(); +}; \ No newline at end of file diff --git a/source/stage_interface.h b/source/stage_interface.h new file mode 100644 index 0000000..5c99936 --- /dev/null +++ b/source/stage_interface.h @@ -0,0 +1,14 @@ +#pragma once + +// Interfaz simple para lo que necesitan Player y Balloon +class IStageInfo { +public: + virtual ~IStageInfo() = default; + + // Lo que necesita Player para recolectar poder + virtual bool canCollectPower() const = 0; + virtual void addPower(int amount) = 0; + + // Lo que necesitan Player y Balloon para ajustar comportamiento + virtual int getCurrentMenaceLevel() const = 0; +}; \ No newline at end of file