Files
coffee_crisis_arcade_edition/source/player.cpp
Sergio Valor 928335576c corregida la llista de inicialització en clang-format
creat Balloon::Config per a inicialitzar globos
2025-08-24 17:16:49 +02:00

983 lines
29 KiB
C++

#include "player.h"
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_FlipMode
#include <algorithm> // Para clamp, max, min
#include <cstdlib> // 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 "stage_interface.h" // Para IStageInfo
#include "texture.h" // Para Texture
#ifdef _DEBUG
#include <iostream>
#endif
// Constructor
Player::Player(const Config &config)
: player_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(0), config.animations.at(0))),
power_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(4), config.animations.at(1))),
enter_name_(std::make_unique<EnterName>()),
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),
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();
}
// 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;
case State::RECOVER:
handleRecoverMovement();
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::handleRecoverMovement() {
if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_recover.wav"); }
if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); }
}
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;
}
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<std::string, SDL_FlipMode> {
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::RECOVER:
player_sprite_->setCurrentAnimation("recover");
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 lowest_hi_score_entry) {
if (isPlaying()) {
score_ += score;
qualifies_for_high_score_ = score_ > lowest_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::RECOVER: {
score_ = 0; // Pon los puntos a cero para que no se vea en el marcador
score_multiplier_ = 1.0F;
setScoreboardMode(Scoreboard::Mode::SCORE);
break;
}
case State::RESPAWNING: {
playSound("voice_thankyou.wav");
setPlayingState(State::PLAYING);
setInvulnerable(true);
break;
}
case State::PLAYING: {
init();
setInvulnerable(true);
setScoreboardMode(Scoreboard::Mode::SCORE);
stage_info_->enablePowerCollection();
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_credit_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 && invulnerable_) {
if (invulnerable_counter_ > 0) {
--invulnerable_counter_;
// Frecuencia fija de parpadeo (como el original)
constexpr int blink_speed = 8;
// Calcula proporción decreciente: menos textura blanca hacia el final
// Al inicio: 50-50, hacia el final: 70-30 (menos blanco)
float progress = 1.0f - (static_cast<float>(invulnerable_counter_) / INVULNERABLE_COUNTER);
int white_frames = static_cast<int>((0.5f - progress * 0.2f) * blink_speed);
// Alterna entre texturas con proporción variable
bool should_show_invulnerable = (invulnerable_counter_ % blink_speed) < white_frames;
size_t target_texture = should_show_invulnerable ? INVULNERABLE_TEXTURE : coffees_;
// Solo cambia textura si es diferente (optimización)
if (player_sprite_->getActiveTexture() != target_texture) {
player_sprite_->setActiveTexture(target_texture);
}
} else {
// Fin de invulnerabilidad
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<int>(pos_x_ + (WIDTH / 2));
collider_.y = (pos_y_ + (HEIGHT / 2));
}
// Pone las texturas del jugador
void Player::setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &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 !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<ManageHiScoreTable>(*hi_score_table_);
if (glowing_entry_ != nullptr) {
*glowing_entry_ = manager->add(ENTRY);
}
manager->saveToFile(Asset::get()->get("score.bin"));
}
void Player::addCredit() {
++credits_used_;
playSound("credit.wav");
}