2537 lines
69 KiB
C++
2537 lines
69 KiB
C++
#include "game.h"
|
|
#include <SDL2/SDL_blendmode.h> // for SDL_BLENDMODE_BLEND
|
|
#include <SDL2/SDL_events.h> // for SDL_PollEvent, SDL_Event, SDL_KEYDOWN
|
|
#include <SDL2/SDL_keycode.h> // for SDLK_1, SDLK_2, SDLK_3, SDLK_4
|
|
#include <SDL2/SDL_pixels.h> // for SDL_PIXELFORMAT_RGBA8888
|
|
#include <SDL2/SDL_rwops.h> // for SDL_RWFromFile, SDL_RWclose, SDL_R...
|
|
#include <SDL2/SDL_timer.h> // for SDL_GetTicks
|
|
#include <SDL2/SDL_video.h> // for SDL_WINDOWEVENT_FOCUS_GAINED, SDL_...
|
|
#include <stdlib.h> // for rand
|
|
#include <algorithm> // for min, remove_if
|
|
#include <fstream> // for basic_ostream, operator<<, basic_i...
|
|
#include <iostream> // for cout
|
|
#include <numeric> // for accumulate
|
|
#include <utility> // for move
|
|
#include "asset.h" // for Asset
|
|
#include "background.h" // for Background
|
|
#include "balloon.h" // for Balloon, BALLOON_SCORE_1, BALLOON_...
|
|
#include "balloon_formations.h" // for Stage, BalloonFormationParams, Bal...
|
|
#include "bullet.h" // for Bullet, BulletType, BulletMoveStatus
|
|
#include "explosions.h" // for Explosions
|
|
#include "fade.h" // for Fade, FadeType
|
|
#include "global_inputs.h" // for check
|
|
#include "input.h" // for InputType, Input, INPUT_DO_NOT_ALL...
|
|
#include "item.h" // for Item, ItemType::COFFEE_MACHINE, ItemType::CLOCK
|
|
#include "jail_audio.h" // for JA_PlaySound, JA_DeleteSound, JA_L...
|
|
#include "lang.h" // for getText
|
|
#include "manage_hiscore_table.h" // for ManageHiScoreTable
|
|
#include "notifier.h" // for Notifier
|
|
#include "options.h" // for options
|
|
#include "param.h" // for param
|
|
#include "player.h" // for Player, PlayerStatus
|
|
#include "scoreboard.h" // for Scoreboard, ScoreboardMode, SCOREB...
|
|
#include "screen.h" // for Screen
|
|
#include "section.h" // for Name, name, Options, options
|
|
#include "smart_sprite.h" // for SpriteSmart
|
|
#include "text.h" // for Text, TEXT_CENTER
|
|
#include "texture.h" // for Texture
|
|
struct JA_Music_t; // lines 35-35
|
|
struct JA_Sound_t; // lines 36-36
|
|
|
|
// Constructor
|
|
Game::Game(int player_id, int current_stage, bool demo, JA_Music_t *music)
|
|
: music_(music),
|
|
current_stage_(current_stage)
|
|
{
|
|
// Copia los punteros
|
|
asset_ = Asset::get();
|
|
input_ = Input::get();
|
|
screen_ = Screen::get();
|
|
renderer_ = screen_->getRenderer();
|
|
|
|
// Pasa variables
|
|
demo_.enabled = demo;
|
|
last_stage_reached_ = current_stage_;
|
|
difficulty_ = options.game.difficulty;
|
|
|
|
// Crea los objetos
|
|
Scoreboard::init(renderer_);
|
|
scoreboard_ = Scoreboard::get();
|
|
fade_ = std::make_unique<Fade>(renderer_);
|
|
|
|
background_ = std::make_unique<Background>(renderer_);
|
|
explosions_ = std::make_unique<Explosions>();
|
|
balloon_formations_ = std::make_unique<BalloonFormations>();
|
|
|
|
// Carga los recursos
|
|
loadMedia();
|
|
|
|
// Inicializa los vectores con los datos para la demo
|
|
if (demo_.enabled)
|
|
{ // Aleatoriza la asignación del fichero
|
|
const auto index1 = rand() % 2;
|
|
const auto index2 = (index1 + 1) % 2;
|
|
loadDemoFile(asset_->get("demo1.bin"), &this->demo_.data_file[index1]);
|
|
loadDemoFile(asset_->get("demo2.bin"), &this->demo_.data_file[index2]);
|
|
}
|
|
|
|
background_->setPos(param.game.play_area.rect);
|
|
|
|
explosions_->addTexture(1, explosions_textures_[0], explosions_animations_[0]);
|
|
explosions_->addTexture(2, explosions_textures_[1], explosions_animations_[1]);
|
|
explosions_->addTexture(3, explosions_textures_[2], explosions_animations_[2]);
|
|
explosions_->addTexture(4, explosions_textures_[3], explosions_animations_[3]);
|
|
|
|
canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h);
|
|
SDL_SetTextureBlendMode(canvas_, SDL_BLENDMODE_BLEND);
|
|
|
|
// Inicializa las variables necesarias para la sección 'Game'
|
|
init(player_id);
|
|
}
|
|
|
|
Game::~Game()
|
|
{
|
|
// Guarda las puntuaciones en un fichero
|
|
auto manager = std::make_unique<ManageHiScoreTable>(&options.game.hi_score_table);
|
|
manager->saveToFile(asset_->get("score.bin"));
|
|
#ifdef RECORDING
|
|
saveDemoFile(asset->get("demo1.bin"));
|
|
#endif
|
|
|
|
// Elimina todos los objetos contenidos en vectores
|
|
deleteAllVectorObjects();
|
|
|
|
// Libera los recursos
|
|
unloadMedia();
|
|
|
|
Scoreboard::destroy();
|
|
|
|
SDL_DestroyTexture(canvas_);
|
|
}
|
|
|
|
// Inicializa las variables necesarias para la sección 'Game'
|
|
void Game::init(int player_id)
|
|
{
|
|
ticks_ = 0;
|
|
ticks_speed_ = 15;
|
|
|
|
// Elimina qualquier jugador que hubiese antes de crear los nuevos
|
|
players_.clear();
|
|
|
|
// Crea los dos jugadores
|
|
auto player1 = std::make_unique<Player>(1, (param.game.play_area.first_quarter_x * ((0 * 2) + 1)) - 11, param.game.play_area.rect.h - 30, demo_.enabled, ¶m.game.play_area.rect, player_textures_[0], player_animations_);
|
|
player1->setScoreBoardPanel(SCOREBOARD_LEFT_PANEL);
|
|
player1->setName(lang::getText(53));
|
|
const auto controller1 = getController(player1->getId());
|
|
player1->setController(controller1);
|
|
players_.push_back(std::move(player1));
|
|
|
|
auto player2 = std::make_unique<Player>(2, (param.game.play_area.first_quarter_x * ((1 * 2) + 1)) - 11, param.game.play_area.rect.h - 30, demo_.enabled, ¶m.game.play_area.rect, player_textures_[1], player_animations_);
|
|
player2->setScoreBoardPanel(SCOREBOARD_RIGHT_PANEL);
|
|
player2->setName(lang::getText(54));
|
|
const auto controller2 = getController(player2->getId());
|
|
player2->setController(controller2);
|
|
players_.push_back(std::move(player2));
|
|
|
|
// Obtiene mediante "playerID" el jugador que va a empezar jugar
|
|
auto main_player = getPlayer(player_id);
|
|
|
|
// Cambia el estado del jugador seleccionado
|
|
main_player->setStatusPlaying(PlayerStatus::PLAYING);
|
|
|
|
// Como es el principio del juego, empieza sin inmunidad
|
|
main_player->setInvulnerable(false);
|
|
|
|
// Variables relacionadas con la dificultad
|
|
switch (difficulty_)
|
|
{
|
|
case GameDifficulty::EASY:
|
|
{
|
|
default_balloon_speed_ = BALLOON_SPEED_1;
|
|
difficulty_score_multiplier_ = 0.5f;
|
|
difficulty_color_ = difficulty_easy_color;
|
|
scoreboard_->setColor(difficulty_color_);
|
|
break;
|
|
}
|
|
|
|
case GameDifficulty::NORMAL:
|
|
{
|
|
default_balloon_speed_ = BALLOON_SPEED_1;
|
|
difficulty_score_multiplier_ = 1.0f;
|
|
difficulty_color_ = difficulty_normal_color;
|
|
scoreboard_->setColor(scoreboard_color);
|
|
break;
|
|
}
|
|
|
|
case GameDifficulty::HARD:
|
|
{
|
|
default_balloon_speed_ = BALLOON_SPEED_5;
|
|
difficulty_score_multiplier_ = 1.5f;
|
|
difficulty_color_ = difficulty_hard_color;
|
|
scoreboard_->setColor(difficulty_color_);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Variables para el marcador
|
|
scoreboard_->setPos({param.scoreboard.x, param.scoreboard.y, param.scoreboard.w, param.scoreboard.h});
|
|
for (const auto &player : players_)
|
|
{
|
|
scoreboard_->setName(player->getScoreBoardPanel(), player->getName());
|
|
if (player->isWaiting())
|
|
{
|
|
scoreboard_->setMode(player->getScoreBoardPanel(), ScoreboardMode::WAITING);
|
|
}
|
|
}
|
|
scoreboard_->setMode(SCOREBOARD_CENTER_PANEL, ScoreboardMode::STAGE_INFO);
|
|
|
|
// Resto de variables
|
|
hi_score_.score = options.game.hi_score_table[0].score;
|
|
hi_score_.name = options.game.hi_score_table[0].name;
|
|
paused_ = false;
|
|
game_completed_ = false;
|
|
game_completed_counter_ = 0;
|
|
section::name = section::Name::GAME;
|
|
section::options = section::Options::GAME_PLAY_1P;
|
|
current_power_ = 0;
|
|
menace_current_ = 0;
|
|
menace_threshold_ = 0;
|
|
hi_score_achieved_ = false;
|
|
stage_bitmap_counter_ = STAGE_COUNTER;
|
|
game_over_counter_ = GAME_OVER_COUNTER;
|
|
time_stopped_ = false;
|
|
time_stopped_counter_ = 0;
|
|
counter_ = 0;
|
|
last_ballon_deploy_ = 0;
|
|
balloon_deploy_counter_ = 0;
|
|
balloon_speed_ = default_balloon_speed_;
|
|
helper_.need_coffee = false;
|
|
helper_.need_coffee_machine = false;
|
|
helper_.need_power_ball = false;
|
|
helper_.counter = HELP_COUNTER;
|
|
helper_.item_disk_odds = ITEM_POINTS_1_DISK_ODDS;
|
|
helper_.item_gavina_odds = ITEM_POINTS_2_GAVINA_ODDS;
|
|
helper_.item_pacmar_odds = ITEM_POINTS_3_PACMAR_ODDS;
|
|
helper_.item_clock_odds = ITEM_CLOCK_ODDS;
|
|
helper_.item_coffee_odds = ITEM_COFFEE_ODDS;
|
|
helper_.item_coffee_machine_odds = ITEM_COFFEE_MACHINE_ODDS;
|
|
power_ball_enabled_ = false;
|
|
power_ball_counter_ = 0;
|
|
coffee_machine_enabled_ = false;
|
|
balloons_popped_ = 0;
|
|
#ifdef DEBUG
|
|
auto_pop_balloons_ = false;
|
|
#endif
|
|
|
|
// Inicializa las variables para el modo DEMO
|
|
if (demo_.enabled)
|
|
{
|
|
// Selecciona una pantalla al azar
|
|
constexpr auto demos = 3;
|
|
const auto demo = rand() % demos;
|
|
const int stages[demos] = {0, 3, 5};
|
|
current_stage_ = stages[demo];
|
|
|
|
// Actualiza el numero de globos explotados según la fase de la demo
|
|
for (int i = 0; i < current_stage_; ++i)
|
|
{
|
|
balloons_popped_ += balloon_formations_->getStage(i).power_to_complete;
|
|
}
|
|
|
|
// Activa o no al otro jugador
|
|
if (rand() % 2 == 0)
|
|
{
|
|
const auto other_player_id = player_id == 1 ? 2 : 1;
|
|
auto other_player = getPlayer(other_player_id);
|
|
other_player->setStatusPlaying(PlayerStatus::PLAYING);
|
|
}
|
|
|
|
for (auto &player : players_)
|
|
{
|
|
// Añade 0, 1 o 2 cafes al jugador
|
|
for (int i = 0; i < rand() % 3; ++i)
|
|
{
|
|
player->giveExtraHit();
|
|
}
|
|
|
|
// Empieza sin inmunidad
|
|
player->setInvulnerable(false);
|
|
}
|
|
|
|
// Deshabilita los sonidos
|
|
JA_EnableSound(false);
|
|
|
|
// Configura los marcadores
|
|
scoreboard_->setMode(SCOREBOARD_LEFT_PANEL, ScoreboardMode::DEMO);
|
|
scoreboard_->setMode(SCOREBOARD_RIGHT_PANEL, ScoreboardMode::DEMO);
|
|
}
|
|
|
|
initPaths();
|
|
|
|
total_power_to_complete_game_ = 0;
|
|
for (int i = 0; i < 10; ++i)
|
|
{
|
|
total_power_to_complete_game_ += balloon_formations_->getStage(i).power_to_complete;
|
|
}
|
|
|
|
// Modo grabar demo
|
|
#ifdef RECORDING
|
|
demo.recording = true;
|
|
#else
|
|
demo_.recording = false;
|
|
#endif
|
|
demo_.counter = 0;
|
|
|
|
// Inicializa el objeto para el fundido
|
|
fade_->setColor(fade_color.r, fade_color.g, fade_color.b);
|
|
fade_->setPost(param.fade.post_duration);
|
|
fade_->setType(FadeType::VENETIAN);
|
|
|
|
// Con los globos creados, calcula el nivel de amenaza
|
|
evaluateAndSetMenace();
|
|
|
|
// Inicializa los sprites con los textos que aparecen al coger items
|
|
smart_sprites_.clear();
|
|
}
|
|
|
|
// Carga los recursos necesarios para la sección 'Game'
|
|
void Game::loadMedia()
|
|
{
|
|
std::cout << "\n** LOADING RESOURCES FOR GAME SECTION" << std::endl;
|
|
|
|
// Limpia
|
|
{
|
|
player_animations_.clear();
|
|
balloon_animations_.clear();
|
|
item_animations_.clear();
|
|
player1_textures_.clear();
|
|
player2_textures_.clear();
|
|
item_textures_.clear();
|
|
balloon_textures_.clear();
|
|
explosions_textures_.clear();
|
|
game_text_textures_.clear();
|
|
}
|
|
|
|
// Texturas
|
|
{
|
|
bullet_texture_ = std::make_shared<Texture>(renderer_, asset_->get("bullet.png"));
|
|
}
|
|
|
|
// Texturas - Game_text
|
|
{
|
|
game_text_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("game_text_1000_points.png")));
|
|
game_text_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("game_text_2500_points.png")));
|
|
game_text_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("game_text_5000_points.png")));
|
|
game_text_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("game_text_powerup.png")));
|
|
game_text_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("game_text_one_hit.png")));
|
|
}
|
|
|
|
// Texturas - Globos
|
|
{
|
|
balloon_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("balloon1.png")));
|
|
balloon_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("balloon2.png")));
|
|
balloon_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("balloon3.png")));
|
|
balloon_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("balloon4.png")));
|
|
balloon_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("powerball.png")));
|
|
}
|
|
|
|
// Texturas - Explosiones
|
|
{
|
|
explosions_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("explosion1.png")));
|
|
explosions_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("explosion2.png")));
|
|
explosions_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("explosion3.png")));
|
|
explosions_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("explosion4.png")));
|
|
}
|
|
|
|
// Texturas - Items
|
|
{
|
|
item_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("item_points1_disk.png")));
|
|
item_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("item_points2_gavina.png")));
|
|
item_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("item_points3_pacmar.png")));
|
|
item_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("item_clock.png")));
|
|
item_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("item_coffee.png")));
|
|
item_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("item_coffee_machine.png")));
|
|
}
|
|
|
|
// Texturas - Player1
|
|
{
|
|
player1_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("player1.gif")));
|
|
player1_textures_.back()->addPalette(asset_->get("player1_pal1.gif"));
|
|
player1_textures_.back()->addPalette(asset_->get("player1_pal2.gif"));
|
|
player1_textures_.back()->addPalette(asset_->get("player1_pal3.gif"));
|
|
|
|
player1_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("player_power.gif")));
|
|
player1_textures_.back()->addPalette(asset_->get("player_power_pal.gif"));
|
|
|
|
player_textures_.push_back(player1_textures_);
|
|
}
|
|
|
|
// Texturas - Player2
|
|
{
|
|
player2_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("player2.gif")));
|
|
player2_textures_.back()->addPalette(asset_->get("player2_pal1.gif"));
|
|
player2_textures_.back()->addPalette(asset_->get("player2_pal2.gif"));
|
|
player2_textures_.back()->addPalette(asset_->get("player2_pal3.gif"));
|
|
|
|
player2_textures_.emplace_back(std::make_shared<Texture>(renderer_, asset_->get("player_power.gif")));
|
|
player2_textures_.back()->addPalette(asset_->get("player_power_pal.gif"));
|
|
player2_textures_.back()->setPalette(1);
|
|
|
|
player_textures_.push_back(player2_textures_);
|
|
}
|
|
|
|
// Animaciones -- Jugador
|
|
{
|
|
std::vector<std::string> *player_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("player.ani"), player_animations);
|
|
player_animations_.push_back(player_animations);
|
|
|
|
std::vector<std::string> *player_power_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("player_power.ani"), player_power_animations);
|
|
player_animations_.push_back(player_power_animations);
|
|
}
|
|
|
|
// Animaciones -- Globos
|
|
{
|
|
std::vector<std::string> *balloon1_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("balloon1.ani"), balloon1_animations);
|
|
balloon_animations_.push_back(balloon1_animations);
|
|
|
|
std::vector<std::string> *balloon2_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("balloon2.ani"), balloon2_animations);
|
|
balloon_animations_.push_back(balloon2_animations);
|
|
|
|
std::vector<std::string> *balloon3_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("balloon3.ani"), balloon3_animations);
|
|
balloon_animations_.push_back(balloon3_animations);
|
|
|
|
std::vector<std::string> *balloon4_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("balloon4.ani"), balloon4_animations);
|
|
balloon_animations_.push_back(balloon4_animations);
|
|
|
|
std::vector<std::string> *balloon5_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("powerball.ani"), balloon5_animations);
|
|
balloon_animations_.push_back(balloon5_animations);
|
|
}
|
|
|
|
// Animaciones -- Explosiones
|
|
{
|
|
std::vector<std::string> *explosions1_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("explosion1.ani"), explosions1_animations);
|
|
explosions_animations_.push_back(explosions1_animations);
|
|
|
|
std::vector<std::string> *explosions2_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("explosion2.ani"), explosions2_animations);
|
|
explosions_animations_.push_back(explosions2_animations);
|
|
|
|
std::vector<std::string> *explosions3_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("explosion3.ani"), explosions3_animations);
|
|
explosions_animations_.push_back(explosions3_animations);
|
|
|
|
std::vector<std::string> *explosions4_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("explosion4.ani"), explosions4_animations);
|
|
explosions_animations_.push_back(explosions4_animations);
|
|
}
|
|
|
|
// Animaciones -- Items
|
|
{
|
|
std::vector<std::string> *item1_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("item_points1_disk.ani"), item1_animations);
|
|
item_animations_.push_back(item1_animations);
|
|
|
|
std::vector<std::string> *item2_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("item_points2_gavina.ani"), item2_animations);
|
|
item_animations_.push_back(item2_animations);
|
|
|
|
std::vector<std::string> *item3_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("item_points3_pacmar.ani"), item3_animations);
|
|
item_animations_.push_back(item3_animations);
|
|
|
|
std::vector<std::string> *item4_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("item_clock.ani"), item4_animations);
|
|
item_animations_.push_back(item4_animations);
|
|
|
|
std::vector<std::string> *item5_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("item_coffee.ani"), item5_animations);
|
|
item_animations_.push_back(item5_animations);
|
|
|
|
std::vector<std::string> *item6_animations = new std::vector<std::string>;
|
|
loadAnimations(asset_->get("item_coffee_machine.ani"), item6_animations);
|
|
item_animations_.push_back(item6_animations);
|
|
}
|
|
|
|
// Texto
|
|
{
|
|
text_ = std::make_unique<Text>(asset_->get("smb2.gif"), asset_->get("smb2.txt"), renderer_);
|
|
text_big_ = std::make_unique<Text>(asset_->get("smb2_big.png"), asset_->get("smb2_big.txt"), renderer_);
|
|
text_nokia2_ = std::make_unique<Text>(asset_->get("nokia2.png"), asset_->get("nokia2.txt"), renderer_);
|
|
text_nokia2_big_ = std::make_unique<Text>(asset_->get("nokia_big2.png"), asset_->get("nokia_big2.txt"), renderer_);
|
|
}
|
|
|
|
// Sonidos
|
|
{
|
|
balloon_sound_ = JA_LoadSound(asset_->get("balloon.wav").c_str());
|
|
bubble1_sound_ = JA_LoadSound(asset_->get("bubble1.wav").c_str());
|
|
bubble2_sound_ = JA_LoadSound(asset_->get("bubble2.wav").c_str());
|
|
bubble3_sound_ = JA_LoadSound(asset_->get("bubble3.wav").c_str());
|
|
bubble4_sound_ = JA_LoadSound(asset_->get("bubble4.wav").c_str());
|
|
bullet_sound_ = JA_LoadSound(asset_->get("bullet.wav").c_str());
|
|
clock_sound_ = JA_LoadSound(asset_->get("clock.wav").c_str());
|
|
coffee_out_sound_ = JA_LoadSound(asset_->get("coffeeout.wav").c_str());
|
|
hi_score_sound_ = JA_LoadSound(asset_->get("hiscore.wav").c_str());
|
|
item_drop_sound_ = JA_LoadSound(asset_->get("itemdrop.wav").c_str());
|
|
item_pick_up_sound_ = JA_LoadSound(asset_->get("itempickup.wav").c_str());
|
|
player_collision_sound_ = JA_LoadSound(asset_->get("player_collision.wav").c_str());
|
|
power_ball_sound_ = JA_LoadSound(asset_->get("powerball.wav").c_str());
|
|
stage_change_sound_ = JA_LoadSound(asset_->get("stage_change.wav").c_str());
|
|
coffee_machine_sound_ = JA_LoadSound(asset_->get("title.wav").c_str());
|
|
}
|
|
|
|
std::cout << "** RESOURCES FOR GAME SECTION LOADED\n"
|
|
<< std::endl;
|
|
}
|
|
|
|
// Libera los recursos previamente cargados
|
|
void Game::unloadMedia()
|
|
{
|
|
// Texturas
|
|
player1_textures_.clear();
|
|
player2_textures_.clear();
|
|
item_textures_.clear();
|
|
balloon_textures_.clear();
|
|
explosions_textures_.clear();
|
|
|
|
// Animaciones
|
|
player_animations_.clear();
|
|
balloon_animations_.clear();
|
|
explosions_animations_.clear();
|
|
item_animations_.clear();
|
|
|
|
// Sonidos
|
|
JA_DeleteSound(balloon_sound_);
|
|
JA_DeleteSound(bullet_sound_);
|
|
JA_DeleteSound(player_collision_sound_);
|
|
JA_DeleteSound(hi_score_sound_);
|
|
JA_DeleteSound(item_drop_sound_);
|
|
JA_DeleteSound(item_pick_up_sound_);
|
|
JA_DeleteSound(coffee_out_sound_);
|
|
JA_DeleteSound(stage_change_sound_);
|
|
JA_DeleteSound(bubble1_sound_);
|
|
JA_DeleteSound(bubble2_sound_);
|
|
JA_DeleteSound(bubble3_sound_);
|
|
JA_DeleteSound(bubble4_sound_);
|
|
JA_DeleteSound(clock_sound_);
|
|
JA_DeleteSound(power_ball_sound_);
|
|
JA_DeleteSound(coffee_machine_sound_);
|
|
}
|
|
|
|
// Carga el fichero de datos para la demo
|
|
bool Game::loadDemoFile(const std::string &file_path, DemoKeys (*data_file)[TOTAL_DEMO_DATA])
|
|
{
|
|
// Indicador de éxito en la carga
|
|
auto success = true;
|
|
const std::string file_name = file_path.substr(file_path.find_last_of("\\/") + 1);
|
|
auto file = SDL_RWFromFile(file_path.c_str(), "r+b");
|
|
if (!file)
|
|
{ // El fichero no existe
|
|
std::cout << "Warning: Unable to open " << file_name.c_str() << " file" << std::endl;
|
|
|
|
// Creamos el fichero para escritura
|
|
file = SDL_RWFromFile(file_path.c_str(), "w+b");
|
|
|
|
// Si ha creado el fichero
|
|
if (file)
|
|
{
|
|
std::cout << "New file (" << file_name.c_str() << ") created!" << std::endl;
|
|
|
|
// Inicializas los datos y los guarda en el fichero
|
|
for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
|
|
{
|
|
DemoKeys dk;
|
|
dk.left = 0;
|
|
dk.right = 0;
|
|
dk.no_input = 0;
|
|
dk.fire = 0;
|
|
dk.fire_left = 0;
|
|
dk.fire_right = 0;
|
|
(*data_file)[i] = dk;
|
|
SDL_RWwrite(file, &dk, sizeof(DemoKeys), 1);
|
|
}
|
|
|
|
// Cerramos el fichero
|
|
SDL_RWclose(file);
|
|
}
|
|
else
|
|
{ // Si no puede crear el fichero
|
|
std::cout << "Error: Unable to create file " << file_name.c_str() << std::endl;
|
|
success = false;
|
|
}
|
|
}
|
|
// El fichero existe
|
|
else
|
|
{
|
|
// Mensaje de proceder a la carga de los datos
|
|
std::cout << "Reading file: " << file_name.c_str() << std::endl;
|
|
|
|
// Lee todos los datos del fichero y los deja en el destino
|
|
for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
|
|
{
|
|
DemoKeys tmp;
|
|
SDL_RWread(file, &tmp, sizeof(DemoKeys), 1);
|
|
(*data_file)[i] = tmp;
|
|
}
|
|
|
|
// Cierra el fichero
|
|
SDL_RWclose(file);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
#ifdef RECORDING
|
|
// Guarda el fichero de datos para la demo
|
|
bool Game::saveDemoFile(const std::string &file_path)
|
|
{
|
|
auto success = true;
|
|
const std::string file_name = file_path.substr(file_path.find_last_of("\\/") + 1);
|
|
|
|
auto file = SDL_RWFromFile(file_path.c_str(), "w+b");
|
|
if (file)
|
|
{
|
|
// Guarda los datos
|
|
for (int i = 0; i < TOTAL_DEMO_DATA; ++i)
|
|
{
|
|
SDL_RWwrite(file, &demo.dataFile[0][i], sizeof(DemoKeys), 1);
|
|
}
|
|
|
|
std::cout << "Writing file " << file_name.c_str() << std::endl;
|
|
|
|
// Cierra el fichero
|
|
SDL_RWclose(file);
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Error: Unable to save " << file_name.c_str() << " file! " << SDL_GetError() << std::endl;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
#endif // RECORDING
|
|
|
|
// Crea una formación de enemigos
|
|
void Game::deployBalloonFormation()
|
|
{
|
|
// Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última
|
|
if (balloon_deploy_counter_ == 0)
|
|
{
|
|
|
|
// En este punto se decide entre crear una powerball o una formación enemiga
|
|
if ((rand() % 100 < 15) && (canPowerBallBeCreated()))
|
|
{
|
|
// Crea una powerball
|
|
createPowerBall();
|
|
|
|
// Da un poco de margen para que se creen mas enemigos
|
|
balloon_deploy_counter_ = 300;
|
|
}
|
|
else
|
|
{
|
|
// Decrementa el contador de despliegues enemigos de la PowerBall
|
|
power_ball_counter_ > 0 ? power_ball_counter_-- : power_ball_counter_ = 0;
|
|
|
|
// Elige una formación enemiga la azar
|
|
auto set = rand() % 10;
|
|
|
|
// Evita repetir la ultima formación enemiga desplegada
|
|
if (set == last_ballon_deploy_)
|
|
{
|
|
++set %= 10;
|
|
}
|
|
|
|
last_ballon_deploy_ = set;
|
|
|
|
const Stage stage = balloon_formations_->getStage(current_stage_);
|
|
const auto numEnemies = stage.balloon_pool->set[set]->number_of_balloons;
|
|
for (int i = 0; i < numEnemies; ++i)
|
|
{
|
|
createBalloon(stage.balloon_pool->set[set]->init[i].x,
|
|
stage.balloon_pool->set[set]->init[i].y,
|
|
stage.balloon_pool->set[set]->init[i].kind,
|
|
stage.balloon_pool->set[set]->init[i].vel_x,
|
|
balloon_speed_,
|
|
stage.balloon_pool->set[set]->init[i].creation_counter);
|
|
}
|
|
|
|
balloon_deploy_counter_ = 300;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Aumenta el poder de la fase
|
|
void Game::increaseStageCurrentPower(int power)
|
|
{
|
|
current_power_ += power;
|
|
}
|
|
|
|
// Actualiza el valor de hiScore en caso necesario
|
|
void Game::updateHiScore()
|
|
{
|
|
// Si la puntuación actual es mayor que la máxima puntuación
|
|
for (const auto &player : players_)
|
|
{
|
|
if (player->getScore() > hi_score_.score)
|
|
{
|
|
// Actualiza la máxima puntuación
|
|
hi_score_.score = player->getScore();
|
|
|
|
// Si se supera la máxima puntuación emite sonido
|
|
if (hi_score_achieved_ == false)
|
|
{
|
|
hi_score_achieved_ = true;
|
|
JA_PlaySound(hi_score_sound_);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Actualiza las variables del jugador
|
|
void Game::updatePlayers()
|
|
{
|
|
for (auto &player : players_)
|
|
{
|
|
player->update();
|
|
|
|
if (player->isPlaying())
|
|
{
|
|
// Comprueba la colisión entre el jugador y los globos
|
|
if (checkPlayerBalloonCollision(player))
|
|
{
|
|
killPlayer(player);
|
|
|
|
if (demo_.enabled && allPlayersAreNotPlaying())
|
|
{
|
|
fade_->setType(FadeType::RANDOM_SQUARE);
|
|
fade_->activate();
|
|
}
|
|
}
|
|
|
|
// Comprueba las colisiones entre el jugador y los items
|
|
checkPlayerItemCollision(player);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dibuja a los jugadores
|
|
void Game::renderPlayers()
|
|
{
|
|
for (auto &player : players_)
|
|
{
|
|
if (!player->isWaiting())
|
|
{
|
|
player->render();
|
|
#ifdef DEBUG
|
|
// SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
|
// const Circle c = player->getCollider();
|
|
// DrawCircle(renderer, c.x, c.y, c.r);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
// Comprueba si hay cambio de fase y actualiza las variables
|
|
void Game::updateStage()
|
|
{
|
|
if (current_power_ >= balloon_formations_->getStage(current_stage_).power_to_complete)
|
|
{
|
|
// Cambio de fase
|
|
current_stage_++;
|
|
current_power_ = 0;
|
|
last_stage_reached_ = current_stage_;
|
|
if (current_stage_ == 10)
|
|
{ // Ha llegado al final el juego
|
|
game_completed_ = true; // Marca el juego como completado
|
|
current_stage_ = 9; // Deja el valor dentro de los limites
|
|
destroyAllBalloons(); // Destruye a todos los enemigos
|
|
current_power_ = 0; // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos lo globos
|
|
menace_current_ = 255; // Sube el nivel de amenaza para que no cree mas globos
|
|
for (auto &player : players_)
|
|
{ // Añade un millon de puntos a los jugadores que queden vivos
|
|
if (player->isPlaying())
|
|
{
|
|
player->addScore(1000000);
|
|
}
|
|
}
|
|
updateHiScore();
|
|
JA_StopMusic();
|
|
}
|
|
JA_PlaySound(stage_change_sound_);
|
|
stage_bitmap_counter_ = 0;
|
|
balloon_speed_ = default_balloon_speed_;
|
|
setBalloonSpeed(balloon_speed_);
|
|
screen_->flash(flash_color, 5);
|
|
screen_->shake();
|
|
}
|
|
|
|
// Incrementa el contador del bitmap que aparece mostrando el cambio de fase
|
|
if (stage_bitmap_counter_ < STAGE_COUNTER)
|
|
{
|
|
stage_bitmap_counter_++;
|
|
}
|
|
|
|
// Si el juego se ha completado, el bitmap se detiene en el centro de la pantalla
|
|
if (game_completed_)
|
|
{
|
|
if (stage_bitmap_counter_ > 100)
|
|
{
|
|
stage_bitmap_counter_ = 100;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Actualiza el estado de fin de la partida
|
|
void Game::updateGameOver()
|
|
{
|
|
// Si todos estan en estado de Game Over
|
|
if (allPlayersAreGameOver())
|
|
{
|
|
if (game_over_counter_ > 0)
|
|
{
|
|
game_over_counter_--;
|
|
|
|
if ((game_over_counter_ == 250) || (game_over_counter_ == 200) || (game_over_counter_ == 180) || (game_over_counter_ == 120) || (game_over_counter_ == 60))
|
|
{
|
|
// Hace sonar aleatoriamente uno de los 4 sonidos de burbujas
|
|
const auto index = rand() % 4;
|
|
JA_Sound_t *sound[4] = {bubble1_sound_, bubble2_sound_, bubble3_sound_, bubble4_sound_};
|
|
JA_PlaySound(sound[index], 0);
|
|
}
|
|
|
|
if (game_over_counter_ == 150)
|
|
{
|
|
fade_->activate();
|
|
}
|
|
}
|
|
|
|
if (fade_->hasEnded())
|
|
{
|
|
section::name = section::Name::HI_SCORE_TABLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Actualiza los globos
|
|
void Game::updateBalloons()
|
|
{
|
|
for (auto balloon : balloons_)
|
|
{
|
|
balloon->update();
|
|
}
|
|
}
|
|
|
|
// Pinta en pantalla todos los globos activos
|
|
void Game::renderBalloons()
|
|
{
|
|
for (auto &balloon : balloons_)
|
|
{
|
|
balloon->render();
|
|
}
|
|
}
|
|
|
|
// Crea un globo nuevo en el vector de globos
|
|
std::shared_ptr<Balloon> Game::createBalloon(float x, int y, int kind, float velx, float speed, int creation_timer)
|
|
{
|
|
const auto index = (kind - 1) % 4;
|
|
auto b = std::make_shared<Balloon>(x, y, kind, velx, speed, creation_timer, balloon_textures_[index], balloon_animations_[index]);
|
|
balloons_.push_back(b);
|
|
return b;
|
|
}
|
|
|
|
// Crea una PowerBall
|
|
void Game::createPowerBall()
|
|
{
|
|
constexpr auto values = 6;
|
|
constexpr auto posY = -BLOCK;
|
|
|
|
const auto left = param.game.play_area.rect.x;
|
|
const auto center = param.game.play_area.center_x - (BALLOON_WIDTH_4 / 2);
|
|
const auto right = param.game.play_area.rect.w - BALLOON_WIDTH_4;
|
|
|
|
const auto vel_pos = BALLOON_VELX_POSITIVE;
|
|
const auto vel_neg = BALLOON_VELX_NEGATIVE;
|
|
|
|
const auto luck = rand() % values;
|
|
const int x[values] = {left, left, center, center, right, right};
|
|
const float vx[values] = {vel_pos, vel_pos, vel_pos, vel_neg, vel_neg, vel_neg};
|
|
|
|
auto b = std::make_unique<Balloon>(x[luck], posY, POWER_BALL, vx[luck], balloon_speed_, 300, balloon_textures_[4], balloon_animations_[4]);
|
|
balloons_.push_back(std::move(b));
|
|
|
|
power_ball_enabled_ = true;
|
|
power_ball_counter_ = POWERBALL_COUNTER;
|
|
}
|
|
|
|
// Establece la velocidad de los globos
|
|
void Game::setBalloonSpeed(float speed)
|
|
{
|
|
for (auto &balloon : balloons_)
|
|
{
|
|
if (balloon->isEnabled())
|
|
{
|
|
balloon->setSpeed(speed);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
|
|
void Game::updateBalloonSpeed()
|
|
{
|
|
const float percent = (float)current_power_ / (float)balloon_formations_->getStage(current_stage_).power_to_complete;
|
|
float old_balloon_speed = balloon_speed_;
|
|
|
|
// Comprueba si se ha de modificar la velocidad de los globos
|
|
if (balloon_speed_ == BALLOON_SPEED_1 && percent > 0.2f)
|
|
{
|
|
balloon_speed_ = BALLOON_SPEED_2;
|
|
}
|
|
|
|
else if (balloon_speed_ == BALLOON_SPEED_2 && percent > 0.4f)
|
|
{
|
|
balloon_speed_ = BALLOON_SPEED_3;
|
|
}
|
|
|
|
else if (balloon_speed_ == BALLOON_SPEED_3 && percent > 0.6f)
|
|
{
|
|
balloon_speed_ = BALLOON_SPEED_4;
|
|
}
|
|
|
|
else if (balloon_speed_ == BALLOON_SPEED_4 && percent > 0.8f)
|
|
{
|
|
balloon_speed_ = BALLOON_SPEED_5;
|
|
}
|
|
|
|
// Si ha habido cambio, se aplica a todos los globos
|
|
if (old_balloon_speed != balloon_speed_)
|
|
{
|
|
setBalloonSpeed(balloon_speed_);
|
|
}
|
|
}
|
|
|
|
// Explosiona un globo. Lo destruye y crea otros dos si es el caso
|
|
void Game::popBalloon(std::shared_ptr<Balloon> balloon)
|
|
{
|
|
// Aumenta el poder de la fase
|
|
increaseStageCurrentPower(1);
|
|
balloons_popped_++;
|
|
|
|
const auto kind = balloon->getKind();
|
|
if (kind == POWER_BALL)
|
|
{
|
|
destroyAllBalloons();
|
|
power_ball_enabled_ = false;
|
|
balloon_deploy_counter_ = 20;
|
|
}
|
|
else
|
|
{
|
|
const auto size = balloon->getSize();
|
|
if (size == BALLOON_SIZE_1)
|
|
{ // Si es del tipo más pequeño, simplemente elimina el globo
|
|
explosions_->add(balloon->getPosX(), balloon->getPosY(), size);
|
|
balloon->pop();
|
|
}
|
|
else
|
|
{ // En cualquier otro caso, crea dos globos de un tipo inferior
|
|
auto balloon_left = createBalloon(0, balloon->getPosY(), balloon->getKind() - 1, BALLOON_VELX_NEGATIVE, balloon_speed_, 0);
|
|
balloon_left->allignTo(balloon->getPosX() + (balloon->getWidth() / 2));
|
|
balloon_left->setVelY(balloon_left->getClass() == BALLOON_CLASS ? -2.50f : BALLOON_VELX_NEGATIVE);
|
|
|
|
auto balloon_right = createBalloon(0, balloon->getPosY(), balloon->getKind() - 1, BALLOON_VELX_POSITIVE, balloon_speed_, 0);
|
|
balloon_right->allignTo(balloon->getPosX() + (balloon->getWidth() / 2));
|
|
balloon_right->setVelY(balloon_right->getClass() == BALLOON_CLASS ? -2.50f : BALLOON_VELX_NEGATIVE);
|
|
|
|
// Elimina el globo
|
|
explosions_->add(balloon->getPosX(), balloon->getPosY(), size);
|
|
balloon->pop();
|
|
}
|
|
}
|
|
|
|
// Recalcula el nivel de amenaza
|
|
evaluateAndSetMenace();
|
|
}
|
|
|
|
// Explosiona un globo. Lo destruye = no crea otros globos
|
|
void Game::destroyBalloon(std::shared_ptr<Balloon> &balloon)
|
|
{
|
|
auto score = 0;
|
|
|
|
// Calcula la puntuación y el poder que generaria el globo en caso de romperlo a él y a sus hijos
|
|
const auto size = balloon->getSize();
|
|
switch (size)
|
|
{
|
|
case BALLOON_SIZE_4:
|
|
{
|
|
score = BALLOON_SCORE_4 + (2 * BALLOON_SCORE_3) + (4 * BALLOON_SCORE_2) + (8 * BALLOON_SCORE_1);
|
|
break;
|
|
}
|
|
|
|
case BALLOON_SIZE_3:
|
|
{
|
|
score = BALLOON_SCORE_3 + (2 * BALLOON_SCORE_2) + (4 * BALLOON_SCORE_1);
|
|
break;
|
|
}
|
|
|
|
case BALLOON_SIZE_2:
|
|
{
|
|
score = BALLOON_SCORE_2 + (2 * BALLOON_SCORE_1);
|
|
break;
|
|
}
|
|
|
|
case BALLOON_SIZE_1:
|
|
{
|
|
score = BALLOON_SCORE_1;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
score = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Otorga los puntos correspondientes al globo
|
|
for (auto &player : players_)
|
|
{
|
|
player->addScore(score * player->getScoreMultiplier() * difficulty_score_multiplier_);
|
|
}
|
|
updateHiScore();
|
|
|
|
// Aumenta el poder de la fase
|
|
const auto power = balloon->getPower();
|
|
increaseStageCurrentPower(power);
|
|
balloons_popped_ += power;
|
|
|
|
// Destruye el globo
|
|
explosions_->add(balloon->getPosX(), balloon->getPosY(), size);
|
|
balloon->pop();
|
|
|
|
// Recalcula el nivel de amenaza
|
|
evaluateAndSetMenace();
|
|
}
|
|
|
|
// Destruye todos los globos
|
|
void Game::destroyAllBalloons()
|
|
{
|
|
for (auto &balloon : balloons_)
|
|
{
|
|
if (balloon->canBeDestroyed())
|
|
{
|
|
destroyBalloon(balloon);
|
|
}
|
|
}
|
|
|
|
balloon_deploy_counter_ = 300;
|
|
JA_PlaySound(power_ball_sound_);
|
|
screen_->flash(flash_color, 5);
|
|
screen_->shake();
|
|
}
|
|
|
|
// Detiene todos los globos
|
|
void Game::stopAllBalloons(int time)
|
|
{
|
|
for (auto &balloon : balloons_)
|
|
{
|
|
if (balloon->isEnabled())
|
|
{
|
|
balloon->setStop(true);
|
|
balloon->setStoppedTimer(time);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pone en marcha todos los globos
|
|
void Game::startAllBalloons()
|
|
{
|
|
for (auto &balloon : balloons_)
|
|
{
|
|
if ((balloon->isEnabled()) && (!balloon->isBeingCreated()))
|
|
{
|
|
balloon->setStop(false);
|
|
balloon->setStoppedTimer(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Vacia del vector de globos los globos que ya no sirven
|
|
void Game::freeBalloons()
|
|
{
|
|
auto it = std::remove_if(balloons_.begin(), balloons_.end(),
|
|
[](const auto &balloon)
|
|
{ return !balloon->isEnabled(); });
|
|
balloons_.erase(it, balloons_.end());
|
|
}
|
|
|
|
// Comprueba la colisión entre el jugador y los globos activos
|
|
bool Game::checkPlayerBalloonCollision(std::shared_ptr<Player> &player)
|
|
{
|
|
for (auto &balloon : balloons_)
|
|
{
|
|
if ((balloon->isEnabled()) && !(balloon->isStopped()) && !(balloon->isInvulnerable()))
|
|
{
|
|
if (checkCollision(player->getCollider(), balloon->getCollider()))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Comprueba la colisión entre el jugador y los items
|
|
void Game::checkPlayerItemCollision(std::shared_ptr<Player> &player)
|
|
{
|
|
if (!player->isPlaying())
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (auto &item : items_)
|
|
{
|
|
if (item->isEnabled())
|
|
{
|
|
if (checkCollision(player->getCollider(), item->getCollider()))
|
|
{
|
|
switch (item->getType())
|
|
{
|
|
case ItemType::DISK:
|
|
{
|
|
player->addScore(1000);
|
|
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (game_text_textures_[0]->getWidth() / 2), player->getPosY(), game_text_textures_[0]);
|
|
break;
|
|
}
|
|
|
|
case ItemType::GAVINA:
|
|
{
|
|
player->addScore(2500);
|
|
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (game_text_textures_[1]->getWidth() / 2), player->getPosY(), game_text_textures_[1]);
|
|
break;
|
|
}
|
|
|
|
case ItemType::PACMAR:
|
|
{
|
|
player->addScore(5000);
|
|
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (game_text_textures_[2]->getWidth() / 2), player->getPosY(), game_text_textures_[2]);
|
|
break;
|
|
}
|
|
|
|
case ItemType::CLOCK:
|
|
{
|
|
enableTimeStopItem();
|
|
break;
|
|
}
|
|
|
|
case ItemType::COFFEE:
|
|
{
|
|
if (player->getCoffees() == 2)
|
|
{
|
|
player->addScore(5000);
|
|
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (game_text_textures_[2]->getWidth() / 2), player->getPosY(), game_text_textures_[2]);
|
|
}
|
|
else
|
|
{
|
|
player->giveExtraHit();
|
|
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (game_text_textures_[4]->getWidth() / 2), player->getPosY(), game_text_textures_[4]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ItemType::COFFEE_MACHINE:
|
|
{
|
|
player->setPowerUp();
|
|
coffee_machine_enabled_ = false;
|
|
createItemScoreSprite(item->getPosX() + (item->getWidth() / 2) - (game_text_textures_[3]->getWidth() / 2), player->getPosY(), game_text_textures_[3]);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
updateHiScore();
|
|
JA_PlaySound(item_pick_up_sound_);
|
|
item->disable();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Comprueba y procesa la colisión entre las balas y los globos
|
|
void Game::checkBulletBalloonCollision()
|
|
{
|
|
for (auto &bullet : bullets_)
|
|
{
|
|
for (auto &balloon : balloons_)
|
|
{
|
|
if (balloon->isEnabled() && (!balloon->isInvulnerable()) && bullet->isEnabled())
|
|
{
|
|
if (checkCollision(balloon->getCollider(), bullet->getCollider()))
|
|
{
|
|
// Otorga los puntos correspondientes al globo al jugador que disparó la bala
|
|
auto player = getPlayer(bullet->getOwner());
|
|
if (!player)
|
|
{
|
|
return;
|
|
}
|
|
player->incScoreMultiplier();
|
|
player->addScore(balloon->getScore() * player->getScoreMultiplier() * difficulty_score_multiplier_);
|
|
updateHiScore();
|
|
|
|
// Suelta el item si se da el caso
|
|
const auto droppeditem = dropItem();
|
|
if (droppeditem != ItemType::NONE && !demo_.recording)
|
|
{
|
|
if (droppeditem != ItemType::COFFEE_MACHINE)
|
|
{
|
|
createItem(droppeditem, balloon->getPosX(), balloon->getPosY());
|
|
JA_PlaySound(item_drop_sound_);
|
|
}
|
|
else
|
|
{
|
|
createItem(droppeditem, player->getPosX(), 0);
|
|
coffee_machine_enabled_ = true;
|
|
}
|
|
}
|
|
|
|
// Explota el globo
|
|
popBalloon(balloon);
|
|
|
|
// Sonido de explosión
|
|
JA_PlaySound(balloon_sound_);
|
|
|
|
// Deshabilita la bala
|
|
bullet->disable();
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mueve las balas activas
|
|
void Game::moveBullets()
|
|
{
|
|
for (auto &bullet : bullets_)
|
|
{
|
|
if (bullet->isEnabled())
|
|
{
|
|
if (bullet->move() == BulletMoveStatus::OUT)
|
|
{
|
|
getPlayer(bullet->getOwner())->decScoreMultiplier();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pinta las balas activas
|
|
void Game::renderBullets()
|
|
{
|
|
for (auto &bullet : bullets_)
|
|
{
|
|
if (bullet->isEnabled())
|
|
{
|
|
bullet->render();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Crea un objeto bala
|
|
void Game::createBullet(int x, int y, BulletType kind, bool powered_up, int owner)
|
|
{
|
|
auto b = std::make_unique<Bullet>(x, y, kind, powered_up, owner, &(param.game.play_area.rect), bullet_texture_);
|
|
bullets_.push_back(std::move(b));
|
|
}
|
|
|
|
// Vacia el vector de balas
|
|
void Game::freeBullets()
|
|
{
|
|
if (!bullets_.empty())
|
|
{
|
|
for (int i = bullets_.size() - 1; i >= 0; --i)
|
|
{
|
|
if (!bullets_[i]->isEnabled())
|
|
{
|
|
bullets_.erase(bullets_.begin() + i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Actualiza los items
|
|
void Game::updateItems()
|
|
{
|
|
for (auto &item : items_)
|
|
{
|
|
if (item->isEnabled())
|
|
{
|
|
item->update();
|
|
if (item->isOnFloor())
|
|
{
|
|
JA_PlaySound(coffee_machine_sound_);
|
|
screen_->shake();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pinta los items activos
|
|
void Game::renderItems()
|
|
{
|
|
for (auto &item : items_)
|
|
{
|
|
item->render();
|
|
}
|
|
}
|
|
|
|
// Devuelve un item al azar y luego segun sus probabilidades
|
|
ItemType Game::dropItem()
|
|
{
|
|
const auto lucky_number = rand() % 100;
|
|
const auto item = rand() % 6;
|
|
|
|
switch (item)
|
|
{
|
|
case 0:
|
|
if (lucky_number < helper_.item_disk_odds)
|
|
{
|
|
return ItemType::DISK;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
if (lucky_number < helper_.item_gavina_odds)
|
|
{
|
|
return ItemType::GAVINA;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if (lucky_number < helper_.item_pacmar_odds)
|
|
{
|
|
return ItemType::GAVINA;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
if (lucky_number < helper_.item_clock_odds)
|
|
{
|
|
return ItemType::CLOCK;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
if (lucky_number < helper_.item_coffee_odds)
|
|
{
|
|
helper_.item_coffee_odds = ITEM_COFFEE_ODDS;
|
|
return ItemType::COFFEE;
|
|
}
|
|
else
|
|
{
|
|
if (helper_.need_coffee)
|
|
{
|
|
helper_.item_coffee_odds++;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
if (lucky_number < helper_.item_coffee_machine_odds)
|
|
{
|
|
helper_.item_coffee_machine_odds = ITEM_COFFEE_MACHINE_ODDS;
|
|
if (!coffee_machine_enabled_ && helper_.need_coffee_machine)
|
|
{
|
|
return ItemType::COFFEE_MACHINE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (helper_.need_coffee_machine)
|
|
{
|
|
helper_.item_coffee_machine_odds++;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ItemType::NONE;
|
|
}
|
|
|
|
// Crea un objeto item
|
|
void Game::createItem(ItemType type, float x, float y)
|
|
{
|
|
items_.emplace_back(std::make_unique<Item>(type, x, y, &(param.game.play_area.rect), item_textures_[static_cast<int>(type) - 1], item_animations_[static_cast<int>(type) - 1]));
|
|
}
|
|
|
|
// Vacia el vector de items
|
|
void Game::freeItems()
|
|
{
|
|
if (!items_.empty())
|
|
{
|
|
for (int i = items_.size() - 1; i >= 0; --i)
|
|
{
|
|
if (!items_[i]->isEnabled())
|
|
{
|
|
items_.erase(items_.begin() + i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Crea un objeto SpriteSmart para mostrar la puntuación al coger un objeto
|
|
void Game::createItemScoreSprite(int x, int y, std::shared_ptr<Texture> texture)
|
|
{
|
|
smart_sprites_.emplace_back(std::make_unique<SmartSprite>(texture));
|
|
|
|
// Inicializa
|
|
smart_sprites_.back()->setPos({0, 0, texture->getWidth(), texture->getHeight()});
|
|
smart_sprites_.back()->setSpriteClip(smart_sprites_.back()->getPosition());
|
|
smart_sprites_.back()->setPosX(x);
|
|
smart_sprites_.back()->setPosY(y);
|
|
smart_sprites_.back()->setDestX(x);
|
|
smart_sprites_.back()->setDestY(y - 25);
|
|
smart_sprites_.back()->setVelY(-0.5f);
|
|
smart_sprites_.back()->setAccelY(-0.1f);
|
|
smart_sprites_.back()->setEnabled(true);
|
|
smart_sprites_.back()->setFinishedCounter(100);
|
|
}
|
|
|
|
// Vacia el vector de smartsprites
|
|
void Game::freeSpriteSmarts()
|
|
{
|
|
if (!smart_sprites_.empty())
|
|
{
|
|
for (int i = smart_sprites_.size() - 1; i >= 0; --i)
|
|
{
|
|
if (smart_sprites_[i]->hasFinished())
|
|
{
|
|
smart_sprites_.erase(smart_sprites_.begin() + i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Crea un SpriteSmart para arrojar el item café al recibir un impacto
|
|
void Game::throwCoffee(int x, int y)
|
|
{
|
|
smart_sprites_.emplace_back(std::make_unique<SmartSprite>(item_textures_[4]));
|
|
|
|
smart_sprites_.back()->setPosX(x - 8);
|
|
smart_sprites_.back()->setPosY(y - 8);
|
|
smart_sprites_.back()->setWidth(param.game.item_size);
|
|
smart_sprites_.back()->setHeight(param.game.item_size);
|
|
smart_sprites_.back()->setVelX(-1.0f + ((rand() % 5) * 0.5f));
|
|
smart_sprites_.back()->setVelY(-4.0f);
|
|
smart_sprites_.back()->setAccelX(0.0f);
|
|
smart_sprites_.back()->setAccelY(0.2f);
|
|
smart_sprites_.back()->setDestX(x + (smart_sprites_.back()->getVelX() * 50));
|
|
smart_sprites_.back()->setDestY(param.game.height + 1);
|
|
smart_sprites_.back()->setEnabled(true);
|
|
smart_sprites_.back()->setFinishedCounter(1);
|
|
smart_sprites_.back()->setSpriteClip(0, param.game.item_size, param.game.item_size, param.game.item_size);
|
|
smart_sprites_.back()->enableRotate();
|
|
smart_sprites_.back()->setRotateSpeed(10);
|
|
smart_sprites_.back()->setRotateAmount(90.0);
|
|
}
|
|
|
|
// Actualiza los SpriteSmarts
|
|
void Game::updateSpriteSmarts()
|
|
{
|
|
for (auto &ss : smart_sprites_)
|
|
{
|
|
ss->update();
|
|
}
|
|
}
|
|
|
|
// Pinta los SpriteSmarts activos
|
|
void Game::renderSpriteSmarts()
|
|
{
|
|
for (auto &ss : smart_sprites_)
|
|
{
|
|
ss->render();
|
|
}
|
|
}
|
|
|
|
// Acciones a realizar cuando el jugador muere
|
|
void Game::killPlayer(std::shared_ptr<Player> &player)
|
|
{
|
|
if (!player->isPlaying() || player->isInvulnerable())
|
|
{ // Si no está jugando o tiene inmunidad, no hace nada
|
|
return;
|
|
}
|
|
|
|
// Si tiene cafes
|
|
if (player->hasExtraHit())
|
|
{
|
|
// Lo pierde
|
|
player->removeExtraHit();
|
|
throwCoffee(player->getPosX() + (player->getWidth() / 2), player->getPosY() + (player->getHeight() / 2));
|
|
JA_PlaySound(coffee_out_sound_);
|
|
screen_->shake();
|
|
}
|
|
else
|
|
{
|
|
// Si no tiene cafes, muere
|
|
if (!demo_.enabled)
|
|
{
|
|
JA_PauseMusic();
|
|
}
|
|
stopAllBalloons(10);
|
|
JA_PlaySound(player_collision_sound_);
|
|
screen_->shake();
|
|
JA_PlaySound(coffee_out_sound_);
|
|
player->setStatusPlaying(PlayerStatus::DYING);
|
|
if (!demo_.enabled)
|
|
{
|
|
// En el modo DEMO ni se para la musica ni se añade la puntuación a la tabla
|
|
allPlayersAreNotPlaying() ? JA_StopMusic() : JA_ResumeMusic();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calcula y establece el valor de amenaza en funcion de los globos activos
|
|
void Game::evaluateAndSetMenace()
|
|
{
|
|
menace_current_ = std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon)
|
|
{ return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); });
|
|
}
|
|
|
|
// Obtiene el valor de la variable
|
|
int Game::getMenace() const
|
|
{
|
|
return menace_current_;
|
|
}
|
|
|
|
// Establece el valor de la variable
|
|
void Game::setTimeStopped(bool value)
|
|
{
|
|
time_stopped_ = value;
|
|
}
|
|
|
|
// Obtiene el valor de la variable
|
|
bool Game::isTimeStopped() const
|
|
{
|
|
return time_stopped_;
|
|
}
|
|
|
|
// Establece el valor de la variable
|
|
void Game::setTimeStoppedCounter(int value)
|
|
{
|
|
time_stopped_counter_ = value;
|
|
}
|
|
|
|
// Incrementa el valor de la variable
|
|
void Game::incTimeStoppedCounter(int value)
|
|
{
|
|
time_stopped_counter_ += value;
|
|
}
|
|
|
|
// Actualiza y comprueba el valor de la variable
|
|
void Game::updateTimeStoppedCounter()
|
|
{
|
|
if (isTimeStopped())
|
|
{
|
|
if (time_stopped_counter_ > 0)
|
|
{
|
|
time_stopped_counter_--;
|
|
stopAllBalloons(TIME_STOPPED_COUNTER);
|
|
}
|
|
else
|
|
{
|
|
disableTimeStopItem();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Actualiza la variable enemyDeployCounter
|
|
void Game::updateBalloonDeployCounter()
|
|
{
|
|
if (balloon_deploy_counter_ > 0)
|
|
{
|
|
--balloon_deploy_counter_;
|
|
}
|
|
}
|
|
|
|
// Actualiza el juego
|
|
void Game::update()
|
|
{
|
|
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
|
|
if (SDL_GetTicks() - ticks_ > ticks_speed_)
|
|
{
|
|
// Actualiza el contador de ticks
|
|
ticks_ = SDL_GetTicks();
|
|
|
|
// Actualiza el contador de juego
|
|
counter_++;
|
|
|
|
if (demo_.enabled)
|
|
{
|
|
// Incrementa el contador de la demo
|
|
if (demo_.counter < TOTAL_DEMO_DATA)
|
|
{
|
|
demo_.counter++;
|
|
}
|
|
|
|
// Activa el fundido antes de acabar con los datos de la demo
|
|
if (demo_.counter == TOTAL_DEMO_DATA - 200)
|
|
{
|
|
fade_->setType(FadeType::RANDOM_SQUARE);
|
|
fade_->activate();
|
|
}
|
|
|
|
// Si ha terminado el fundido, cambia de sección
|
|
if (fade_->hasEnded())
|
|
{
|
|
section::name = section::Name::HI_SCORE_TABLE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifdef RECORDING
|
|
// Solo mira y guarda el input en cada update
|
|
checkInput();
|
|
|
|
// Incrementa el contador de la demo
|
|
if (demo.counter < TOTAL_DEMO_DATA)
|
|
{
|
|
demo.counter++;
|
|
}
|
|
|
|
// Si se ha llenado el vector con datos, sale del programa
|
|
else
|
|
{
|
|
section::name = section::Name::QUIT;
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef DEBUG
|
|
if (auto_pop_balloons_ && !game_completed_)
|
|
{
|
|
balloons_popped_++;
|
|
increaseStageCurrentPower(1);
|
|
}
|
|
#endif
|
|
if (!paused_)
|
|
{
|
|
// Actualiza el objeto fade
|
|
fade_->update();
|
|
|
|
// Actualiza las variables del jugador
|
|
updatePlayers();
|
|
|
|
// Actualiza el marcador
|
|
checkPlayersStatusPlaying();
|
|
updateScoreboard();
|
|
|
|
// Actualiza el fondo
|
|
updateBackground();
|
|
|
|
// Mueve los globos
|
|
updateBalloons();
|
|
|
|
// Actualiza el objeto encargado de las explosiones
|
|
explosions_->update();
|
|
|
|
// Mueve las balas
|
|
moveBullets();
|
|
|
|
// Actualiza los items
|
|
updateItems();
|
|
|
|
// Comprueba si hay cambio de fase y actualiza las variables
|
|
updateStage();
|
|
|
|
// Actualiza el estado de muerte
|
|
updateGameOver();
|
|
|
|
// Actualiza los SpriteSmarts
|
|
updateSpriteSmarts();
|
|
|
|
// Actualiza los contadores de estado y efectos
|
|
updateTimeStoppedCounter();
|
|
updateBalloonDeployCounter();
|
|
|
|
// Actualiza el ayudante
|
|
updateHelper();
|
|
|
|
// Comprueba las colisiones entre globos y balas
|
|
checkBulletBalloonCollision();
|
|
|
|
// Comprueba el nivel de amenaza para ver si se han de crear nuevos enemigos
|
|
updateMenace();
|
|
|
|
// Actualiza la velocidad de los enemigos
|
|
if (difficulty_ == GameDifficulty::NORMAL)
|
|
{
|
|
updateBalloonSpeed();
|
|
}
|
|
|
|
// Actualiza el tramo final de juego, una vez completado
|
|
updateGameCompleted();
|
|
|
|
// Vacia los vectores
|
|
freeBullets();
|
|
freeBalloons();
|
|
freeItems();
|
|
freeSpriteSmarts();
|
|
}
|
|
|
|
// Comprueba si la música ha de estar sonando
|
|
checkMusicStatus();
|
|
|
|
// Actualiza el objeto screen
|
|
screen_->update();
|
|
|
|
// Dibuja los graficos de la zona de juego en la textura
|
|
fillCanvas();
|
|
}
|
|
}
|
|
|
|
// Actualiza el fondo
|
|
void Game::updateBackground()
|
|
{
|
|
// Si el juego está completado, se reduce la velocidad de las nubes
|
|
if (game_completed_)
|
|
{
|
|
balloons_popped_ = (balloons_popped_ > 400) ? (balloons_popped_ - 25) : 200;
|
|
}
|
|
|
|
// Calcula la velocidad en función de los globos explotados y el total de globos a explotar para acabar el juego
|
|
constexpr auto clouds_initial_speed = 0.05f;
|
|
constexpr auto clouds_final_speed = 2.00f - clouds_initial_speed;
|
|
const float cloudsSpeed = (-clouds_initial_speed) + (-clouds_final_speed * ((float)balloons_popped_ / (float)total_power_to_complete_game_));
|
|
background_->setCloudsSpeed(cloudsSpeed);
|
|
|
|
// Calcula la transición de los diferentes fondos
|
|
const float gradient_number = std::min(((float)balloons_popped_ / 1250.0f), 3.0f);
|
|
const float percent = gradient_number - (int)gradient_number;
|
|
background_->setGradientNumber((int)gradient_number);
|
|
background_->setTransition(percent);
|
|
|
|
// Actualiza el objeto
|
|
background_->update();
|
|
}
|
|
|
|
// Dibuja los elementos de la zona de juego en su textura
|
|
void Game::fillCanvas()
|
|
{
|
|
// Dibujamos el contenido de la zona de juego en su textura
|
|
auto temp = SDL_GetRenderTarget(renderer_);
|
|
SDL_SetRenderTarget(renderer_, canvas_);
|
|
|
|
// Dibuja los objetos
|
|
background_->render();
|
|
renderItems();
|
|
renderSpriteSmarts();
|
|
explosions_->render();
|
|
renderBalloons();
|
|
renderBullets();
|
|
renderMessages();
|
|
renderPlayers();
|
|
|
|
// Deja el renderizador apuntando donde estaba
|
|
SDL_SetRenderTarget(renderer_, temp);
|
|
}
|
|
|
|
// Dibuja el juego
|
|
void Game::render()
|
|
{
|
|
// Prepara para empezar a dibujar en la textura de juego
|
|
screen_->start();
|
|
|
|
// Copia la textura con la zona de juego a la pantalla
|
|
SDL_RenderCopy(renderer_, canvas_, nullptr, ¶m.game.play_area.rect);
|
|
|
|
// Dibuja el marcador
|
|
scoreboard_->render();
|
|
|
|
// Dibuja el fade
|
|
fade_->render();
|
|
|
|
// Vuelca el contenido del renderizador en pantalla
|
|
screen_->blit();
|
|
}
|
|
|
|
// Gestiona el nivel de amenaza
|
|
void Game::updateMenace()
|
|
{
|
|
if (game_completed_)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const auto stage = balloon_formations_->getStage(current_stage_);
|
|
const float percent = current_power_ / stage.power_to_complete;
|
|
const int difference = stage.max_menace - stage.min_menace;
|
|
|
|
// Aumenta el nivel de amenaza en función de la puntuación
|
|
menace_threshold_ = stage.min_menace + (difference * percent);
|
|
|
|
// Si el nivel de amenza es inferior al umbral
|
|
if (menace_current_ < menace_threshold_)
|
|
{
|
|
// Crea una formación de enemigos
|
|
deployBalloonFormation();
|
|
|
|
// Recalcula el nivel de amenaza con el nuevo globo
|
|
evaluateAndSetMenace();
|
|
}
|
|
}
|
|
|
|
// Pinta diferentes mensajes en la pantalla
|
|
void Game::renderMessages()
|
|
{
|
|
// GetReady
|
|
if (counter_ < STAGE_COUNTER && !demo_.enabled)
|
|
{
|
|
text_nokia2_big_->write((int)get_ready_bitmap_path_[counter_], param.game.play_area.center_y - 8, lang::getText(75), -2);
|
|
}
|
|
|
|
// Time Stopped
|
|
if (time_stopped_)
|
|
{
|
|
if (time_stopped_counter_ > 100 || time_stopped_counter_ % 10 > 4)
|
|
{
|
|
text_nokia2_->writeDX(TEXT_CENTER, param.game.play_area.center_x, param.game.play_area.first_quarter_y, lang::getText(36) + std::to_string(time_stopped_counter_ / 10), -1, no_color, 1, shdw_txt_color);
|
|
}
|
|
|
|
if (time_stopped_counter_ > 100)
|
|
{
|
|
if (time_stopped_counter_ % 30 == 0)
|
|
{
|
|
JA_PlaySound(clock_sound_);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (time_stopped_counter_ % 15 == 0)
|
|
{
|
|
JA_PlaySound(clock_sound_);
|
|
}
|
|
}
|
|
}
|
|
|
|
// STAGE NUMBER
|
|
if (stage_bitmap_counter_ < STAGE_COUNTER)
|
|
{
|
|
const auto stage_number = balloon_formations_->getStage(current_stage_).number;
|
|
std::string text;
|
|
|
|
if (stage_number == 10)
|
|
{ // Ultima fase
|
|
text = lang::getText(79);
|
|
}
|
|
else
|
|
{ // X fases restantes
|
|
text = std::to_string(11 - stage_number) + lang::getText(38);
|
|
}
|
|
|
|
if (!game_completed_)
|
|
{ // Escribe el número de fases restantes
|
|
text_nokia2_big_->writeDX(TEXT_CENTER, param.game.play_area.center_x, stage_bitmap_path_[stage_bitmap_counter_], text, -2, no_color, 2, shdw_txt_color);
|
|
}
|
|
else
|
|
{ // Escribe el texto de juego completado
|
|
text = lang::getText(50);
|
|
text_nokia2_big_->writeDX(TEXT_CENTER, param.game.play_area.center_x, stage_bitmap_path_[stage_bitmap_counter_], text, -2, no_color, 1, shdw_txt_color);
|
|
text_nokia2_->writeDX(TEXT_CENTER, param.game.play_area.center_x, stage_bitmap_path_[stage_bitmap_counter_] + text_nokia2_big_->getCharacterSize() + 2, lang::getText(76), -1, no_color, 1, shdw_txt_color);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Habilita el efecto del item de detener el tiempo
|
|
void Game::enableTimeStopItem()
|
|
{
|
|
stopAllBalloons(TIME_STOPPED_COUNTER);
|
|
setTimeStopped(true);
|
|
incTimeStoppedCounter(TIME_STOPPED_COUNTER);
|
|
if (JA_GetMusicState() == JA_MUSIC_PLAYING && !demo_.enabled)
|
|
{
|
|
JA_PauseMusic();
|
|
}
|
|
}
|
|
|
|
// Deshabilita el efecto del item de detener el tiempo
|
|
void Game::disableTimeStopItem()
|
|
{
|
|
time_stopped_ = false;
|
|
setTimeStoppedCounter(0);
|
|
startAllBalloons();
|
|
if (JA_GetMusicState() == JA_MUSIC_PAUSED && !demo_.enabled)
|
|
{
|
|
JA_ResumeMusic();
|
|
}
|
|
}
|
|
|
|
// Comprueba si la música ha de estar sonando
|
|
void Game::checkMusicStatus()
|
|
{
|
|
// Si la música no está sonando
|
|
if (JA_GetMusicState() == JA_MUSIC_INVALID || JA_GetMusicState() == JA_MUSIC_STOPPED)
|
|
{
|
|
// Si se ha completado el juego o los jugadores han terminado, detiene la música
|
|
game_completed_ || allPlayersAreGameOver() ? JA_StopMusic() : JA_PlayMusic(music_);
|
|
}
|
|
}
|
|
|
|
// Bucle para el juego
|
|
void Game::run()
|
|
{
|
|
while (section::name == section::Name::GAME)
|
|
{
|
|
#ifndef RECORDING
|
|
checkInput();
|
|
#endif
|
|
update();
|
|
checkEvents(); // Tiene que ir antes del render
|
|
render();
|
|
}
|
|
|
|
// Vuelve a dejar el sonido como estaba
|
|
(demo_.enabled) ? JA_EnableSound(options.audio.sound.enabled) : JA_StopMusic();
|
|
}
|
|
|
|
// Indica si se puede crear una powerball
|
|
bool Game::canPowerBallBeCreated()
|
|
{
|
|
return (!power_ball_enabled_) && (calculateScreenPower() > POWERBALL_SCREENPOWER_MINIMUM) && (power_ball_counter_ == 0);
|
|
}
|
|
|
|
// Calcula el poder actual de los globos en pantalla
|
|
int Game::calculateScreenPower()
|
|
{
|
|
return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon)
|
|
{ return sum + (balloon->isEnabled() ? balloon->getPower() : 0); });
|
|
}
|
|
|
|
// Inicializa las variables que contienen puntos de ruta para mover objetos
|
|
void Game::initPaths()
|
|
{
|
|
// Vector con los valores del seno para 360 grados
|
|
float sin[360];
|
|
for (int i = 0; i < 360; ++i)
|
|
{
|
|
sin[i] = SDL_sinf((float)i * 3.14f / 180.0f);
|
|
}
|
|
|
|
// Letrero de STAGE #
|
|
constexpr auto first_part = STAGE_COUNTER / 4; // 50
|
|
constexpr auto second_part = first_part * 3; // 150
|
|
const auto center_point = param.game.play_area.center_y - (BLOCK * 2);
|
|
const auto distance = (param.game.play_area.rect.h) - (param.game.play_area.center_y - 16);
|
|
|
|
for (int i = 0; i < first_part; ++i)
|
|
{
|
|
stage_bitmap_path_[i] = (sin[(int)((i * 1.8f) + 90)] * (distance) + center_point);
|
|
}
|
|
|
|
for (int i = first_part; i < second_part; ++i)
|
|
{
|
|
stage_bitmap_path_[i] = (int)center_point;
|
|
}
|
|
|
|
for (int i = second_part; i < STAGE_COUNTER; ++i)
|
|
{
|
|
stage_bitmap_path_[i] = (sin[(int)(((i - 149) * 1.8f) + 90)] * (center_point + 17) - 17);
|
|
}
|
|
|
|
// Letrero de GetReady
|
|
const auto size = text_nokia2_big_->lenght(lang::getText(75), -2);
|
|
|
|
const float start1 = param.game.play_area.rect.x - size;
|
|
const float finish1 = param.game.play_area.center_x - (size / 2);
|
|
const float finish2 = param.game.play_area.rect.w;
|
|
|
|
const float distance1 = finish1 - start1;
|
|
const float distance2 = finish2 - finish1;
|
|
|
|
for (int i = 0; i < first_part; ++i)
|
|
{
|
|
get_ready_bitmap_path_[i] = sin[(int)(i * 1.8f)];
|
|
get_ready_bitmap_path_[i] *= distance1;
|
|
get_ready_bitmap_path_[i] -= size;
|
|
}
|
|
|
|
for (int i = first_part; i < second_part; ++i)
|
|
{
|
|
get_ready_bitmap_path_[i] = (int)finish1;
|
|
}
|
|
|
|
for (int i = second_part; i < STAGE_COUNTER; ++i)
|
|
{
|
|
get_ready_bitmap_path_[i] = sin[(int)((i - second_part) * 1.8f)];
|
|
get_ready_bitmap_path_[i] *= distance2;
|
|
get_ready_bitmap_path_[i] += finish1;
|
|
}
|
|
}
|
|
|
|
// Actualiza el tramo final de juego, una vez completado
|
|
void Game::updateGameCompleted()
|
|
{
|
|
if (game_completed_)
|
|
{
|
|
game_completed_counter_++;
|
|
}
|
|
|
|
if (game_completed_counter_ == GAME_COMPLETED_END)
|
|
{
|
|
section::name = section::Name::TITLE;
|
|
section::options = section::Options::TITLE_1;
|
|
}
|
|
}
|
|
|
|
// Actualiza las variables de ayuda
|
|
void Game::updateHelper()
|
|
{
|
|
// Solo ofrece ayuda cuando la amenaza es elevada
|
|
if (menace_current_ > 15)
|
|
{
|
|
helper_.need_coffee = true;
|
|
helper_.need_coffee_machine = true;
|
|
for (const auto &player : players_)
|
|
{
|
|
if (player->isPlaying())
|
|
{
|
|
helper_.need_coffee &= (player->getCoffees() == 0);
|
|
helper_.need_coffee_machine &= (!player->isPowerUp());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
helper_.need_coffee = helper_.need_coffee_machine = false;
|
|
}
|
|
}
|
|
|
|
// Comprueba si todos los jugadores han terminado de jugar
|
|
bool Game::allPlayersAreWaitingOrGameOver()
|
|
{
|
|
auto success = true;
|
|
for (const auto &player : players_)
|
|
{
|
|
success &= player->isWaiting() || player->isGameOver();
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
// Comprueba si todos los jugadores han terminado de jugar
|
|
bool Game::allPlayersAreGameOver()
|
|
{
|
|
auto success = true;
|
|
for (const auto &player : players_)
|
|
{
|
|
success &= player->isGameOver();
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
// Comprueba si todos los jugadores han terminado de jugar
|
|
bool Game::allPlayersAreNotPlaying()
|
|
{
|
|
auto success = true;
|
|
for (const auto &player : players_)
|
|
{
|
|
success &= !player->isPlaying();
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
// Comprueba los eventos que hay en cola
|
|
void Game::checkEvents()
|
|
{
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event))
|
|
{
|
|
// Evento de salida de la aplicación
|
|
if (event.type == SDL_QUIT)
|
|
{
|
|
section::name = section::Name::QUIT;
|
|
break;
|
|
}
|
|
|
|
else if (event.type == SDL_WINDOWEVENT)
|
|
{
|
|
switch (event.window.event)
|
|
{
|
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
|
{
|
|
if (!demo_.enabled)
|
|
{
|
|
pause(true);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
|
{
|
|
pause(false);
|
|
break;
|
|
}
|
|
|
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
|
{
|
|
reloadTextures();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
else if (event.type == SDL_KEYDOWN)
|
|
{
|
|
switch (event.key.keysym.sym)
|
|
{
|
|
// Crea una powerball
|
|
case SDLK_1:
|
|
{
|
|
createPowerBall();
|
|
break;
|
|
}
|
|
|
|
// Crea dos BALLON4
|
|
case SDLK_2:
|
|
{
|
|
const auto set = 0;
|
|
const auto stage = balloon_formations_->getStage(0);
|
|
const auto numEnemies = stage.balloon_pool->set[set]->number_of_balloons;
|
|
for (int i = 0; i < numEnemies; ++i)
|
|
{
|
|
createBalloon(stage.balloon_pool->set[set]->init[i].x,
|
|
stage.balloon_pool->set[set]->init[i].y,
|
|
stage.balloon_pool->set[set]->init[i].kind,
|
|
stage.balloon_pool->set[set]->init[i].vel_x,
|
|
balloon_speed_,
|
|
stage.balloon_pool->set[set]->init[i].creation_counter);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Activa el modo para pasar el juego automaticamente
|
|
case SDLK_3:
|
|
{
|
|
auto_pop_balloons_ = !auto_pop_balloons_;
|
|
Notifier::get()->showText("auto_pop_balloons_ " + boolToString(auto_pop_balloons_));
|
|
break;
|
|
}
|
|
|
|
// Ralentiza mucho la lógica
|
|
case SDLK_4:
|
|
{
|
|
ticks_speed_ *= 10;
|
|
break;
|
|
}
|
|
|
|
// Acelera mucho la lógica
|
|
case SDLK_5:
|
|
{
|
|
ticks_speed_ /= 10;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Carga las animaciones
|
|
void Game::loadAnimations(std::string filePath, std::vector<std::string> *buffer)
|
|
{
|
|
std::ifstream file(filePath);
|
|
std::string line;
|
|
|
|
if (file)
|
|
{
|
|
|
|
std::cout << "Animation loaded: " << filePath.substr(filePath.find_last_of("\\/") + 1).c_str() << std::endl;
|
|
while (std::getline(file, line))
|
|
{
|
|
buffer->push_back(line);
|
|
}
|
|
file.close();
|
|
}
|
|
}
|
|
|
|
// Elimina todos los objetos contenidos en vectores
|
|
void Game::deleteAllVectorObjects()
|
|
{
|
|
players_.clear();
|
|
balloons_.clear();
|
|
bullets_.clear();
|
|
items_.clear();
|
|
smart_sprites_.clear();
|
|
}
|
|
|
|
// Recarga las texturas
|
|
void Game::reloadTextures()
|
|
{
|
|
for (auto &texture : item_textures_)
|
|
{
|
|
texture->reLoad();
|
|
}
|
|
|
|
for (auto &texture : balloon_textures_)
|
|
{
|
|
texture->reLoad();
|
|
}
|
|
|
|
for (auto &texture : player1_textures_)
|
|
{
|
|
texture->reLoad();
|
|
}
|
|
|
|
for (auto &texture : player2_textures_)
|
|
{
|
|
texture->reLoad();
|
|
}
|
|
|
|
for (auto &texture : game_text_textures_)
|
|
{
|
|
texture->reLoad();
|
|
}
|
|
|
|
bullet_texture_->reLoad();
|
|
background_->reloadTextures();
|
|
}
|
|
|
|
// Actualiza el marcador
|
|
void Game::updateScoreboard()
|
|
{
|
|
for (const auto &player : players_)
|
|
{
|
|
scoreboard_->setScore(player->getScoreBoardPanel(), player->getScore());
|
|
scoreboard_->setMult(player->getScoreBoardPanel(), player->getScoreMultiplier());
|
|
}
|
|
|
|
// Resto de marcador
|
|
scoreboard_->setStage(balloon_formations_->getStage(current_stage_).number);
|
|
scoreboard_->setPower((float)current_power_ / (float)balloon_formations_->getStage(current_stage_).power_to_complete);
|
|
scoreboard_->setHiScore(hi_score_.score);
|
|
scoreboard_->setHiScoreName(hi_score_.name);
|
|
|
|
// Lógica del marcador
|
|
scoreboard_->update();
|
|
}
|
|
|
|
// Pausa el juego
|
|
void Game::pause(bool value)
|
|
{
|
|
paused_ = value;
|
|
screen_->attenuate(paused_);
|
|
}
|
|
|
|
// Añade una puntuación a la tabla de records
|
|
void Game::addScoreToScoreBoard(const std::string &name, int score)
|
|
{
|
|
const auto entry = (HiScoreEntry){trim(name), score};
|
|
auto manager = std::make_unique<ManageHiScoreTable>(&options.game.hi_score_table);
|
|
manager->add(entry);
|
|
manager->saveToFile(asset_->get("score.bin"));
|
|
}
|
|
|
|
// Saca del estado de GAME OVER al jugador si el otro está activo
|
|
void Game::checkAndUpdatePlayerStatus(int activePlayerIndex, int inactivePlayerIndex)
|
|
{
|
|
if (players_[activePlayerIndex]->isGameOver() &&
|
|
!players_[inactivePlayerIndex]->isGameOver() &&
|
|
!players_[inactivePlayerIndex]->isWaiting())
|
|
{
|
|
players_[activePlayerIndex]->setStatusPlaying(PlayerStatus::WAITING);
|
|
}
|
|
}
|
|
|
|
// Comprueba el estado de los jugadores
|
|
void Game::checkPlayersStatusPlaying()
|
|
{
|
|
if (demo_.enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Comprueba si todos los jugadores estan esperando
|
|
if (allPlayersAreWaitingOrGameOver())
|
|
{
|
|
// Entonces los pone en estado de Game Over
|
|
for (auto &player : players_)
|
|
{
|
|
player->setStatusPlaying(PlayerStatus::GAME_OVER);
|
|
}
|
|
}
|
|
|
|
// Comprobar estado de ambos jugadores
|
|
checkAndUpdatePlayerStatus(0, 1);
|
|
checkAndUpdatePlayerStatus(1, 0);
|
|
}
|
|
|
|
// Obtiene un jugador a partir de su "id"
|
|
std::shared_ptr<Player> Game::getPlayer(int id)
|
|
{
|
|
for (const auto &player : players_)
|
|
{
|
|
if (player->getId() == id)
|
|
{
|
|
return player;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Obtiene un controlador a partir del "id" del jugador
|
|
int Game::getController(int player_id)
|
|
{
|
|
for (int i = 0; i < (int)options.controller.size(); ++i)
|
|
{
|
|
if (options.controller[i].player_id == player_id)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Gestiona la entrada durante el juego
|
|
void Game::checkInput()
|
|
{
|
|
checkPauseInput(); // Verifica si se debe pausar el juego.
|
|
|
|
if (demo_.enabled)
|
|
{
|
|
handleDemoMode(); // Controla el comportamiento de los jugadores en modo demo.
|
|
}
|
|
else
|
|
{
|
|
handlePlayersInput(); // Gestiona el input normal de los jugadores.
|
|
}
|
|
|
|
screen_->checkInput(); // Verifica el input en la pantalla del juego.
|
|
globalInputs::check(); // Verifica los inputs globales.
|
|
}
|
|
|
|
// Verifica si alguno de los controladores ha solicitado una pausa y actualiza el estado de pausa del juego.
|
|
void Game::checkPauseInput()
|
|
{
|
|
for (int i = 0; i < input_->getNumControllers(); ++i)
|
|
{
|
|
if (input_->checkModInput(InputType::SERVICE, InputType::PAUSE, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i))
|
|
{
|
|
pause(!paused_); // Alterna entre pausado y no pausado.
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Gestiona las entradas de los jugadores en el modo demo, incluyendo movimientos y disparos automáticos.
|
|
void Game::handleDemoMode()
|
|
{
|
|
int i = 0;
|
|
for (auto &player : players_)
|
|
{
|
|
if (player->isPlaying())
|
|
{
|
|
handleDemoPlayerInput(player, i); // Maneja el input específico del jugador en modo demo.
|
|
}
|
|
if (input_->checkAnyButtonPressed())
|
|
{
|
|
section::name = section::Name::TITLE; // Salir del modo demo y regresar al menú principal.
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// Procesa las entradas para un jugador específico durante el modo demo.
|
|
// Incluye movimientos (izquierda, derecha, sin movimiento) y disparos automáticos.
|
|
void Game::handleDemoPlayerInput(const std::shared_ptr<Player> &player, int index)
|
|
{
|
|
const auto &demoData = demo_.data_file[index][demo_.counter];
|
|
|
|
if (demoData.left == 1)
|
|
player->setInput(InputType::LEFT);
|
|
else if (demoData.right == 1)
|
|
player->setInput(InputType::RIGHT);
|
|
else if (demoData.no_input == 1)
|
|
player->setInput(InputType::NONE);
|
|
|
|
if (demoData.fire == 1)
|
|
handleFireInput(player, BulletType::UP);
|
|
else if (demoData.fire_left == 1)
|
|
handleFireInput(player, BulletType::LEFT);
|
|
else if (demoData.fire_right == 1)
|
|
handleFireInput(player, BulletType::RIGHT);
|
|
}
|
|
|
|
// Maneja el disparo de un jugador, incluyendo la creación de balas y la gestión del tiempo de espera entre disparos.
|
|
void Game::handleFireInput(const std::shared_ptr<Player> &player, BulletType bulletType)
|
|
{
|
|
if (player->canFire())
|
|
{
|
|
player->setInput(bulletType == BulletType::UP ? InputType::FIRE_CENTER : bulletType == BulletType::LEFT ? InputType::FIRE_LEFT
|
|
: InputType::FIRE_RIGHT);
|
|
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), bulletType, player->isPowerUp(), player->getId());
|
|
player->setFireCooldown(10); // Establece un tiempo de espera para el próximo disparo.
|
|
}
|
|
}
|
|
|
|
// Gestiona las entradas de todos los jugadores en el modo normal (fuera del modo demo).
|
|
void Game::handlePlayersInput()
|
|
{
|
|
for (auto &player : players_)
|
|
{
|
|
if (player->isPlaying())
|
|
{
|
|
handleNormalPlayerInput(player); // Maneja el input de los jugadores en modo normal.
|
|
}
|
|
else if (player->isContinue() || player->isWaiting())
|
|
{
|
|
handlePlayerContinue(player); // Gestiona la continuación del jugador.
|
|
}
|
|
else if (player->isEnteringName())
|
|
{
|
|
handleNameInput(player); // Gestiona la introducción del nombre del jugador.
|
|
}
|
|
}
|
|
}
|
|
|
|
// Maneja las entradas de movimiento y disparo para un jugador en modo normal.
|
|
void Game::handleNormalPlayerInput(const std::shared_ptr<Player> &player)
|
|
{
|
|
const auto controllerIndex = player->getController();
|
|
const bool autofire = player->isPowerUp() || options.game.autofire;
|
|
|
|
if (input_->checkInput(InputType::LEFT, INPUT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
player->setInput(InputType::LEFT);
|
|
#ifdef RECORDING
|
|
demo.keys.left = 1;
|
|
#endif
|
|
}
|
|
else if (input_->checkInput(InputType::RIGHT, INPUT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
player->setInput(InputType::RIGHT);
|
|
#ifdef RECORDING
|
|
demo.keys.right = 1;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
player->setInput(InputType::NONE);
|
|
#ifdef RECORDING
|
|
demo.keys.no_input = 1;
|
|
#endif
|
|
}
|
|
|
|
handleFireInputs(player, autofire, controllerIndex); // Verifica y maneja todas las posibles entradas de disparo.
|
|
}
|
|
|
|
// Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado.
|
|
void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire, int controllerIndex)
|
|
{
|
|
if (input_->checkInput(InputType::FIRE_CENTER, autofire, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
handleFireInput(player, BulletType::UP);
|
|
#ifdef RECORDING
|
|
demo.keys.fire = 1;
|
|
#endif
|
|
}
|
|
else if (input_->checkInput(InputType::FIRE_LEFT, autofire, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
handleFireInput(player, BulletType::LEFT);
|
|
#ifdef RECORDING
|
|
demo.keys.fire_left = 1;
|
|
#endif
|
|
}
|
|
else if (input_->checkInput(InputType::FIRE_RIGHT, autofire, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
handleFireInput(player, BulletType::RIGHT);
|
|
#ifdef RECORDING
|
|
demo.keys.fire_right = 1;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio.
|
|
void Game::handlePlayerContinue(const std::shared_ptr<Player> &player)
|
|
{
|
|
const auto controllerIndex = player->getController();
|
|
if (input_->checkInput(InputType::START, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
player->setStatusPlaying(PlayerStatus::PLAYING);
|
|
}
|
|
|
|
// Disminuye el contador de continuación si se presiona cualquier botón de disparo.
|
|
if (input_->checkInput(InputType::FIRE_LEFT, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index) ||
|
|
input_->checkInput(InputType::FIRE_CENTER, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index) ||
|
|
input_->checkInput(InputType::FIRE_RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
player->decContinueCounter();
|
|
}
|
|
}
|
|
|
|
// Procesa las entradas para la introducción del nombre del jugador.
|
|
void Game::handleNameInput(const std::shared_ptr<Player> &player)
|
|
{
|
|
const auto controllerIndex = player->getController();
|
|
if (input_->checkInput(InputType::FIRE_LEFT, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index) ||
|
|
input_->checkInput(InputType::FIRE_CENTER, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index) ||
|
|
input_->checkInput(InputType::FIRE_RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
if (player->getRecordNamePos() == 7)
|
|
{
|
|
player->setInput(InputType::START);
|
|
addScoreToScoreBoard(player->getRecordName(), player->getScore());
|
|
player->setStatusPlaying(PlayerStatus::CONTINUE);
|
|
}
|
|
else
|
|
{
|
|
player->setInput(InputType::RIGHT);
|
|
}
|
|
}
|
|
else if (input_->checkInput(InputType::UP, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
player->setInput(InputType::UP);
|
|
}
|
|
else if (input_->checkInput(InputType::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
player->setInput(InputType::DOWN);
|
|
}
|
|
else if (input_->checkInput(InputType::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
player->setInput(InputType::LEFT);
|
|
}
|
|
else if (input_->checkInput(InputType::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
player->setInput(InputType::RIGHT);
|
|
}
|
|
else if (input_->checkInput(InputType::START, INPUT_DO_NOT_ALLOW_REPEAT, options.controller[controllerIndex].device_type, options.controller[controllerIndex].index))
|
|
{
|
|
player->setInput(InputType::START);
|
|
addScoreToScoreBoard(player->getRecordName(), player->getScore());
|
|
player->setStatusPlaying(PlayerStatus::CONTINUE);
|
|
}
|
|
}
|