#include "player.h" #include // Para SDL_GetTicks, SDL_FlipMode #include // Para clamp, max, min #include // Para rand #include "animated_sprite.h" // Para AnimatedSprite #include "asset.h" // Para Asset #include "audio.h" // Para Audio #include "input.h" // Para Input #include "input_types.h" // Para InputAction #include "manage_hiscore_table.h" // Para ManageHiScoreTable, HiScoreEntry #include "param.h" // Para Param, ParamGame, param #include "scoreboard.h" // Para Scoreboard #include "stage.h" // Para power_can_be_added #include "texture.h" // Para Texture #ifdef _DEBUG #include #endif // Constructor Player::Player(const Config &config) : player_sprite_(std::make_unique(config.texture.at(0), config.animations.at(0))), power_sprite_(std::make_unique(config.texture.at(4), config.animations.at(1))), enter_name_(std::make_unique()), hi_score_table_(config.hi_score_table), glowing_entry_(config.glowing_entry), play_area_(*config.play_area), id_(config.id), default_pos_x_(config.x), default_pos_y_(config.y), demo_(config.demo) { // Configura objetos player_sprite_->addTexture(config.texture.at(1)); player_sprite_->addTexture(config.texture.at(2)); player_sprite_->addTexture(config.texture.at(3)); player_sprite_->setActiveTexture(coffees_); power_sprite_->getTexture()->setAlpha(224); power_up_x_offset_ = (power_sprite_->getWidth() - player_sprite_->getWidth()) / 2; power_sprite_->setPosY(default_pos_y_ - (power_sprite_->getHeight() - player_sprite_->getHeight())); // Inicializa variables pos_x_ = default_pos_x_; init(); } // Iniciador void Player::init() { // Inicializa variables de estado pos_y_ = default_pos_y_; walking_state_ = State::WALKING_STOP; firing_state_ = State::FIRING_NONE; invulnerable_ = false; invulnerable_counter_ = 0; power_up_ = false; power_up_counter_ = POWERUP_COUNTER; extra_hit_ = false; coffees_ = 0; continue_ticks_ = 0; continue_counter_ = 10; name_entry_ticks_ = 0; name_entry_idle_counter_ = 0; name_entry_total_counter_ = 0; shiftColliders(); vel_x_ = 0; vel_y_ = 0; score_ = 0; score_multiplier_ = 1.0F; cant_fire_counter_ = 10; enter_name_->init(last_enter_name_); // Establece la posición del sprite player_sprite_->clear(); shiftSprite(); // Selecciona un frame para pintar // player_sprite_->setCurrentAnimation("stand"); } // Actua en consecuencia de la entrada recibida void Player::setInput(Input::Action action) { switch (playing_state_) { case State::PLAYING: { setInputPlaying(action); break; } case State::ENTERING_NAME: case State::ENTERING_NAME_GAME_COMPLETED: { setInputEnteringName(action); break; } default: break; } } // Procesa inputs para cuando está jugando void Player::setInputPlaying(Input::Action action) { switch (action) { case Input::Action::LEFT: { vel_x_ = -BASE_SPEED; setWalkingState(State::WALKING_LEFT); break; } case Input::Action::RIGHT: { vel_x_ = BASE_SPEED; setWalkingState(State::WALKING_RIGHT); break; } case Input::Action::FIRE_CENTER: { setFiringState(State::FIRING_UP); break; } case Input::Action::FIRE_LEFT: { setFiringState(State::FIRING_LEFT); break; } case Input::Action::FIRE_RIGHT: { setFiringState(State::FIRING_RIGHT); break; } default: { vel_x_ = 0; setWalkingState(State::WALKING_STOP); break; } } } // Procesa inputs para cuando está introduciendo el nombre void Player::setInputEnteringName(Input::Action action) { switch (action) { case Input::Action::LEFT: enter_name_->decPosition(); break; case Input::Action::RIGHT: enter_name_->incPosition(); break; case Input::Action::UP: enter_name_->incIndex(); break; case Input::Action::DOWN: enter_name_->decIndex(); break; case Input::Action::START: last_enter_name_ = getRecordName(); break; default: break; } name_entry_idle_counter_ = 0; } // Mueve el jugador a la posición y animación que le corresponde void Player::move() { switch (playing_state_) { case State::PLAYING: handlePlayingMovement(); break; case State::ROLLING: handleRollingMovement(); break; case State::TITLE_ANIMATION: handleTitleAnimation(); break; case State::CONTINUE_TIME_OUT: handleContinueTimeOut(); break; case State::LEAVING_SCREEN: handleLeavingScreen(); break; case State::ENTERING_SCREEN: handleEnteringScreen(); break; case State::CREDITS: handleCreditsMovement(); break; case State::WAITING: handleWaitingMovement(); break; default: break; } } void Player::handlePlayingMovement() { // Mueve el jugador a derecha o izquierda pos_x_ += vel_x_; // Si el jugador abandona el area de juego por los laterales, restaura su posición const float MIN_X = play_area_.x - 5; const float MAX_X = play_area_.w + 5 - WIDTH; pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); shiftSprite(); } void Player::handleRollingMovement() { handleRollingBoundaryCollision(); handleRollingGroundCollision(); } void Player::handleRollingBoundaryCollision() { const int X = player_sprite_->getPosX(); const int MIN_X = play_area_.x; const int MAX_X = play_area_.x + play_area_.w - WIDTH; if ((X < MIN_X) || (X > MAX_X)) { player_sprite_->setPosX(std::clamp(X, MIN_X, MAX_X)); player_sprite_->setVelX(-player_sprite_->getVelX()); playSound("jump.wav"); } } void Player::handleRollingGroundCollision() { if (player_sprite_->getPosY() <= play_area_.h - HEIGHT) { return; } // const float VEL_Y = player_sprite_->getVelY(); // if (VEL_Y >= 0.0F && VEL_Y < 2.0F) { if (player_sprite_->getVelY() < 2.0F) { handleRollingStop(); } else { handleRollingBounce(); } } void Player::handleRollingStop() { const auto NEXT_PLAYER_STATE = qualifiesForHighScore() ? State::ENTERING_NAME : State::CONTINUE; const auto NEXT_STATE = demo_ ? State::LYING_ON_THE_FLOOR_FOREVER : NEXT_PLAYER_STATE; setPlayingState(NEXT_STATE); pos_x_ = player_sprite_->getPosX(); pos_y_ = default_pos_y_; player_sprite_->clear(); shiftSprite(); playSound("jump.wav"); } void Player::handleRollingBounce() { player_sprite_->setPosY(play_area_.h - HEIGHT); player_sprite_->setVelY(player_sprite_->getVelY() * -0.5F); player_sprite_->setVelX(player_sprite_->getVelX() * 0.75F); player_sprite_->setAnimationSpeed(player_sprite_->getAnimationSpeed() * 2); playSound("jump.wav"); } void Player::handleTitleAnimation() { setInputBasedOnPlayerId(); pos_x_ += vel_x_ * 2.0F; const float MIN_X = -WIDTH; const float MAX_X = play_area_.w; pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); shiftSprite(); if (pos_x_ == MIN_X || pos_x_ == MAX_X) { setPlayingState(State::TITLE_HIDDEN); } } void Player::handleContinueTimeOut() { // Si el cadaver desaparece por el suelo, cambia de estado if (player_sprite_->getPosY() > play_area_.h) { player_sprite_->stop(); setPlayingState(State::WAITING); } } void Player::handleLeavingScreen() { updateStepCounter(); setInputBasedOnPlayerId(); pos_x_ += vel_x_; const float MIN_X = -WIDTH; const float MAX_X = play_area_.w; pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); shiftSprite(); if (pos_x_ == MIN_X || pos_x_ == MAX_X) { setPlayingState(State::GAME_OVER); } } void Player::handleEnteringScreen() { updateStepCounter(); switch (id_) { case Id::PLAYER1: handlePlayer1Entering(); break; case Id::PLAYER2: handlePlayer2Entering(); break; default: break; } shiftSprite(); } void Player::handlePlayer1Entering() { setInputPlaying(Input::Action::RIGHT); pos_x_ += vel_x_; if (pos_x_ > default_pos_x_) { pos_x_ = default_pos_x_; setPlayingState(State::PLAYING); } } void Player::handlePlayer2Entering() { setInputPlaying(Input::Action::LEFT); pos_x_ += vel_x_; if (pos_x_ < default_pos_x_) { pos_x_ = default_pos_x_; setPlayingState(State::PLAYING); } } void Player::handleCreditsMovement() { pos_x_ += vel_x_ / 2.0F; if (vel_x_ > 0) { handleCreditsRightMovement(); } else { handleCreditsLeftMovement(); } updateWalkingStateForCredits(); shiftSprite(); } void Player::handleCreditsRightMovement() { if (pos_x_ > param.game.game_area.rect.w - WIDTH) { pos_x_ = param.game.game_area.rect.w - WIDTH; vel_x_ *= -1; } } void Player::handleCreditsLeftMovement() { if (pos_x_ < param.game.game_area.rect.x) { pos_x_ = param.game.game_area.rect.x; vel_x_ *= -1; } } void Player::handleWaitingMovement() { ++waiting_counter_; if (waiting_counter_ == WAITING_COUNTER) { waiting_counter_ = 0; player_sprite_->resetAnimation(); } } void Player::updateWalkingStateForCredits() { if (pos_x_ > param.game.game_area.center_x - WIDTH / 2) { setWalkingState(State::WALKING_LEFT); } else { setWalkingState(State::WALKING_RIGHT); } } void Player::setInputBasedOnPlayerId() { switch (id_) { case Id::PLAYER1: setInputPlaying(Input::Action::LEFT); break; case Id::PLAYER2: setInputPlaying(Input::Action::RIGHT); break; default: break; } } void Player::updateStepCounter() { ++step_counter_; if (step_counter_ % 10 == 0) { playSound("walk.wav"); } } // 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(); } } // Calcula la animacion de moverse y disparar del jugador auto Player::computeAnimation() const -> std::pair { const std::string BASE_ANIM = (walking_state_ == State::WALKING_STOP) ? "stand" : "walk"; std::string anim_name; SDL_FlipMode flip_mode = SDL_FLIP_NONE; switch (firing_state_) { case State::FIRING_NONE: anim_name = BASE_ANIM; flip_mode = (walking_state_ == State::WALKING_RIGHT) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; break; case State::FIRING_UP: anim_name = BASE_ANIM + "-fire-center"; flip_mode = (walking_state_ == State::WALKING_RIGHT) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; break; case State::FIRING_LEFT: case State::FIRING_RIGHT: anim_name = BASE_ANIM + "-fire-side"; flip_mode = (firing_state_ == State::FIRING_RIGHT) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; break; case State::RECOILING_UP: anim_name = BASE_ANIM + "-recoil-center"; flip_mode = (walking_state_ == State::WALKING_RIGHT) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; break; case State::RECOILING_LEFT: case State::RECOILING_RIGHT: anim_name = BASE_ANIM + "-recoil-side"; flip_mode = (firing_state_ == State::RECOILING_RIGHT) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; break; case State::COOLING_UP: anim_name = BASE_ANIM + "-cool-center"; flip_mode = (walking_state_ == State::WALKING_RIGHT) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; break; case State::COOLING_LEFT: case State::COOLING_RIGHT: anim_name = BASE_ANIM + "-cool-side"; flip_mode = (firing_state_ == State::COOLING_RIGHT) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; break; default: anim_name = BASE_ANIM; flip_mode = (walking_state_ == State::WALKING_RIGHT) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; break; } return {anim_name, flip_mode}; } // Establece la animación correspondiente al estado void Player::setAnimation() { switch (playing_state_) { case State::PLAYING: case State::ENTERING_NAME_GAME_COMPLETED: case State::ENTERING_SCREEN: case State::LEAVING_SCREEN: case State::TITLE_ANIMATION: case State::CREDITS: { auto [animName, flipMode] = computeAnimation(); player_sprite_->setCurrentAnimation(animName, false); player_sprite_->setFlip(flipMode); break; } case State::WAITING: case State::GAME_OVER: player_sprite_->setCurrentAnimation("hello"); break; case State::ROLLING: case State::CONTINUE_TIME_OUT: player_sprite_->setCurrentAnimation("rolling"); break; case State::LYING_ON_THE_FLOOR_FOREVER: case State::ENTERING_NAME: case State::CONTINUE: player_sprite_->setCurrentAnimation("dizzy"); break; case State::CELEBRATING: player_sprite_->setCurrentAnimation("celebration"); break; default: break; } player_sprite_->update(); power_sprite_->update(); } // Actualiza el valor de la variable void Player::updateCooldown() { if (playing_state_ != State::PLAYING) { return; } if (cant_fire_counter_ > 0) { handleFiringCooldown(); } else { handleRecoilAndCooling(); } } void Player::handleFiringCooldown() { cooling_state_counter_ = COOLING_DURATION; // Transition to recoiling state at halfway point if (cant_fire_counter_ == recoiling_state_duration_ / 2) { transitionToRecoiling(); } --cant_fire_counter_; if (cant_fire_counter_ == 0) { recoiling_state_counter_ = recoiling_state_duration_; } } void Player::handleRecoilAndCooling() { if (recoiling_state_counter_ > 0) { --recoiling_state_counter_; return; } handleCoolingState(); } void Player::handleCoolingState() { if (cooling_state_counter_ > COOLING_COMPLETE) { if (cooling_state_counter_ == COOLING_DURATION) { transitionToCooling(); } --cooling_state_counter_; } if (cooling_state_counter_ == COOLING_COMPLETE) { completeCooling(); } } void Player::transitionToRecoiling() { switch (firing_state_) { case State::FIRING_LEFT: setFiringState(State::RECOILING_LEFT); break; case State::FIRING_RIGHT: setFiringState(State::RECOILING_RIGHT); break; case State::FIRING_UP: setFiringState(State::RECOILING_UP); break; default: break; } } void Player::transitionToCooling() { switch (firing_state_) { case State::RECOILING_LEFT: setFiringState(State::COOLING_LEFT); break; case State::RECOILING_RIGHT: setFiringState(State::COOLING_RIGHT); break; case State::RECOILING_UP: setFiringState(State::COOLING_UP); break; default: break; } } void Player::completeCooling() { setFiringState(State::FIRING_NONE); cooling_state_counter_ = -1; } // Actualiza al jugador a su posicion, animación y controla los contadores void Player::update() { move(); setAnimation(); shiftColliders(); updateCooldown(); updatePowerUp(); updateInvulnerable(); updateScoreboard(); updateContinueCounter(); updateEnterNameCounter(); updateShowingName(); } // Incrementa la puntuación del jugador void Player::addScore(int score, int last_hi_score_entry) { if (isPlaying()) { score_ += score; qualifies_for_high_score_ = score_ > last_hi_score_entry; } } // Actualiza el panel del marcador void Player::updateScoreboard() { switch (playing_state_) { case State::CONTINUE: { Scoreboard::get()->setContinue(scoreboard_panel_, getContinueCounter()); break; } case State::ENTERING_NAME: case State::ENTERING_NAME_GAME_COMPLETED: { Scoreboard::get()->setRecordName(scoreboard_panel_, enter_name_->getCurrentName()); Scoreboard::get()->setSelectorPos(scoreboard_panel_, getRecordNamePos()); break; } default: break; } } // Cambia el modo del marcador void Player::setScoreboardMode(Scoreboard::Mode mode) const { if (!demo_) { Scoreboard::get()->setMode(scoreboard_panel_, mode); } } // Establece el estado del jugador en el juego void Player::setPlayingState(State state) { playing_state_ = state; switch (playing_state_) { case State::RESPAWNING: { playSound("voice_thankyou.wav"); setPlayingState(State::PLAYING); setInvulnerable(true); break; } case State::PLAYING: { init(); setInvulnerable(true); setScoreboardMode(Scoreboard::Mode::SCORE); Stage::power_can_be_added = true; break; } case State::CONTINUE: { // Inicializa el contador de continuar continue_ticks_ = SDL_GetTicks(); continue_counter_ = 9; playSound("continue_clock.wav"); setScoreboardMode(Scoreboard::Mode::CONTINUE); break; } case State::WAITING: { switch (id_) { case Id::PLAYER1: pos_x_ = param.game.game_area.rect.x; break; case Id::PLAYER2: pos_x_ = param.game.game_area.rect.w - WIDTH; break; default: pos_x_ = 0; break; } pos_y_ = default_pos_y_; waiting_counter_ = 0; shiftSprite(); player_sprite_->setCurrentAnimation("hello"); player_sprite_->animtionPause(); setScoreboardMode(Scoreboard::Mode::WAITING); break; } case State::ENTERING_NAME: { setScoreboardMode(Scoreboard::Mode::ENTER_NAME); break; } case State::SHOWING_NAME: { showing_name_ticks_ = SDL_GetTicks(); setScoreboardMode(Scoreboard::Mode::SHOW_NAME); Scoreboard::get()->setRecordName(scoreboard_panel_, last_enter_name_); addScoreToScoreBoard(); break; } case State::ROLLING: { // Activa la animación de rodar dando botes player_sprite_->setCurrentAnimation("rolling"); player_sprite_->setAnimationSpeed(4); player_sprite_->setVelY(-6.6F); // Velocidad inicial player_sprite_->setAccelY(0.2F); // Gravedad player_sprite_->setPosY(pos_y_ - 2); // Para "sacarlo" del suelo, ya que está hundido un pixel para ocultar el outline de los pies (rand() % 2 == 0) ? player_sprite_->setVelX(3.3F) : player_sprite_->setVelX(-3.3F); break; } case State::TITLE_ANIMATION: { // Activa la animación de rodar player_sprite_->setCurrentAnimation("walk"); playSound("voice_thankyou.wav"); break; } case State::TITLE_HIDDEN: { player_sprite_->setVelX(0.0F); player_sprite_->setVelY(0.0F); break; } case State::CONTINUE_TIME_OUT: { // Activa la animación de sacar al jugador de la zona de juego player_sprite_->setAccelY(0.2F); player_sprite_->setVelY(-4.0F); player_sprite_->setVelX(0.0F); player_sprite_->setCurrentAnimation("rolling"); player_sprite_->setAnimationSpeed(5); setScoreboardMode(Scoreboard::Mode::GAME_OVER); playSound("voice_aw_aw_aw.wav"); playSound("jump.wav"); break; } case State::GAME_OVER: { setScoreboardMode(Scoreboard::Mode::GAME_OVER); break; } case State::CELEBRATING: { game_completed_ = true; setScoreboardMode(Scoreboard::Mode::SCORE); break; } case State::ENTERING_NAME_GAME_COMPLETED: { setWalkingState(State::WALKING_STOP); setFiringState(State::FIRING_NONE); setScoreboardMode(Scoreboard::Mode::ENTER_NAME); break; } case State::LEAVING_SCREEN: { step_counter_ = 0; setScoreboardMode(Scoreboard::Mode::GAME_COMPLETED); break; } case State::ENTERING_SCREEN: { init(); step_counter_ = 0; setScoreboardMode(Scoreboard::Mode::SCORE); switch (id_) { case Id::PLAYER1: pos_x_ = param.game.game_area.rect.x - WIDTH; break; case Id::PLAYER2: pos_x_ = param.game.game_area.rect.x + param.game.game_area.rect.w; break; default: break; } break; } case State::CREDITS: { vel_x_ = (walking_state_ == State::WALKING_RIGHT) ? BASE_SPEED : -BASE_SPEED; break; } default: break; } } // 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); } // Establece el valor del estado void Player::setInvulnerable(bool value) { invulnerable_ = value; invulnerable_counter_ = invulnerable_ ? INVULNERABLE_COUNTER : 0; } // Monitoriza el estado void Player::updateInvulnerable() { if (playing_state_ == State::PLAYING) { if (invulnerable_) { if (invulnerable_counter_ > 0) { --invulnerable_counter_; invulnerable_counter_ % 8 > 3 ? player_sprite_->setActiveTexture(coffees_) : player_sprite_->setActiveTexture(3); } else { setInvulnerable(false); player_sprite_->setActiveTexture(coffees_); } } } } // Establece el valor de la variable void Player::setPowerUp() { power_up_ = true; power_up_counter_ = POWERUP_COUNTER; } // Actualiza el valor de la variable void Player::updatePowerUp() { if (playing_state_ == State::PLAYING) { if (power_up_) { --power_up_counter_; power_up_ = power_up_counter_ > 0; } } } // Concede un toque extra al jugador void Player::giveExtraHit() { extra_hit_ = true; if (coffees_ < 2) { coffees_++; player_sprite_->setActiveTexture(coffees_); } } // Quita el toque extra al jugador void Player::removeExtraHit() { if (coffees_ > 0) { coffees_--; setInvulnerable(true); player_sprite_->setActiveTexture(coffees_); } extra_hit_ = coffees_ != 0; } // 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 = (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]); } // Actualiza el contador de continue void Player::updateContinueCounter() { if (playing_state_ == State::CONTINUE) { constexpr int TICKS_SPEED = 1000; if (SDL_GetTicks() - continue_ticks_ > TICKS_SPEED) { decContinueCounter(); } } } // Actualiza el contador de entrar nombre void Player::updateEnterNameCounter() { if (playing_state_ == State::ENTERING_NAME || playing_state_ == State::ENTERING_NAME_GAME_COMPLETED) { constexpr int TICKS_SPEED = 1000; if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED) { decNameEntryCounter(); } } } // Actualiza el estado de SHOWING_NAME void Player::updateShowingName() { if (playing_state_ == State::SHOWING_NAME) { constexpr int TICKS_SPEED = 5000; if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED) { game_completed_ ? setPlayingState(State::LEAVING_SCREEN) : setPlayingState(State::CONTINUE); } } } // Decrementa el contador de continuar void Player::decContinueCounter() { continue_ticks_ = SDL_GetTicks(); --continue_counter_; if (continue_counter_ < 0) { setPlayingState(State::CONTINUE_TIME_OUT); } else { playSound("continue_clock.wav"); } } // Decrementa el contador de entrar nombre void Player::decNameEntryCounter() { name_entry_ticks_ = SDL_GetTicks(); // Actualiza contadores ++name_entry_idle_counter_; ++name_entry_total_counter_; // Comprueba los contadores if ((name_entry_total_counter_ >= param.game.name_entry_total_time) || (name_entry_idle_counter_ >= param.game.name_entry_idle_time)) { name_entry_total_counter_ = 0; name_entry_idle_counter_ = 0; if (playing_state_ == State::ENTERING_NAME) { last_enter_name_ = getRecordName(); setPlayingState(State::SHOWING_NAME); } else { setPlayingState(State::LEAVING_SCREEN); } } } // Obtiene la posición que se está editando del nombre del jugador para la tabla de mejores puntuaciones auto Player::getRecordNamePos() const -> int { if (enter_name_) { return enter_name_->getPosition(); } return 0; } // Recoloca los sprites void Player::shiftSprite() { player_sprite_->setPosX(pos_x_); player_sprite_->setPosY(pos_y_); power_sprite_->setPosX(getPosX() - power_up_x_offset_); } // Hace sonar un sonido void Player::playSound(const std::string &name) const { if (demo_) { return; } static auto *audio_ = Audio::get(); audio_->playSound(name); } // Indica si se puede dibujar el objeto auto Player::isRenderable() const -> bool { // return !isGameOver() && !isTitleHidden(); return !isTitleHidden(); }; // Añade una puntuación a la tabla de records void Player::addScoreToScoreBoard() const { if (hi_score_table_ == nullptr) { return; // Verificar esto antes de crear el manager } const auto ENTRY = HiScoreEntry(trim(getLastEnterName()), getScore(), get1CC()); auto manager = std::make_unique(*hi_score_table_); if (glowing_entry_ != nullptr) { *glowing_entry_ = manager->add(ENTRY); } manager->saveToFile(Asset::get()->get("score.bin")); }