#include "player.h" #include // Para SDL_FLIP_HORIZONTAL, SDL_FLIP_NONE #include // Para SDL_GetTicks #include // Para rand #include // Para max, min #include "animated_sprite.h" // Para AnimatedSprite #include "input.h" // Para InputType #include "manage_hiscore_table.h" // Para HiScoreEntry #include "options.h" // Para Options, OptionsGame, options #include "param.h" // Para Param, ParamGame, param #include "scoreboard.h" // Para Scoreboard, ScoreboardMode #include "texture.h" // Para Texture // Constructor Player::Player(int id, float x, int y, bool demo, SDL_Rect &play_area, std::vector> texture, const std::vector> &animations) : player_sprite_(std::make_unique(texture[0], animations[0])), power_sprite_(std::make_unique(texture[1], animations[1])), enter_name_(std::make_unique()), id_(id), play_area_(play_area), default_pos_x_(x), default_pos_y_(y), demo_(demo) { // Configura objetos power_sprite_->getTexture()->setAlpha(224); power_up_desp_x_ = (power_sprite_->getWidth() - player_sprite_->getWidth()) / 2; power_sprite_->setPosY(y - (power_sprite_->getHeight() - player_sprite_->getHeight())); // Inicializa variables setRecordName(enter_name_->getName()); init(); } // Iniciador void Player::init() { // Inicializa variables de estado pos_x_ = default_pos_x_; pos_y_ = default_pos_y_; status_walking_ = PlayerStatus::WALKING_STOP; status_firing_ = PlayerStatus::FIRING_NONE; status_playing_ = PlayerStatus::WAITING; invulnerable_ = true; invulnerable_counter_ = INVULNERABLE_COUNTER_; power_up_ = false; power_up_counter_ = POWERUP_COUNTER_; extra_hit_ = false; coffees_ = 0; continue_ticks_ = 0; continue_counter_ = 10; enter_name_ticks_ = 0; enter_name_counter_ = param.game.enter_name_seconds; shiftColliders(); vel_x_ = 0; vel_y_ = 0; score_ = 0; score_multiplier_ = 1.0f; cooldown_ = 10; // Establece la posición del sprite player_sprite_->clear(); player_sprite_->setPosX(pos_x_); player_sprite_->setPosY(pos_y_); // Selecciona un frame para pintar player_sprite_->setCurrentAnimation("stand"); } // Actua en consecuencia de la entrada recibida void Player::setInput(InputType input) { switch (status_playing_) { case PlayerStatus::PLAYING: { setInputPlaying(input); break; } case PlayerStatus::ENTERING_NAME: { setInputEnteringName(input); break; } default: break; } } // Procesa inputs para cuando está jugando void Player::setInputPlaying(InputType input) { switch (input) { case InputType::LEFT: { vel_x_ = -BASE_SPEED_; setWalkingStatus(PlayerStatus::WALKING_LEFT); break; } case InputType::RIGHT: { vel_x_ = BASE_SPEED_; setWalkingStatus(PlayerStatus::WALKING_RIGHT); break; } case InputType::FIRE_CENTER: { setFiringStatus(PlayerStatus::FIRING_UP); break; } case InputType::FIRE_LEFT: { setFiringStatus(PlayerStatus::FIRING_LEFT); break; } case InputType::FIRE_RIGHT: { setFiringStatus(PlayerStatus::FIRING_RIGHT); break; } default: { vel_x_ = 0; setWalkingStatus(PlayerStatus::WALKING_STOP); break; } } } // Procesa inputs para cuando está introduciendo el nombre void Player::setInputEnteringName(InputType input) { switch (input) { case InputType::LEFT: enter_name_->decPosition(); break; case InputType::RIGHT: enter_name_->incPosition(); break; case InputType::UP: enter_name_->incIndex(); break; case InputType::DOWN: enter_name_->decIndex(); break; case InputType::START: setRecordName(enter_name_->getName()); break; default: break; } setRecordName(enter_name_->getName()); } // Mueve el jugador a la posición y animación que le corresponde void Player::move() { if (isPlaying()) { // Mueve el jugador a derecha o izquierda pos_x_ += vel_x_; // Si el jugador abandona el area de juego por los laterales if ((pos_x_ < param.game.play_area.rect.x - 5) || (pos_x_ + WIDTH_ > play_area_.w + 5)) { // Restaura su posición pos_x_ -= vel_x_; } // Actualiza la posición del sprite player_sprite_->setPosX(getPosX()); player_sprite_->setPosY(pos_y_); power_sprite_->setPosX(getPosX() - power_up_desp_x_); } else if (isDying()) { player_sprite_->update(); // Si el cadaver abandona el area de juego por los laterales if ((player_sprite_->getPosX() < param.game.play_area.rect.x) || (player_sprite_->getPosX() + WIDTH_ > play_area_.w)) { // Restaura su posición const float vx = player_sprite_->getVelX(); player_sprite_->setPosX(player_sprite_->getPosX() - vx); // Rebota player_sprite_->setVelX(-vx); } // Si el cadaver abandona el area de juego por abajo if (player_sprite_->getPosY() > param.game.play_area.rect.h) { setStatusPlaying(PlayerStatus::DIED); } } } // Pinta el jugador en pantalla void Player::render() { if (power_up_ && isPlaying()) { if (power_up_counter_ > (POWERUP_COUNTER_ / 4) || power_up_counter_ % 20 > 4) { power_sprite_->render(); } } if (isRenderable()) player_sprite_->render(); } // Establece el estado del jugador cuando camina void Player::setWalkingStatus(PlayerStatus status) { status_walking_ = status; } // Establece el estado del jugador cuando dispara void Player::setFiringStatus(PlayerStatus status) { status_firing_ = status; } // Establece la animación correspondiente al estado void Player::setAnimation() { // Crea cadenas de texto para componer el nombre de la animación const std::string a_walking = status_walking_ == PlayerStatus::WALKING_STOP ? "stand" : "walk"; const std::string a_firing = status_firing_ == PlayerStatus::FIRING_UP ? "centershoot" : "sideshoot"; const SDL_RendererFlip flip_walk = status_walking_ == PlayerStatus::WALKING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; const SDL_RendererFlip flip_fire = status_firing_ == PlayerStatus::FIRING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; // Establece la animación a partir de las cadenas if (isPlaying()) { if (status_firing_ == PlayerStatus::FIRING_NONE) { // No esta disparando player_sprite_->setCurrentAnimation(a_walking); player_sprite_->setFlip(flip_walk); } else { // Está disparando player_sprite_->setCurrentAnimation(a_walking + "-" + a_firing); // Si dispara de lado, invierte el sprite segun hacia donde dispara // Si dispara recto, invierte el sprite segun hacia donde camina a_firing == "centershoot" ? player_sprite_->setFlip(flip_walk) : player_sprite_->setFlip(flip_fire); } } else { player_sprite_->setCurrentAnimation("death"); } // Actualiza las animaciones de los sprites player_sprite_->update(); // powerSprite->setFlip(flip_walk); power_sprite_->update(); } // Obtiene el valor de la variable int Player::getPosX() const { return int(pos_x_); } // Obtiene el valor de la variable int Player::getPosY() const { return pos_y_; } // Obtiene el valor de la variable int Player::getWidth() const { return WIDTH_; } // Obtiene el valor de la variable int Player::getHeight() const { return HEIGHT_; } // Indica si el jugador puede disparar bool Player::canFire() const { // Si el contador a llegado a cero, podemos disparar. En caso contrario decrementamos el contador return cooldown_ > 0 ? false : true; } // Establece el valor de la variable void Player::setFireCooldown(int time) { cooldown_ = time; } // Actualiza el valor de la variable void Player::updateCooldown() { if (cooldown_ > 0) { cooldown_--; if (power_up_) { cooldown_--; } } else { setFiringStatus(PlayerStatus::FIRING_NONE); } } // Actualiza al jugador a su posicion, animación y controla los contadores void Player::update() { move(); setAnimation(); shiftColliders(); updateCooldown(); updatePowerUp(); updateInvulnerable(); updateContinueCounter(); updateEnterNameCounter(); updateScoreboard(); } // Obtiene la puntuación del jugador int Player::getScore() const { return score_; } // Asigna un valor a la puntuación del jugador void Player::setScore(int score) { score_ = score; } // Incrementa la puntuación del jugador void Player::addScore(int score) { if (isPlaying()) { score_ += score; } } // Indica si el jugador está jugando bool Player::isPlaying() const { return status_playing_ == PlayerStatus::PLAYING; } // Indica si el jugador está continuando bool Player::isContinue() const { return status_playing_ == PlayerStatus::CONTINUE; } // Indica si el jugador está esperando bool Player::isWaiting() const { return status_playing_ == PlayerStatus::WAITING; } // Indica si el jugador está introduciendo su nombre bool Player::isEnteringName() const { return status_playing_ == PlayerStatus::ENTERING_NAME; } // Indica si el jugador está muriendose bool Player::isDying() const { return status_playing_ == PlayerStatus::DYING; } // Indica si el jugador ha terminado de morir bool Player::hasDied() const { return status_playing_ == PlayerStatus::DIED; } // Indica si el jugador ya ha terminado de jugar bool Player::isGameOver() const { return status_playing_ == PlayerStatus::GAME_OVER; } // Actualiza el panel del marcador void Player::updateScoreboard() { switch (status_playing_) { case PlayerStatus::CONTINUE: { Scoreboard::get()->setContinue(getScoreBoardPanel(), getContinueCounter()); break; } case PlayerStatus::ENTERING_NAME: { Scoreboard::get()->setRecordName(getScoreBoardPanel(), getRecordName()); Scoreboard::get()->setSelectorPos(getScoreBoardPanel(), getRecordNamePos()); break; } default: break; } } // Cambia el modo del marcador void Player::setScoreboardMode(ScoreboardMode mode) { if (!demo_) { Scoreboard::get()->setMode(getScoreBoardPanel(), mode); } } // Establece el estado del jugador en el juego void Player::setStatusPlaying(PlayerStatus value) { status_playing_ = value; switch (status_playing_) { case PlayerStatus::PLAYING: { init(); status_playing_ = PlayerStatus::PLAYING; setScoreboardMode(ScoreboardMode::SCORE); break; } case PlayerStatus::CONTINUE: { // Inicializa el contador de continuar continue_ticks_ = SDL_GetTicks(); continue_counter_ = 9; enter_name_->init(); setScoreboardMode(ScoreboardMode::CONTINUE); break; } case PlayerStatus::WAITING: { setScoreboardMode(ScoreboardMode::WAITING); break; } case PlayerStatus::ENTERING_NAME: { setScoreboardMode(ScoreboardMode::ENTER_NAME); break; } case PlayerStatus::DYING: { // Activa la animación de morir player_sprite_->setAccelY(0.2f); player_sprite_->setVelY(-6.6f); rand() % 2 == 0 ? player_sprite_->setVelX(3.3f) : player_sprite_->setVelX(-3.3f); break; } case PlayerStatus::DIED: { const auto nextPlayerStatus = IsEligibleForHighScore() ? PlayerStatus::ENTERING_NAME : PlayerStatus::CONTINUE; demo_ ? setStatusPlaying(PlayerStatus::WAITING) : setStatusPlaying(nextPlayerStatus); break; } case PlayerStatus::GAME_OVER: { setScoreboardMode(ScoreboardMode::GAME_OVER); break; } default: break; } } // Obtiene el estado del jugador en el juego PlayerStatus Player::getStatusPlaying() const { return status_playing_; } // Obtiene el valor de la variable float Player::getScoreMultiplier() const { return score_multiplier_; } // Establece el valor de la variable void Player::setScoreMultiplier(float value) { score_multiplier_ = value; } // Aumenta el valor de la variable hasta un máximo void Player::incScoreMultiplier() { score_multiplier_ += 0.1f; score_multiplier_ = std::min(score_multiplier_, 5.0f); } // Decrementa el valor de la variable hasta un mínimo void Player::decScoreMultiplier() { score_multiplier_ -= 0.1f; score_multiplier_ = std::max(score_multiplier_, 1.0f); } // Obtiene el valor de la variable bool Player::isInvulnerable() const { return invulnerable_; } // Establece el valor del estado void Player::setInvulnerable(bool value) { invulnerable_ = value; invulnerable_counter_ = invulnerable_ ? INVULNERABLE_COUNTER_ : 0; } // Obtiene el valor de la variable int Player::getInvulnerableCounter() const { return invulnerable_counter_; } // Establece el valor de la variable void Player::setInvulnerableCounter(int value) { invulnerable_counter_ = value; } // Monitoriza el estado void Player::updateInvulnerable() { if (invulnerable_) { 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_); } } } // Obtiene el valor de la variable bool Player::isPowerUp() const { return power_up_; } // Establece el valor de la variable void Player::setPowerUp() { power_up_ = true; power_up_counter_ = POWERUP_COUNTER_; } // Obtiene el valor de la variable int Player::getPowerUpCounter() const { return power_up_counter_; } // Establece el valor de la variable void Player::setPowerUpCounter(int value) { power_up_counter_ = value; } // Actualiza el valor de la variable void Player::updatePowerUp() { if (power_up_) { --power_up_counter_; power_up_ = power_up_counter_ > 0; } } // Obtiene el valor de la variable bool Player::hasExtraHit() const { return extra_hit_; } // Concede un toque extra al jugador void Player::giveExtraHit() { extra_hit_ = true; if (coffees_ < 2) { coffees_++; player_sprite_->getTexture()->setPalette(coffees_); } } // Quita el toque extra al jugador void Player::removeExtraHit() { if (coffees_ > 0) { coffees_--; setInvulnerable(true); player_sprite_->getTexture()->setPalette(coffees_); } extra_hit_ = coffees_ == 0 ? false : true; } // Devuelve el número de cafes actuales int Player::getCoffees() const { return coffees_; } // Obtiene el circulo de colisión Circle &Player::getCollider() { return collider_; } // Actualiza el circulo de colisión a la posición del jugador void Player::shiftColliders() { collider_.x = static_cast(pos_x_ + (WIDTH_ / 2)); collider_.y = static_cast(pos_y_ + (HEIGHT_ / 2)); } // Pone las texturas del jugador void Player::setPlayerTextures(const std::vector> &texture) { player_sprite_->setTexture(texture[0]); power_sprite_->setTexture(texture[1]); } // Obtiene el valor de la variable int Player::getContinueCounter() const { return continue_counter_; } // Actualiza el contador de continue void Player::updateContinueCounter() { if (status_playing_ == PlayerStatus::CONTINUE) { constexpr int TICKS_SPEED = 1000; if (SDL_GetTicks() - continue_ticks_ > TICKS_SPEED) { decContinueCounter(); } } } // Actualiza el contador de entrar nombre void Player::updateEnterNameCounter() { if (status_playing_ == PlayerStatus::ENTERING_NAME) { constexpr int TICKS_SPEED = 1000; if (SDL_GetTicks() - enter_name_ticks_ > TICKS_SPEED) { decEnterNameCounter(); } } } // Le asigna un panel en el marcador al jugador void Player::setScoreBoardPanel(int panel) { scoreboard_panel_ = panel; } // Obtiene el valor de la variable int Player::getScoreBoardPanel() const { return scoreboard_panel_; } // Decrementa el contador de continuar void Player::decContinueCounter() { continue_ticks_ = SDL_GetTicks(); --continue_counter_; if (continue_counter_ < 0) { setStatusPlaying(PlayerStatus::GAME_OVER); } } // Decrementa el contador de entrar nombre void Player::decEnterNameCounter() { enter_name_ticks_ = SDL_GetTicks(); --enter_name_counter_; if (enter_name_counter_ < 0) { enter_name_counter_ = param.game.enter_name_seconds; setStatusPlaying(PlayerStatus::CONTINUE); } } // Establece el nombre del jugador void Player::setName(const std::string &name) { name_ = name; } // Establece el nombre del jugador para la tabla de mejores puntuaciones void Player::setRecordName(const std::string &record_name) { record_name_ = record_name.substr(0, 8); } // Obtiene el nombre del jugador std::string Player::getName() const { return name_; } // Obtiene el nombre del jugador para la tabla de mejores puntuaciones std::string Player::getRecordName() const { return record_name_; } // Obtiene la posici´´on que se está editando del nombre del jugador para la tabla de mejores puntuaciones int Player::getRecordNamePos() const { if (enter_name_) { return enter_name_->getPosition(); } return 0; } // Establece el mando que usará para ser controlado void Player::setController(int index) { controller_index_ = index; } // Obtiene el mando que usa para ser controlado int Player::getController() const { return controller_index_; } // Obtiene el "id" del jugador int Player::getId() const { return id_; } // Indica si el jugador se puede dibujar bool Player::isRenderable() const { return isPlaying() || isDying(); } // Comprueba si la puntuación entra en la tabla de mejores puntuaciones bool Player::IsEligibleForHighScore() { return score_ > options.game.hi_score_table.back().score; }