Files
coffee_crisis_arcade_edition/source/sections/instructions.cpp

351 lines
13 KiB
C++

#include "instructions.h"
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget, SDL_Re...
#include <algorithm> // Para max
#include <array> // Para array
#include <string> // Para basic_string, string
#include <utility> // Para move
#include <vector> // Para vector
#include "audio.h" // Para Audio
#include "fade.h" // Para Fade, FadeMode, FadeType
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input
#include "lang.h" // Para getText
#include "param.h" // Para Param, param, ParamGame, ParamFade, Param...
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name, Options, options
#include "sprite.h" // Para Sprite
#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR, TEXT_SHADOW
#include "tiled_bg.h" // Para TiledBG, TiledBGMode
#include "utils.h" // Para Color, SHADOW_TEXT_COLOR, Zone, NO_TEXT_C...
// Constructor
Instructions::Instructions()
: renderer_(Screen::get()->getRenderer()),
texture_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
text_(Resource::get()->getText("smb2")),
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::STATIC)),
fade_(std::make_unique<Fade>()) {
// Configura las texturas
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND);
// Inicializa variables
Section::name = Section::Name::INSTRUCTIONS;
view_ = param.game.game_area.rect;
// Inicializa objetos
tiled_bg_->setColor(param.title.bg_color);
fade_->setColor(param.fade.color);
fade_->setType(FadeType::FULLSCREEN);
fade_->setPostDuration(param.fade.post_duration);
fade_->setMode(FadeMode::IN);
fade_->activate();
// Inicializa las líneas con un retraso progresivo de 50 ms
lines_ = initializeLines(256);
// Rellena la textura de texto
fillTexture();
// Inicializa los sprites de los items
iniSprites();
}
// Destructor
Instructions::~Instructions() {
item_textures_.clear();
sprites_.clear();
SDL_DestroyTexture(backbuffer_);
SDL_DestroyTexture(texture_);
}
// Inicializa los sprites de los items
void Instructions::iniSprites() {
// Inicializa las texturas
item_textures_.emplace_back(Resource::get()->getTexture("item_points1_disk.png"));
item_textures_.emplace_back(Resource::get()->getTexture("item_points2_gavina.png"));
item_textures_.emplace_back(Resource::get()->getTexture("item_points3_pacmar.png"));
item_textures_.emplace_back(Resource::get()->getTexture("item_clock.png"));
item_textures_.emplace_back(Resource::get()->getTexture("item_coffee.png"));
// Inicializa los sprites
for (int i = 0; i < (int)item_textures_.size(); ++i) {
auto sprite = std::make_unique<Sprite>(item_textures_[i], 0, 0, param.game.item_size, param.game.item_size);
sprite->setPosition((SDL_FPoint){sprite_pos_.x, sprite_pos_.y + ((param.game.item_size + item_space_) * i)});
sprites_.push_back(std::move(sprite));
}
}
// Actualiza los sprites
void Instructions::updateSprites() {
SDL_FRect src_rect = {0, 0, param.game.item_size, param.game.item_size};
// Disquito
src_rect.y = param.game.item_size * (((counter_ + 12) / 36) % 2);
sprites_[0]->setSpriteClip(src_rect);
// Gavina
src_rect.y = param.game.item_size * (((counter_ + 9) / 36) % 2);
sprites_[1]->setSpriteClip(src_rect);
// Pacmar
src_rect.y = param.game.item_size * (((counter_ + 6) / 36) % 2);
sprites_[2]->setSpriteClip(src_rect);
// Time Stopper
src_rect.y = param.game.item_size * (((counter_ + 3) / 36) % 2);
sprites_[3]->setSpriteClip(src_rect);
// Coffee
src_rect.y = param.game.item_size * (((counter_ + 0) / 36) % 2);
sprites_[4]->setSpriteClip(src_rect);
}
// Rellena la textura de texto
void Instructions::fillTexture() {
const int desp_x = param.game.item_size + 8;
// Modifica el renderizador para pintar en la textura
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, texture_);
// Limpia la textura
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_RenderClear(renderer_);
// Constantes
constexpr int num_lines = 4;
constexpr int num_item_lines = 4;
constexpr int num_post_headers = 2;
constexpr int num_pre_headers = 1;
constexpr int space_post_header = 20;
constexpr int space_pre_header = 28;
const int space_between_lines = text_->getCharacterSize() * 1.5f;
const int space_between_item_lines = param.game.item_size + item_space_;
const int space_new_paragraph = space_between_lines * 0.5f;
const int size = (num_lines * space_between_lines) + (num_item_lines * space_between_item_lines) + (num_post_headers * space_post_header) + (num_pre_headers * space_pre_header) + (space_new_paragraph);
const int first_line = (param.game.height - size) / 2;
// Calcula cual es el texto más largo de las descripciones de los items
int lenght = 0;
const std::array<std::string, 5> ITEM_DESCRIPTIONS = {
Lang::getText("[INSTRUCTIONS] 07"),
Lang::getText("[INSTRUCTIONS] 08"),
Lang::getText("[INSTRUCTIONS] 09"),
Lang::getText("[INSTRUCTIONS] 10"),
Lang::getText("[INSTRUCTIONS] 11")};
for (const auto &desc : ITEM_DESCRIPTIONS) {
const int l = text_->lenght(desc);
lenght = l > lenght ? l : lenght;
}
const int ANCHOR_ITEM = (param.game.width - (lenght + desp_x)) / 2;
constexpr Color ORANGE_COLOR = Color(0XFF, 0X7A, 0X00);
// Escribe el texto de las instrucciones
text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, first_line, Lang::getText("[INSTRUCTIONS] 01"), 1, ORANGE_COLOR, 1, SHADOW_TEXT_COLOR);
const int anchor1 = first_line + space_post_header;
text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_between_lines * 0, Lang::getText("[INSTRUCTIONS] 02"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR);
text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_between_lines * 1, Lang::getText("[INSTRUCTIONS] 03"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR);
text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_new_paragraph + space_between_lines * 2, Lang::getText("[INSTRUCTIONS] 04"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR);
text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_new_paragraph + space_between_lines * 3, Lang::getText("[INSTRUCTIONS] 05"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR);
// Escribe el texto de los objetos y sus puntos
const int anchor2 = anchor1 + space_pre_header + space_new_paragraph + space_between_lines * 3;
text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor2, Lang::getText("[INSTRUCTIONS] 06"), 1, ORANGE_COLOR, 1, SHADOW_TEXT_COLOR);
const int anchor3 = anchor2 + space_post_header;
text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 0, Lang::getText("[INSTRUCTIONS] 07"), SHADOW_TEXT_COLOR);
text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 1, Lang::getText("[INSTRUCTIONS] 08"), SHADOW_TEXT_COLOR);
text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 2, Lang::getText("[INSTRUCTIONS] 09"), SHADOW_TEXT_COLOR);
text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 3, Lang::getText("[INSTRUCTIONS] 10"), SHADOW_TEXT_COLOR);
text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 4, Lang::getText("[INSTRUCTIONS] 11"), SHADOW_TEXT_COLOR);
// Deja el renderizador como estaba
SDL_SetRenderTarget(renderer_, temp);
// Da valor a la variable
sprite_pos_.x = ANCHOR_ITEM;
sprite_pos_.y = anchor3 - ((param.game.item_size - text_->getCharacterSize()) / 2);
}
// Rellena el backbuffer
void Instructions::fillBackbuffer() {
// Modifica el renderizador para pintar en la textura
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
// Limpia la textura
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_RenderClear(renderer_);
// Coloca el texto de fondo
SDL_RenderTexture(renderer_, texture_, nullptr, nullptr);
// Dibuja los sprites
for (auto &sprite : sprites_) {
sprite->render();
}
// Deja el renderizador como estaba
SDL_SetRenderTarget(renderer_, temp);
}
// Actualiza las variables
void Instructions::update() {
if (SDL_GetTicks() - ticks_ > param.game.speed) {
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Actualiza el objeto screen
Screen::get()->update();
// Incrementa el contador
counter_++;
// Actualiza los sprites
updateSprites();
// Gestiona la textura con los graficos
updateBackbuffer();
// Actualiza el mosaico de fondo
tiled_bg_->update();
// Actualiza el objeto "fade"
fade_->update();
// Rellena el backbuffer
fillBackbuffer();
}
}
// Pinta en pantalla
void Instructions::render() {
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
// Limpia la pantalla
Screen::get()->clean();
// Dibuja el mosacico de fondo
tiled_bg_->render();
// Copia la textura y el backbuffer al renderizador
if (view_.y == 0)
renderLines(renderer_, backbuffer_, lines_);
else
SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_);
fade_->render();
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
}
// Comprueba los eventos
void Instructions::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
GlobalEvents::check(event);
}
}
// Comprueba las entradas
void Instructions::checkInput() {
Input::get()->update();
GlobalInputs::check();
}
// Bucle para la pantalla de instrucciones
void Instructions::run() {
Audio::get()->playMusic("title.ogg");
while (Section::name == Section::Name::INSTRUCTIONS) {
checkInput();
update();
checkEvents(); // Tiene que ir antes del render
render();
}
}
// Método para inicializar las líneas
std::vector<Line> Instructions::initializeLines(int height) {
std::vector<Line> lines;
for (int y = 0; y < height; y++) {
int direction = (y % 2 == 0) ? -1 : 1; // Pares a la izquierda, impares a la derecha
lines.emplace_back(y, 0.0f, direction);
}
return lines;
}
// Método para mover las líneas con suavizado
bool Instructions::moveLines(std::vector<Line> &lines, int width, float duration, Uint32 startDelay) {
Uint32 current_time = SDL_GetTicks();
bool all_lines_off_screen = true;
for (auto &line : lines) {
// Establecer startTime en el primer cuadro de animación
if (line.startTime == 0) {
line.startTime = current_time + line.y * startDelay;
}
float elapsed_time = (current_time - line.startTime) / 1000.0f; // Convertir a segundos
if (elapsed_time < 0) {
all_lines_off_screen = false; // Si aún no se debe mover esta línea, no están todas fuera de pantalla
continue;
}
if (elapsed_time >= duration) {
continue; // Si la línea ha salido de los límites, no la muevas más
}
float t = elapsed_time / duration;
float smooth_factor = easeInOutQuint(t);
line.x = line.direction * smooth_factor * width;
all_lines_off_screen = false; // Si alguna línea aún se está moviendo, no están todas fuera de pantalla
}
return all_lines_off_screen;
}
// Método para renderizar las líneas
void Instructions::renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines) {
for (const auto &LINE : lines) {
SDL_FRect srcRect = {0, static_cast<float>(LINE.y), 320, 1};
SDL_FRect dstRect = {static_cast<float>(LINE.x), static_cast<float>(LINE.y), 320, 1};
SDL_RenderTexture(renderer, texture, &srcRect, &dstRect);
}
}
// Gestiona la textura con los graficos
void Instructions::updateBackbuffer() {
// Establece la ventana del backbuffer
view_.y = std::max(0.0f, param.game.height - counter_ + 100);
// Verifica si view_.y == 0 y gestiona el temporizador
if (view_.y == 0) {
if (!start_delay_triggered_) {
// Activa el temporizador si no ha sido activado
start_delay_triggered_ = true;
start_delay_time_ = SDL_GetTicks();
} else if (SDL_GetTicks() - start_delay_time_ >= 4000) {
// Han pasado tres segundos, mover líneas
all_lines_off_screen_ = moveLines(lines_, 320, 1.0f, 5);
}
}
// Comprueba si el contador ha llegado al final
if (all_lines_off_screen_) {
Section::name = Section::Name::TITLE;
Section::options = Section::Options::TITLE_1;
}
}