diff --git a/data/gfx/player/player.ani b/data/gfx/player/player.ani index cea7556..c276435 100644 --- a/data/gfx/player/player.ani +++ b/data/gfx/player/player.ani @@ -16,78 +16,106 @@ frames=4,5,6,7 [/animation] [animation] -name=walk-sideshoot +name=walk-fire-side speed=5 loop=0 frames=8,9,10,11 [/animation] [animation] -name=walk-sideshoot-cooldown +name=walk-recoil-side speed=5 loop=0 frames=12,13,14,15 [/animation] [animation] -name=stand-sideshoot +name=walk-cool-side speed=5 loop=0 frames=16,17,18,19 [/animation] [animation] -name=stand-sideshoot-cooldown +name=stand-fire-side speed=5 loop=0 -frames=15 +frames=20 [/animation] [animation] -name=walk-centershoot +name=stand-recoil-side speed=5 loop=0 -frames=20,21,22,23 +frames=21 [/animation] [animation] -name=walk-centershoot-cooldown +name=stand-cool-side speed=5 loop=0 -frames=24,25,26,27 +frames=22 [/animation] [animation] -name=stand-centershoot +name=walk-fire-center speed=5 loop=0 -frames=28,29,30,31 +frames=23,24,25,26 [/animation] [animation] -name=stand-centershoot-cooldown +name=walk-recoil-center speed=5 loop=0 -frames=27 +frames=27,28,29,30 [/animation] [animation] -name=dying +name=walk-cool-center +speed=5 +loop=0 +frames=31,32,33,34 +[/animation] + +[animation] +name=stand-fire-center +speed=5 +loop=0 +frames=35 +[/animation] + +[animation] +name=stand-recoil-center +speed=5 +loop=0 +frames=36 +[/animation] + +[animation] +name=stand-cool-center +speed=5 +loop=0 +frames=37 +[/animation] + +[animation] +name=rolling speed=10 loop=0 -frames=32,33,34,35 -[/animation] - -[animation] -name=dead -speed=3 -loop=0 -frames=44,45,46,47,48,49,50 +frames=38,39,40,41 [/animation] [animation] name=celebration speed=10 loop=-1 -frames=36,36,36,36,36,36,37,38,39,40,40,40,40,40,40,39,39,39,40,40,40,39,39,39,38,37,36,36,36 +frames=42,42,42,42,42,42,43,44,45,46,46,46,46,46,46,45,45,45,46,46,46,45,45,45,44,43,42,42,42 +[/animation] + +[animation] +name=dead +speed=5 +loop=0 +frames=47,48,49,50,51,52,53 [/animation] \ No newline at end of file diff --git a/data/gfx/player/player1.gif b/data/gfx/player/player1.gif index fe6044b..ce1f9e8 100644 Binary files a/data/gfx/player/player1.gif and b/data/gfx/player/player1.gif differ diff --git a/source/balloon_manager.h b/source/balloon_manager.h index f317933..b7dd22c 100644 --- a/source/balloon_manager.h +++ b/source/balloon_manager.h @@ -65,7 +65,7 @@ public: // Configuración de juego void setPlayArea(SDL_FRect play_area) { play_area_ = play_area; }; // Define el área de juego void setCreationTimeEnabled(bool value) { creation_time_enabled_ = value; }; // Activa o desactiva el tiempo de creación de globos - void setDeployBalloons(bool value) { can_deploy_balloons_ = value; }; // Activa o desactiva la generación de globos + void enableBalloonDeployment(bool value) { can_deploy_balloons_ = value; }; // Activa o desactiva la generación de globos // Obtención de información int getMenace(); // Obtiene el nivel de amenaza generado por los globos diff --git a/source/director.cpp b/source/director.cpp index 6b64580..1a1dd79 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -49,7 +49,7 @@ Director::Director(int argc, const char *argv[]) Section::name = Section::Name::GAME; Section::options = Section::Options::GAME_PLAY_1P; #elif DEBUG - Section::name = Section::Name::LOGO; + Section::name = Section::Name::GAME; Section::options = Section::Options::GAME_PLAY_1P; #else // NORMAL GAME Section::name = Section::Name::LOGO; diff --git a/source/player.cpp b/source/player.cpp index e1408d8..1e50498 100644 --- a/source/player.cpp +++ b/source/player.cpp @@ -60,7 +60,7 @@ void Player::init() vel_y_ = 0; score_ = 0; score_multiplier_ = 1.0f; - cool_down_ = 10; + cant_fire_counter_ = 10; enter_name_->init(last_enter_name_); // Establece la posición del sprite @@ -358,41 +358,49 @@ void Player::setAnimation() case PlayerState::CREDITS: { // Crea cadenas de texto para componer el nombre de la animación - const std::string WALKING_ANIMATION = walking_state_ == PlayerState::WALKING_STOP ? "stand" : "walk"; - const std::string FIRING_ANIMATION = firing_state_ == PlayerState::FIRING_UP ? "centershoot" : "sideshoot"; - const std::string COOLING_ANIMATION = firing_state_ == PlayerState::COOLING_UP ? "centershoot" : "sideshoot"; + const std::string WALK_ANIMATION = walking_state_ == PlayerState::WALKING_STOP ? "stand" : "walk"; + const std::string FIRE_ANIMATION = firing_state_ == PlayerState::FIRING_UP ? "-fire-center" : "-fire-side"; + const std::string RECOIL_ANIMATION = firing_state_ == PlayerState::RECOILING_UP ? "-recoil-center" : "-recoil-side"; + const std::string COOL_ANIMATION = firing_state_ == PlayerState::COOLING_UP ? "-cool-center" : "-cool-side"; const SDL_FlipMode FLIP_WALK = walking_state_ == PlayerState::WALKING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; const SDL_FlipMode FLIP_FIRE = firing_state_ == PlayerState::FIRING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; - const SDL_FlipMode FLIP_COOLING = firing_state_ == PlayerState::COOLING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; + const SDL_FlipMode FLIP_RECOIL = firing_state_ == PlayerState::RECOILING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; + const SDL_FlipMode FLIP_COOL = firing_state_ == PlayerState::COOLING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; // Establece la animación a partir de las cadenas if (firing_state_ == PlayerState::FIRING_NONE) { // No esta disparando - player_sprite_->setCurrentAnimation(WALKING_ANIMATION); + player_sprite_->setCurrentAnimation(WALK_ANIMATION); player_sprite_->setFlip(FLIP_WALK); } + else if (isRecoiling()) + { + // Retroceso + player_sprite_->setCurrentAnimation(WALK_ANIMATION + RECOIL_ANIMATION); + player_sprite_->setFlip(FLIP_RECOIL); + } else if (isCooling()) { // Acaba de disparar - player_sprite_->setCurrentAnimation(WALKING_ANIMATION + "-" + COOLING_ANIMATION + "-cooldown"); - player_sprite_->setFlip(FLIP_COOLING); + player_sprite_->setCurrentAnimation(WALK_ANIMATION + COOL_ANIMATION); + player_sprite_->setFlip(FLIP_COOL); } else { // Está disparando - player_sprite_->setCurrentAnimation(WALKING_ANIMATION + "-" + FIRING_ANIMATION); + player_sprite_->setCurrentAnimation(WALK_ANIMATION + FIRE_ANIMATION); // Si dispara de lado, invierte el sprite segun hacia donde dispara // Si dispara recto, invierte el sprite segun hacia donde camina - player_sprite_->setFlip(FIRING_ANIMATION == "centershoot" ? FLIP_WALK : FLIP_FIRE); + player_sprite_->setFlip(FIRE_ANIMATION == "-fire-center" ? FLIP_WALK : FLIP_FIRE); } break; } case PlayerState::DYING: case PlayerState::CONTINUE_TIME_OUT: { - player_sprite_->setCurrentAnimation("dying"); + player_sprite_->setCurrentAnimation("rolling"); break; } case PlayerState::LYING_ON_THE_FLOOR_FOREVER: @@ -419,38 +427,64 @@ void Player::setAnimation() // Actualiza el valor de la variable void Player::updateCooldown() { - if (cool_down_ > 0) + if (playing_state_ == PlayerState::PLAYING) { - --cool_down_; - cooling_state_counter_ = 50; - } - else - { - if (cooling_state_counter_ > 0) + if (cant_fire_counter_ > 0) { - if (cooling_state_counter_ == 40) + cooling_state_counter_ = COOLING_DURATION_; + + // La mitad del tiempo que no puede disparar tiene el brazo arriba (PlayerState::FIRING) + // y la otra mitad en retroceso (PlayerState::RECOILING) + if (cant_fire_counter_ == recoiling_state_duration_ / 2) { switch (firing_state_) { case PlayerState::FIRING_LEFT: - setFiringState(PlayerState::COOLING_LEFT); + setFiringState(PlayerState::RECOILING_LEFT); break; case PlayerState::FIRING_RIGHT: - setFiringState(PlayerState::COOLING_RIGHT); + setFiringState(PlayerState::RECOILING_RIGHT); break; case PlayerState::FIRING_UP: - setFiringState(PlayerState::COOLING_UP); + setFiringState(PlayerState::RECOILING_UP); break; default: break; } } - --cooling_state_counter_; + + --cant_fire_counter_; } else { - setFiringState(PlayerState::FIRING_NONE); - cooling_state_counter_ = 0; + if (cooling_state_counter_ > COOLING_COMPLETE_) + { + if (cooling_state_counter_ == COOLING_DURATION_) + { + switch (firing_state_) + { + case PlayerState::RECOILING_LEFT: + setFiringState(PlayerState::COOLING_LEFT); + break; + case PlayerState::RECOILING_RIGHT: + setFiringState(PlayerState::COOLING_RIGHT); + break; + case PlayerState::RECOILING_UP: + setFiringState(PlayerState::COOLING_UP); + break; + default: + break; + } + } + + --cooling_state_counter_; + } + + if (cooling_state_counter_ == COOLING_COMPLETE_) + { + setFiringState(PlayerState::FIRING_NONE); + cooling_state_counter_ = -1; + } } } } @@ -464,10 +498,10 @@ void Player::update() updateCooldown(); updatePowerUp(); updateInvulnerable(); + updateScoreboard(); updateContinueCounter(); updateEnterNameCounter(); updateShowingName(); - updateScoreboard(); } // Incrementa la puntuación del jugador @@ -650,19 +684,20 @@ void Player::setInvulnerable(bool value) // Monitoriza el estado void Player::updateInvulnerable() { - if (invulnerable_) - { - if (invulnerable_counter_ > 0) + if (playing_state_ == PlayerState::PLAYING) + if (invulnerable_) { - --invulnerable_counter_; - invulnerable_counter_ % 8 > 3 ? player_sprite_->getTexture()->setPalette(coffees_) : player_sprite_->getTexture()->setPalette(3); + if (invulnerable_counter_ > 0) + { + --invulnerable_counter_; + invulnerable_counter_ % 8 > 3 ? player_sprite_->getTexture()->setPalette(coffees_) : player_sprite_->getTexture()->setPalette(3); + } + else + { + setInvulnerable(false); + player_sprite_->getTexture()->setPalette(coffees_); + } } - else - { - setInvulnerable(false); - player_sprite_->getTexture()->setPalette(coffees_); - } - } } // Establece el valor de la variable @@ -675,11 +710,12 @@ void Player::setPowerUp() // Actualiza el valor de la variable void Player::updatePowerUp() { - if (power_up_) - { - --power_up_counter_; - power_up_ = power_up_counter_ > 0; - } + if (playing_state_ == PlayerState::PLAYING) + if (power_up_) + { + --power_up_counter_; + power_up_ = power_up_counter_ > 0; + } } // Concede un toque extra al jugador diff --git a/source/player.h b/source/player.h index f0761b1..9fed7b1 100644 --- a/source/player.h +++ b/source/player.h @@ -28,6 +28,11 @@ enum class PlayerState FIRING_RIGHT, // Disparando hacia la derecha FIRING_NONE, // No está disparando + // Estados de retroceso tras disparar + RECOILING_UP, // Retroceso tras disparar hacia arriba + RECOILING_LEFT, // Retroceso tras disparar hacia la izquierda + RECOILING_RIGHT, // Retroceso tras disparar hacia la derecha + // Estados de enfriamiento tras disparar COOLING_UP, // Enfriando tras disparar hacia arriba COOLING_LEFT, // Enfriando tras disparar hacia la izquierda @@ -109,9 +114,10 @@ public: bool isWaiting() const { return playing_state_ == PlayerState::WAITING; } // Getters - bool canFire() const { return cool_down_ <= 0; } + bool canFire() const { return cant_fire_counter_ <= 0; } bool hasExtraHit() const { return extra_hit_; } bool isCooling() const { return firing_state_ == PlayerState::COOLING_LEFT || firing_state_ == PlayerState::COOLING_UP || firing_state_ == PlayerState::COOLING_RIGHT; } + bool isRecoiling() const { return firing_state_ == PlayerState::RECOILING_LEFT || firing_state_ == PlayerState::RECOILING_UP || firing_state_ == PlayerState::RECOILING_RIGHT; } bool IsEligibleForHighScore() const { return score_ > Options::settings.hi_score_table.back().score; } bool isInvulnerable() const { return invulnerable_; } bool isPowerUp() const { return power_up_; } @@ -138,7 +144,7 @@ public: // Setters inline void setController(int index) { controller_index_ = index; } - void setFireCooldown(int time) { cool_down_ = time; } + void setCantFireCounter(int counter) { recoiling_state_duration_ = cant_fire_counter_ = counter; } void setFiringState(PlayerState state) { firing_state_ = state; } void setInvulnerableCounter(int value) { invulnerable_counter_ = value; } void setName(const std::string &name) { name_ = name; } @@ -156,6 +162,8 @@ private: static constexpr int WIDTH_ = 30; // Anchura static constexpr int HEIGHT_ = 30; // Altura static constexpr float BASE_SPEED_ = 1.5f; // Velocidad base del jugador + static constexpr int COOLING_DURATION_ = 30; + static constexpr int COOLING_COMPLETE_ = 0; // --- Objetos y punteros --- std::unique_ptr player_sprite_; // Sprite para dibujar el jugador @@ -171,7 +179,9 @@ private: int default_pos_y_; // Posición inicial para el jugador float vel_x_ = 0.0f; // Cantidad de píxeles a desplazarse en el eje X int vel_y_ = 0.0f; // Cantidad de píxeles a desplazarse en el eje Y - int cool_down_ = 0; // Contador durante el cual no puede disparar + int cant_fire_counter_ = 0; // Contador durante el cual no puede disparar + int recoiling_state_counter_ = 0; // Contador para la animación del estado de retroceso + int recoiling_state_duration_ = 0; // Numero de frames que dura el estado de retroceso int cooling_state_counter_ = 0; // Contador para la animación del estado cooling int score_ = 0; // Puntos del jugador float score_multiplier_ = 1.0f; // Multiplicador de puntos diff --git a/source/sections/game.cpp b/source/sections/game.cpp index 1a0e7e4..e2b7f8b 100644 --- a/source/sections/game.cpp +++ b/source/sections/game.cpp @@ -1431,37 +1431,65 @@ void Game::handleFireInput(const std::shared_ptr &player, BulletType bul { if (player->canFire()) { - player->setInput(bulletType == BulletType::UP ? InputAction::FIRE_CENTER : bulletType == BulletType::LEFT ? InputAction::FIRE_LEFT - : InputAction::FIRE_RIGHT); + switch (bulletType) + { + case BulletType::UP: + player->setInput(InputAction::FIRE_CENTER); + break; + case BulletType::LEFT: + player->setInput(InputAction::FIRE_LEFT); + break; + case BulletType::RIGHT: + player->setInput(InputAction::FIRE_RIGHT); + break; + default: + break; + } createBullet(player->getPosX() + (player->getWidth() / 2) - 6, player->getPosY() + (player->getHeight() / 2), bulletType, player->isPowerUp(), player->getId()); playSound("bullet.wav"); // Establece un tiempo de espera para el próximo disparo. - const int cooldown = player->isPowerUp() ? 5 : Options::settings.autofire ? 10 - : 7; - player->setFireCooldown(cooldown); + constexpr int POWERUP_COOLDOWN = 5; + constexpr int AUTOFIRE_COOLDOWN = 10; + constexpr int NORMAL_COOLDOWN = 7; + + int cant_fire_counter; + if (player->isPowerUp()) + { + cant_fire_counter = POWERUP_COOLDOWN; + } + else if (Options::settings.autofire) + { + cant_fire_counter = AUTOFIRE_COOLDOWN; + } + else + { + cant_fire_counter = NORMAL_COOLDOWN; + } + + player->setCantFireCounter(cant_fire_counter); } } // Gestiona las entradas de todos los jugadores en el modo normal (fuera del modo demo). void Game::handlePlayersInput() { - for (const auto &player : players_) + for (const auto &PLAYER : players_) { - if (player->isPlaying()) + if (PLAYER->isPlaying()) { // Maneja el input de los jugadores en modo normal. - handleNormalPlayerInput(player); + handleNormalPlayerInput(PLAYER); } - else if (player->isContinue() || player->isWaiting()) + else if (PLAYER->isContinue() || PLAYER->isWaiting()) { // Gestiona la continuación del jugador. - handlePlayerContinue(player); + handlePlayerContinue(PLAYER); } - else if (player->isEnteringName() || player->isEnteringNameGameCompleted() || player->isShowingName()) + else if (PLAYER->isEnteringName() || PLAYER->isEnteringNameGameCompleted() || PLAYER->isShowingName()) { // Gestiona la introducción del nombre del jugador. - handleNameInput(player); + handleNameInput(PLAYER); } } } @@ -1469,17 +1497,17 @@ void Game::handlePlayersInput() // Maneja las entradas de movimiento y disparo para un jugador en modo normal. void Game::handleNormalPlayerInput(const std::shared_ptr &player) { - const auto &controller = Options::controllers.at(player->getController()); - const bool autofire = player->isPowerUp() || Options::settings.autofire; + const auto &CONTROLLER = Options::controllers.at(player->getController()); + const bool AUTOFIRE = player->isPowerUp() || Options::settings.autofire; - if (input_->checkInput(InputAction::LEFT, INPUT_ALLOW_REPEAT, controller.type, controller.index)) + if (input_->checkInput(InputAction::LEFT, INPUT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) { player->setInput(InputAction::LEFT); #ifdef RECORDING demo_.keys.left = 1; #endif } - else if (input_->checkInput(InputAction::RIGHT, INPUT_ALLOW_REPEAT, controller.type, controller.index)) + else if (input_->checkInput(InputAction::RIGHT, INPUT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) { player->setInput(InputAction::RIGHT); #ifdef RECORDING @@ -1494,7 +1522,7 @@ void Game::handleNormalPlayerInput(const std::shared_ptr &player) #endif } - handleFireInputs(player, autofire, player->getController()); // Verifica y maneja todas las posibles entradas de disparo. + handleFireInputs(player, AUTOFIRE, player->getController()); // Verifica y maneja todas las posibles entradas de disparo. } // Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado. @@ -1987,9 +2015,11 @@ void Game::checkDebugEvents(const SDL_Event &event) balloon_manager_->createPowerBall(); break; } - case SDLK_2: // Crea dos globos gordos + case SDLK_2: // Activa o desactiva la aparición de globos { - balloon_manager_->createTwoBigBalloons(); + static bool deploy_balloons = true; + deploy_balloons = !deploy_balloons; + balloon_manager_->enableBalloonDeployment(deploy_balloons); break; } case SDLK_3: // Activa el modo para pasar el juego automaticamente @@ -2001,7 +2031,7 @@ void Game::checkDebugEvents(const SDL_Event &event) balloon_manager_->destroyAllBalloons(); playSound("power_ball_explosion.wav"); } - balloon_manager_->setDeployBalloons(!auto_pop_balloons_); + balloon_manager_->enableBalloonDeployment(!auto_pop_balloons_); break; } case SDLK_4: // Suelta un item