370 lines
15 KiB
C++
370 lines
15 KiB
C++
#include "instructions.hpp"
|
|
|
|
#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.hpp" // Para Audio
|
|
#include "color.hpp" // Para Color, Colors::SHADOW_TEXT, Zone, NO_TEXT_C...
|
|
#include "fade.hpp" // Para Fade, FadeMode, FadeType
|
|
#include "global_events.hpp" // Para check
|
|
#include "global_inputs.hpp" // Para check
|
|
#include "input.hpp" // Para Input
|
|
#include "item.hpp" // Para Item
|
|
#include "lang.hpp" // Para getText
|
|
#include "param.hpp" // Para Param, param, ParamGame, ParamFade, Param...
|
|
#include "resource.hpp" // Para Resource
|
|
#include "screen.hpp" // Para Screen
|
|
#include "section.hpp" // Para Name, name, Options, options
|
|
#include "sprite.hpp" // Para Sprite
|
|
#include "text.hpp" // Para Text, Text::CENTER, Text::COLOR, Text::SHADOW
|
|
#include "tiled_bg.hpp" // Para TiledBG, TiledBGMode
|
|
#include "utils.hpp"
|
|
|
|
// 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(Fade::Type::FULLSCREEN);
|
|
fade_->setPostDuration(param.fade.post_duration_ms);
|
|
fade_->setMode(Fade::Mode::IN);
|
|
fade_->activate();
|
|
|
|
// Inicializa las líneas con un retraso progresivo
|
|
lines_ = initializeLines(256, LINE_START_DELAY_S);
|
|
|
|
// 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, Item::WIDTH, Item::HEIGHT);
|
|
sprite->setPosition((SDL_FPoint){sprite_pos_.x, sprite_pos_.y + ((Item::HEIGHT + item_space_) * i)});
|
|
sprites_.push_back(std::move(sprite));
|
|
}
|
|
}
|
|
|
|
// Actualiza los sprites
|
|
void Instructions::updateSprites() {
|
|
SDL_FRect src_rect = {0, 0, Item::WIDTH, Item::HEIGHT};
|
|
|
|
// Disquito (desplazamiento 12/60 = 0.2s)
|
|
src_rect.y = Item::HEIGHT * (static_cast<int>((elapsed_time_ + 0.2F) / SPRITE_ANIMATION_CYCLE_S) % 2);
|
|
sprites_[0]->setSpriteClip(src_rect);
|
|
|
|
// Gavina (desplazamiento 9/60 = 0.15s)
|
|
src_rect.y = Item::HEIGHT * (static_cast<int>((elapsed_time_ + 0.15F) / SPRITE_ANIMATION_CYCLE_S) % 2);
|
|
sprites_[1]->setSpriteClip(src_rect);
|
|
|
|
// Pacmar (desplazamiento 6/60 = 0.1s)
|
|
src_rect.y = Item::HEIGHT * (static_cast<int>((elapsed_time_ + 0.1F) / SPRITE_ANIMATION_CYCLE_S) % 2);
|
|
sprites_[2]->setSpriteClip(src_rect);
|
|
|
|
// Time Stopper (desplazamiento 3/60 = 0.05s)
|
|
src_rect.y = Item::HEIGHT * (static_cast<int>((elapsed_time_ + 0.05F) / SPRITE_ANIMATION_CYCLE_S) % 2);
|
|
sprites_[3]->setSpriteClip(src_rect);
|
|
|
|
// Coffee (sin desplazamiento)
|
|
src_rect.y = Item::HEIGHT * (static_cast<int>(elapsed_time_ / SPRITE_ANIMATION_CYCLE_S) % 2);
|
|
sprites_[4]->setSpriteClip(src_rect);
|
|
}
|
|
|
|
// Rellena la textura de texto
|
|
void Instructions::fillTexture() {
|
|
const int X_OFFSET = Item::WIDTH + 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 = Item::HEIGHT + 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 length = 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_->length(desc);
|
|
length = L > length ? L : length;
|
|
}
|
|
const int ANCHOR_ITEM = (param.game.width - (length + X_OFFSET)) / 2;
|
|
|
|
auto caption_style = Text::Style(Text::CENTER | Text::COLOR | Text::SHADOW, Colors::ORANGE_TEXT, Colors::SHADOW_TEXT);
|
|
auto text_style = Text::Style(Text::CENTER | Text::COLOR | Text::SHADOW, Colors::NO_COLOR_MOD, Colors::SHADOW_TEXT);
|
|
|
|
// Escribe el texto de las instrucciones
|
|
text_->writeStyle(param.game.game_area.center_x, FIRST_LINE, Lang::getText("[INSTRUCTIONS] 01"), caption_style);
|
|
|
|
const int ANCHOR1 = FIRST_LINE + SPACE_POST_HEADER;
|
|
text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + (SPACE_BETWEEN_LINES * 0), Lang::getText("[INSTRUCTIONS] 02"), text_style);
|
|
text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + (SPACE_BETWEEN_LINES * 1), Lang::getText("[INSTRUCTIONS] 03"), text_style);
|
|
text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + SPACE_NEW_PARAGRAPH + (SPACE_BETWEEN_LINES * 2), Lang::getText("[INSTRUCTIONS] 04"), text_style);
|
|
text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + SPACE_NEW_PARAGRAPH + (SPACE_BETWEEN_LINES * 3), Lang::getText("[INSTRUCTIONS] 05"), text_style);
|
|
|
|
// Escribe el texto de los objetos y sus puntos
|
|
const int ANCHOR2 = ANCHOR1 + SPACE_PRE_HEADER + SPACE_NEW_PARAGRAPH + (SPACE_BETWEEN_LINES * 3);
|
|
text_->writeStyle(param.game.game_area.center_x, ANCHOR2, Lang::getText("[INSTRUCTIONS] 06"), caption_style);
|
|
|
|
const int ANCHOR3 = ANCHOR2 + SPACE_POST_HEADER;
|
|
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 0), Lang::getText("[INSTRUCTIONS] 07"), Colors::SHADOW_TEXT);
|
|
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 1), Lang::getText("[INSTRUCTIONS] 08"), Colors::SHADOW_TEXT);
|
|
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 2), Lang::getText("[INSTRUCTIONS] 09"), Colors::SHADOW_TEXT);
|
|
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 3), Lang::getText("[INSTRUCTIONS] 10"), Colors::SHADOW_TEXT);
|
|
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 4), Lang::getText("[INSTRUCTIONS] 11"), Colors::SHADOW_TEXT);
|
|
|
|
// Deja el renderizador como estaba
|
|
SDL_SetRenderTarget(renderer_, temp);
|
|
|
|
// Da valor a la variable
|
|
sprite_pos_.x = ANCHOR_ITEM;
|
|
sprite_pos_.y = ANCHOR3 - ((Item::HEIGHT - 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(float delta_time) {
|
|
elapsed_time_ += delta_time; // Incrementa el tiempo transcurrido
|
|
|
|
static auto* const SCREEN = Screen::get();
|
|
SCREEN->update(delta_time); // Actualiza el objeto screen
|
|
Audio::update(); // Actualiza el objeto audio
|
|
|
|
updateSprites(); // Actualiza los sprites
|
|
updateBackbuffer(delta_time); // Gestiona la textura con los graficos
|
|
tiled_bg_->update(delta_time); // Actualiza el mosaico de fondo
|
|
fade_->update(delta_time); // Actualiza el objeto "fade"
|
|
fillBackbuffer(); // Rellena el backbuffer
|
|
}
|
|
|
|
// Pinta en pantalla
|
|
void Instructions::render() {
|
|
static auto* const SCREEN = Screen::get();
|
|
|
|
SCREEN->start(); // Prepara para empezar a dibujar en la textura de juego
|
|
SCREEN->clean(); // Limpia la pantalla
|
|
|
|
tiled_bg_->render(); // Dibuja el mosacico de fondo
|
|
|
|
// 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(); // Renderiza el fundido
|
|
|
|
SCREEN->render(); // Vuelca el contenido del renderizador en pantalla
|
|
}
|
|
|
|
// Comprueba los eventos
|
|
void Instructions::checkEvents() {
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event)) {
|
|
GlobalEvents::handle(event);
|
|
}
|
|
}
|
|
|
|
// Comprueba las entradas
|
|
void Instructions::checkInput() {
|
|
Input::get()->update();
|
|
GlobalInputs::check();
|
|
}
|
|
|
|
// Calcula el tiempo transcurrido desde el último frame
|
|
auto Instructions::calculateDeltaTime() -> float {
|
|
const Uint64 CURRENT_TIME = SDL_GetTicks();
|
|
const float DELTA_TIME = static_cast<float>(CURRENT_TIME - last_time_) / 1000.0F; // Convertir ms a segundos
|
|
last_time_ = CURRENT_TIME;
|
|
return DELTA_TIME;
|
|
}
|
|
|
|
// Bucle para la pantalla de instrucciones
|
|
void Instructions::run() {
|
|
last_time_ = SDL_GetTicks();
|
|
Audio::get()->playMusic("title.ogg");
|
|
|
|
while (Section::name == Section::Name::INSTRUCTIONS) {
|
|
const float DELTA_TIME = calculateDeltaTime();
|
|
|
|
checkInput();
|
|
update(DELTA_TIME);
|
|
checkEvents(); // Tiene que ir antes del render
|
|
render();
|
|
}
|
|
}
|
|
|
|
// Método para inicializar las líneas
|
|
auto Instructions::initializeLines(int height, float line_delay) -> std::vector<Line> {
|
|
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
|
|
float delay = y * line_delay; // Retraso progresivo basado en la línea
|
|
lines.emplace_back(y, 0.0F, direction, delay);
|
|
}
|
|
return lines;
|
|
}
|
|
|
|
// Método para mover las líneas con suavizado (usando delta_time puro)
|
|
auto Instructions::moveLines(std::vector<Line>& lines, int width, float duration, float delta_time) -> bool {
|
|
bool all_lines_off_screen = true;
|
|
|
|
for (auto& line : lines) {
|
|
// Verificar si la línea ha superado su tiempo de retraso
|
|
if (!line.started) {
|
|
line.delay_time -= delta_time;
|
|
if (line.delay_time <= 0.0F) {
|
|
line.started = true;
|
|
line.accumulated_time = 0.0F;
|
|
} else {
|
|
all_lines_off_screen = false; // Aún hay líneas esperando para empezar
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Si la línea ya ha completado su movimiento, saltarla
|
|
if (line.accumulated_time >= duration) {
|
|
continue; // Esta línea ya terminó
|
|
}
|
|
|
|
// Acumular tiempo y calcular posición
|
|
line.accumulated_time += delta_time;
|
|
|
|
if (line.accumulated_time >= duration) {
|
|
// La línea ha completado su movimiento
|
|
line.accumulated_time = duration;
|
|
} else {
|
|
// La línea aún se está moviendo
|
|
all_lines_off_screen = false;
|
|
}
|
|
|
|
// Calcular posición con suavizado
|
|
float t = line.accumulated_time / duration;
|
|
float smooth_factor = easeInOutQuint(t);
|
|
line.x = line.direction * smooth_factor * width;
|
|
}
|
|
|
|
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 src_rect = {0, static_cast<float>(line.y), 320, 1};
|
|
SDL_FRect dst_rect = {static_cast<float>(line.x), static_cast<float>(line.y), 320, 1};
|
|
SDL_RenderTexture(renderer, texture, &src_rect, &dst_rect);
|
|
}
|
|
}
|
|
|
|
// Gestiona la textura con los graficos
|
|
void Instructions::updateBackbuffer(float delta_time) {
|
|
// Establece la ventana del backbuffer usando velocidad en pixels por segundo
|
|
// El scroll comienza desde (param.game.height + 100) y desciende a 0
|
|
// IMPORTANTE: Se redondea a entero para evitar deformaciones de textura causadas por sub-pixel rendering
|
|
float scroll_offset = elapsed_time_ * SCROLL_SPEED_PPS;
|
|
view_.y = std::round(std::max(0.0F, (param.game.height + 100.0F) - scroll_offset));
|
|
|
|
// Verifica si view_.y == 0 y gestiona el temporizador
|
|
if (view_.y == 0.0F) {
|
|
if (!start_delay_triggered_) {
|
|
// Activa el temporizador si no ha sido activado
|
|
start_delay_triggered_ = true;
|
|
start_delay_timer_ = 0.0F;
|
|
} else {
|
|
start_delay_timer_ += delta_time;
|
|
if (start_delay_timer_ >= START_DELAY_S) {
|
|
// Han pasado los segundos de retraso, mover líneas
|
|
all_lines_off_screen_ = moveLines(lines_, 320, LINE_MOVE_DURATION_S, delta_time);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Comprueba si todas las líneas han terminado
|
|
if (all_lines_off_screen_) {
|
|
Section::name = Section::Name::TITLE;
|
|
Section::options = Section::Options::TITLE_1;
|
|
}
|
|
} |