927 lines
22 KiB
C++
927 lines
22 KiB
C++
#include "player.h"
|
|
#include <SDL3/SDL_surface.h> // Para SDL_FlipMode
|
|
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
|
|
#include <stdlib.h> // Para rand
|
|
#include <algorithm> // Para clamp, max, min
|
|
#include <array> // Para array
|
|
#include "animated_sprite.h" // Para AnimatedSprite
|
|
#include "input.h" // Para InputAction
|
|
#include "audio.h" // Para JA_PlaySound
|
|
#include "param.h" // Para Param, ParamGame, param
|
|
#include "resource.h" // Para Resource
|
|
#include "scoreboard.h" // Para Scoreboard, ScoreboardMode
|
|
#include "stage.h" // Para power_can_be_added
|
|
#include "texture.h" // Para Texture
|
|
#include "manage_hiscore_table.h" // Para ManageHiScoreTable, HiScoreEntry
|
|
|
|
// Constructor
|
|
Player::Player(int id, float x, int y, bool demo, SDL_FRect &play_area, std::vector<std::shared_ptr<Texture>> texture, const std::vector<std::vector<std::string>> &animations)
|
|
: player_sprite_(std::make_unique<AnimatedSprite>(texture[0], animations[0])),
|
|
power_sprite_(std::make_unique<AnimatedSprite>(texture[1], animations[1])),
|
|
enter_name_(std::make_unique<EnterName>()),
|
|
id_(id),
|
|
play_area_(play_area),
|
|
default_pos_x_(x),
|
|
default_pos_y_(y),
|
|
demo_(demo)
|
|
{
|
|
// Configura objetos
|
|
player_sprite_->getTexture()->setPalette(coffees_);
|
|
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
|
|
pos_x_ = default_pos_x_;
|
|
invulnerable_ = false;
|
|
invulnerable_counter_ = 0;
|
|
init();
|
|
}
|
|
|
|
// Iniciador
|
|
void Player::init()
|
|
{
|
|
// Inicializa variables de estado
|
|
pos_y_ = default_pos_y_;
|
|
walking_state_ = PlayerState::WALKING_STOP;
|
|
firing_state_ = PlayerState::FIRING_NONE;
|
|
playing_state_ = PlayerState::WAITING;
|
|
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(InputAction input)
|
|
{
|
|
switch (playing_state_)
|
|
{
|
|
case PlayerState::PLAYING:
|
|
{
|
|
setInputPlaying(input);
|
|
break;
|
|
}
|
|
case PlayerState::ENTERING_NAME:
|
|
case PlayerState::ENTERING_NAME_GAME_COMPLETED:
|
|
{
|
|
setInputEnteringName(input);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Procesa inputs para cuando está jugando
|
|
void Player::setInputPlaying(InputAction input)
|
|
{
|
|
switch (input)
|
|
{
|
|
case InputAction::LEFT:
|
|
{
|
|
vel_x_ = -BASE_SPEED_;
|
|
setWalkingState(PlayerState::WALKING_LEFT);
|
|
break;
|
|
}
|
|
case InputAction::RIGHT:
|
|
{
|
|
vel_x_ = BASE_SPEED_;
|
|
setWalkingState(PlayerState::WALKING_RIGHT);
|
|
break;
|
|
}
|
|
case InputAction::FIRE_CENTER:
|
|
{
|
|
setFiringState(PlayerState::FIRING_UP);
|
|
break;
|
|
}
|
|
case InputAction::FIRE_LEFT:
|
|
{
|
|
setFiringState(PlayerState::FIRING_LEFT);
|
|
break;
|
|
}
|
|
case InputAction::FIRE_RIGHT:
|
|
{
|
|
setFiringState(PlayerState::FIRING_RIGHT);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
vel_x_ = 0;
|
|
setWalkingState(PlayerState::WALKING_STOP);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Procesa inputs para cuando está introduciendo el nombre
|
|
void Player::setInputEnteringName(InputAction input)
|
|
{
|
|
switch (input)
|
|
{
|
|
case InputAction::LEFT:
|
|
enter_name_->decPosition();
|
|
break;
|
|
case InputAction::RIGHT:
|
|
enter_name_->incPosition();
|
|
break;
|
|
case InputAction::UP:
|
|
enter_name_->incIndex();
|
|
break;
|
|
case InputAction::DOWN:
|
|
enter_name_->decIndex();
|
|
break;
|
|
case InputAction::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 PlayerState::PLAYING:
|
|
{
|
|
// 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();
|
|
break;
|
|
}
|
|
case PlayerState::ROLLING:
|
|
{
|
|
// Si el jugador abandona el area de juego por los laterales lo hace rebotar
|
|
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");
|
|
}
|
|
|
|
// Si el jugador toca el suelo rebota y si tiene poca velocidad, se detiene y cambia de estado
|
|
if (player_sprite_->getPosY() > play_area_.h - HEIGHT_)
|
|
{
|
|
if (player_sprite_->getVelY() < 2.0f)
|
|
{
|
|
// Si la velocidad de rebote es baja, termina de rebotar y cambia de estado
|
|
const auto nextPlayerStatus = IsEligibleForHighScore() ? PlayerState::ENTERING_NAME : PlayerState::CONTINUE;
|
|
demo_ ? setPlayingState(PlayerState::LYING_ON_THE_FLOOR_FOREVER) : setPlayingState(nextPlayerStatus);
|
|
pos_x_ = player_sprite_->getPosX();
|
|
pos_y_ = default_pos_y_;
|
|
player_sprite_->clear();
|
|
shiftSprite();
|
|
playSound("jump.wav");
|
|
}
|
|
else
|
|
{
|
|
// Decrementa las velocidades de rebote
|
|
player_sprite_->setPosY(play_area_.h - HEIGHT_);
|
|
player_sprite_->setVelY(player_sprite_->getVelY() * -0.5f);
|
|
player_sprite_->setVelX(player_sprite_->getVelX() * 0.75f);
|
|
playSound("jump.wav");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case PlayerState::TITLE_ANIMATION:
|
|
{
|
|
// Si el jugador abandona el area de juego por los laterales lo detiene
|
|
const int X = player_sprite_->getPosX();
|
|
const int MIN_X = play_area_.x - WIDTH_;
|
|
const int MAX_X = play_area_.x + play_area_.w;
|
|
if ((X < MIN_X) || (X > MAX_X))
|
|
{
|
|
setPlayingState(PlayerState::TITLE_HIDDEN);
|
|
}
|
|
|
|
// Si el jugador toca el suelo rebota lo detiene
|
|
if (player_sprite_->getPosY() > play_area_.h)
|
|
{
|
|
setPlayingState(PlayerState::TITLE_HIDDEN);
|
|
}
|
|
break;
|
|
}
|
|
case PlayerState::CONTINUE_TIME_OUT:
|
|
{
|
|
// Si el cadaver desaparece por el suelo, cambia de estado
|
|
if (player_sprite_->getPosY() > play_area_.h)
|
|
{
|
|
setPlayingState(PlayerState::WAITING);
|
|
}
|
|
break;
|
|
}
|
|
case PlayerState::LEAVING_SCREEN:
|
|
{
|
|
++step_counter_;
|
|
if (step_counter_ % 10 == 0)
|
|
{
|
|
playSound("walk.wav");
|
|
}
|
|
|
|
switch (id_)
|
|
{
|
|
case 1:
|
|
setInputPlaying(InputAction::LEFT);
|
|
break;
|
|
case 2:
|
|
setInputPlaying(InputAction::RIGHT);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
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(PlayerState::GAME_OVER);
|
|
}
|
|
break;
|
|
}
|
|
case PlayerState::ENTERING_SCREEN:
|
|
{
|
|
++step_counter_;
|
|
if (step_counter_ % 10 == 0)
|
|
{
|
|
playSound("walk.wav");
|
|
}
|
|
|
|
switch (id_)
|
|
{
|
|
case 1:
|
|
setInputPlaying(InputAction::RIGHT);
|
|
pos_x_ += vel_x_;
|
|
if (pos_x_ > default_pos_x_)
|
|
{
|
|
pos_x_ = default_pos_x_;
|
|
setPlayingState(PlayerState::PLAYING);
|
|
setInvulnerable(false);
|
|
}
|
|
break;
|
|
case 2:
|
|
setInputPlaying(InputAction::LEFT);
|
|
pos_x_ += vel_x_;
|
|
if (pos_x_ < default_pos_x_)
|
|
{
|
|
pos_x_ = default_pos_x_;
|
|
setPlayingState(PlayerState::PLAYING);
|
|
setInvulnerable(false);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
shiftSprite();
|
|
break;
|
|
}
|
|
case PlayerState::CREDITS:
|
|
{
|
|
pos_x_ += vel_x_ / 2.0f;
|
|
if (vel_x_ > 0)
|
|
{
|
|
// setInputPlaying(InputAction::RIGHT);
|
|
if (pos_x_ > param.game.game_area.rect.w - WIDTH_)
|
|
{
|
|
pos_x_ = param.game.game_area.rect.w - WIDTH_;
|
|
vel_x_ *= -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// setInputPlaying(InputAction::LEFT);
|
|
if (pos_x_ < param.game.game_area.rect.x)
|
|
{
|
|
pos_x_ = param.game.game_area.rect.x;
|
|
vel_x_ *= -1;
|
|
}
|
|
}
|
|
|
|
if (pos_x_ > param.game.game_area.center_x - WIDTH_ / 2)
|
|
{
|
|
setWalkingState(PlayerState::WALKING_LEFT);
|
|
}
|
|
else
|
|
{
|
|
setWalkingState(PlayerState::WALKING_RIGHT);
|
|
}
|
|
shiftSprite();
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 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 la animación correspondiente al estado
|
|
void Player::setAnimation()
|
|
{
|
|
switch (playing_state_)
|
|
{
|
|
case PlayerState::PLAYING:
|
|
case PlayerState::ENTERING_NAME_GAME_COMPLETED:
|
|
case PlayerState::ENTERING_SCREEN:
|
|
case PlayerState::LEAVING_SCREEN:
|
|
case PlayerState::CREDITS:
|
|
{
|
|
// Crea cadenas de texto para componer el nombre de la animación
|
|
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_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(WALK_ANIMATION, false);
|
|
player_sprite_->setFlip(FLIP_WALK);
|
|
}
|
|
else if (isRecoiling())
|
|
{
|
|
// Retroceso
|
|
player_sprite_->setCurrentAnimation(WALK_ANIMATION + RECOIL_ANIMATION, false);
|
|
player_sprite_->setFlip(FLIP_RECOIL);
|
|
}
|
|
else if (isCooling())
|
|
{
|
|
// Acaba de disparar
|
|
player_sprite_->setCurrentAnimation(WALK_ANIMATION + COOL_ANIMATION, false);
|
|
player_sprite_->setFlip(FLIP_COOL);
|
|
}
|
|
else
|
|
{
|
|
// Está disparando
|
|
player_sprite_->setCurrentAnimation(WALK_ANIMATION + FIRE_ANIMATION, false);
|
|
// Si dispara de lado, invierte el sprite segun hacia donde dispara
|
|
// Si dispara recto, invierte el sprite segun hacia donde camina
|
|
player_sprite_->setFlip(FIRE_ANIMATION == "-fire-center" ? FLIP_WALK : FLIP_FIRE);
|
|
}
|
|
break;
|
|
}
|
|
case PlayerState::ROLLING:
|
|
case PlayerState::CONTINUE_TIME_OUT:
|
|
case PlayerState::TITLE_ANIMATION:
|
|
{
|
|
player_sprite_->setCurrentAnimation("rolling");
|
|
break;
|
|
}
|
|
case PlayerState::LYING_ON_THE_FLOOR_FOREVER:
|
|
case PlayerState::ENTERING_NAME:
|
|
case PlayerState::CONTINUE:
|
|
{
|
|
player_sprite_->setCurrentAnimation("dead");
|
|
break;
|
|
}
|
|
case PlayerState::CELEBRATING:
|
|
{
|
|
player_sprite_->setCurrentAnimation("celebration");
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Actualiza las animaciones de los sprites
|
|
player_sprite_->update(); // Hace avanzar las animaciones y mueve el cadaver del jugador
|
|
power_sprite_->update();
|
|
}
|
|
|
|
// Actualiza el valor de la variable
|
|
void Player::updateCooldown()
|
|
{
|
|
if (playing_state_ == PlayerState::PLAYING)
|
|
{
|
|
if (cant_fire_counter_ > 0)
|
|
{
|
|
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::RECOILING_LEFT);
|
|
break;
|
|
case PlayerState::FIRING_RIGHT:
|
|
setFiringState(PlayerState::RECOILING_RIGHT);
|
|
break;
|
|
case PlayerState::FIRING_UP:
|
|
setFiringState(PlayerState::RECOILING_UP);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
--cant_fire_counter_;
|
|
if (cant_fire_counter_ == 0)
|
|
{
|
|
recoiling_state_counter_ = recoiling_state_duration_;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (recoiling_state_counter_ > 0)
|
|
{
|
|
--recoiling_state_counter_;
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
if (isPlaying())
|
|
{
|
|
score_ += score;
|
|
}
|
|
}
|
|
|
|
// Actualiza el panel del marcador
|
|
void Player::updateScoreboard()
|
|
{
|
|
switch (playing_state_)
|
|
{
|
|
case PlayerState::CONTINUE:
|
|
{
|
|
Scoreboard::get()->setContinue(getScoreBoardPanel(), getContinueCounter());
|
|
break;
|
|
}
|
|
case PlayerState::ENTERING_NAME:
|
|
case PlayerState::ENTERING_NAME_GAME_COMPLETED:
|
|
{
|
|
Scoreboard::get()->setRecordName(getScoreBoardPanel(), enter_name_->getCurrentName());
|
|
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::setPlayingState(PlayerState state)
|
|
{
|
|
playing_state_ = state;
|
|
|
|
switch (playing_state_)
|
|
{
|
|
case PlayerState::RESPAWNING:
|
|
{
|
|
setInvulnerable(true);
|
|
addCredit();
|
|
playSound("voice_thankyou.wav");
|
|
setPlayingState(PlayerState::PLAYING);
|
|
}
|
|
case PlayerState::PLAYING:
|
|
{
|
|
init();
|
|
playing_state_ = PlayerState::PLAYING;
|
|
setScoreboardMode(ScoreboardMode::SCORE);
|
|
Stage::power_can_be_added = true;
|
|
break;
|
|
}
|
|
case PlayerState::CONTINUE:
|
|
{
|
|
// Inicializa el contador de continuar
|
|
continue_ticks_ = SDL_GetTicks();
|
|
continue_counter_ = 9;
|
|
setScoreboardMode(ScoreboardMode::CONTINUE);
|
|
playSound("continue_clock.wav");
|
|
break;
|
|
}
|
|
case PlayerState::WAITING:
|
|
{
|
|
pos_x_ = default_pos_x_;
|
|
setScoreboardMode(ScoreboardMode::WAITING);
|
|
break;
|
|
}
|
|
case PlayerState::ENTERING_NAME:
|
|
{
|
|
setScoreboardMode(ScoreboardMode::ENTER_NAME);
|
|
break;
|
|
}
|
|
case PlayerState::SHOWING_NAME:
|
|
{
|
|
showing_name_ticks_ = SDL_GetTicks();
|
|
setScoreboardMode(ScoreboardMode::SHOW_NAME);
|
|
Scoreboard::get()->setRecordName(scoreboard_panel_, last_enter_name_);
|
|
addScoreToScoreBoard();
|
|
break;
|
|
}
|
|
case PlayerState::ROLLING:
|
|
{
|
|
// Activa la animación de rodar
|
|
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 PlayerState::TITLE_ANIMATION:
|
|
{
|
|
// Activa la animación de rodar
|
|
player_sprite_->setAccelY(0.2f);
|
|
player_sprite_->setVelY(-6.6f);
|
|
playSound("voice_thankyou.wav");
|
|
break;
|
|
}
|
|
case PlayerState::TITLE_HIDDEN:
|
|
{
|
|
player_sprite_->setVelX(0.0f);
|
|
player_sprite_->setVelY(0.0f);
|
|
break;
|
|
}
|
|
case PlayerState::CONTINUE_TIME_OUT:
|
|
{
|
|
// Activa la animación de morir
|
|
player_sprite_->setAccelY(0.2f);
|
|
player_sprite_->setVelY(-4.0f);
|
|
player_sprite_->setVelX(0.0f);
|
|
player_sprite_->setCurrentAnimation("rolling");
|
|
player_sprite_->setAnimationSpeed(5);
|
|
setScoreboardMode(ScoreboardMode::GAME_OVER);
|
|
playSound("voice_aw_aw_aw.wav");
|
|
playSound("jump.wav");
|
|
break;
|
|
}
|
|
case PlayerState::GAME_OVER:
|
|
{
|
|
setScoreboardMode(ScoreboardMode::GAME_OVER);
|
|
break;
|
|
}
|
|
case PlayerState::CELEBRATING:
|
|
{
|
|
game_completed_ = true;
|
|
setScoreboardMode(ScoreboardMode::SCORE);
|
|
break;
|
|
}
|
|
case PlayerState::ENTERING_NAME_GAME_COMPLETED:
|
|
{
|
|
setWalkingState(PlayerState::WALKING_STOP);
|
|
setFiringState(PlayerState::FIRING_NONE);
|
|
setScoreboardMode(ScoreboardMode::ENTER_NAME);
|
|
break;
|
|
}
|
|
case PlayerState::LEAVING_SCREEN:
|
|
{
|
|
step_counter_ = 0;
|
|
setScoreboardMode(ScoreboardMode::GAME_COMPLETED);
|
|
break;
|
|
}
|
|
case PlayerState::ENTERING_SCREEN:
|
|
{
|
|
step_counter_ = 0;
|
|
setScoreboardMode(ScoreboardMode::SCORE);
|
|
switch (id_)
|
|
{
|
|
case 1:
|
|
pos_x_ = param.game.game_area.rect.x - WIDTH_;
|
|
break;
|
|
|
|
case 2:
|
|
pos_x_ = param.game.game_area.rect.x + param.game.game_area.rect.w;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case PlayerState::CREDITS:
|
|
{
|
|
vel_x_ = (walking_state_ == PlayerState::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_ == PlayerState::PLAYING)
|
|
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_);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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_ == PlayerState::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_->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;
|
|
}
|
|
|
|
// 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 = static_cast<int>(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_ == PlayerState::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_ == PlayerState::ENTERING_NAME || playing_state_ == PlayerState::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_ == PlayerState::SHOWING_NAME)
|
|
{
|
|
constexpr int TICKS_SPEED = 5000;
|
|
if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED)
|
|
{
|
|
game_completed_ ? setPlayingState(PlayerState::LEAVING_SCREEN) : setPlayingState(PlayerState::CONTINUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Decrementa el contador de continuar
|
|
void Player::decContinueCounter()
|
|
{
|
|
continue_ticks_ = SDL_GetTicks();
|
|
--continue_counter_;
|
|
if (continue_counter_ < 0)
|
|
{
|
|
setPlayingState(PlayerState::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_ == PlayerState::ENTERING_NAME)
|
|
{
|
|
last_enter_name_ = getRecordName();
|
|
setPlayingState(PlayerState::SHOWING_NAME);
|
|
}
|
|
else
|
|
{
|
|
setPlayingState(PlayerState::LEAVING_SCREEN);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Obtiene la posición 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;
|
|
}
|
|
|
|
// Recoloca los sprites
|
|
void Player::shiftSprite()
|
|
{
|
|
player_sprite_->setPosX(pos_x_);
|
|
player_sprite_->setPosY(pos_y_);
|
|
power_sprite_->setPosX(getPosX() - power_up_desp_x_);
|
|
}
|
|
|
|
void Player::playSound(const std::string &name)
|
|
{
|
|
if (demo_)
|
|
return;
|
|
|
|
static auto audio = Audio::get();
|
|
audio->playSound(name);
|
|
}
|
|
|
|
// Añade una puntuación a la tabla de records
|
|
void Player::addScoreToScoreBoard()
|
|
{
|
|
const auto entry = HiScoreEntry(trim(getLastEnterName()), getScore(), get1CC());
|
|
auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table);
|
|
Options::settings.last_hi_score_entry.at(getId() - 1) = manager->add(entry);
|
|
manager->saveToFile(Asset::get()->get("score.bin"));
|
|
} |