No canviava al marcador, ni el nom del que tenia la maxima puntuació en calent ni al posar nom retallat el nom de 8 a 6 caracters, i tots en majuscula pa que capia en el marcador ja actualitza be la cadena amb el nom al posar nom per segona vegada en la mateixa partida
597 lines
14 KiB
C++
597 lines
14 KiB
C++
#include "player.h"
|
|
#include <SDL2/SDL_render.h> // Para SDL_FLIP_HORIZONTAL, SDL_FLIP_NONE
|
|
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks
|
|
#include <stdlib.h> // Para rand
|
|
#include <algorithm> // 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<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
|
|
init();
|
|
}
|
|
|
|
// Iniciador
|
|
void Player::init()
|
|
{
|
|
// Inicializa variables de estado
|
|
pos_x_ = default_pos_x_;
|
|
pos_y_ = default_pos_y_;
|
|
walking_state_ = PlayerState::WALKING_STOP;
|
|
firing_state_ = PlayerState::FIRING_NONE;
|
|
playing_state_ = PlayerState::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_ = 999999;
|
|
score_multiplier_ = 1.0f;
|
|
cooldown_ = 10;
|
|
enter_name_->init();
|
|
|
|
// 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(InputType 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(InputType input)
|
|
{
|
|
switch (input)
|
|
{
|
|
case InputType::LEFT:
|
|
{
|
|
vel_x_ = -BASE_SPEED_;
|
|
setWalkingState(PlayerState::WALKING_LEFT);
|
|
break;
|
|
}
|
|
case InputType::RIGHT:
|
|
{
|
|
vel_x_ = BASE_SPEED_;
|
|
setWalkingState(PlayerState::WALKING_RIGHT);
|
|
break;
|
|
}
|
|
case InputType::FIRE_CENTER:
|
|
{
|
|
setFiringState(PlayerState::FIRING_UP);
|
|
break;
|
|
}
|
|
case InputType::FIRE_LEFT:
|
|
{
|
|
setFiringState(PlayerState::FIRING_LEFT);
|
|
break;
|
|
}
|
|
case InputType::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(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:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 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 = param.game.play_area.rect.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::DYING:
|
|
{
|
|
// Si el cadaver abandona el area de juego por los laterales lo hace rebotar
|
|
if ((player_sprite_->getPosX() < param.game.play_area.rect.x) || (player_sprite_->getPosX() + WIDTH_ > play_area_.w))
|
|
player_sprite_->setVelX(-player_sprite_->getVelX());
|
|
|
|
// Si el cadaver abandona el area de juego por abajo
|
|
if (player_sprite_->getPosY() > param.game.play_area.rect.h)
|
|
setPlayingState(PlayerState::DIED);
|
|
break;
|
|
}
|
|
case PlayerState::GAME_COMPLETED:
|
|
{
|
|
switch (id_)
|
|
{
|
|
case 1:
|
|
setInputPlaying(InputType::LEFT);
|
|
break;
|
|
case 2:
|
|
setInputPlaying(InputType::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;
|
|
}
|
|
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()
|
|
{
|
|
// Crea cadenas de texto para componer el nombre de la animación
|
|
const std::string a_walking = walking_state_ == PlayerState::WALKING_STOP ? "stand" : "walk";
|
|
const std::string a_firing = firing_state_ == PlayerState::FIRING_UP ? "centershoot" : "sideshoot";
|
|
const std::string a_cooling = firing_state_ == PlayerState::COOLING_UP ? "centershoot" : "sideshoot";
|
|
|
|
const SDL_RendererFlip flip_walk = walking_state_ == PlayerState::WALKING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE;
|
|
const SDL_RendererFlip flip_fire = firing_state_ == PlayerState::FIRING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE;
|
|
const SDL_RendererFlip flip_cooling = firing_state_ == PlayerState::COOLING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE;
|
|
|
|
// Establece la animación a partir de las cadenas
|
|
if (isPlaying() || isEnteringNameGameCompleted() || isGameCompleted())
|
|
{
|
|
if (firing_state_ == PlayerState::FIRING_NONE)
|
|
{ // No esta disparando
|
|
player_sprite_->setCurrentAnimation(a_walking);
|
|
player_sprite_->setFlip(flip_walk);
|
|
}
|
|
else if (isCooling())
|
|
{ // Acaba de disparar
|
|
player_sprite_->setCurrentAnimation(a_walking + "-" + a_cooling + "-cooldown");
|
|
player_sprite_->setFlip(flip_cooling);
|
|
}
|
|
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
|
|
player_sprite_->setFlip(a_firing == "centershoot" ? flip_walk : flip_fire);
|
|
}
|
|
}
|
|
else if (isDying() || hasDied())
|
|
{
|
|
player_sprite_->setCurrentAnimation("death");
|
|
}
|
|
else if (isCelebrating())
|
|
{
|
|
player_sprite_->setCurrentAnimation("celebration");
|
|
}
|
|
|
|
// 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 (cooldown_ > 0)
|
|
{
|
|
cooldown_ -= power_up_ ? 2 : 1;
|
|
}
|
|
else
|
|
{
|
|
if (!isCooling())
|
|
{
|
|
cooling_status_counter_ = 40;
|
|
switch (firing_state_)
|
|
{
|
|
case PlayerState::FIRING_LEFT:
|
|
firing_state_ = PlayerState::COOLING_LEFT;
|
|
break;
|
|
case PlayerState::FIRING_RIGHT:
|
|
firing_state_ = PlayerState::COOLING_RIGHT;
|
|
break;
|
|
case PlayerState::FIRING_UP:
|
|
firing_state_ = PlayerState::COOLING_UP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (cooling_status_counter_ > 0)
|
|
{
|
|
--cooling_status_counter_;
|
|
}
|
|
else
|
|
{
|
|
setFiringState(PlayerState::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();
|
|
}
|
|
|
|
// 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_->getName());
|
|
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::PLAYING:
|
|
{
|
|
init();
|
|
playing_state_ = PlayerState::PLAYING;
|
|
setScoreboardMode(ScoreboardMode::SCORE);
|
|
break;
|
|
}
|
|
case PlayerState::CONTINUE:
|
|
{
|
|
// Inicializa el contador de continuar
|
|
continue_ticks_ = SDL_GetTicks();
|
|
continue_counter_ = 9;
|
|
setScoreboardMode(ScoreboardMode::CONTINUE);
|
|
break;
|
|
}
|
|
case PlayerState::WAITING:
|
|
{
|
|
setScoreboardMode(ScoreboardMode::WAITING);
|
|
break;
|
|
}
|
|
case PlayerState::ENTERING_NAME:
|
|
{
|
|
setScoreboardMode(ScoreboardMode::ENTER_NAME);
|
|
break;
|
|
}
|
|
case PlayerState::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 PlayerState::DIED:
|
|
{
|
|
const auto nextPlayerStatus = IsEligibleForHighScore() ? PlayerState::ENTERING_NAME : PlayerState::CONTINUE;
|
|
demo_ ? setPlayingState(PlayerState::WAITING) : setPlayingState(nextPlayerStatus);
|
|
break;
|
|
}
|
|
case PlayerState::GAME_OVER:
|
|
{
|
|
setScoreboardMode(ScoreboardMode::GAME_OVER);
|
|
break;
|
|
}
|
|
case PlayerState::CELEBRATING:
|
|
{
|
|
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::GAME_COMPLETED:
|
|
{
|
|
setScoreboardMode(ScoreboardMode::GAME_COMPLETED);
|
|
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 (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 (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() - enter_name_ticks_ > TICKS_SPEED)
|
|
{
|
|
decEnterNameCounter();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Decrementa el contador de continuar
|
|
void Player::decContinueCounter()
|
|
{
|
|
continue_ticks_ = SDL_GetTicks();
|
|
--continue_counter_;
|
|
if (continue_counter_ < 0)
|
|
{
|
|
setPlayingState(PlayerState::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;
|
|
if (playing_state_ == PlayerState::ENTERING_NAME)
|
|
setPlayingState(PlayerState::CONTINUE);
|
|
else
|
|
setPlayingState(PlayerState::GAME_COMPLETED);
|
|
}
|
|
}
|
|
|
|
// 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_);
|
|
} |