Files
coffee_crisis_arcade_edition/source/player.cpp

809 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "player.h"
#include <SDL2/SDL_render.h> // for SDL_FLIP_HORIZONTAL, SDL_FLIP_NONE, SDL...
#include <SDL2/SDL_timer.h> // for SDL_GetTicks
#include <stdlib.h> // for rand
#include <algorithm> // for max, min
#include "animated_sprite.h" // for AnimatedSprite
#include "input.h" // for inputs_e
#include "param.h" // for param
#include "texture.h" // for Texture
#include "scoreboard.h" // for Texture
#include "options.h"
// Constructor
Player::Player(int id, float x, int y, bool demo, SDL_Rect *play_area, std::vector<std::shared_ptr<Texture>> texture, std::vector<std::vector<std::string> *> animations)
{
// Reserva memoria para los objetos
player_sprite_ = std::make_unique<AnimatedSprite>(texture[0], "", animations[0]);
power_sprite_ = std::make_unique<AnimatedSprite>(texture[1], "", animations[1]);
power_sprite_->getTexture()->setAlpha(224);
enter_name_ = std::make_unique<EnterName>();
// Rectangulo con la zona de juego
play_area_ = play_area;
// Establece la posición inicial del jugador
default_pos_x_ = pos_x_ = x;
default_pos_y_ = pos_y_ = y;
// Establece los offsets para el sprite de PowerUp
power_up_desp_x_ = (power_sprite_->getWidth() - player_sprite_->getWidth()) / 2;
power_sprite_->setPosY(y - (power_sprite_->getHeight() - player_sprite_->getHeight()));
// Inicializa variables
id_ = id;
demo_ = demo;
status_playing_ = PlayerStatus::WAITING;
scoreboard_panel_ = 0;
name_ = "";
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_NO;
invulnerable_ = true;
invulnerable_counter_ = PLAYER_INVULNERABLE_COUNTER;
power_up_ = false;
power_up_counter_ = PLAYER_POWERUP_COUNTER;
extra_hit_ = false;
coffees_ = 0;
input_ = true;
continue_ticks_ = 0;
continue_counter_ = 20;
width_ = 30;
height_ = 30;
collider_.r = 9;
shiftColliders();
vel_x_ = 0;
vel_y_ = 0;
base_speed_ = 1.5;
score_ = 0;
score_multiplier_ = 1.0f;
cooldown_ = 10;
// Establece la posición del sprite
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_->decPos();
break;
case InputType::RIGHT:
enter_name_->incPos();
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_ > (PLAYER_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)
{
// Si cambiamos de estado, reiniciamos la animación
if (status_walking_ != status)
{
status_walking_ = status;
}
}
// Establece el estado del jugador cuando dispara
void Player::setFiringStatus(PlayerStatus status)
{
// Si cambiamos de estado, reiniciamos la animación
if (status_firing_ != 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_NO)
{ // 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_->animate();
// powerSprite->setFlip(flip_walk);
power_sprite_->animate();
}
// 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_NO);
}
}
// Actualiza al jugador a su posicion, animación y controla los contadores
void Player::update()
{
move();
setAnimation();
shiftColliders();
updateCooldown();
updatePowerUpCounter();
updateInvulnerable();
updateContinueCounter();
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:
{
status_playing_ = PlayerStatus::PLAYING;
init();
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_ ? PLAYER_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_ = PLAYER_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::updatePowerUpCounter()
{
if ((power_up_counter_ > 0) && (power_up_))
{
power_up_counter_--;
}
else
{
power_up_ = false;
}
}
// 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;
}
// Habilita la entrada de ordenes
void Player::enableInput()
{
input_ = true;
}
// Deshabilita la entrada de ordenes
void Player::disableInput()
{
input_ = false;
}
// 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 = int(pos_x_ + (width_ / 2));
collider_.y = int(pos_y_ + (height_ / 2));
}
// Pone las texturas del jugador
void Player::setPlayerTextures(std::vector<std::shared_ptr<Texture>> 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 Uint32 ticks_speed = 1000;
if (SDL_GetTicks() - continue_ticks_ > ticks_speed)
{
decContinueCounter();
}
}
}
// 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);
}
}
// Establece el nombre del jugador
void Player::setName(std::string name)
{
name_ = name;
}
// Establece el nombre del jugador para la tabla de mejores puntuaciones
void Player::setRecordName(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_->getPos();
}
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;
}