Compare commits

...

4 Commits

Author SHA1 Message Date
6f31751d42 aproximant-se 2026-04-03 18:24:58 +02:00
43de2c0b35 treballant en la visió original de la intro 2026-04-03 18:10:27 +02:00
1ca9d0c01b arreglos menors en intro 2026-04-03 17:46:03 +02:00
90d5e6c3cc afegit debug.yaml 2026-04-03 17:37:51 +02:00
11 changed files with 624 additions and 268 deletions

View File

@@ -81,6 +81,7 @@ set(APP_SOURCES
# --- Sprites y Gráficos ---
source/animated_sprite.cpp
source/background.cpp
source/card_sprite.cpp
source/fade.cpp
source/moving_sprite.cpp
source/path_sprite.cpp

View File

@@ -94,6 +94,7 @@ APP_SOURCES := \
source/sections/title.cpp \
source/animated_sprite.cpp \
source/background.cpp \
source/card_sprite.cpp \
source/fade.cpp \
source/moving_sprite.cpp \
source/path_sprite.cpp \

228
source/card_sprite.cpp Normal file
View File

@@ -0,0 +1,228 @@
#include "card_sprite.hpp"
#include <algorithm> // Para std::clamp
#include <functional> // Para function
#include <utility> // Para move
#include "texture.hpp" // Para Texture
#include "utils.hpp" // Para easeOutBounce, easeOutCubic
// Constructor
CardSprite::CardSprite(std::shared_ptr<Texture> texture)
: MovingSprite(std::move(texture)),
entry_easing_(easeOutBounce) {}
// Inicia la animación de entrada (solo si está en IDLE)
void CardSprite::enable() {
if (state_ != CardState::IDLE) {
return;
}
state_ = CardState::ENTERING;
entry_elapsed_ = 0.0F;
first_touch_ = false;
// Posición inicial (borde de pantalla)
setPos(entry_start_x_, entry_start_y_);
// Zoom inicial grande (como si estuviera cerca de la cámara)
horizontal_zoom_ = start_zoom_;
vertical_zoom_ = start_zoom_;
// Ángulo inicial
rotate_.angle = start_angle_;
rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F};
shadow_visible_ = true;
}
// Inicia la animación de salida (solo si está en LANDED)
void CardSprite::startExit() {
if (state_ != CardState::LANDED) {
return;
}
state_ = CardState::EXITING;
shadow_visible_ = false;
// Velocidad y aceleración de salida
vx_ = exit_vx_;
vy_ = exit_vy_;
ax_ = exit_ax_;
ay_ = exit_ay_;
// Rotación continua
rotate_.enabled = true;
rotate_.amount = exit_rotate_amount_;
rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F};
}
// Actualiza según el estado
void CardSprite::update(float delta_time) {
switch (state_) {
case CardState::ENTERING:
updateEntering(delta_time);
break;
case CardState::EXITING:
updateExiting(delta_time);
break;
default:
break;
}
}
// Animación de entrada: interpola posición, zoom y ángulo
void CardSprite::updateEntering(float delta_time) {
entry_elapsed_ += delta_time;
float progress = std::clamp(entry_elapsed_ / entry_duration_s_, 0.0F, 1.0F);
double eased = entry_easing_(static_cast<double>(progress));
// Zoom: de start_zoom_ a 1.0 con rebote
auto current_zoom = static_cast<float>(start_zoom_ + (1.0 - start_zoom_) * eased);
horizontal_zoom_ = current_zoom;
vertical_zoom_ = current_zoom;
// Ángulo: de start_angle_ a 0 con rebote
rotate_.angle = start_angle_ * (1.0 - eased);
// Posición: de entry_start a landing con easing suave (sin rebote)
// Usamos easeOutCubic para que el desplazamiento sea fluido
double pos_eased = easeOutCubic(static_cast<double>(progress));
auto current_x = static_cast<float>(entry_start_x_ + (landing_x_ - entry_start_x_) * pos_eased);
auto current_y = static_cast<float>(entry_start_y_ + (landing_y_ - entry_start_y_) * pos_eased);
setPos(current_x, current_y);
// Detecta el primer toque (cuando el easing alcanza ~1.0 por primera vez)
if (!first_touch_ && eased >= FIRST_TOUCH_THRESHOLD) {
first_touch_ = true;
}
// Transición a LANDED cuando termina la animación completa
if (progress >= 1.0F) {
horizontal_zoom_ = 1.0F;
vertical_zoom_ = 1.0F;
rotate_.angle = 0.0;
setPos(landing_x_, landing_y_);
state_ = CardState::LANDED;
first_touch_ = true;
}
}
// Animación de salida: movimiento + rotación continua
void CardSprite::updateExiting(float delta_time) {
move(delta_time);
rotate(delta_time);
if (isOffScreen()) {
state_ = CardState::FINISHED;
}
}
// Renderiza el sprite y su sombra
void CardSprite::render() {
if (state_ == CardState::IDLE || state_ == CardState::FINISHED) {
return;
}
// Sombra primero (debajo de la tarjeta)
if (shadow_visible_ && shadow_texture_) {
renderShadow();
}
// Tarjeta
MovingSprite::render();
}
// Renderiza la sombra con efecto de perspectiva
// Cuanto más alta la tarjeta (zoom grande), la sombra es más pequeña y más separada.
// Cuando la tarjeta está en la mesa (zoom=1.0), la sombra tiene tamaño real y offset base.
void CardSprite::renderShadow() {
// La sombra siempre está en la mesa: su escala es inversamente proporcional al zoom
float shadow_zoom = 1.0F / horizontal_zoom_;
// El offset aumenta con la altura (más lejos de la tarjeta cuanto más alta)
float scaled_offset_x = shadow_offset_x_ * horizontal_zoom_;
float scaled_offset_y = shadow_offset_y_ * horizontal_zoom_;
shadow_texture_->render(
pos_.x + scaled_offset_x,
pos_.y + scaled_offset_y,
&sprite_clip_,
shadow_zoom,
shadow_zoom,
rotate_.angle,
&rotate_.center,
flip_);
}
// Comprueba si el sprite está fuera de pantalla
auto CardSprite::isOffScreen() const -> bool {
float effective_width = pos_.w * horizontal_zoom_;
float effective_height = pos_.h * vertical_zoom_;
return (pos_.x + effective_width < -OFF_SCREEN_MARGIN ||
pos_.x > screen_width_ + OFF_SCREEN_MARGIN ||
pos_.y + effective_height < -OFF_SCREEN_MARGIN ||
pos_.y > screen_height_ + OFF_SCREEN_MARGIN);
}
// --- Consultas de estado ---
auto CardSprite::hasLanded() const -> bool {
return state_ == CardState::LANDED || state_ == CardState::EXITING || state_ == CardState::FINISHED;
}
auto CardSprite::hasFirstTouch() const -> bool {
return first_touch_;
}
auto CardSprite::hasFinished() const -> bool {
return state_ == CardState::FINISHED;
}
auto CardSprite::isExiting() const -> bool {
return state_ == CardState::EXITING;
}
auto CardSprite::getState() const -> CardState {
return state_;
}
// --- Configuración ---
void CardSprite::setEntryParams(float start_zoom, double start_angle, float duration_s, std::function<double(double)> easing) {
start_zoom_ = start_zoom;
start_angle_ = start_angle;
entry_duration_s_ = duration_s;
entry_easing_ = std::move(easing);
}
void CardSprite::setEntryPosition(float start_x, float start_y) {
entry_start_x_ = start_x;
entry_start_y_ = start_y;
}
void CardSprite::setLandingPosition(float x, float y) {
landing_x_ = x;
landing_y_ = y;
}
void CardSprite::setExitParams(float vx, float vy, float ax, float ay, double rotate_amount) {
exit_vx_ = vx;
exit_vy_ = vy;
exit_ax_ = ax;
exit_ay_ = ay;
exit_rotate_amount_ = rotate_amount;
}
void CardSprite::setShadowTexture(std::shared_ptr<Texture> texture) {
shadow_texture_ = std::move(texture);
}
void CardSprite::setShadowOffset(float offset_x, float offset_y) {
shadow_offset_x_ = offset_x;
shadow_offset_y_ = offset_y;
}
void CardSprite::setScreenBounds(float width, float height) {
screen_width_ = width;
screen_height_ = height;
}

105
source/card_sprite.hpp Normal file
View File

@@ -0,0 +1,105 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FPoint
#include <functional> // Para function
#include <memory> // Para shared_ptr
#include "moving_sprite.hpp" // Para MovingSprite
class Texture;
// --- Estados de la tarjeta ---
enum class CardState {
IDLE, // No activada todavía
ENTERING, // Animación de entrada (zoom + rotación + desplazamiento con rebote)
LANDED, // En reposo sobre la mesa
EXITING, // Saliendo de pantalla girando
FINISHED, // Fuera de pantalla
};
// --- Clase CardSprite: tarjeta animada con zoom, rotación y sombra integrada ---
//
// Simula una tarjeta lanzada sobre una mesa desde un borde de la pantalla.
// Durante la entrada, interpola posición, zoom y rotación con easing (rebote).
// Durante la salida, se desplaza fuera de pantalla girando, sin sombra.
class CardSprite : public MovingSprite {
public:
explicit CardSprite(std::shared_ptr<Texture> texture);
~CardSprite() override = default;
// --- Ciclo principal ---
void update(float delta_time) override;
void render() override;
// --- Control de estado ---
void enable(); // Inicia la animación de entrada
void startExit(); // Inicia la animación de salida
// --- Consultas de estado ---
[[nodiscard]] auto hasLanded() const -> bool; // ¿Ha aterrizado definitivamente?
[[nodiscard]] auto hasFirstTouch() const -> bool; // ¿Ha tocado la mesa por primera vez? (primer rebote)
[[nodiscard]] auto hasFinished() const -> bool; // ¿Ha terminado completamente?
[[nodiscard]] auto isExiting() const -> bool; // ¿Está saliendo de pantalla?
[[nodiscard]] auto getState() const -> CardState; // Estado actual
// --- Configuración de entrada ---
void setEntryParams(float start_zoom, double start_angle, float duration_s, std::function<double(double)> easing);
void setEntryPosition(float start_x, float start_y); // Posición inicial (borde de pantalla)
void setLandingPosition(float x, float y); // Posición final centrada
// --- Configuración de salida ---
void setExitParams(float vx, float vy, float ax, float ay, double rotate_amount);
// --- Sombra ---
void setShadowTexture(std::shared_ptr<Texture> texture);
void setShadowOffset(float offset_x, float offset_y);
// --- Limites de pantalla (para detectar salida) ---
void setScreenBounds(float width, float height);
private:
// --- Estado ---
CardState state_ = CardState::IDLE;
bool first_touch_ = false; // Primer contacto con la mesa (eased >= umbral)
// --- Umbral para detectar el primer toque ---
static constexpr double FIRST_TOUCH_THRESHOLD = 0.98;
// --- Parámetros de entrada ---
float start_zoom_ = 1.8F;
double start_angle_ = 15.0;
float entry_duration_s_ = 1.5F;
float entry_elapsed_ = 0.0F;
std::function<double(double)> entry_easing_;
float entry_start_x_ = 0.0F; // Posición inicial X (borde)
float entry_start_y_ = 0.0F; // Posición inicial Y (borde)
float landing_x_ = 0.0F;
float landing_y_ = 0.0F;
// --- Parámetros de salida ---
float exit_vx_ = 0.0F;
float exit_vy_ = 0.0F;
float exit_ax_ = 0.0F;
float exit_ay_ = 0.0F;
double exit_rotate_amount_ = 0.0;
// --- Sombra ---
std::shared_ptr<Texture> shadow_texture_;
float shadow_offset_x_ = 8.0F;
float shadow_offset_y_ = 8.0F;
bool shadow_visible_ = true;
// --- Límites de pantalla ---
float screen_width_ = 320.0F;
float screen_height_ = 240.0F;
// --- Margen fuera de pantalla para considerar FINISHED ---
static constexpr float OFF_SCREEN_MARGIN = 50.0F;
// --- Métodos internos ---
void updateEntering(float delta_time);
void updateExiting(float delta_time);
void renderShadow();
[[nodiscard]] auto isOffScreen() const -> bool;
};

View File

@@ -6,6 +6,7 @@
#include <cstdlib> // Para srand, exit, rand, EXIT_FAILURE
#include <ctime> // Para time
#include <filesystem> // Para path, absolute
#include <fstream> // Para ifstream, ofstream
#include <iostream> // Para basic_ostream, operator<<, cerr
#include <memory> // Para make_unique, unique_ptr
#include <span> // Para span
@@ -14,6 +15,7 @@
#include "asset.hpp" // Para Asset
#include "audio.hpp" // Para Audio
#include "external/fkyaml_node.hpp" // Para fkyaml::node
#include "input.hpp" // Para Input
#include "lang.hpp" // Para setLanguage
#include "manage_hiscore_table.hpp" // Para ManageHiScoreTable
@@ -40,16 +42,6 @@
// Constructor
Director::Director(int argc, std::span<char*> argv) {
#ifdef RECORDING
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P;
#elif _DEBUG
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P;
#else // NORMAL GAME
Section::name = Section::Name::LOGO;
Section::options = Section::Options::NONE;
#endif
Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
// Establece el nivel de prioridad de la categoría de registro
@@ -68,6 +60,17 @@ Director::Director(int argc, std::span<char*> argv) {
createSystemFolder("jailgames");
createSystemFolder("jailgames/coffee_crisis_arcade_edition");
// Establecer sección inicial según modo de compilación
#ifdef RECORDING
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P;
#elif _DEBUG
loadDebugConfig();
#else
Section::name = Section::Name::LOGO;
Section::options = Section::Options::NONE;
#endif
init();
}
@@ -124,9 +127,9 @@ void Director::init() {
Logger::section("INIT RESOURCES");
#ifdef _DEBUG
Resource::init(Resource::LoadingMode::PRELOAD); // Inicializa el sistema de gestión de recursos
Resource::init(debug_config.resource_loading == "lazy" ? Resource::LoadingMode::LAZY_LOAD : Resource::LoadingMode::PRELOAD);
#else
Resource::init(Resource::LoadingMode::PRELOAD); // Inicializa el sistema de gestión de recursos
Resource::init(Resource::LoadingMode::PRELOAD);
#endif
ServiceMenu::init(); // Inicializa el menú de servicio
Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones
@@ -224,6 +227,103 @@ void Director::checkProgramArguments(int argc, std::span<char*> argv) {
}
}
// Carga debug.yaml desde la carpeta del sistema (solo en _DEBUG)
void Director::loadDebugConfig() {
const std::string DEBUG_FILE = system_folder_ + "/debug.yaml";
std::ifstream file(DEBUG_FILE);
if (!file.good()) {
// Crear fichero por defecto
std::ofstream out(DEBUG_FILE);
if (out.is_open()) {
out << "# Coffee Crisis Arcade Edition - Debug Configuration\n";
out << "# This file is only read in DEBUG builds.\n";
out << "#\n";
out << "# initial_section: logo, intro, title, game, credits, instructions, hiscore\n";
out << "# initial_options: none, 1p, 2p, both\n";
out << "# initial_stage: 0-based stage index (only when section is game)\n";
out << "# show_render_info: show FPS/driver/preset overlay\n";
out << "# resource_loading: preload, lazy\n";
out << "\n";
out << "initial_section: game\n";
out << "initial_options: 1p\n";
out << "initial_stage: 0\n";
out << "show_render_info: true\n";
out << "resource_loading: preload\n";
out.close();
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Debug config created: %s", DEBUG_FILE.c_str());
}
// Usar defaults de DebugConfig
} else {
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
file.close();
try {
auto yaml = fkyaml::node::deserialize(content);
if (yaml.contains("initial_section")) {
try {
debug_config.initial_section = yaml["initial_section"].get_value<std::string>();
} catch (...) {}
}
if (yaml.contains("initial_options")) {
try {
debug_config.initial_options = yaml["initial_options"].get_value<std::string>();
} catch (...) {}
}
if (yaml.contains("initial_stage")) {
try {
debug_config.initial_stage = yaml["initial_stage"].get_value<int>();
} catch (...) {}
}
if (yaml.contains("show_render_info")) {
try {
debug_config.show_render_info = yaml["show_render_info"].get_value<bool>();
} catch (...) {}
}
if (yaml.contains("resource_loading")) {
try {
debug_config.resource_loading = yaml["resource_loading"].get_value<std::string>();
} catch (...) {}
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Debug config loaded: section=%s options=%s stage=%d", debug_config.initial_section.c_str(), debug_config.initial_options.c_str(), debug_config.initial_stage);
} catch (...) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Error parsing debug.yaml, using defaults");
}
}
// Mapear strings a enums
const auto& sec = debug_config.initial_section;
if (sec == "logo") {
Section::name = Section::Name::LOGO;
} else if (sec == "intro") {
Section::name = Section::Name::INTRO;
} else if (sec == "title") {
Section::name = Section::Name::TITLE;
} else if (sec == "game") {
Section::name = Section::Name::GAME;
} else if (sec == "credits") {
Section::name = Section::Name::CREDITS;
} else if (sec == "instructions") {
Section::name = Section::Name::INSTRUCTIONS;
} else if (sec == "hiscore") {
Section::name = Section::Name::HI_SCORE_TABLE;
} else {
Section::name = Section::Name::GAME;
}
const auto& opt = debug_config.initial_options;
if (opt == "none") {
Section::options = Section::Options::NONE;
} else if (opt == "1p") {
Section::options = Section::Options::GAME_PLAY_1P;
} else if (opt == "2p") {
Section::options = Section::Options::GAME_PLAY_2P;
} else if (opt == "both") {
Section::options = Section::Options::GAME_PLAY_BOTH;
} else {
Section::options = Section::Options::GAME_PLAY_1P;
}
}
// Crea la carpeta del sistema donde guardar datos
void Director::createSystemFolder(const std::string& folder) {
auto result = SystemUtils::createApplicationFolder(folder, system_folder_);
@@ -272,7 +372,7 @@ void Director::runGame() {
}
#ifdef _DEBUG
constexpr int CURRENT_STAGE = 0;
const int CURRENT_STAGE = debug_config.initial_stage;
#else
constexpr int CURRENT_STAGE = 0;
#endif

View File

@@ -17,6 +17,21 @@ class Director {
// --- Bucle principal ---
static auto run() -> int;
// --- Debug config (accesible desde otras clases) ---
struct DebugConfig {
std::string initial_section;
std::string initial_options;
int initial_stage = 0;
bool show_render_info = true;
std::string resource_loading;
DebugConfig()
: initial_section("game"),
initial_options("1p"),
resource_loading("preload") {}
};
static inline DebugConfig debug_config;
private:
// --- Variables internas ---
std::string executable_path_; // Ruta del ejecutable
@@ -30,6 +45,7 @@ class Director {
static void loadParams(); // Carga los parámetros del programa
static void loadScoreFile(); // Carga el fichero de puntuaciones
void createSystemFolder(const std::string& folder); // Crea la carpeta del sistema
void loadDebugConfig(); // Carga debug.yaml (solo en _DEBUG)
// --- Gestión de entrada y archivos ---
void loadAssets(); // Crea el índice de archivos disponibles

View File

@@ -9,6 +9,7 @@
#include <vector> // Para vector
#include "asset.hpp" // Para Asset
#include "director.hpp" // Para Director::debug_config
#include "mouse.hpp" // Para updateCursorVisibility
#include "options.hpp" // Para Video, video, Window, window
#include "param.hpp" // Para Param, param, ParamGame, ParamDebug
@@ -60,7 +61,7 @@ Screen::Screen()
#ifdef _DEBUG
debug_info_.text = text_;
setDebugInfoEnabled(true);
setDebugInfoEnabled(Director::debug_config.show_render_info);
#endif
// Renderizar una vez la textura vacía para que tenga contenido válido antes de inicializar los shaders (evita pantalla negra)

View File

@@ -3,25 +3,24 @@
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderDrawColor, SDL_FRect, SDL_RenderFillRect, SDL_GetRenderTarget, SDL_RenderClear, SDL_RenderRect, SDL_SetRenderTarget, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_PollEvent, SDL_RenderTexture, SDL_TextureAccess, SDL_Event, Uint64
#include <array> // Para array
#include <functional> // Para function
#include <string> // Para basic_string, string
#include <utility> // Para move
#include "audio.hpp" // Para Audio
#include "card_sprite.hpp" // Para CardSprite
#include "color.hpp" // Para Color
#include "global_events.hpp" // Para handle
#include "global_inputs.hpp" // Para check
#include "input.hpp" // Para Input
#include "lang.hpp" // Para getText
#include "param.hpp" // Para Param, param, ParamGame, ParamIntro, ParamTitle
#include "path_sprite.hpp" // Para PathSprite, PathType
#include "resource.hpp" // Para Resource
#include "screen.hpp" // Para Screen
#include "section.hpp" // Para Name, name, Options, options
#include "text.hpp" // Para Text
#include "texture.hpp" // Para Texture
#include "tiled_bg.hpp" // Para TiledBG, TiledBGMode
#include "utils.hpp" // Para Zone, easeInOutExpo, easeInElastic, easeOutBounce, easeOutElastic, easeOutQuad, easeOutQuint
#include "utils.hpp" // Para easeOutBounce
#include "writer.hpp" // Para Writer
// Constructor
@@ -31,7 +30,7 @@ Intro::Intro()
Section::name = Section::Name::INTRO;
Section::options = Section::Options::NONE;
// Inicializa las imagens
// Inicializa las tarjetas
initSprites();
// Inicializa los textos
@@ -58,6 +57,11 @@ void Intro::checkInput() {
// Actualiza las escenas de la intro
void Intro::updateScenes() {
// Cuando la tarjeta actual toca la mesa por primera vez, la anterior sale despedida
if (scene_ > 0 && card_sprites_.at(scene_)->hasFirstTouch()) {
card_sprites_.at(scene_ - 1)->startExit();
}
switch (scene_) {
case 0:
updateScene0();
@@ -83,25 +87,25 @@ void Intro::updateScenes() {
}
void Intro::updateScene0() {
// Primera imagen - UPV
enableCardAndShadow(0);
// Primera imagen - UPV: activa la tarjeta
card_sprites_.at(0)->enable();
// Primer texto de la primera imagen
if (card_sprites_.at(0)->hasFinished() && !texts_.at(0)->hasFinished()) {
// Primer texto cuando aterriza
if (card_sprites_.at(0)->hasLanded() && !texts_.at(0)->hasFinished()) {
texts_.at(0)->setEnabled(true);
}
// Segundo texto de la primera imagen
// Segundo texto
if (texts_.at(0)->hasFinished() && !texts_.at(1)->hasFinished()) {
switchText(0, 1);
}
// Tercer texto de la primera imagen
// Tercer texto
if (texts_.at(1)->hasFinished() && !texts_.at(2)->hasFinished()) {
switchText(1, 2);
}
// Fin de la primera escena
// Fin de la primera escena: la tarjeta sale despedida
if (texts_.at(2)->hasFinished()) {
texts_.at(2)->setEnabled(false);
scene_++;
@@ -110,10 +114,10 @@ void Intro::updateScene0() {
void Intro::updateScene1() {
// Segunda imagen - Máquina
enableCardAndShadow(1);
card_sprites_.at(1)->enable();
// Primer texto de la segunda imagen
if (card_sprites_.at(1)->hasFinished() && !texts_.at(3)->hasFinished()) {
// Texto cuando aterriza
if (card_sprites_.at(1)->hasLanded() && !texts_.at(3)->hasFinished()) {
texts_.at(3)->setEnabled(true);
}
@@ -125,34 +129,34 @@ void Intro::updateScene1() {
}
void Intro::updateScene2() {
// Tercera imagen junto con primer texto - GRITO
// Tercera imagen - GRITO: tarjeta y texto a la vez
card_sprites_.at(2)->enable();
if (!texts_.at(4)->hasFinished()) {
enableCardAndShadow(2);
texts_.at(4)->setEnabled(true);
}
// Fin de la tercera escena
if (card_sprites_.at(2)->hasFinished() && texts_.at(4)->hasFinished()) {
if (card_sprites_.at(2)->hasLanded() && texts_.at(4)->hasFinished()) {
texts_.at(4)->setEnabled(false);
scene_++;
}
}
void Intro::updateScene3() {
// Cuarta imagen junto con primer texto - Reflexión
enableCardAndShadow(3);
// Cuarta imagen - Reflexión
card_sprites_.at(3)->enable();
if (!texts_.at(5)->hasFinished()) {
texts_.at(5)->setEnabled(true);
}
// Segundo texto de la cuarta imagen
// Segundo texto
if (texts_.at(5)->hasFinished() && !texts_.at(6)->hasFinished()) {
switchText(5, 6);
}
// Fin de la cuarta escena
if (card_sprites_.at(3)->hasFinished() && texts_.at(6)->hasFinished()) {
if (card_sprites_.at(3)->hasLanded() && texts_.at(6)->hasFinished()) {
texts_.at(6)->setEnabled(false);
scene_++;
}
@@ -160,23 +164,22 @@ void Intro::updateScene3() {
void Intro::updateScene4() {
// Quinta imagen - Patada
enableCardAndShadow(4);
card_sprites_.at(4)->enable();
// Primer texto de la quinta imagen
if (!texts_.at(7)->hasFinished()) {
texts_.at(7)->setEnabled(true);
}
// Fin de la quinta escena
if (card_sprites_.at(4)->hasFinished() && texts_.at(7)->hasFinished()) {
if (card_sprites_.at(4)->hasLanded() && texts_.at(7)->hasFinished()) {
texts_.at(7)->setEnabled(false);
scene_++;
}
}
void Intro::updateScene5() {
// Sexta imagen junto con texto - Globos de café
enableCardAndShadow(5);
// Sexta imagen - Globos de café
card_sprites_.at(5)->enable();
if (!texts_.at(8)->hasFinished()) {
texts_.at(8)->setEnabled(true);
@@ -187,19 +190,14 @@ void Intro::updateScene5() {
texts_.at(8)->setEnabled(false);
}
// Acaba la ultima imagen
if (card_sprites_.at(5)->hasFinished() && texts_.at(8)->hasFinished()) {
// Última tarjeta: sale "como si se la llevara el viento" y transición a POST
if (card_sprites_.at(5)->hasLanded() && texts_.at(8)->hasFinished()) {
card_sprites_.at(5)->startExit();
state_ = State::POST;
state_start_time_ = SDL_GetTicks() / 1000.0F;
}
}
// Helper methods to reduce code duplication
void Intro::enableCardAndShadow(int index) {
card_sprites_.at(index)->enable();
shadow_sprites_.at(index)->enable();
}
void Intro::switchText(int from_index, int to_index) {
texts_.at(from_index)->setEnabled(false);
texts_.at(to_index)->setEnabled(true);
@@ -221,6 +219,7 @@ void Intro::update(float delta_time) {
break;
case State::POST:
updateSprites(delta_time); // La última tarjeta puede estar saliendo durante POST
updatePostState();
break;
}
@@ -243,6 +242,7 @@ void Intro::render() {
break;
}
case State::POST:
renderSprites(); // La última tarjeta puede estar saliendo
break;
}
@@ -272,7 +272,7 @@ void Intro::run() {
}
}
// Inicializa las imagens
// Inicializa las tarjetas
void Intro::initSprites() {
// Listado de imagenes a usar
const std::array<std::string, 6> TEXTURE_LIST = {
@@ -291,24 +291,21 @@ void Intro::initSprites() {
const float CARD_WIDTH = texture->getWidth() + (BORDER * 2);
const float CARD_HEIGHT = texture->getHeight() + (BORDER * 2);
// Crea las texturas para las tarjetas
// Crea las texturas para las tarjetas (imagen con marco)
std::vector<std::shared_ptr<Texture>> card_textures;
for (int i = 0; i < TOTAL_SPRITES; ++i) {
// Crea la textura
auto card_texture = std::make_unique<Texture>(Screen::get()->getRenderer());
card_texture->createBlank(CARD_WIDTH, CARD_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
card_texture->setBlendMode(SDL_BLENDMODE_BLEND);
// Apuntamos el renderizador a la textura
auto* temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
card_texture->setAsRenderTarget(Screen::get()->getRenderer());
// Limpia la textura
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0);
SDL_RenderClear(Screen::get()->getRenderer());
// Pone color en el marco de la textura
// Marco de la tarjeta
auto color = param.intro.card_color;
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), color.r, color.g, color.b, color.a);
SDL_FRect rect1 = {.x = 1, .y = 0, .w = CARD_WIDTH - 2, .h = CARD_HEIGHT};
@@ -316,85 +313,103 @@ void Intro::initSprites() {
SDL_RenderRect(Screen::get()->getRenderer(), &rect1);
SDL_RenderRect(Screen::get()->getRenderer(), &rect2);
// Copia la textura con la imagen dentro del marco
// Imagen dentro del marco
SDL_FRect dest = {.x = BORDER, .y = BORDER, .w = CARD_WIDTH - (BORDER * 2), .h = CARD_HEIGHT - (BORDER * 2)};
SDL_RenderTexture(Screen::get()->getRenderer(), Resource::get()->getTexture(TEXTURE_LIST.at(i))->getSDLTexture(), nullptr, &dest);
// Deja el renderizador como estaba y añade la textura a la lista
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
card_textures.push_back(std::move(card_texture));
}
// Inicializa los sprites para las tarjetas
for (int i = 0; i < TOTAL_SPRITES; ++i) {
auto sprite = std::make_unique<PathSprite>(card_textures.at(i));
sprite->setWidth(CARD_WIDTH);
sprite->setHeight(CARD_HEIGHT);
sprite->setSpriteClip(0, 0, CARD_WIDTH, CARD_HEIGHT);
card_sprites_.push_back(std::move(sprite));
}
const float X_DEST = param.game.game_area.center_x - (CARD_WIDTH / 2);
const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4);
card_sprites_.at(0)->addPath(-CARD_WIDTH - CARD_OFFSET_MARGIN, X_DEST, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_NORMAL, easeInOutExpo, 0.0F);
card_sprites_.at(1)->addPath(param.game.width, X_DEST, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_NORMAL, easeOutBounce, 0.0F);
card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0.0F);
card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0.0F);
card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0.0F);
card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG_S);
card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0.0F);
// Constantes
const float DESP = SHADOW_OFFSET;
const float SHADOW_SPRITE_WIDTH = CARD_WIDTH;
const float SHADOW_SPRITE_HEIGHT = CARD_HEIGHT;
// Crea la textura para las sombras de las tarjetas
// Crea la textura de sombra (compartida entre todas las tarjetas)
auto shadow_texture = std::make_shared<Texture>(Screen::get()->getRenderer());
shadow_texture->createBlank(SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
shadow_texture->createBlank(CARD_WIDTH, CARD_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
shadow_texture->setBlendMode(SDL_BLENDMODE_BLEND);
// Apuntamos el renderizador a la textura
auto* temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
shadow_texture->setAsRenderTarget(Screen::get()->getRenderer());
// Limpia la textura
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0);
SDL_RenderClear(Screen::get()->getRenderer());
// Dibuja la sombra sobre la textura
auto shadow_color = param.intro.shadow_color;
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), shadow_color.r, shadow_color.g, shadow_color.b, Color::MAX_ALPHA_VALUE);
SDL_FRect rect1 = {.x = 1, .y = 0, .w = SHADOW_SPRITE_WIDTH - 2, .h = SHADOW_SPRITE_HEIGHT};
SDL_FRect rect2 = {.x = 0, .y = 1, .w = SHADOW_SPRITE_WIDTH, .h = SHADOW_SPRITE_HEIGHT - 2};
SDL_RenderFillRect(Screen::get()->getRenderer(), &rect1);
SDL_RenderFillRect(Screen::get()->getRenderer(), &rect2);
SDL_FRect shadow_rect1 = {.x = 1, .y = 0, .w = CARD_WIDTH - 2, .h = CARD_HEIGHT};
SDL_FRect shadow_rect2 = {.x = 0, .y = 1, .w = CARD_WIDTH, .h = CARD_HEIGHT - 2};
SDL_RenderFillRect(Screen::get()->getRenderer(), &shadow_rect1);
SDL_RenderFillRect(Screen::get()->getRenderer(), &shadow_rect2);
// Deja el renderizador como estaba y añade la textura a la lista
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
shadow_texture->setAlpha(shadow_color.a);
// Inicializa los sprites para la sombras usando la texturas con la sombra
// Posición de aterrizaje (centro de la zona de juego)
const float X_DEST = param.game.game_area.center_x - (CARD_WIDTH / 2);
const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4);
// Configuración por tarjeta: posición de entrada, ángulo, salida
// Cada tarjeta viene de un borde diferente (gente alrededor de una mesa lanzando cartas al centro)
struct CardConfig {
float entry_x; // Posición inicial X
float entry_y; // Posición inicial Y
double entry_angle; // Ángulo de entrada
float exit_vx; // Velocidad de salida X
float exit_vy; // Velocidad de salida Y
float exit_ax; // Aceleración de salida X
float exit_ay; // Aceleración de salida Y
double exit_rotation; // Velocidad de rotación de salida
};
const float W = param.game.width;
const float H = param.game.height;
const float S = CARD_EXIT_SPEED;
const float A = CARD_EXIT_ACCEL;
const double R = CARD_EXIT_ROTATION;
const CardConfig CARD_CONFIGS[] = {
// 0: Entra desde la izquierda. La 1 entra desde la derecha → sale empujada hacia la izquierda
{-CARD_WIDTH, Y_DEST - 20.0F, CARD_ANGLE_0, -S, S * 0.1F, -A, 0.0F, -R},
// 1: Entra desde la derecha. La 2 entra desde arriba → sale empujada hacia abajo
{W + CARD_WIDTH, Y_DEST + 15.0F, CARD_ANGLE_1, S * 0.15F, S, 0.0F, A, R * 1.1},
// 2: Entra desde arriba. La 3 entra desde abajo → sale empujada hacia arriba
{X_DEST + 30.0F, -CARD_HEIGHT, CARD_ANGLE_2, -S * 0.15F, -S, 0.0F, -A, -R * 0.9},
// 3: Entra desde abajo. La 4 entra desde arriba-izquierda → sale empujada hacia abajo-derecha
{X_DEST - 25.0F, H + CARD_HEIGHT, CARD_ANGLE_3, S * 0.8F, S * 0.6F, A * 0.5F, A * 0.4F, R},
// 4: Entra desde arriba-izquierda. La 5 entra desde derecha-abajo → sale empujada hacia arriba-izquierda
{-CARD_WIDTH * 0.5F, -CARD_HEIGHT, CARD_ANGLE_4, -S * 0.7F, -S * 0.5F, -A * 0.5F, -A * 0.3F, -R * 1.2},
// 5: Entra desde la derecha-abajo. Última: sale hacia la izquierda suave (viento)
{W + CARD_WIDTH, H * 0.6F, CARD_ANGLE_5, -S * 0.6F, -S * 0.1F, -A * 0.5F, 0.0F, -R * 0.7},
};
// Inicializa los CardSprites
for (int i = 0; i < TOTAL_SPRITES; ++i) {
auto shadow_color = param.intro.shadow_color;
auto sprite = std::make_unique<PathSprite>(shadow_texture);
sprite->setWidth(SHADOW_SPRITE_WIDTH);
sprite->setHeight(SHADOW_SPRITE_HEIGHT);
sprite->setSpriteClip(0, 0, SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT);
sprite->getTexture()->setAlpha(shadow_color.a);
shadow_sprites_.push_back(std::move(sprite));
auto card = std::make_unique<CardSprite>(card_textures.at(i));
card->setWidth(CARD_WIDTH);
card->setHeight(CARD_HEIGHT);
card->setSpriteClip(0, 0, CARD_WIDTH, CARD_HEIGHT);
const auto& cfg = CARD_CONFIGS[i];
// Posición de aterrizaje (centro)
card->setLandingPosition(X_DEST, Y_DEST);
// Posición de entrada (borde de pantalla)
card->setEntryPosition(cfg.entry_x, cfg.entry_y);
// Parámetros de entrada: zoom, ángulo, duración, easing
card->setEntryParams(CARD_START_ZOOM, cfg.entry_angle, CARD_ENTRY_DURATION_S, easeOutBounce);
// Parámetros de salida
card->setExitParams(cfg.exit_vx, cfg.exit_vy, cfg.exit_ax, cfg.exit_ay, cfg.exit_rotation);
// Sombra
card->setShadowTexture(shadow_texture);
card->setShadowOffset(SHADOW_OFFSET, SHADOW_OFFSET);
// Límites de pantalla
card->setScreenBounds(param.game.width, param.game.height);
card_sprites_.push_back(std::move(card));
}
const float S_X_DEST = X_DEST + DESP;
const float S_Y_DEST = Y_DEST + DESP;
shadow_sprites_.at(0)->addPath(param.game.height + CARD_OFFSET_MARGIN, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_NORMAL, easeInOutExpo, 0.0F);
shadow_sprites_.at(1)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_NORMAL, easeOutBounce, 0.0F);
shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0.0F);
shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0.0F);
shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0.0F);
shadow_sprites_.at(5)->addPath(param.game.width, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG_S);
shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0.0F);
}
// Inicializa los textos
@@ -420,7 +435,7 @@ void Intro::initTexts() {
// Fins que un desaprensiu...
texts_.at(2)->setCaption(Lang::getText("[INTRO] 3"));
texts_.at(2)->setSpeedS(TEXT_SPEED_FAST);
texts_.at(2)->setSpeedS(TEXT_SPEED_SLOW);
// HEY! ME ANE A FERME UN CORTAET...
texts_.at(3)->setCaption(Lang::getText("[INTRO] 4"));
@@ -428,23 +443,23 @@ void Intro::initTexts() {
// UAAAAAAAAAAAAA!!!
texts_.at(4)->setCaption(Lang::getText("[INTRO] 5"));
texts_.at(4)->setSpeedS(TEXT_SPEED_VERY_SLOW);
texts_.at(4)->setSpeedS(TEXT_SPEED_ULTRA_FAST);
// Espera un moment...
texts_.at(5)->setCaption(Lang::getText("[INTRO] 6"));
texts_.at(5)->setSpeedS(TEXT_SPEED_VERY_FAST);
texts_.at(5)->setSpeedS(TEXT_SPEED_VERY_SLOW);
// Si resulta que no tinc solt!
texts_.at(6)->setCaption(Lang::getText("[INTRO] 7"));
texts_.at(6)->setSpeedS(TEXT_SPEED_SLOW);
texts_.at(6)->setSpeedS(TEXT_SPEED_VERY_FAST);
// MERDA DE MAQUINA!
texts_.at(7)->setCaption(Lang::getText("[INTRO] 8"));
texts_.at(7)->setSpeedS(TEXT_SPEED_MEDIUM_SLOW);
texts_.at(7)->setSpeedS(TEXT_SPEED_FAST);
// Blop... blop... blop...
texts_.at(8)->setCaption(Lang::getText("[INTRO] 9"));
texts_.at(8)->setSpeedS(TEXT_SPEED_ULTRA_FAST);
texts_.at(8)->setSpeedS(TEXT_SPEED_ULTRA_SLOW);
for (auto& text : texts_) {
text->center(param.game.game_area.center_x);
@@ -456,10 +471,6 @@ void Intro::updateSprites(float delta_time) {
for (auto& sprite : card_sprites_) {
sprite->update(delta_time);
}
for (auto& sprite : shadow_sprites_) {
sprite->update(delta_time);
}
}
// Actualiza los textos
@@ -469,10 +480,11 @@ void Intro::updateTexts(float delta_time) {
}
}
// Dibuja los sprites
// Dibuja los sprites (todas las tarjetas activas, para que convivan la saliente y la entrante)
void Intro::renderSprites() {
shadow_sprites_.at(scene_)->render();
card_sprites_.at(scene_)->render();
for (auto& card : card_sprites_) {
card->render();
}
}
// Dibuja los textos
@@ -525,4 +537,4 @@ void Intro::renderTextRect() {
static SDL_FRect rect_ = {.x = 0.0F, .y = param.game.height - param.intro.text_distance_from_bottom - HEIGHT, .w = param.game.width, .h = HEIGHT * 3};
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), param.intro.shadow_color.r, param.intro.shadow_color.g, param.intro.shadow_color.b, param.intro.shadow_color.a);
SDL_RenderFillRect(Screen::get()->getRenderer(), &rect_);
}
}

View File

@@ -5,9 +5,9 @@
#include <memory> // Para unique_ptr
#include <vector> // Para vector
#include "card_sprite.hpp" // Para CardSprite
#include "color.hpp" // Para Color
#include "param.hpp" // Para Param, ParamIntro, param
#include "path_sprite.hpp" // Para PathSprite
#include "tiled_bg.hpp" // Para TiledBG
#include "writer.hpp" // Para Writer
@@ -18,14 +18,11 @@
//
// Funcionalidades principales:
// • Sistema de escenas secuencial: 6 escenas con transiciones automáticas
// • Animaciones de tarjetas: efectos de entrada con diferentes tipos de easing
// • Animaciones de tarjetas: efecto de lanzamiento sobre mesa con zoom, rotación y rebote
// • Texto narrativo: velocidades de escritura configurables por escena
// • Efectos visuales: sombras, bordes y transiciones de color
// • Audio sincronizado: música de fondo durante toda la secuencia
// • Estado POST: transición suave hacia el menú principal
//
// Todas las duraciones y velocidades están configuradas mediante constantes
// para facilitar el ajuste fino de la experiencia cinematográfica.
class Intro {
public:
@@ -38,35 +35,39 @@ class Intro {
private:
// --- Constantes de tiempo (en segundos) ---
static constexpr float TEXT_DISPLAY_DURATION_S = 3.0F; // Duración de visualización de texto (180 frames a 60fps)
static constexpr float POST_BG_STOP_DELAY_S = 1.0F; // Retraso antes de detener el fondo
static constexpr float POST_END_DELAY_S = 1.0F; // Retraso antes de finalizar intro
static constexpr float TEXT_DISPLAY_DURATION_S = 3.0F; // Duración de visualización de texto
static constexpr float POST_BG_STOP_DELAY_S = 1.0F; // Retraso antes de detener el fondo
static constexpr float POST_END_DELAY_S = 1.0F; // Retraso antes de finalizar intro
// --- Constantes de layout ---
static constexpr float CARD_BORDER_SIZE = 2.0F; // Tamaño del borde de tarjetas
static constexpr float SHADOW_OFFSET = 8.0F; // Desplazamiento de sombra
static constexpr float TILED_BG_SPEED = 18.0F; // Velocidad del fondo mosaico (pixels/segundo)
static constexpr int TEXT_KERNING = -2; // Espaciado entre caracteres
static constexpr int TEXT_KERNING = -2; // Espaciado entre caracteres
// --- Constantes de velocidades de texto (segundos entre caracteres) ---
static constexpr float TEXT_SPEED_NORMAL = 0.133F; // Velocidad normal (8 frames * 16.67ms = 133ms)
static constexpr float TEXT_SPEED_FAST = 0.2F; // Velocidad rápida (12 frames * 16.67ms = 200ms)
static constexpr float TEXT_SPEED_VERY_SLOW = 0.0167F; // Velocidad muy lenta (1 frame * 16.67ms = 16.7ms)
static constexpr float TEXT_SPEED_VERY_FAST = 0.267F; // Velocidad muy rápida (16 frames * 16.67ms = 267ms)
static constexpr float TEXT_SPEED_SLOW = 0.033F; // Velocidad lenta (2 frames * 16.67ms = 33ms)
static constexpr float TEXT_SPEED_MEDIUM_SLOW = 0.05F; // Velocidad medio-lenta (3 frames * 16.67ms = 50ms)
static constexpr float TEXT_SPEED_ULTRA_FAST = 0.333F; // Velocidad ultra rápida (20 frames * 16.67ms = 333ms)
// --- Constantes de velocidades de texto (segundos entre caracteres, menor = más rápido) ---
static constexpr float TEXT_SPEED_ULTRA_FAST = 0.0167F; // Ultra rápida (1 frame a 60fps)
static constexpr float TEXT_SPEED_VERY_FAST = 0.033F; // Muy rápida (2 frames a 60fps)
static constexpr float TEXT_SPEED_FAST = 0.05F; // Rápida (3 frames a 60fps)
static constexpr float TEXT_SPEED_NORMAL = 0.133F; // Normal (8 frames a 60fps)
static constexpr float TEXT_SPEED_SLOW = 0.2F; // Lenta (12 frames a 60fps)
static constexpr float TEXT_SPEED_VERY_SLOW = 0.267F; // Muy lenta (16 frames a 60fps)
static constexpr float TEXT_SPEED_ULTRA_SLOW = 0.333F; // Ultra lenta (20 frames a 60fps)
// --- Constantes de animaciones de tarjetas (duraciones en segundos) ---
static constexpr float CARD_ANIM_DURATION_NORMAL = 100.0F / 60.0F; // ≈ 1.6667 s
static constexpr float CARD_ANIM_DURATION_FAST = 40.0F / 60.0F; // ≈ 0.6667 s
static constexpr float CARD_ANIM_DURATION_MEDIUM = 70.0F / 60.0F; // ≈ 1.1667 s
static constexpr float CARD_ANIM_DURATION_SHORT = 80.0F / 60.0F; // ≈ 1.3333 s
static constexpr float CARD_ANIM_DURATION_SLOW = 250.0F / 60.0F; // ≈ 4.1667 s
static constexpr float CARD_ANIM_DURATION_VERY_SLOW = 300.0F / 60.0F; // ≈ 5.0000 s
// --- Constantes de animaciones de tarjetas ---
static constexpr float CARD_ENTRY_DURATION_S = 1.5F; // Duración de la animación de entrada
static constexpr float CARD_START_ZOOM = 1.8F; // Zoom inicial (como si estuviera cerca)
static constexpr float CARD_EXIT_SPEED = 400.0F; // Velocidad base de salida (pixels/s)
static constexpr float CARD_EXIT_ACCEL = 200.0F; // Aceleración de salida (pixels/s²)
static constexpr double CARD_EXIT_ROTATION = 450.0; // Velocidad de rotación en salida (grados/s)
static constexpr float CARD_ANIM_DELAY_LONG_S = 7.5F; // Retraso largo antes de animación
static constexpr float CARD_OFFSET_MARGIN = 10.0F; // Margen fuera de pantalla
// --- Ángulos iniciales de entrada por tarjeta (grados) ---
static constexpr double CARD_ANGLE_0 = 12.0;
static constexpr double CARD_ANGLE_1 = -15.0;
static constexpr double CARD_ANGLE_2 = 8.0;
static constexpr double CARD_ANGLE_3 = -10.0;
static constexpr double CARD_ANGLE_4 = 18.0;
static constexpr double CARD_ANGLE_5 = -7.0;
// --- Estados internos ---
enum class State {
@@ -80,17 +81,16 @@ class Intro {
};
// --- Objetos ---
std::vector<std::unique_ptr<PathSprite>> card_sprites_; // Vector con los sprites inteligentes para los dibujos de la intro
std::vector<std::unique_ptr<PathSprite>> shadow_sprites_; // Vector con los sprites inteligentes para las sombras
std::vector<std::unique_ptr<Writer>> texts_; // Textos de la intro
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
std::vector<std::unique_ptr<CardSprite>> card_sprites_; // Tarjetas animadas con sombra integrada
std::vector<std::unique_ptr<Writer>> texts_; // Textos de la intro
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
// --- Variables ---
Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
int scene_ = 0; // Indica qué escena está activa
State state_ = State::SCENES; // Estado principal de la intro
PostState post_state_ = PostState::STOP_BG; // Estado POST
float state_start_time_; // Tiempo de inicio del estado actual (segundos)
float state_start_time_ = 0.0F; // Tiempo de inicio del estado actual (segundos)
Color bg_color_ = param.intro.bg_color; // Color de fondo
// --- Métodos internos ---
@@ -99,13 +99,13 @@ class Intro {
static void checkInput(); // Comprueba las entradas
static void checkEvents(); // Comprueba los eventos
void updateScenes(); // Actualiza las escenas de la intro
void initSprites(); // Inicializa las imágenes
void initSprites(); // Inicializa las tarjetas
void initTexts(); // Inicializa los textos
void updateSprites(float delta_time); // Actualiza los sprites
void updateTexts(float delta_time); // Actualiza los textos
void renderSprites(); // Dibuja los sprites
void renderTexts(); // Dibuja los textos
static void renderTextRect(); // Dibuja el rectangulo de fondo del texto;
static void renderTextRect(); // Dibuja el rectángulo de fondo del texto
void updatePostState(); // Actualiza el estado POST
auto calculateDeltaTime() -> float; // Calcula el tiempo transcurrido desde el último frame
@@ -117,7 +117,6 @@ class Intro {
void updateScene4();
void updateScene5();
// --- Métodos auxiliares para reducir duplicación de código ---
void enableCardAndShadow(int index);
// --- Métodos auxiliares ---
void switchText(int from_index, int to_index);
};

View File

@@ -30,11 +30,6 @@
class Texture;
#ifdef _DEBUG
#include <iomanip> // Para operator<<, setfill, setw
#include <iostream> // Para basic_ostream, basic_ostream::operator<<, operator<<, cout, hex
#endif
// Constructor
Title::Title()
: text_(Resource::get()->getText("smb2_grad")),
@@ -43,12 +38,7 @@ Title::Title()
game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)),
mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))),
state_(State::LOGO_ANIMATING),
num_controllers_(Input::get()->getNumGamepads())
#ifdef _DEBUG
,
debug_color_(param.title.bg_color)
#endif
{
num_controllers_(Input::get()->getNumGamepads()) {
// Configura objetos
tiled_bg_->setColor(param.title.bg_color);
tiled_bg_->setSpeed(0.0F);
@@ -132,88 +122,9 @@ void Title::checkEvents() {
}
}
void Title::handleKeyDownEvent(const SDL_Event& event) {
#ifdef _DEBUG
bool is_repeat = static_cast<int>(event.key.repeat) == 1;
if (is_repeat) {
handleDebugColorKeys(event.key.key);
}
#endif
void Title::handleKeyDownEvent(const SDL_Event& /*event*/) {
}
#ifdef _DEBUG
void Title::handleDebugColorKeys(SDL_Keycode key) {
adjustColorComponent(key, debug_color_);
counter_time_ = 0.0F;
tiled_bg_->setColor(debug_color_);
printColorValue(debug_color_);
}
void Title::adjustColorComponent(SDL_Keycode key, Color& color) {
switch (key) {
case SDLK_A:
incrementColorComponent(color.r);
break;
case SDLK_Z:
decrementColorComponent(color.r);
break;
case SDLK_S:
incrementColorComponent(color.g);
break;
case SDLK_X:
decrementColorComponent(color.g);
break;
case SDLK_D:
incrementColorComponent(color.b);
break;
case SDLK_C:
decrementColorComponent(color.b);
break;
case SDLK_F:
incrementAllComponents(color);
break;
case SDLK_V:
decrementAllComponents(color);
break;
default:
break;
}
}
void Title::incrementColorComponent(uint8_t& component) {
if (component < 255) {
++component;
}
}
void Title::decrementColorComponent(uint8_t& component) {
if (component > 0) {
--component;
}
}
void Title::incrementAllComponents(Color& color) {
incrementColorComponent(color.r);
incrementColorComponent(color.g);
incrementColorComponent(color.b);
}
void Title::decrementAllComponents(Color& color) {
decrementColorComponent(color.r);
decrementColorComponent(color.g);
decrementColorComponent(color.b);
}
void Title::printColorValue(const Color& color) {
std::cout << "#"
<< std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(color.r)
<< std::setw(2) << std::setfill('0') << static_cast<int>(color.g)
<< std::setw(2) << std::setfill('0') << static_cast<int>(color.b)
<< '\n';
}
#endif
// Comprueba las entradas
void Title::checkInput() {
Input::get()->update();

View File

@@ -2,12 +2,10 @@
#include <SDL3/SDL.h> // Para SDL_Keycode, SDL_Event, Uint64
#include <cstdint> // Para uint8_t
#include <memory> // Para shared_ptr, unique_ptr
#include <string_view> // Para string_view
#include <vector> // Para vector
#include "color.hpp" // for Color
#include "player.hpp" // for Player
#include "section.hpp" // for Options, Name (ptr only)
@@ -33,7 +31,6 @@ namespace Options {
// • Efectos visuales: parpadeos, transiciones y efectos de fondo
// • Gestión de controles: soporte para teclado y múltiples gamepads
// • Timeouts automáticos: transición automática si no hay interacción
// • Debug de colores: herramientas de depuración para ajustes visuales
//
// La clase utiliza un sistema de tiempo basado en segundos para garantizar
// comportamiento consistente independientemente del framerate.
@@ -101,10 +98,6 @@ class Title {
bool player1_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 1
bool player2_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 2
#ifdef _DEBUG
Color debug_color_; // Color para depuración en modo debug
#endif
// --- Ciclo de vida del título ---
void update(float delta_time); // Actualiza las variables del objeto
auto calculateDeltaTime() -> float; // Calcula el tiempo transcurrido desde el último frame
@@ -142,15 +135,4 @@ class Title {
static void swapControllers(); // Intercambia la asignación de mandos a los jugadores
static void swapKeyboard(); // Intercambia el teclado de jugador
static void showControllers(); // Muestra información sobre los controles y los jugadores
// --- Depuración (solo en modo DEBUG) ---
#ifdef _DEBUG
void handleDebugColorKeys(SDL_Keycode key); // Maneja las teclas de depuración para colores
static void adjustColorComponent(SDL_Keycode key, Color& color); // Ajusta un componente del color según la tecla
static void incrementColorComponent(uint8_t& component); // Incrementa un componente de color
static void decrementColorComponent(uint8_t& component); // Decrementa un componente de color
static void incrementAllComponents(Color& color); // Incrementa todos los componentes del color
static void decrementAllComponents(Color& color); // Decrementa todos los componentes del color
static void printColorValue(const Color& color); // Imprime el valor actual del color en consola
#endif
};