Compare commits

37 Commits

Author SHA1 Message Date
49a3989ecf magic numbers: game.cpp 2025-09-19 09:11:10 +02:00
af7cb01ead magic numbers: game.cpp 2025-09-19 07:33:27 +02:00
5c82916650 magic numbers: game.cpp i player.cpp 2025-09-18 14:17:54 +02:00
0c0518adac magic numbers: game.cpp i player.cpp (en progres) 2025-09-18 13:15:43 +02:00
cb7b290818 magic numbers: game.cpp 2025-09-17 14:20:13 +02:00
ae30c9b34f magic numbers: title.cpp 2025-09-17 13:53:31 +02:00
9acd9aa631 magic numbers: intro.cpp 2025-09-17 13:25:16 +02:00
577510ff8c magic numbers: logo.cpp 2025-09-17 13:01:43 +02:00
66566913f6 delta-time: game.cpp (funciona pero va un poc massa ràpid) 2025-09-16 22:56:00 +02:00
3e6cc9dfab delta-time: explosions.cpp 2025-09-16 22:43:16 +02:00
a15e29344f delta-time: balloon.cpp
delta-time: balloon_manager.cpp
delta-time: credits.cpp
2025-09-16 22:38:48 +02:00
a96a17e11b delta-time: tabe.cpp 2025-09-16 20:29:35 +02:00
e0f6a424a9 delta-time: bullet.cpp 2025-09-16 20:26:22 +02:00
49e30f947a delta-time: title.cpp 2025-09-16 20:23:10 +02:00
470a07d28c delta-time: player.cpp 2025-09-16 19:21:44 +02:00
65716fce20 delta-time: tiled_bg.cpp 2025-09-16 17:35:03 +02:00
dfa66b0e95 delta-time: game_logo.cpp
delta-time: smart_sprite.cpp
2025-09-16 17:19:05 +02:00
3d9ffe356e Elimina .claude 2025-09-16 17:18:39 +02:00
19768cb72b delta-time: animated_sprite.cpp 2025-09-16 16:51:31 +02:00
26e0fd7247 delta-time: moving_sprite.cpp 2025-09-16 16:37:39 +02:00
e2fd470ad3 migració a delta time 2025-09-16 12:23:02 +02:00
a72ae0a5fc migració a delta time 2025-09-16 12:06:49 +02:00
7579594c22 migració a delta time 2025-09-16 10:48:22 +02:00
6c702e7e23 Logo: convertit per a usar delta time 2025-09-16 08:40:41 +02:00
fb9c78eb49 warning: balloon_manager.cpp varios ordres de inicialització mal 2025-08-27 10:27:18 +02:00
62f65cbd5a warning: balloon_manager.cpp varios ordres de inicialització mal 2025-08-27 10:19:30 +02:00
057d3dcfee bug fix: en la tabla de puntuacions, no apareix la estreleta al completar el joc amb 1CC
bug fix: posant nom al completar el joc, si "passes" el mostrar el nom, mata al jugador en lloc de fer que se'n vaja

Resol #92 i #98
2025-08-26 08:21:33 +02:00
c85336a4d0 fix: el tabe podia spawnejar-se en la seqüencia final.
Resol #89
2025-08-24 18:54:20 +02:00
e4702e4e24 bug fix: Audio::fadeOutMusic no ha de fer fade si la musica no sona 2025-08-24 17:39:07 +02:00
928335576c corregida la llista de inicialització en clang-format
creat Balloon::Config per a inicialitzar globos
2025-08-24 17:16:49 +02:00
fe950e6f17 bug fix: en el modo demo la powerball feia ruido.
Resol #84
2025-08-24 14:57:08 +02:00
6e81b6e60c Merge branch 'main' of https://gitea.sustancia.synology.me/jaildesigner/coffee_crisis_arcade_edition 2025-08-24 14:37:32 +02:00
74f6fe3501 Afegit outline al text 2x
corregit el marcador durant el Player::State::RECOVER
2025-08-24 14:37:30 +02:00
dfdb679054 Merge branch 'main' of https://gitea.sustancia.synology.me/JailDesigner/coffee_crisis_arcade_edition 2025-08-24 10:51:10 +02:00
26ed479306 delete 2025-08-24 10:51:07 +02:00
32e9da55ef Afegit so de service_menu_back
Retocats els audios de service menu
Afegit so a ENTER NAME
Arreglos visuals a ENTER NAME
2025-08-23 21:06:20 +02:00
610083578e style: eliminat soroll de drop item amb maquina de café
Quan ix una maquina de café ja no se sent el so de drop item
per a millorar l'experiència sonora del joc.

Resol #91
2025-08-23 20:05:16 +02:00
99 changed files with 3233 additions and 1651 deletions

View File

@@ -14,6 +14,8 @@ ContinuationIndentWidth: 4
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4
IndentWrappedFunctionNames: false IndentWrappedFunctionNames: false
Cpp11BracedListStyle: true Cpp11BracedListStyle: true
BreakConstructorInitializers: BeforeComma BreakConstructorInitializers: BeforeColon
AllowAllConstructorInitializersOnNextLine: false
PackConstructorInitializers: Never
AllowAllArgumentsOnNextLine: false AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
.vscode .vscode
.claude
build/ build/
data/config/config.txt data/config/config.txt
*.DS_Store *.DS_Store

View File

@@ -52,6 +52,7 @@ SOUND|${PREFIX}/data/sound/notify.wav
SOUND|${PREFIX}/data/sound/player_collision.wav SOUND|${PREFIX}/data/sound/player_collision.wav
SOUND|${PREFIX}/data/sound/power_ball_explosion.wav SOUND|${PREFIX}/data/sound/power_ball_explosion.wav
SOUND|${PREFIX}/data/sound/service_menu_adjust.wav SOUND|${PREFIX}/data/sound/service_menu_adjust.wav
SOUND|${PREFIX}/data/sound/service_menu_back.wav
SOUND|${PREFIX}/data/sound/service_menu_move.wav SOUND|${PREFIX}/data/sound/service_menu_move.wav
SOUND|${PREFIX}/data/sound/service_menu_select.wav SOUND|${PREFIX}/data/sound/service_menu_select.wav
SOUND|${PREFIX}/data/sound/stage_change.wav SOUND|${PREFIX}/data/sound/stage_change.wav
@@ -174,6 +175,7 @@ BITMAP|${PREFIX}/data/gfx/player/hit.png
# Fuentes de texto # Fuentes de texto
BITMAP|${PREFIX}/data/font/04b_25_2x.png BITMAP|${PREFIX}/data/font/04b_25_2x.png
BITMAP|${PREFIX}/data/font/04b_25_2x_white.png
BITMAP|${PREFIX}/data/font/04b_25_flat_2x.png BITMAP|${PREFIX}/data/font/04b_25_flat_2x.png
BITMAP|${PREFIX}/data/font/04b_25_flat.png BITMAP|${PREFIX}/data/font/04b_25_flat.png
BITMAP|${PREFIX}/data/font/04b_25_grey.png BITMAP|${PREFIX}/data/font/04b_25_grey.png

View File

@@ -10,8 +10,8 @@ game.play_area.rect.x 0 # Posición X de la zona jugable
game.play_area.rect.y 0 # Posición Y de la zona jugable game.play_area.rect.y 0 # Posición Y de la zona jugable
game.play_area.rect.w 320 # Ancho de la zona jugable game.play_area.rect.w 320 # Ancho de la zona jugable
game.play_area.rect.h 200 # Alto de la zona jugable game.play_area.rect.h 200 # Alto de la zona jugable
game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada game.name_entry_idle_time 10000 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida game.name_entry_total_time 60000 # Segundos totales para introducir el nombre al finalizar la partida
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop
@@ -40,7 +40,7 @@ scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás (
# --- TITLE --- # --- TITLE ---
title.press_start_position 180 # Posición Y del texto "Press Start" title.press_start_position 180 # Posición Y del texto "Press Start"
title.title_duration 800 # Duración de la pantalla de título (frames) title.title_duration 14000 # Duración de la pantalla de título (milisegundos)
title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition" title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition"
title.title_c_c_position 80 # Posición Y del título principal title.title_c_c_position 80 # Posición Y del título principal
title.bg_color 41526F # Color de fondo en la sección titulo title.bg_color 41526F # Color de fondo en la sección titulo

View File

@@ -10,8 +10,8 @@ game.play_area.rect.x 0 # Posición X de la zona jugable
game.play_area.rect.y 0 # Posición Y de la zona jugable game.play_area.rect.y 0 # Posición Y de la zona jugable
game.play_area.rect.w 320 # Ancho de la zona jugable game.play_area.rect.w 320 # Ancho de la zona jugable
game.play_area.rect.h 216 # Alto de la zona jugable game.play_area.rect.h 216 # Alto de la zona jugable
game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada game.name_entry_idle_time 10000 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida game.name_entry_total_time 60000 # Segundos totales para introducir el nombre al finalizar la partida
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop
@@ -40,7 +40,7 @@ scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás (
# --- TITLE --- # --- TITLE ---
title.press_start_position 180 # Posición Y del texto "Press Start" title.press_start_position 180 # Posición Y del texto "Press Start"
title.title_duration 800 # Duración de la pantalla de título (frames) title.title_duration 14000 # Duración de la pantalla de título (milisegundos)
title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition" title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition"
title.title_c_c_position 80 # Posición Y del título principal title.title_c_c_position 80 # Posición Y del título principal
title.bg_color 41526F # Color de fondo en la sección titulo title.bg_color 41526F # Color de fondo en la sección titulo

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 B

After

Width:  |  Height:  |  Size: 882 B

BIN
data/sound/click-2.wav Normal file

Binary file not shown.

BIN
data/sound/click-3.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -9,8 +9,8 @@
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include <utility> // Para pair #include <utility> // Para pair
#include "texture.h" // Para Texture
#include "resource_helper.h" // Para ResourceHelper #include "resource_helper.h" // Para ResourceHelper
#include "texture.h" // Para Texture
#include "utils.h" // Para printWithDots #include "utils.h" // Para printWithDots
// Carga las animaciones en un vector(Animations) desde un fichero // Carga las animaciones en un vector(Animations) desde un fichero
@@ -43,6 +43,10 @@ auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffe
std::vector<std::string> buffer; std::vector<std::string> buffer;
std::string line; std::string line;
while (std::getline(input_stream, line)) { while (std::getline(input_stream, line)) {
// Eliminar caracteres de retorno de carro (\r) al final de la línea
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
if (!line.empty()) { if (!line.empty()) {
buffer.push_back(line); buffer.push_back(line);
} }
@@ -82,7 +86,7 @@ auto AnimatedSprite::getAnimationIndex(const std::string& name) -> int {
return -1; return -1;
} }
// Calcula el frame correspondiente a la animación // Calcula el frame correspondiente a la animación (frame-based)
void AnimatedSprite::animate() { void AnimatedSprite::animate() {
if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) { if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) {
return; return;
@@ -112,6 +116,39 @@ void AnimatedSprite::animate() {
} }
} }
// Calcula el frame correspondiente a la animación (time-based)
void AnimatedSprite::animate(float deltaTime) {
if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) {
return;
}
// Convertir speed (frames) a tiempo: speed frames = speed * (1000ms/60fps) milisegundos
float frameTime = static_cast<float>(animations_[current_animation_].speed) * (1000.0f / 60.0f);
// Acumular tiempo transcurrido
animations_[current_animation_].time_accumulator += deltaTime;
// Verificar si es momento de cambiar frame
if (animations_[current_animation_].time_accumulator >= frameTime) {
animations_[current_animation_].time_accumulator -= frameTime;
animations_[current_animation_].current_frame++;
// Si alcanza el final de la animación
if (animations_[current_animation_].current_frame >= animations_[current_animation_].frames.size()) {
if (animations_[current_animation_].loop == -1) { // Si no hay loop, deja el último frame
animations_[current_animation_].current_frame = animations_[current_animation_].frames.size() - 1;
animations_[current_animation_].completed = true;
} else { // Si hay loop, vuelve al frame indicado
animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].current_frame = animations_[current_animation_].loop;
}
}
// Actualizar el sprite clip
updateSpriteClip();
}
}
// Comprueba si ha terminado la animación // Comprueba si ha terminado la animación
auto AnimatedSprite::animationIsCompleted() -> bool { auto AnimatedSprite::animationIsCompleted() -> bool {
return animations_[current_animation_].completed; return animations_[current_animation_].completed;
@@ -126,10 +163,12 @@ void AnimatedSprite::setCurrentAnimation(const std::string& name, bool reset) {
if (reset) { if (reset) {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0; animations_[current_animation_].counter = 0;
animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
} else { } else {
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size() - 1); animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size() - 1);
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
animations_[current_animation_].time_accumulator = animations_[OLD_ANIMATION].time_accumulator;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
} }
updateSpriteClip(); updateSpriteClip();
@@ -145,26 +184,35 @@ void AnimatedSprite::setCurrentAnimation(int index, bool reset) {
if (reset) { if (reset) {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0; animations_[current_animation_].counter = 0;
animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
} else { } else {
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size()); animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size());
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
animations_[current_animation_].time_accumulator = animations_[OLD_ANIMATION].time_accumulator;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
} }
updateSpriteClip(); updateSpriteClip();
} }
} }
// Actualiza las variables del objeto // Actualiza las variables del objeto (frame-based)
void AnimatedSprite::update() { void AnimatedSprite::update() {
animate(); animate();
MovingSprite::update(); MovingSprite::update();
} }
// Actualiza las variables del objeto (time-based)
void AnimatedSprite::update(float deltaTime) {
animate(deltaTime);
MovingSprite::update(deltaTime);
}
// Reinicia la animación // Reinicia la animación
void AnimatedSprite::resetAnimation() { void AnimatedSprite::resetAnimation() {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0; animations_[current_animation_].counter = 0;
animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
animations_[current_animation_].paused = false; animations_[current_animation_].paused = false;
updateSpriteClip(); updateSpriteClip();

View File

@@ -21,11 +21,12 @@ struct Animation {
std::string name; // Nombre de la animación std::string name; // Nombre de la animación
std::vector<SDL_FRect> frames; // Frames que componen la animación std::vector<SDL_FRect> frames; // Frames que componen la animación
int speed{DEFAULT_SPEED}; // Velocidad de reproducción int speed{DEFAULT_SPEED}; // Velocidad de reproducción (frame-based)
int loop{0}; // Frame de vuelta al terminar (-1 para no repetir) int loop{0}; // Frame de vuelta al terminar (-1 para no repetir)
bool completed{false}; // Indica si la animación ha finalizado bool completed{false}; // Indica si la animación ha finalizado
size_t current_frame{0}; // Frame actual en reproducción size_t current_frame{0}; // Frame actual en reproducción
int counter{0}; // Contador para la animación int counter{0}; // Contador para la animación (frame-based)
float time_accumulator{0.0f}; // Acumulador de tiempo para animaciones time-based
bool paused{false}; // La animación no avanza bool paused{false}; // La animación no avanza
Animation() = default; Animation() = default;
@@ -50,11 +51,13 @@ class AnimatedSprite : public MovingSprite {
// --- Constructores y destructor --- // --- Constructores y destructor ---
AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path); AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path);
AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations); AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations);
explicit AnimatedSprite(std::shared_ptr<Texture> texture) : MovingSprite(std::move(texture)) {} explicit AnimatedSprite(std::shared_ptr<Texture> texture)
: MovingSprite(std::move(texture)) {}
~AnimatedSprite() override = default; ~AnimatedSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
void update() override; // Actualiza la animación void update() override; // Actualiza la animación (frame-based)
void update(float deltaTime); // Actualiza la animación (time-based)
// --- Control de animaciones --- // --- Control de animaciones ---
void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre
@@ -77,7 +80,8 @@ class AnimatedSprite : public MovingSprite {
int current_animation_ = 0; // Índice de la animación activa int current_animation_ = 0; // Índice de la animación activa
// --- Métodos internos --- // --- Métodos internos ---
void animate(); // Calcula el frame correspondiente a la animación void animate(); // Calcula el frame correspondiente a la animación (frame-based)
void animate(float deltaTime); // Calcula el frame correspondiente a la animación (time-based)
void loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source); // Carga la animación desde un vector de cadenas void loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source); // Carga la animación desde un vector de cadenas
void processConfigLine(const std::string& line, AnimationConfig& config); // Procesa una línea de configuración void processConfigLine(const std::string& line, AnimationConfig& config); // Procesa una línea de configuración
void updateFrameCalculations(AnimationConfig& config); // Actualiza los cálculos basados en las dimensiones del frame void updateFrameCalculations(AnimationConfig& config); // Actualiza los cálculos basados en las dimensiones del frame

View File

@@ -8,8 +8,8 @@
#include <sstream> // Para basic_istringstream #include <sstream> // Para basic_istringstream
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include "utils.h" // Para getFileName
#include "resource_helper.h" // Para ResourceHelper #include "resource_helper.h" // Para ResourceHelper
#include "utils.h" // Para getFileName
// Singleton // Singleton
Asset *Asset::instance = nullptr; Asset *Asset::instance = nullptr;

View File

@@ -1,10 +1,10 @@
#pragma once #pragma once
#include <cstdint> // Para uint8_t
#include <string> // Para string #include <string> // Para string
#include <unordered_map> // Para unordered_map #include <unordered_map> // Para unordered_map
#include <utility> // Para move #include <utility> // Para move
#include <vector> // Para vector #include <vector> // Para vector
#include <cstdint> // Para uint8_t
// --- Clase Asset: gestor optimizado de recursos (singleton) --- // --- Clase Asset: gestor optimizado de recursos (singleton) ---
class Asset { class Asset {
@@ -47,7 +47,9 @@ class Asset {
bool required; // Indica si el archivo es obligatorio bool required; // Indica si el archivo es obligatorio
Item(std::string path, Type asset_type, bool is_required) Item(std::string path, Type asset_type, bool is_required)
: file(std::move(path)), type(asset_type), required(is_required) {} : file(std::move(path)),
type(asset_type),
required(is_required) {}
}; };
// --- Variables internas --- // --- Variables internas ---

View File

@@ -1,7 +1,8 @@
#include "asset_integrated.h" #include "asset_integrated.h"
#include <filesystem> #include <filesystem>
#include <iostream>
#include <fstream> #include <fstream>
#include <iostream>
bool AssetIntegrated::resource_pack_enabled_ = false; bool AssetIntegrated::resource_pack_enabled_ = false;
@@ -11,7 +12,7 @@ void AssetIntegrated::initWithResourcePack(const std::string &executable_path,
Asset::init(executable_path); Asset::init(executable_path);
// Inicializar ResourceLoader // Inicializar ResourceLoader
auto& loader = ResourceLoader::getInstance(); auto &loader = ResourceLoader::getInstance();
if (loader.initialize(resource_pack_path, true)) { if (loader.initialize(resource_pack_path, true)) {
resource_pack_enabled_ = true; resource_pack_enabled_ = true;
std::cout << "Asset system initialized with resource pack: " << resource_pack_path << std::endl; std::cout << "Asset system initialized with resource pack: " << resource_pack_path << std::endl;
@@ -24,7 +25,7 @@ void AssetIntegrated::initWithResourcePack(const std::string &executable_path,
std::vector<uint8_t> AssetIntegrated::loadFile(const std::string &filename) { std::vector<uint8_t> AssetIntegrated::loadFile(const std::string &filename) {
if (shouldUseResourcePack(filename) && resource_pack_enabled_) { if (shouldUseResourcePack(filename) && resource_pack_enabled_) {
// Intentar cargar del pack de recursos // Intentar cargar del pack de recursos
auto& loader = ResourceLoader::getInstance(); auto &loader = ResourceLoader::getInstance();
// Convertir ruta completa a ruta relativa para el pack // Convertir ruta completa a ruta relativa para el pack
std::string relativePath = filename; std::string relativePath = filename;
@@ -52,7 +53,7 @@ std::vector<uint8_t> AssetIntegrated::loadFile(const std::string &filename) {
file.seekg(0, std::ios::beg); file.seekg(0, std::ios::beg);
std::vector<uint8_t> data(fileSize); std::vector<uint8_t> data(fileSize);
if (!file.read(reinterpret_cast<char*>(data.data()), fileSize)) { if (!file.read(reinterpret_cast<char *>(data.data()), fileSize)) {
std::cerr << "Error: Could not read file: " << filename << std::endl; std::cerr << "Error: Could not read file: " << filename << std::endl;
return {}; return {};
} }
@@ -62,7 +63,7 @@ std::vector<uint8_t> AssetIntegrated::loadFile(const std::string &filename) {
bool AssetIntegrated::fileExists(const std::string &filename) const { bool AssetIntegrated::fileExists(const std::string &filename) const {
if (shouldUseResourcePack(filename) && resource_pack_enabled_) { if (shouldUseResourcePack(filename) && resource_pack_enabled_) {
auto& loader = ResourceLoader::getInstance(); auto &loader = ResourceLoader::getInstance();
// Convertir ruta completa a ruta relativa para el pack // Convertir ruta completa a ruta relativa para el pack
std::string relativePath = filename; std::string relativePath = filename;

View File

@@ -1,12 +1,13 @@
#pragma once #pragma once
#include <memory>
#include "asset.h" #include "asset.h"
#include "resource_loader.h" #include "resource_loader.h"
#include <memory>
// Extensión de Asset que integra ResourceLoader // Extensión de Asset que integra ResourceLoader
class AssetIntegrated : public Asset { class AssetIntegrated : public Asset {
public: public:
// Inicializa Asset con ResourceLoader // Inicializa Asset con ResourceLoader
static void initWithResourcePack(const std::string &executable_path, static void initWithResourcePack(const std::string &executable_path,
const std::string &resource_pack_path = "resources.pack"); const std::string &resource_pack_path = "resources.pack");
@@ -20,7 +21,7 @@ public:
// Obtiene la ruta completa para archivos del sistema/config // Obtiene la ruta completa para archivos del sistema/config
std::string getSystemPath(const std::string &filename) const; std::string getSystemPath(const std::string &filename) const;
private: private:
static bool resource_pack_enabled_; static bool resource_pack_enabled_;
// Determina si un archivo debe cargarse del pack o del filesystem // Determina si un archivo debe cargarse del pack o del filesystem

View File

@@ -100,7 +100,7 @@ void Audio::stopAllSounds() const {
// Realiza un fundido de salida de la música // Realiza un fundido de salida de la música
void Audio::fadeOutMusic(int milliseconds) const { void Audio::fadeOutMusic(int milliseconds) const {
if (music_enabled_) { if (music_enabled_ && music_.state == MusicState::PLAYING) {
#ifndef NO_AUDIO #ifndef NO_AUDIO
JA_FadeOutMusic(milliseconds); JA_FadeOutMusic(milliseconds);
#endif #endif

View File

@@ -75,11 +75,15 @@ class Audio {
bool loop; // Indica si la última pista de música se debe reproducir en bucle bool loop; // Indica si la última pista de música se debe reproducir en bucle
// Constructor para inicializar la música con valores predeterminados // Constructor para inicializar la música con valores predeterminados
Music() : state(MusicState::STOPPED), loop(false) {} Music()
: state(MusicState::STOPPED),
loop(false) {}
// Constructor para inicializar con valores específicos // Constructor para inicializar con valores específicos
Music(MusicState init_state, std::string init_name, bool init_loop) Music(MusicState init_state, std::string init_name, bool init_loop)
: state(init_state), name(std::move(init_name)), loop(init_loop) {} : state(init_state),
name(std::move(init_name)),
loop(init_loop) {}
}; };
// --- Variables de estado --- // --- Variables de estado ---

View File

@@ -126,7 +126,14 @@ void Background::initializeTextures() {
} }
// Actualiza la lógica del objeto // Actualiza la lógica del objeto
// Actualiza la lógica del objeto (compatibilidad)
void Background::update() { void Background::update() {
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f; // 16.67ms por frame a 60 FPS
update(FRAME_TIME_MS);
}
// Actualiza la lógica del objeto
void Background::update(float delta_time) {
// Actualiza la progresión y calcula transiciones // Actualiza la progresión y calcula transiciones
if (!manual_mode_) { if (!manual_mode_) {
updateProgression(); updateProgression();

View File

@@ -31,7 +31,8 @@ class Background {
~Background(); // Destructor ~Background(); // Destructor
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la lógica del objeto void update(); // Actualiza la lógica del objeto (compatibilidad)
void update(float delta_time); // Actualiza la lógica del objeto
void render(); // Dibuja el objeto void render(); // Dibuja el objeto
void reset(); // Reinicia la progresión void reset(); // Reinicia la progresión

View File

@@ -11,20 +11,21 @@
#include "texture.h" // Para Texture #include "texture.h" // Para Texture
// Constructor // Constructor
Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float speed, Uint16 creation_timer, SDL_FRect play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation) Balloon::Balloon(const Config& config)
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)), : sprite_(std::make_unique<AnimatedSprite>(config.texture, config.animation)),
x_(x), x_(config.x),
y_(y), y_(config.y),
vx_(vel_x), vx_(config.vel_x),
being_created_(creation_timer > 0), being_created_(config.creation_counter > 0),
invulnerable_(creation_timer > 0), invulnerable_(config.creation_counter > 0),
stopped_(creation_timer > 0), stopped_(config.creation_counter > 0),
creation_counter_(creation_timer), creation_counter_(config.creation_counter),
creation_counter_ini_(creation_timer), creation_counter_ini_(config.creation_counter),
type_(type), type_(config.type),
size_(size), size_(config.size),
speed_(speed), speed_(config.speed),
play_area_(play_area) { play_area_(config.play_area),
sound_(config.sound) {
switch (type_) { switch (type_) {
case Type::BALLOON: { case Type::BALLOON: {
vy_ = 0; vy_ = 0;
@@ -37,9 +38,8 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
power_ = POWER.at(INDEX); power_ = POWER.at(INDEX);
menace_ = MENACE.at(INDEX); menace_ = MENACE.at(INDEX);
score_ = SCORE.at(INDEX); score_ = SCORE.at(INDEX);
bouncing_sound_ = BOUNCING_SOUND.at(INDEX); sound_.bouncing_file = BOUNCING_SOUND.at(INDEX);
popping_sound_ = POPPING_SOUND.at(INDEX); sound_.popping_file = POPPING_SOUND.at(INDEX);
break; break;
} }
@@ -52,17 +52,16 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
power_ = POWER.at(INDEX); power_ = POWER.at(INDEX);
menace_ = MENACE.at(INDEX); menace_ = MENACE.at(INDEX);
score_ = SCORE.at(INDEX); score_ = SCORE.at(INDEX);
bouncing_sound_ = BOUNCING_SOUND.at(INDEX); sound_.bouncing_file = BOUNCING_SOUND.at(INDEX);
popping_sound_ = POPPING_SOUND.at(INDEX); sound_.popping_file = POPPING_SOUND.at(INDEX);
break; break;
} }
case Type::POWERBALL: { case Type::POWERBALL: {
constexpr int INDEX = 3; constexpr int INDEX = 3;
h_ = w_ = WIDTH.at(4); h_ = w_ = WIDTH.at(4);
bouncing_sound_ = BOUNCING_SOUND.at(3); sound_.bouncing_file = BOUNCING_SOUND.at(3);
popping_sound_ = "power_ball_explosion.wav"; sound_.popping_file = "power_ball_explosion.wav";
power_ = score_ = menace_ = 0; power_ = score_ = menace_ = 0;
vy_ = 0; vy_ = 0;
@@ -70,9 +69,8 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
gravity_ = param.balloon.settings.at(INDEX).grav; gravity_ = param.balloon.settings.at(INDEX).grav;
default_vy_ = param.balloon.settings.at(INDEX).vel; default_vy_ = param.balloon.settings.at(INDEX).vel;
sprite_->setRotate(creation_timer <= 0); sprite_->setRotate(config.creation_counter <= 0);
sprite_->setRotateAmount(vx_ > 0.0F ? 2.0 : -2.0); sprite_->setRotateAmount(vx_ > 0.0F ? 2.0 : -2.0);
break; break;
} }
@@ -137,7 +135,7 @@ void Balloon::render() {
} }
} }
// Actualiza la posición y estados del globo // Actualiza la posición y estados del globo (frame-based)
void Balloon::move() { void Balloon::move() {
if (isStopped()) { if (isStopped()) {
return; return;
@@ -148,6 +146,17 @@ void Balloon::move() {
applyGravity(); applyGravity();
} }
// Actualiza la posición y estados del globo (time-based)
void Balloon::move(float deltaTime) {
if (isStopped()) {
return;
}
handleHorizontalMovement(deltaTime);
handleVerticalMovement(deltaTime);
applyGravity(deltaTime);
}
void Balloon::handleHorizontalMovement() { void Balloon::handleHorizontalMovement() {
x_ += vx_ * speed_; x_ += vx_ * speed_;
@@ -160,6 +169,20 @@ void Balloon::handleHorizontalMovement() {
} }
} }
void Balloon::handleHorizontalMovement(float deltaTime) {
// Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
float frameFactor = deltaTime / (1000.0f / 60.0f);
x_ += vx_ * speed_ * frameFactor;
const int CLIP = 2;
const float MIN_X = play_area_.x - CLIP;
const float MAX_X = play_area_.x + play_area_.w - w_ + CLIP;
if (isOutOfHorizontalBounds(MIN_X, MAX_X)) {
handleHorizontalBounce(MIN_X, MAX_X);
}
}
void Balloon::handleVerticalMovement() { void Balloon::handleVerticalMovement() {
y_ += vy_ * speed_; y_ += vy_ * speed_;
@@ -170,6 +193,18 @@ void Balloon::handleVerticalMovement() {
handleBottomCollision(); handleBottomCollision();
} }
void Balloon::handleVerticalMovement(float deltaTime) {
// Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
float frameFactor = deltaTime / (1000.0f / 60.0f);
y_ += vy_ * speed_ * frameFactor;
if (shouldCheckTopCollision()) {
handleTopCollision();
}
handleBottomCollision();
}
auto Balloon::isOutOfHorizontalBounds(float min_x, float max_x) const -> bool { auto Balloon::isOutOfHorizontalBounds(float min_x, float max_x) const -> bool {
return x_ < min_x || x_ > max_x; return x_ < min_x || x_ > max_x;
} }
@@ -232,13 +267,31 @@ void Balloon::applyGravity() {
} }
} }
void Balloon::playBouncingSound() { void Balloon::applyGravity(float deltaTime) {
if (bouncing_sound_enabled_) { // Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
playSound(bouncing_sound_); float frameFactor = deltaTime / (1000.0f / 60.0f);
travel_y_ += speed_ * frameFactor;
if (travel_y_ >= 1.0F) {
travel_y_ -= 1.0F;
vy_ += gravity_;
} }
} }
// Actualiza al globo a su posicion, animación y controla los contadores void Balloon::playBouncingSound() {
if (sound_.enabled && sound_.bouncing_enabled) {
Audio::get()->playSound(sound_.bouncing_file);
}
}
void Balloon::playPoppingSound() {
if (sound_.enabled && sound_.poping_enabled) {
Audio::get()->playSound(sound_.popping_file);
}
}
// Actualiza al globo a su posicion, animación y controla los contadores (frame-based)
void Balloon::update() { void Balloon::update() {
move(); move();
updateState(); updateState();
@@ -249,7 +302,20 @@ void Balloon::update() {
++counter_; ++counter_;
} }
// Actualiza los estados del globo // Actualiza al globo a su posicion, animación y controla los contadores (time-based)
void Balloon::update(float deltaTime) {
move(deltaTime);
updateState(deltaTime);
updateBounceEffect();
shiftSprite();
shiftColliders();
sprite_->update(deltaTime);
// Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
float frameFactor = deltaTime / (1000.0f / 60.0f);
counter_ += frameFactor;
}
// Actualiza los estados del globo (frame-based)
void Balloon::updateState() { void Balloon::updateState() {
// Si se está creando // Si se está creando
if (isBeingCreated()) { if (isBeingCreated()) {
@@ -259,7 +325,7 @@ void Balloon::updateState() {
if (creation_counter_ > 0) { if (creation_counter_ > 0) {
// Desplaza lentamente el globo hacia abajo y hacia un lado // Desplaza lentamente el globo hacia abajo y hacia un lado
if (creation_counter_ % 10 == 0) { if (static_cast<int>(creation_counter_) % 10 == 0) {
y_++; y_++;
x_ += vx_; x_ += vx_;
@@ -286,6 +352,51 @@ void Balloon::updateState() {
} }
} }
// Actualiza los estados del globo (time-based)
void Balloon::updateState(float deltaTime) {
// Si se está creando
if (isBeingCreated()) {
// Actualiza el valor de las variables
stop();
setInvulnerable(true);
if (creation_counter_ > 0) {
// Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
float frameFactor = deltaTime / (1000.0f / 60.0f);
// Desplaza lentamente el globo hacia abajo y hacia un lado
// Cada 10 frames (aproximadamente cada 166ms a 60fps)
movement_accumulator_ += frameFactor;
if (movement_accumulator_ >= 10.0f) {
movement_accumulator_ -= 10.0f;
y_++;
x_ += vx_;
// Comprueba no se salga por los laterales
const int MIN_X = play_area_.x;
const int MAX_X = play_area_.w - w_;
if (x_ < MIN_X || x_ > MAX_X) {
// Corrige y cambia el sentido de la velocidad
x_ -= vx_;
vx_ = -vx_;
}
}
creation_counter_ -= frameFactor;
if (creation_counter_ < 0) creation_counter_ = 0;
}
else {
// El contador ha llegado a cero
being_created_ = false;
start();
setInvulnerable(false);
setAnimation();
}
}
}
// Establece la animación correspondiente al estado // Establece la animación correspondiente al estado
void Balloon::setAnimation() { void Balloon::setAnimation() {
std::string creating_animation; std::string creating_animation;
@@ -368,23 +479,8 @@ void Balloon::useNormalColor() {
setAnimation(); setAnimation();
} }
// Reproduce sonido
void Balloon::playSound(const std::string &name) const {
if (!sound_enabled_) {
return;
}
static auto *audio_ = Audio::get();
audio_->playSound(name);
}
// Explota el globo // Explota el globo
void Balloon::pop(bool should_sound) { void Balloon::pop(bool should_sound) {
if (should_sound) { if (should_sound) { playPoppingSound(); }
if (poping_sound_enabled_) {
playSound(popping_sound_);
}
}
enabled_ = false; enabled_ = false;
} }

View File

@@ -25,10 +25,16 @@ class Balloon {
static constexpr std::array<int, 5> WIDTH = {10, 16, 26, 48, 49}; static constexpr std::array<int, 5> WIDTH = {10, 16, 26, 48, 49};
static constexpr std::array<std::string_view, 4> BOUNCING_SOUND = { static constexpr std::array<std::string_view, 4> BOUNCING_SOUND = {
"balloon_bounce0.wav", "balloon_bounce1.wav", "balloon_bounce2.wav", "balloon_bounce3.wav"}; "balloon_bounce0.wav",
"balloon_bounce1.wav",
"balloon_bounce2.wav",
"balloon_bounce3.wav"};
static constexpr std::array<std::string_view, 4> POPPING_SOUND = { static constexpr std::array<std::string_view, 4> POPPING_SOUND = {
"balloon_pop0.wav", "balloon_pop1.wav", "balloon_pop2.wav", "balloon_pop3.wav"}; "balloon_pop0.wav",
"balloon_pop1.wav",
"balloon_pop2.wav",
"balloon_pop3.wav"};
static constexpr float VELX_POSITIVE = 0.7F; static constexpr float VELX_POSITIVE = 0.7F;
static constexpr float VELX_NEGATIVE = -0.7F; static constexpr float VELX_NEGATIVE = -0.7F;
@@ -52,25 +58,41 @@ class Balloon {
POWERBALL = 2, // Globo de poder POWERBALL = 2, // Globo de poder
}; };
// --- Estructura para manejo de sonido ---
struct Sound {
std::string bouncing_file; // Archivo de sonido al rebotar
std::string popping_file; // Archivo de sonido al explotar
bool bouncing_enabled = false; // Si debe sonar el globo al rebotar
bool poping_enabled = true; // Si debe sonar el globo al explotar
bool enabled = true; // Indica si los globos deben hacer algun sonido
};
// --- Estructura de configuración para inicialización ---
struct Config {
float x = 0.0F;
float y = 0.0F;
Type type = Type::BALLOON;
Size size = Size::EXTRALARGE;
float vel_x = VELX_POSITIVE;
float speed = SPEED.at(0);
Uint16 creation_counter = 0;
SDL_FRect play_area = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
std::shared_ptr<Texture> texture = nullptr;
std::vector<std::string> animation;
Sound sound;
};
// --- Constructores y destructor --- // --- Constructores y destructor ---
Balloon( Balloon(const Config& config);
float x,
float y,
Type type,
Size size,
float vel_x,
float speed,
Uint16 creation_timer,
SDL_FRect play_area,
const std::shared_ptr<Texture>& texture,
const std::vector<std::string>& animation);
~Balloon() = default; ~Balloon() = default;
// --- Métodos principales --- // --- Métodos principales ---
void alignTo(int x); // Centra el globo en la posición X void alignTo(int x); // Centra el globo en la posición X
void render(); // Pinta el globo en la pantalla void render(); // Pinta el globo en la pantalla
void move(); // Actualiza la posición y estados del globo void move(); // Actualiza la posición y estados del globo (frame-based)
void update(); // Actualiza el globo (posición, animación, contadores) void move(float deltaTime); // Actualiza la posición y estados del globo (time-based)
void update(); // Actualiza el globo (posición, animación, contadores) (frame-based)
void update(float deltaTime); // Actualiza el globo (posición, animación, contadores) (time-based)
void stop(); // Detiene el globo void stop(); // Detiene el globo
void start(); // Pone el globo en movimiento void start(); // Pone el globo en movimiento
void pop(bool should_sound = false); // Explota el globo void pop(bool should_sound = false); // Explota el globo
@@ -102,9 +124,9 @@ class Balloon {
void setVelY(float vel_y) { vy_ = vel_y; } void setVelY(float vel_y) { vy_ = vel_y; }
void setSpeed(float speed) { speed_ = speed; } void setSpeed(float speed) { speed_ = speed; }
void setInvulnerable(bool value) { invulnerable_ = value; } void setInvulnerable(bool value) { invulnerable_ = value; }
void setBouncingSound(bool value) { bouncing_sound_enabled_ = value; } void setBouncingSound(bool value) { sound_.bouncing_enabled = value; }
void setPoppingSound(bool value) { poping_sound_enabled_ = value; } void setPoppingSound(bool value) { sound_.poping_enabled = value; }
void setSound(bool value) { sound_enabled_ = value; } void setSound(bool value) { sound_.enabled = value; }
private: private:
// --- Estructura para el efecto de rebote --- // --- Estructura para el efecto de rebote ---
@@ -114,10 +136,28 @@ class Balloon {
// Tablas de valores predefinidos para el efecto de rebote // Tablas de valores predefinidos para el efecto de rebote
static constexpr std::array<float, BOUNCE_FRAMES> HORIZONTAL_ZOOM_VALUES = { static constexpr std::array<float, BOUNCE_FRAMES> HORIZONTAL_ZOOM_VALUES = {
1.10F, 1.05F, 1.00F, 0.95F, 0.90F, 0.95F, 1.00F, 1.02F, 1.05F, 1.02F}; 1.10F,
1.05F,
1.00F,
0.95F,
0.90F,
0.95F,
1.00F,
1.02F,
1.05F,
1.02F};
static constexpr std::array<float, BOUNCE_FRAMES> VERTICAL_ZOOM_VALUES = { static constexpr std::array<float, BOUNCE_FRAMES> VERTICAL_ZOOM_VALUES = {
0.90F, 0.95F, 1.00F, 1.05F, 1.10F, 1.05F, 1.00F, 0.98F, 0.95F, 0.98F}; 0.90F,
0.95F,
1.00F,
1.05F,
1.10F,
1.05F,
1.00F,
0.98F,
0.95F,
0.98F};
// Estado del efecto // Estado del efecto
bool enabled_ = false; // Si el efecto está activo bool enabled_ = false; // Si el efecto está activo
@@ -218,8 +258,8 @@ class Balloon {
bool stopped_; // Si el globo está parado bool stopped_; // Si el globo está parado
bool use_reversed_colors_ = false; // Si se usa el color alternativo bool use_reversed_colors_ = false; // Si se usa el color alternativo
Circle collider_; // Círculo de colisión Circle collider_; // Círculo de colisión
Uint16 creation_counter_; // Temporizador de creación float creation_counter_; // Temporizador de creación
Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación float creation_counter_ini_; // Valor inicial del temporizador de creación
Uint16 score_; // Puntos al destruir el globo Uint16 score_; // Puntos al destruir el globo
Type type_; // Tipo de globo Type type_; // Tipo de globo
Size size_; // Tamaño de globo Size size_; // Tamaño de globo
@@ -227,13 +267,10 @@ class Balloon {
Uint32 counter_ = 0; // Contador interno Uint32 counter_ = 0; // Contador interno
float travel_y_ = 1.0F; // Distancia a recorrer en Y antes de aplicar gravedad float travel_y_ = 1.0F; // Distancia a recorrer en Y antes de aplicar gravedad
float speed_; // Velocidad del globo float speed_; // Velocidad del globo
float movement_accumulator_ = 0.0f; // Acumulador para movimiento durante creación (deltaTime)
Uint8 power_; // Poder que alberga el globo Uint8 power_; // Poder que alberga el globo
SDL_FRect play_area_; // Zona de movimiento del globo SDL_FRect play_area_; // Zona de movimiento del globo
std::string bouncing_sound_; // Archivo de sonido al rebotar Sound sound_; // Configuración de sonido del globo
std::string popping_sound_; // Archivo de sonido al explotar
bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar
bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar
bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido
BounceEffect bounce_effect_; // Efecto de rebote BounceEffect bounce_effect_; // Efecto de rebote
// --- Posicionamiento y transformación --- // --- Posicionamiento y transformación ---
@@ -242,13 +279,16 @@ class Balloon {
// --- Animación y sonido --- // --- Animación y sonido ---
void setAnimation(); // Establece la animación correspondiente void setAnimation(); // Establece la animación correspondiente
void playSound(const std::string& name) const; // Reproduce un sonido por nombre
void playBouncingSound(); // Reproduce el sonido de rebote void playBouncingSound(); // Reproduce el sonido de rebote
void playPoppingSound(); // Reproduce el sonido de reventar
// --- Movimiento y física --- // --- Movimiento y física ---
void handleHorizontalMovement(); // Maneja el movimiento horizontal void handleHorizontalMovement(); // Maneja el movimiento horizontal (frame-based)
void handleVerticalMovement(); // Maneja el movimiento vertical void handleHorizontalMovement(float deltaTime); // Maneja el movimiento horizontal (time-based)
void applyGravity(); // Aplica la gravedad al objeto void handleVerticalMovement(); // Maneja el movimiento vertical (frame-based)
void handleVerticalMovement(float deltaTime); // Maneja el movimiento vertical (time-based)
void applyGravity(); // Aplica la gravedad al objeto (frame-based)
void applyGravity(float deltaTime); // Aplica la gravedad al objeto (time-based)
// --- Rebote --- // --- Rebote ---
void enableBounceEffect(); // Activa el efecto de rebote void enableBounceEffect(); // Activa el efecto de rebote
@@ -263,5 +303,6 @@ class Balloon {
void handleBottomCollision(); // Maneja la colisión inferior void handleBottomCollision(); // Maneja la colisión inferior
// --- Lógica de estado --- // --- Lógica de estado ---
void updateState(); // Actualiza los estados del globo void updateState(); // Actualiza los estados del globo (frame-based)
void updateState(float deltaTime); // Actualiza los estados del globo (time-based)
}; };

View File

@@ -15,19 +15,24 @@ class BalloonFormations {
public: public:
// --- Estructuras --- // --- Estructuras ---
struct SpawnParams { struct SpawnParams {
int x = 0; // Posición en el eje X donde crear el globo float x = 0; // Posición en el eje X donde crear el globo
int y = 0; // Posición en el eje Y donde crear el globo float y = 0; // Posición en el eje Y donde crear el globo
float vel_x = 0.0F; // Velocidad inicial en el eje X float vel_x = 0.0F; // Velocidad inicial en el eje X
Balloon::Type type = Balloon::Type::BALLOON; // Tipo de globo Balloon::Type type = Balloon::Type::BALLOON; // Tipo de globo
Balloon::Size size = Balloon::Size::SMALL; // Tamaño de globo Balloon::Size size = Balloon::Size::SMALL; // Tamaño de globo
int creation_counter = 0; // Temporizador para la creación del globo Uint16 creation_counter = 0; // Temporizador para la creación del globo
// Constructor por defecto // Constructor por defecto
SpawnParams() = default; SpawnParams() = default;
// Constructor con parámetros // Constructor con parámetros
SpawnParams(int x, int y, float vel_x, Balloon::Type type, Balloon::Size size, int creation_counter) SpawnParams(float x, float y, float vel_x, Balloon::Type type, Balloon::Size size, Uint16 creation_counter)
: x(x), y(y), vel_x(vel_x), type(type), size(size), creation_counter(creation_counter) {} : x(x),
y(y),
vel_x(vel_x),
type(type),
size(size),
creation_counter(creation_counter) {}
}; };
struct Formation { struct Formation {

View File

@@ -17,7 +17,9 @@
// Constructor // Constructor
BalloonManager::BalloonManager(IStageInfo *stage_info) BalloonManager::BalloonManager(IStageInfo *stage_info)
: explosions_(std::make_unique<Explosions>()), balloon_formations_(std::make_unique<BalloonFormations>()), stage_info_(stage_info) { init(); } : explosions_(std::make_unique<Explosions>()),
balloon_formations_(std::make_unique<BalloonFormations>()),
stage_info_(stage_info) { init(); }
// Inicializa // Inicializa
void BalloonManager::init() { void BalloonManager::init() {
@@ -60,7 +62,7 @@ void BalloonManager::init() {
explosions_->addTexture(3, explosions_textures_.at(3), explosions_animations_.at(3)); explosions_->addTexture(3, explosions_textures_.at(3), explosions_animations_.at(3));
} }
// Actualiza // Actualiza (frame-based)
void BalloonManager::update() { void BalloonManager::update() {
for (const auto &balloon : balloons_) { for (const auto &balloon : balloons_) {
balloon->update(); balloon->update();
@@ -69,6 +71,15 @@ void BalloonManager::update() {
explosions_->update(); explosions_->update();
} }
// Actualiza (time-based)
void BalloonManager::update(float deltaTime) {
for (const auto &balloon : balloons_) {
balloon->update(deltaTime);
}
updateBalloonDeployCounter(deltaTime);
explosions_->update(deltaTime);
}
// Renderiza los objetos // Renderiza los objetos
void BalloonManager::render() { void BalloonManager::render() {
for (auto &balloon : balloons_) { for (auto &balloon : balloons_) {
@@ -105,7 +116,15 @@ void BalloonManager::deployRandomFormation(int stage) {
// Crea los globos de la formación // Crea los globos de la formación
const auto BALLOONS = balloon_formations_->getFormationFromPool(stage, formation_id).balloons; const auto BALLOONS = balloon_formations_->getFormationFromPool(stage, formation_id).balloons;
for (auto balloon : BALLOONS) { for (auto balloon : BALLOONS) {
createBalloon(balloon.x, balloon.y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, (creation_time_enabled_) ? balloon.creation_counter : 0); Balloon::Config config = {
.x = balloon.x,
.y = balloon.y,
.type = balloon.type,
.size = balloon.size,
.vel_x = balloon.vel_x,
.speed = balloon_speed_,
.creation_counter = static_cast<Uint16>(creation_time_enabled_ ? balloon.creation_counter : 0)};
createBalloon(config);
} }
// Reinicia el contador para el próximo despliegue // Reinicia el contador para el próximo despliegue
@@ -118,15 +137,31 @@ void BalloonManager::deployRandomFormation(int stage) {
void BalloonManager::deployFormation(int formation_id) { void BalloonManager::deployFormation(int formation_id) {
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons; const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
for (auto balloon : BALLOONS) { for (auto balloon : BALLOONS) {
createBalloon(balloon.x, balloon.y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, balloon.creation_counter); Balloon::Config config = {
.x = balloon.x,
.y = balloon.y,
.type = balloon.type,
.size = balloon.size,
.vel_x = balloon.vel_x,
.speed = balloon_speed_,
.creation_counter = balloon.creation_counter};
createBalloon(config);
} }
} }
// Crea una formación de globos específica a una altura determinada // Crea una formación de globos específica a una altura determinada
void BalloonManager::deployFormation(int formation_id, int y) { void BalloonManager::deployFormation(int formation_id, float y) {
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons; const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
for (auto balloon : BALLOONS) { for (auto balloon : BALLOONS) {
createBalloon(balloon.x, y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, balloon.creation_counter); Balloon::Config config = {
.x = balloon.x,
.y = y,
.type = balloon.type,
.size = balloon.size,
.vel_x = balloon.vel_x,
.speed = balloon_speed_,
.creation_counter = balloon.creation_counter};
createBalloon(config);
} }
} }
@@ -136,13 +171,23 @@ void BalloonManager::freeBalloons() {
balloons_.erase(result.begin(), balloons_.end()); balloons_.erase(result.begin(), balloons_.end());
} }
// Actualiza la variable enemyDeployCounter // Actualiza la variable enemyDeployCounter (frame-based)
void BalloonManager::updateBalloonDeployCounter() { void BalloonManager::updateBalloonDeployCounter() {
if (balloon_deploy_counter_ > 0) { if (balloon_deploy_counter_ > 0) {
--balloon_deploy_counter_; --balloon_deploy_counter_;
} }
} }
// Actualiza la variable enemyDeployCounter (time-based)
void BalloonManager::updateBalloonDeployCounter(float deltaTime) {
if (balloon_deploy_counter_ > 0) {
// Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
float frameFactor = deltaTime / (1000.0f / 60.0f);
balloon_deploy_counter_ -= frameFactor;
if (balloon_deploy_counter_ < 0) balloon_deploy_counter_ = 0;
}
}
// Indica si se puede crear una powerball // Indica si se puede crear una powerball
auto BalloonManager::canPowerBallBeCreated() -> bool { return (!power_ball_enabled_) && (calculateScreenPower() > Balloon::POWERBALL_SCREENPOWER_MINIMUM) && (power_ball_counter_ == 0); } auto BalloonManager::canPowerBallBeCreated() -> bool { return (!power_ball_enabled_) && (calculateScreenPower() > Balloon::POWERBALL_SCREENPOWER_MINIMUM) && (power_ball_counter_ == 0); }
@@ -152,13 +197,16 @@ auto BalloonManager::calculateScreenPower() -> int {
} }
// Crea un globo nuevo en el vector de globos // Crea un globo nuevo en el vector de globos
auto BalloonManager::createBalloon(float x, int y, Balloon::Type type, Balloon::Size size, float velx, float speed, int creation_timer) -> std::shared_ptr<Balloon> { auto BalloonManager::createBalloon(Balloon::Config config) -> std::shared_ptr<Balloon> {
if (can_deploy_balloons_) { if (can_deploy_balloons_) {
const int INDEX = static_cast<int>(size); const int INDEX = static_cast<int>(config.size);
balloons_.emplace_back(std::make_shared<Balloon>(x, y, type, size, velx, speed, creation_timer, play_area_, balloon_textures_.at(INDEX), balloon_animations_.at(INDEX))); config.play_area = play_area_;
balloons_.back()->setSound(sound_enabled_); config.texture = balloon_textures_.at(INDEX);
balloons_.back()->setBouncingSound(bouncing_sound_enabled_); config.animation = balloon_animations_.at(INDEX);
balloons_.back()->setPoppingSound(poping_sound_enabled_); config.sound.enabled = sound_enabled_;
config.sound.bouncing_enabled = bouncing_sound_enabled_;
config.sound.poping_enabled = poping_sound_enabled_;
balloons_.emplace_back(std::make_shared<Balloon>(config));
return balloons_.back(); return balloons_.back();
} }
@@ -169,19 +217,24 @@ auto BalloonManager::createBalloon(float x, int y, Balloon::Type type, Balloon::
void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction) { void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction) {
if (can_deploy_balloons_) { if (can_deploy_balloons_) {
// Calcula parametros // Calcula parametros
const float VX = direction == "LEFT" ? Balloon::VELX_NEGATIVE : Balloon::VELX_POSITIVE;
const auto SIZE = static_cast<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1);
const int PARENT_HEIGHT = balloon->getHeight(); const int PARENT_HEIGHT = balloon->getHeight();
const int CHILD_HEIGHT = Balloon::WIDTH.at(static_cast<int>(balloon->getSize()) - 1); const int CHILD_HEIGHT = Balloon::WIDTH.at(static_cast<int>(balloon->getSize()) - 1);
const int CHILD_WIDTH = CHILD_HEIGHT; const int CHILD_WIDTH = CHILD_HEIGHT;
const float Y = balloon->getPosY() + ((PARENT_HEIGHT - CHILD_HEIGHT) / 2);
float x = direction == "LEFT" ? balloon->getPosX() + (balloon->getWidth() / 3) : balloon->getPosX() + (2 * (balloon->getWidth() / 3)); const float X = direction == "LEFT" ? balloon->getPosX() + (balloon->getWidth() / 3) : balloon->getPosX() + (2 * (balloon->getWidth() / 3));
const float MIN_X = play_area_.x; const float MIN_X = play_area_.x;
const float MAX_X = play_area_.w - CHILD_WIDTH; const float MAX_X = play_area_.w - CHILD_WIDTH;
x = std::clamp(x - (CHILD_WIDTH / 2), MIN_X, MAX_X);
Balloon::Config config = {
.x = std::clamp(X - (CHILD_WIDTH / 2), MIN_X, MAX_X),
.y = balloon->getPosY() + ((PARENT_HEIGHT - CHILD_HEIGHT) / 2),
.size = static_cast<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1),
.vel_x = direction == "LEFT" ? Balloon::VELX_NEGATIVE : Balloon::VELX_POSITIVE,
.speed = balloon_speed_,
.creation_counter = 0};
// Crea el globo // Crea el globo
auto b = createBalloon(x, Y, balloon->getType(), SIZE, VX, balloon_speed_, 0); auto b = createBalloon(config);
// Establece parametros // Establece parametros
b->setVelY(b->getType() == Balloon::Type::BALLOON ? -2.50F : Balloon::VELX_NEGATIVE * 2.0F); b->setVelY(b->getType() == Balloon::Type::BALLOON ? -2.50F : Balloon::VELX_NEGATIVE * 2.0F);
@@ -196,18 +249,32 @@ void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon,
void BalloonManager::createPowerBall() { void BalloonManager::createPowerBall() {
if (can_deploy_balloons_) { if (can_deploy_balloons_) {
constexpr int VALUES = 6; constexpr int VALUES = 6;
constexpr float POS_Y = -Balloon::WIDTH.at(4); const int LUCK = rand() % VALUES;
constexpr int CREATION_TIME = 0;
const float LEFT = param.game.play_area.rect.x; const float LEFT = param.game.play_area.rect.x;
const float CENTER = param.game.play_area.center_x - (Balloon::WIDTH.at(4) / 2); const float CENTER = param.game.play_area.center_x - (Balloon::WIDTH.at(4) / 2);
const float RIGHT = param.game.play_area.rect.w - Balloon::WIDTH.at(4); const float RIGHT = param.game.play_area.rect.w - Balloon::WIDTH.at(4);
const int LUCK = rand() % VALUES;
const std::array<float, VALUES> POS_X = {LEFT, LEFT, CENTER, CENTER, RIGHT, RIGHT}; const std::array<float, VALUES> POS_X = {LEFT, LEFT, CENTER, CENTER, RIGHT, RIGHT};
const std::array<float, VALUES> VEL_X = {Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE}; const std::array<float, VALUES> VEL_X = {Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE};
balloons_.emplace_back(std::make_unique<Balloon>(POS_X[LUCK], POS_Y, Balloon::Type::POWERBALL, Balloon::Size::EXTRALARGE, VEL_X[LUCK], balloon_speed_, CREATION_TIME, play_area_, balloon_textures_[4], balloon_animations_[4])); Balloon::Config config = {
.x = POS_X.at(LUCK),
.y = -Balloon::WIDTH.at(4),
.type = Balloon::Type::POWERBALL,
.size = Balloon::Size::EXTRALARGE,
.vel_x = VEL_X.at(LUCK),
.speed = balloon_speed_,
.creation_counter = 0,
.play_area = play_area_,
.texture = balloon_textures_.at(4),
.animation = balloon_animations_.at(4),
.sound = {
.bouncing_enabled = bouncing_sound_enabled_,
.poping_enabled = poping_sound_enabled_,
.enabled = sound_enabled_}};
balloons_.emplace_back(std::make_unique<Balloon>(config));
balloons_.back()->setInvulnerable(true); balloons_.back()->setInvulnerable(true);
power_ball_enabled_ = true; power_ball_enabled_ = true;
@@ -332,19 +399,6 @@ void BalloonManager::createTwoBigBalloons() {
deployFormation(1); deployFormation(1);
} }
// Crea una disposición de globos aleatoria
void BalloonManager::createRandomBalloons() {
const int NUM_BALLOONS = 2 + (rand() % 4);
for (int i = 0; i < NUM_BALLOONS; ++i) {
const float X = param.game.game_area.rect.x + (rand() % static_cast<int>(param.game.game_area.rect.w)) - Balloon::WIDTH.at(3);
const int Y = param.game.game_area.rect.y + (rand() % 50);
const auto SIZE = static_cast<Balloon::Size>(rand() % 4);
const float VEL_X = (rand() % 2 == 0) ? Balloon::VELX_POSITIVE : Balloon::VELX_NEGATIVE;
const int CREATION_COUNTER = 0;
createBalloon(X, Y, Balloon::Type::BALLOON, SIZE, VEL_X, balloon_speed_, CREATION_COUNTER);
}
}
// Obtiene el nivel de ameza actual generado por los globos // Obtiene el nivel de ameza actual generado por los globos
auto BalloonManager::getMenace() -> int { auto BalloonManager::getMenace() -> int {
return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); }); return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); });

View File

@@ -28,7 +28,8 @@ class BalloonManager {
~BalloonManager() = default; ~BalloonManager() = default;
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza el estado de los globos void update(); // Actualiza el estado de los globos (frame-based)
void update(float deltaTime); // Actualiza el estado de los globos (time-based)
void render(); // Renderiza los globos en pantalla void render(); // Renderiza los globos en pantalla
// --- Gestión de globos --- // --- Gestión de globos ---
@@ -37,20 +38,20 @@ class BalloonManager {
// --- Creación de formaciones enemigas --- // --- Creación de formaciones enemigas ---
void deployRandomFormation(int stage); // Crea una formación de globos aleatoria void deployRandomFormation(int stage); // Crea una formación de globos aleatoria
void deployFormation(int formation_id); // Crea una formación específica void deployFormation(int formation_id); // Crea una formación específica
void deployFormation(int formation_id, int y); // Crea una formación específica con coordenadas void deployFormation(int formation_id, float y); // Crea una formación específica con coordenadas
// --- Creación de globos --- // --- Creación de globos ---
auto createBalloon(float x, int y, Balloon::Type type, Balloon::Size size, float velx, float speed, int creation_timer) -> std::shared_ptr<Balloon>; // Crea un nuevo globo auto createBalloon(Balloon::Config config) -> std::shared_ptr<Balloon>; // Crea un nuevo globo
void createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction); // Crea un globo a partir de otro void createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction); // Crea un globo a partir de otro
void createPowerBall(); // Crea una PowerBall void createPowerBall(); // Crea una PowerBall
void createTwoBigBalloons(); // Crea dos globos grandes void createTwoBigBalloons(); // Crea dos globos grandes
void createRandomBalloons(); // Crea una disposición aleatoria de globos
// --- Control de velocidad y despliegue --- // --- Control de velocidad y despliegue ---
void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos
void setDefaultBalloonSpeed(float speed) { default_balloon_speed_ = speed; }; // Establece la velocidad base void setDefaultBalloonSpeed(float speed) { default_balloon_speed_ = speed; }; // Establece la velocidad base
void resetBalloonSpeed() { setBalloonSpeed(default_balloon_speed_); }; // Restablece la velocidad de los globos void resetBalloonSpeed() { setBalloonSpeed(default_balloon_speed_); }; // Restablece la velocidad de los globos
void updateBalloonDeployCounter(); // Actualiza el contador de despliegue void updateBalloonDeployCounter(); // Actualiza el contador de despliegue (frame-based)
void updateBalloonDeployCounter(float deltaTime); // Actualiza el contador de despliegue (time-based)
auto canPowerBallBeCreated() -> bool; // Indica si se puede crear una PowerBall auto canPowerBallBeCreated() -> bool; // Indica si se puede crear una PowerBall
auto calculateScreenPower() -> int; // Calcula el poder de los globos en pantalla auto calculateScreenPower() -> int; // Calcula el poder de los globos en pantalla
@@ -99,7 +100,7 @@ class BalloonManager {
SDL_FRect play_area_ = param.game.play_area.rect; SDL_FRect play_area_ = param.game.play_area.rect;
float balloon_speed_ = Balloon::SPEED.at(0); float balloon_speed_ = Balloon::SPEED.at(0);
float default_balloon_speed_ = Balloon::SPEED.at(0); float default_balloon_speed_ = Balloon::SPEED.at(0);
int balloon_deploy_counter_ = 0; float balloon_deploy_counter_ = 0;
int power_ball_counter_ = 0; int power_ball_counter_ = 0;
int last_balloon_deploy_ = 0; int last_balloon_deploy_ = 0;
bool power_ball_enabled_ = false; bool power_ball_enabled_ = false;

View File

@@ -60,13 +60,19 @@ void Bullet::render() {
} }
} }
// Actualiza el estado del objeto // Actualiza el estado del objeto (frame-based)
auto Bullet::update() -> BulletMoveStatus { auto Bullet::update() -> BulletMoveStatus {
sprite_->update(); sprite_->update();
return move(); return move();
} }
// Implementación del movimiento usando BulletMoveStatus // Actualiza el estado del objeto (time-based)
auto Bullet::update(float deltaTime) -> BulletMoveStatus {
sprite_->update(deltaTime);
return move(deltaTime);
}
// Implementación del movimiento usando BulletMoveStatus (frame-based)
auto Bullet::move() -> BulletMoveStatus { auto Bullet::move() -> BulletMoveStatus {
pos_x_ += vel_x_; pos_x_ += vel_x_;
if (pos_x_ < param.game.play_area.rect.x - WIDTH || pos_x_ > param.game.play_area.rect.w) { if (pos_x_ < param.game.play_area.rect.x - WIDTH || pos_x_ > param.game.play_area.rect.w) {
@@ -86,6 +92,29 @@ auto Bullet::move() -> BulletMoveStatus {
return BulletMoveStatus::OK; return BulletMoveStatus::OK;
} }
// Implementación del movimiento usando BulletMoveStatus (time-based)
auto Bullet::move(float deltaTime) -> BulletMoveStatus {
// Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
float frameFactor = deltaTime / (1000.0f / 60.0f);
pos_x_ += vel_x_ * frameFactor;
if (pos_x_ < param.game.play_area.rect.x - WIDTH || pos_x_ > param.game.play_area.rect.w) {
disable();
return BulletMoveStatus::OUT;
}
pos_y_ += VEL_Y * frameFactor;
if (pos_y_ < param.game.play_area.rect.y - HEIGHT) {
disable();
return BulletMoveStatus::OUT;
}
shiftSprite();
shiftColliders();
return BulletMoveStatus::OK;
}
auto Bullet::isEnabled() const -> bool { auto Bullet::isEnabled() const -> bool {
return bullet_type_ != BulletType::NONE; return bullet_type_ != BulletType::NONE;
} }

View File

@@ -35,7 +35,8 @@ class Bullet {
// --- Métodos principales --- // --- Métodos principales ---
void render(); // Dibuja la bala en pantalla void render(); // Dibuja la bala en pantalla
auto update() -> BulletMoveStatus; // Actualiza el estado del objeto auto update() -> BulletMoveStatus; // Actualiza el estado del objeto (frame-based)
auto update(float deltaTime) -> BulletMoveStatus; // Actualiza el estado del objeto (time-based)
void disable(); // Desactiva la bala void disable(); // Desactiva la bala
// --- Getters --- // --- Getters ---
@@ -64,7 +65,8 @@ class Bullet {
// --- Métodos internos --- // --- Métodos internos ---
void shiftColliders(); // Ajusta el círculo de colisión void shiftColliders(); // Ajusta el círculo de colisión
void shiftSprite(); // Ajusta el sprite void shiftSprite(); // Ajusta el sprite
auto move() -> BulletMoveStatus; // Mueve la bala y devuelve su estado auto move() -> BulletMoveStatus; // Mueve la bala y devuelve su estado (frame-based)
auto move(float deltaTime) -> BulletMoveStatus; // Mueve la bala y devuelve su estado (time-based)
static auto calculateVelocity(BulletType bullet_type) -> float; // Calcula la velocidad horizontal de la bala static auto calculateVelocity(BulletType bullet_type) -> float; // Calcula la velocidad horizontal de la bala
static auto buildAnimationString(BulletType bullet_type, bool powered) -> std::string; // Construye el string de animación static auto buildAnimationString(BulletType bullet_type, bool powered) -> std::string; // Construye el string de animación
}; };

View File

@@ -116,8 +116,8 @@ constexpr auto Color::hsvToRgb(HSV hsv) -> Color {
// Implementaciones del namespace Colors // Implementaciones del namespace Colors
namespace Colors { namespace Colors {
// Obtiene un color del vector de colores imitando al Coche Fantástico // Obtiene un color del vector de colores imitando al Coche Fantástico
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color { auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color {
int cycle_length = (colors.size() * 2) - 2; int cycle_length = (colors.size() * 2) - 2;
size_t n = counter % cycle_length; size_t n = counter % cycle_length;
@@ -129,9 +129,9 @@ namespace Colors {
} }
return colors[index]; return colors[index];
} }
auto generateMirroredCycle(Color base, ColorCycleStyle style) -> Cycle { auto generateMirroredCycle(Color base, ColorCycleStyle style) -> Cycle {
Cycle result{}; Cycle result{};
HSV base_hsv = Color::rgbToHsv(base); HSV base_hsv = Color::rgbToHsv(base);
@@ -182,5 +182,5 @@ namespace Colors {
} }
return result; return result;
}
} }
} // namespace Colors

View File

@@ -36,10 +36,17 @@ struct Color {
Uint8 r, g, b, a; Uint8 r, g, b, a;
constexpr Color() : r(MIN_COLOR_VALUE), g(MIN_COLOR_VALUE), b(MIN_COLOR_VALUE), a(DEFAULT_ALPHA) {} constexpr Color()
: r(MIN_COLOR_VALUE),
g(MIN_COLOR_VALUE),
b(MIN_COLOR_VALUE),
a(DEFAULT_ALPHA) {}
explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha = DEFAULT_ALPHA) explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha = DEFAULT_ALPHA)
: r(red), g(green), b(blue), a(alpha) {} : r(red),
g(green),
b(blue),
a(alpha) {}
[[nodiscard]] constexpr auto INVERSE() const -> Color { [[nodiscard]] constexpr auto INVERSE() const -> Color {
return Color(MAX_COLOR_VALUE - r, MAX_COLOR_VALUE - g, MAX_COLOR_VALUE - b, a); return Color(MAX_COLOR_VALUE - r, MAX_COLOR_VALUE - g, MAX_COLOR_VALUE - b, a);
@@ -108,25 +115,25 @@ enum class ColorCycleStyle {
// --- Namespace Colors: constantes y utilidades de color --- // --- Namespace Colors: constantes y utilidades de color ---
namespace Colors { namespace Colors {
// --- Constantes --- // --- Constantes ---
constexpr size_t CYCLE_SIZE = 6; // Mitad del ciclo espejado constexpr size_t CYCLE_SIZE = 6; // Mitad del ciclo espejado
// --- Alias --- // --- Alias ---
using Cycle = std::array<Color, 2 * CYCLE_SIZE>; using Cycle = std::array<Color, 2 * CYCLE_SIZE>;
// --- Colores predefinidos --- // --- Colores predefinidos ---
constexpr Color NO_COLOR_MOD = Color(0XFF, 0XFF, 0XFF); constexpr Color NO_COLOR_MOD = Color(0XFF, 0XFF, 0XFF);
constexpr Color SHADOW_TEXT = Color(0X43, 0X43, 0X4F); constexpr Color SHADOW_TEXT = Color(0X43, 0X43, 0X4F);
constexpr Color TITLE_SHADOW_TEXT = Color(0x14, 0x87, 0xc4); constexpr Color TITLE_SHADOW_TEXT = Color(0x14, 0x87, 0xc4);
constexpr Color ORANGE_TEXT = Color(0XFF, 0X7A, 0X00); constexpr Color ORANGE_TEXT = Color(0XFF, 0X7A, 0X00);
constexpr Color FLASH = Color(0XFF, 0XFF, 0XFF); constexpr Color FLASH = Color(0XFF, 0XFF, 0XFF);
constexpr Color BLUE_SKY = Color(0X02, 0X88, 0XD1); constexpr Color BLUE_SKY = Color(0X02, 0X88, 0XD1);
constexpr Color PINK_SKY = Color(0XFF, 0X6B, 0X97); constexpr Color PINK_SKY = Color(0XFF, 0X6B, 0X97);
constexpr Color GREEN_SKY = Color(0X00, 0X79, 0X6B); constexpr Color GREEN_SKY = Color(0X00, 0X79, 0X6B);
// --- Funciones --- // --- Funciones ---
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color; auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color;
auto generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SUBTLE_PULSE) -> Cycle; auto generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SUBTLE_PULSE) -> Cycle;
} } // namespace Colors

View File

@@ -15,8 +15,8 @@ namespace Game {
constexpr float WIDTH = 320.0F; constexpr float WIDTH = 320.0F;
constexpr float HEIGHT = 256.0F; constexpr float HEIGHT = 256.0F;
constexpr float ITEM_SIZE = 20.0F; constexpr float ITEM_SIZE = 20.0F;
constexpr int NAME_ENTRY_IDLE_TIME = 10; constexpr int NAME_ENTRY_IDLE_TIME = 10000; // 10 segundos en milisegundos
constexpr int NAME_ENTRY_TOTAL_TIME = 60; constexpr int NAME_ENTRY_TOTAL_TIME = 60000; // 60 segundos en milisegundos
constexpr bool HIT_STOP = false; constexpr bool HIT_STOP = false;
constexpr int HIT_STOP_MS = 500; constexpr int HIT_STOP_MS = 500;
constexpr const char* ITEM_TEXT_OUTLINE_COLOR = "FFFFFF00"; // 255, 255, 255, 0 constexpr const char* ITEM_TEXT_OUTLINE_COLOR = "FFFFFF00"; // 255, 255, 255, 0
@@ -58,7 +58,7 @@ constexpr int SKIP_COUNTDOWN_VALUE = 8;
// --- TITLE --- // --- TITLE ---
namespace Title { namespace Title {
constexpr int PRESS_START_POSITION = 180; constexpr int PRESS_START_POSITION = 180;
constexpr int DURATION = 800; constexpr float DURATION = 14000;
constexpr int ARCADE_EDITION_POSITION = 123; constexpr int ARCADE_EDITION_POSITION = 123;
constexpr int TITLE_C_C_POSITION = 80; constexpr int TITLE_C_C_POSITION = 80;
constexpr const char* BG_COLOR = "41526F"; constexpr const char* BG_COLOR = "41526F";
@@ -76,7 +76,8 @@ struct BalloonSettings {
float vel; float vel;
float grav; float grav;
constexpr BalloonSettings(float v, float g) constexpr BalloonSettings(float v, float g)
: vel(v), grav(g) {} : vel(v),
grav(g) {}
}; };
constexpr std::array<BalloonSettings, 4> SETTINGS = {{ constexpr std::array<BalloonSettings, 4> SETTINGS = {{

View File

@@ -25,7 +25,9 @@ class DefineButtons {
int button; int button;
Button(std::string label, Input::Action action, int button) Button(std::string label, Input::Action action, int button)
: label(std::move(label)), action(action), button(button) {} : label(std::move(label)),
action(action),
button(button) {}
}; };
// --- Constructor y destructor --- // --- Constructor y destructor ---

View File

@@ -18,9 +18,9 @@
#include "manage_hiscore_table.h" // Para ManageHiScoreTable #include "manage_hiscore_table.h" // Para ManageHiScoreTable
#include "options.h" // Para loadFromFile, saveToFile, Settings, settings, setConfigFile, setControllersFile #include "options.h" // Para loadFromFile, saveToFile, Settings, settings, setConfigFile, setControllersFile
#include "param.h" // Para loadParamsFromFile #include "param.h" // Para loadParamsFromFile
#include "resource_helper.h" // Para ResourceHelper
#include "player.h" // Para Player #include "player.h" // Para Player
#include "resource.h" // Para Resource #include "resource.h" // Para Resource
#include "resource_helper.h" // Para ResourceHelper
#include "screen.h" // Para Screen #include "screen.h" // Para Screen
#include "section.hpp" // Para Name, Options, name, options, AttractMode, attract_mode #include "section.hpp" // Para Name, Options, name, options, AttractMode, attract_mode
#include "sections/credits.h" // Para Credits #include "sections/credits.h" // Para Credits

View File

@@ -45,7 +45,10 @@ void EnterName::incPosition() {
} else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGTH } else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGTH
{ {
// Copiamos el índice del carácter anterior si es posible. // Copiamos el índice del carácter anterior si es posible.
character_index_[position_] = character_index_[position_ - 1]; // character_index_[position_] = character_index_[position_ - 1];
// Ponemos el caracter "espacio"
character_index_[position_] = 0;
} else { } else {
// Si position_ es 0, inicializamos el carácter actual. // Si position_ es 0, inicializamos el carácter actual.
character_index_[position_] = 0; character_index_[position_] = 0;
@@ -144,12 +147,19 @@ auto EnterName::findIndex(char character) const -> int {
// Devuelve un nombre al azar // Devuelve un nombre al azar
auto EnterName::getRandomName() -> std::string { auto EnterName::getRandomName() -> std::string {
static constexpr std::array<std::string_view, 8> NAMES = { static constexpr std::array<std::string_view, 8> NAMES = {
"BAL1", "TABE", "DOC", "MON", "SAM1", "JORDI", "JDES", "PEPE"}; "BAL1",
"TABE",
"DOC",
"MON",
"SAM1",
"JORDI",
"JDES",
"PEPE"};
return std::string(NAMES[rand() % NAMES.size()]); return std::string(NAMES[rand() % NAMES.size()]);
} }
// Obtiene el nombre final introducido // Obtiene el nombre final introducido
auto EnterName::getFinalName() -> std::string { auto EnterName::getFinalName() -> std::string {
auto name = trim(name_.substr(0, position_)); auto name = trim(name_.substr(0, position_ + 1)); // Devuelve el texto intruducido incluyendo el del selector
if (name.empty()) { if (name.empty()) {
name = getRandomName(); name = getRandomName();
} }

View File

@@ -6,7 +6,7 @@
class Texture; // lines 4-4 class Texture; // lines 4-4
// Actualiza la lógica de la clase // Actualiza la lógica de la clase (frame-based)
void Explosions::update() { void Explosions::update() {
for (auto &explosion : explosions_) { for (auto &explosion : explosions_) {
explosion->update(); explosion->update();
@@ -16,6 +16,16 @@ void Explosions::update() {
freeExplosions(); freeExplosions();
} }
// Actualiza la lógica de la clase (time-based)
void Explosions::update(float deltaTime) {
for (auto &explosion : explosions_) {
explosion->update(deltaTime);
}
// Vacia el vector de elementos finalizados
freeExplosions();
}
// Dibuja el objeto en pantalla // Dibuja el objeto en pantalla
void Explosions::render() { void Explosions::render() {
for (auto &explosion : explosions_) { for (auto &explosion : explosions_) {

View File

@@ -16,7 +16,9 @@ struct ExplosionTexture {
std::vector<std::string> animation; // Animación para la textura std::vector<std::string> animation; // Animación para la textura
ExplosionTexture(int sz, std::shared_ptr<Texture> tex, const std::vector<std::string> &anim) ExplosionTexture(int sz, std::shared_ptr<Texture> tex, const std::vector<std::string> &anim)
: size(sz), texture(std::move(tex)), animation(anim) {} : size(sz),
texture(std::move(tex)),
animation(anim) {}
}; };
// --- Clase Explosions: gestor de explosiones --- // --- Clase Explosions: gestor de explosiones ---
@@ -27,7 +29,8 @@ class Explosions {
~Explosions() = default; // Destructor por defecto ~Explosions() = default; // Destructor por defecto
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la lógica de la clase void update(); // Actualiza la lógica de la clase (frame-based)
void update(float deltaTime); // Actualiza la lógica de la clase (time-based)
void render(); // Dibuja el objeto en pantalla void render(); // Dibuja el objeto en pantalla
// --- Configuración --- // --- Configuración ---

View File

@@ -82,6 +82,11 @@ void Fade::update() {
} }
} }
// Compatibilidad delta-time (ignora el parámetro ya que usa SDL_GetTicks)
void Fade::update(float delta_time) {
update(); // Llama al método original
}
void Fade::updatePreState() { void Fade::updatePreState() {
// Sistema basado en tiempo únicamente // Sistema basado en tiempo únicamente
Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_; Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_;

View File

@@ -39,7 +39,8 @@ class Fade {
// --- Métodos principales --- // --- Métodos principales ---
void reset(); // Resetea variables para reutilizar el fade void reset(); // Resetea variables para reutilizar el fade
void render(); // Dibuja la transición en pantalla void render(); // Dibuja la transición en pantalla
void update(); // Actualiza el estado interno void update(); // Actualiza el estado interno (ya usa tiempo real)
void update(float delta_time); // Compatibilidad delta-time (ignora el parámetro)
void activate(); // Activa el fade void activate(); // Activa el fade
// --- Configuración --- // --- Configuración ---

View File

@@ -45,6 +45,7 @@ void GameLogo::init() {
arcade_edition_status_ = Status::DISABLED; arcade_edition_status_ = Status::DISABLED;
shake_.init(1, 2, 8, XP); shake_.init(1, 2, 8, XP);
zoom_ = 3.0F * ZOOM_FACTOR; zoom_ = 3.0F * ZOOM_FACTOR;
post_finished_time_accumulator_ = 0.0f;
// Inicializa el bitmap de 'Coffee' // Inicializa el bitmap de 'Coffee'
coffee_sprite_->setPosX(XP); coffee_sprite_->setPosX(XP);
@@ -112,13 +113,20 @@ void GameLogo::render() {
} }
} }
// Actualiza la lógica de la clase // Actualiza la lógica de la clase (frame-based)
void GameLogo::update() { void GameLogo::update() {
updateCoffeeCrisis(); updateCoffeeCrisis();
updateArcadeEdition(); updateArcadeEdition();
updatePostFinishedCounter(); updatePostFinishedCounter();
} }
// Actualiza la lógica de la clase (time-based)
void GameLogo::update(float deltaTime) {
updateCoffeeCrisis(deltaTime);
updateArcadeEdition(deltaTime);
updatePostFinishedCounter(deltaTime);
}
void GameLogo::updateCoffeeCrisis() { void GameLogo::updateCoffeeCrisis() {
switch (coffee_crisis_status_) { switch (coffee_crisis_status_) {
case Status::MOVING: case Status::MOVING:
@@ -135,6 +143,22 @@ void GameLogo::updateCoffeeCrisis() {
} }
} }
void GameLogo::updateCoffeeCrisis(float deltaTime) {
switch (coffee_crisis_status_) {
case Status::MOVING:
handleCoffeeCrisisMoving(deltaTime);
break;
case Status::SHAKING:
handleCoffeeCrisisShaking(deltaTime);
break;
case Status::FINISHED:
handleCoffeeCrisisFinished(deltaTime);
break;
default:
break;
}
}
void GameLogo::updateArcadeEdition() { void GameLogo::updateArcadeEdition() {
switch (arcade_edition_status_) { switch (arcade_edition_status_) {
case Status::MOVING: case Status::MOVING:
@@ -148,6 +172,19 @@ void GameLogo::updateArcadeEdition() {
} }
} }
void GameLogo::updateArcadeEdition(float deltaTime) {
switch (arcade_edition_status_) {
case Status::MOVING:
handleArcadeEditionMoving(deltaTime);
break;
case Status::SHAKING:
handleArcadeEditionShaking(deltaTime);
break;
default:
break;
}
}
void GameLogo::handleCoffeeCrisisMoving() { void GameLogo::handleCoffeeCrisisMoving() {
coffee_sprite_->update(); coffee_sprite_->update();
crisis_sprite_->update(); crisis_sprite_->update();
@@ -158,6 +195,16 @@ void GameLogo::handleCoffeeCrisisMoving() {
} }
} }
void GameLogo::handleCoffeeCrisisMoving(float deltaTime) {
coffee_sprite_->update(deltaTime);
crisis_sprite_->update(deltaTime);
if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished()) {
coffee_crisis_status_ = Status::SHAKING;
playTitleEffects();
}
}
void GameLogo::handleCoffeeCrisisShaking() { void GameLogo::handleCoffeeCrisisShaking() {
if (shake_.remaining > 0) { if (shake_.remaining > 0) {
processShakeEffect(coffee_sprite_.get(), crisis_sprite_.get()); processShakeEffect(coffee_sprite_.get(), crisis_sprite_.get());
@@ -168,10 +215,24 @@ void GameLogo::handleCoffeeCrisisShaking() {
updateDustSprites(); updateDustSprites();
} }
void GameLogo::handleCoffeeCrisisShaking(float deltaTime) {
if (shake_.remaining > 0) {
processShakeEffect(coffee_sprite_.get(), crisis_sprite_.get(), deltaTime);
} else {
finishCoffeeCrisisShaking();
}
updateDustSprites(deltaTime);
}
void GameLogo::handleCoffeeCrisisFinished() { void GameLogo::handleCoffeeCrisisFinished() {
updateDustSprites(); updateDustSprites();
} }
void GameLogo::handleCoffeeCrisisFinished(float deltaTime) {
updateDustSprites(deltaTime);
}
void GameLogo::handleArcadeEditionMoving() { void GameLogo::handleArcadeEditionMoving() {
zoom_ -= 0.1F * ZOOM_FACTOR; zoom_ -= 0.1F * ZOOM_FACTOR;
arcade_edition_sprite_->setZoom(zoom_); arcade_edition_sprite_->setZoom(zoom_);
@@ -181,6 +242,16 @@ void GameLogo::handleArcadeEditionMoving() {
} }
} }
void GameLogo::handleArcadeEditionMoving(float deltaTime) {
// Convertir 0.1F * ZOOM_FACTOR por frame a por milisegundo (asumiendo 60fps)
zoom_ -= (0.1F * ZOOM_FACTOR) * deltaTime / (1000.0F / 60.0F);
arcade_edition_sprite_->setZoom(zoom_);
if (zoom_ <= 1.0F) {
finishArcadeEditionMoving();
}
}
void GameLogo::handleArcadeEditionShaking() { void GameLogo::handleArcadeEditionShaking() {
if (shake_.remaining > 0) { if (shake_.remaining > 0) {
processArcadeEditionShake(); processArcadeEditionShake();
@@ -190,6 +261,15 @@ void GameLogo::handleArcadeEditionShaking() {
} }
} }
void GameLogo::handleArcadeEditionShaking(float deltaTime) {
if (shake_.remaining > 0) {
processArcadeEditionShake(deltaTime);
} else {
arcade_edition_sprite_->setX(shake_.origin);
arcade_edition_status_ = Status::FINISHED;
}
}
void GameLogo::processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite) { void GameLogo::processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite) {
if (shake_.counter > 0) { if (shake_.counter > 0) {
shake_.counter--; shake_.counter--;
@@ -204,6 +284,23 @@ void GameLogo::processShakeEffect(SmartSprite* primary_sprite, SmartSprite* seco
} }
} }
void GameLogo::processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite, float deltaTime) {
// Convertir delay (frames) a tiempo: delay frames = delay * (1000ms/60fps)
float delayTime = static_cast<float>(shake_.delay) * (1000.0f / 60.0f);
shake_.time_accumulator += deltaTime;
if (shake_.time_accumulator >= delayTime) {
shake_.time_accumulator -= delayTime;
const auto DISPLACEMENT = calculateShakeDisplacement();
primary_sprite->setPosX(shake_.origin + DISPLACEMENT);
if (secondary_sprite != nullptr) {
secondary_sprite->setPosX(shake_.origin + DISPLACEMENT + 15);
}
shake_.remaining--;
}
}
void GameLogo::processArcadeEditionShake() { void GameLogo::processArcadeEditionShake() {
if (shake_.counter > 0) { if (shake_.counter > 0) {
shake_.counter--; shake_.counter--;
@@ -215,6 +312,20 @@ void GameLogo::processArcadeEditionShake() {
} }
} }
void GameLogo::processArcadeEditionShake(float deltaTime) {
// Convertir delay (frames) a tiempo: delay frames = delay * (1000ms/60fps)
float delayTime = static_cast<float>(shake_.delay) * (1000.0f / 60.0f);
shake_.time_accumulator += deltaTime;
if (shake_.time_accumulator >= delayTime) {
shake_.time_accumulator -= delayTime;
const auto DISPLACEMENT = calculateShakeDisplacement();
arcade_edition_sprite_->setX(shake_.origin + DISPLACEMENT);
shake_.remaining--;
}
}
auto GameLogo::calculateShakeDisplacement() const -> int { auto GameLogo::calculateShakeDisplacement() const -> int {
return shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp; return shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp;
} }
@@ -245,6 +356,11 @@ void GameLogo::updateDustSprites() {
dust_left_sprite_->update(); dust_left_sprite_->update();
} }
void GameLogo::updateDustSprites(float deltaTime) {
dust_right_sprite_->update(deltaTime);
dust_left_sprite_->update(deltaTime);
}
void GameLogo::updatePostFinishedCounter() { void GameLogo::updatePostFinishedCounter() {
if (coffee_crisis_status_ == Status::FINISHED && if (coffee_crisis_status_ == Status::FINISHED &&
arcade_edition_status_ == Status::FINISHED && arcade_edition_status_ == Status::FINISHED &&
@@ -253,6 +369,23 @@ void GameLogo::updatePostFinishedCounter() {
} }
} }
void GameLogo::updatePostFinishedCounter(float deltaTime) {
if (coffee_crisis_status_ == Status::FINISHED &&
arcade_edition_status_ == Status::FINISHED &&
post_finished_counter_ > 0) {
// Convertir 1 frame a tiempo: 1 frame = 1000ms/60fps = 16.67ms
float frameTime = 1000.0f / 60.0f;
post_finished_time_accumulator_ += deltaTime;
if (post_finished_time_accumulator_ >= frameTime) {
post_finished_time_accumulator_ -= frameTime;
--post_finished_counter_;
}
}
}
// Activa la clase // Activa la clase
void GameLogo::enable() { void GameLogo::enable() {
init(); init();

View File

@@ -17,7 +17,8 @@ class GameLogo {
// --- Métodos principales --- // --- Métodos principales ---
void render(); // Pinta la clase en pantalla void render(); // Pinta la clase en pantalla
void update(); // Actualiza la lógica de la clase void update(); // Actualiza la lógica de la clase (frame-based)
void update(float deltaTime); // Actualiza la lógica de la clase (time-based)
void enable(); // Activa la clase void enable(); // Activa la clase
// --- Getters --- // --- Getters ---
@@ -35,15 +36,21 @@ class GameLogo {
// --- Estructuras privadas --- // --- Estructuras privadas ---
struct Shake { struct Shake {
int desp = 1; // Pixels de desplazamiento para agitar la pantalla en el eje x int desp = 1; // Pixels de desplazamiento para agitar la pantalla en el eje x
int delay = 2; // Retraso entre cada desplazamiento de la pantalla al agitarse int delay = 2; // Retraso entre cada desplazamiento de la pantalla al agitarse (frame-based)
int length = 8; // Cantidad de desplazamientos a realizar int length = 8; // Cantidad de desplazamientos a realizar
int remaining = length; // Cantidad de desplazamientos pendientes a realizar int remaining = length; // Cantidad de desplazamientos pendientes a realizar
int counter = delay; // Contador para el retraso int counter = delay; // Contador para el retraso (frame-based)
float time_accumulator = 0.0f; // Acumulador de tiempo para deltaTime
int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento
Shake() = default; Shake() = default;
Shake(int d, int de, int l, int o) Shake(int d, int de, int l, int o)
: desp(d), delay(de), length(l), remaining(l), counter(de), origin(o) {} : desp(d),
delay(de),
length(l),
remaining(l),
counter(de),
origin(o) {}
void init(int d, int de, int l, int o) { void init(int d, int de, int l, int o) {
desp = d; desp = d;
@@ -51,6 +58,7 @@ class GameLogo {
length = l; length = l;
remaining = l; remaining = l;
counter = de; counter = de;
time_accumulator = 0.0f;
origin = o; origin = o;
} }
}; };
@@ -74,32 +82,44 @@ class GameLogo {
float x_; // Posición X del logo float x_; // Posición X del logo
float y_; // Posición Y del logo float y_; // Posición Y del logo
float zoom_ = 1.0F; // Zoom aplicado al texto "ARCADE EDITION" float zoom_ = 1.0F; // Zoom aplicado al texto "ARCADE EDITION"
int post_finished_counter_ = 1; // Contador final tras animaciones int post_finished_counter_ = 1; // Contador final tras animaciones (frame-based)
float post_finished_time_accumulator_ = 0.0f; // Acumulador de tiempo para post_finished_counter
// --- Inicialización --- // --- Inicialización ---
void init(); // Inicializa las variables void init(); // Inicializa las variables
[[nodiscard]] auto getInitialVerticalDesp() const -> int; // Calcula el desplazamiento vertical inicial [[nodiscard]] auto getInitialVerticalDesp() const -> int; // Calcula el desplazamiento vertical inicial
// --- Actualización de estados específicos --- // --- Actualización de estados específicos ---
void updateCoffeeCrisis(); // Actualiza el estado de "Coffee Crisis" void updateCoffeeCrisis(); // Actualiza el estado de "Coffee Crisis" (frame-based)
void updateArcadeEdition(); // Actualiza el estado de "Arcade Edition" void updateCoffeeCrisis(float deltaTime); // Actualiza el estado de "Coffee Crisis" (time-based)
void updatePostFinishedCounter(); // Actualiza el contador tras finalizar una animación void updateArcadeEdition(); // Actualiza el estado de "Arcade Edition" (frame-based)
void updateArcadeEdition(float deltaTime); // Actualiza el estado de "Arcade Edition" (time-based)
void updatePostFinishedCounter(); // Actualiza el contador tras finalizar una animación (frame-based)
void updatePostFinishedCounter(float deltaTime); // Actualiza el contador tras finalizar una animación (time-based)
// --- Efectos visuales: movimiento y sacudidas --- // --- Efectos visuales: movimiento y sacudidas ---
void handleCoffeeCrisisMoving(); // Maneja el movimiento de "Coffee Crisis" void handleCoffeeCrisisMoving(); // Maneja el movimiento de "Coffee Crisis" (frame-based)
void handleCoffeeCrisisShaking(); // Maneja la sacudida de "Coffee Crisis" void handleCoffeeCrisisMoving(float deltaTime); // Maneja el movimiento de "Coffee Crisis" (time-based)
void handleArcadeEditionMoving(); // Maneja el movimiento de "Arcade Edition" void handleCoffeeCrisisShaking(); // Maneja la sacudida de "Coffee Crisis" (frame-based)
void handleArcadeEditionShaking(); // Maneja la sacudida de "Arcade Edition" void handleCoffeeCrisisShaking(float deltaTime); // Maneja la sacudida de "Coffee Crisis" (time-based)
void processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite = nullptr); // Procesa el efecto de sacudida en sprites void handleArcadeEditionMoving(); // Maneja el movimiento de "Arcade Edition" (frame-based)
void processArcadeEditionShake(); // Procesa la sacudida específica de "Arcade Edition" void handleArcadeEditionMoving(float deltaTime); // Maneja el movimiento de "Arcade Edition" (time-based)
void handleArcadeEditionShaking(); // Maneja la sacudida de "Arcade Edition" (frame-based)
void handleArcadeEditionShaking(float deltaTime); // Maneja la sacudida de "Arcade Edition" (time-based)
void processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite = nullptr); // Procesa el efecto de sacudida en sprites (frame-based)
void processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite, float deltaTime); // Procesa el efecto de sacudida en sprites (time-based)
void processArcadeEditionShake(); // Procesa la sacudida específica de "Arcade Edition" (frame-based)
void processArcadeEditionShake(float deltaTime); // Procesa la sacudida específica de "Arcade Edition" (time-based)
[[nodiscard]] auto calculateShakeDisplacement() const -> int; // Calcula el desplazamiento de la sacudida [[nodiscard]] auto calculateShakeDisplacement() const -> int; // Calcula el desplazamiento de la sacudida
// --- Gestión de finalización de efectos --- // --- Gestión de finalización de efectos ---
void handleCoffeeCrisisFinished(); // Maneja el final de la animación "Coffee Crisis" void handleCoffeeCrisisFinished(); // Maneja el final de la animación "Coffee Crisis" (frame-based)
void handleCoffeeCrisisFinished(float deltaTime); // Maneja el final de la animación "Coffee Crisis" (time-based)
void finishCoffeeCrisisShaking(); // Finaliza la sacudida de "Coffee Crisis" void finishCoffeeCrisisShaking(); // Finaliza la sacudida de "Coffee Crisis"
void finishArcadeEditionMoving(); // Finaliza el movimiento de "Arcade Edition" void finishArcadeEditionMoving(); // Finaliza el movimiento de "Arcade Edition"
// --- Utilidades --- // --- Utilidades ---
static void playTitleEffects(); // Reproduce efectos visuales/sonoros del título static void playTitleEffects(); // Reproduce efectos visuales/sonoros del título
void updateDustSprites(); // Actualiza los sprites de polvo void updateDustSprites(); // Actualiza los sprites de polvo (frame-based)
void updateDustSprites(float deltaTime); // Actualiza los sprites de polvo (time-based)
}; };

View File

@@ -32,7 +32,9 @@ class Input {
bool just_pressed; // Se acaba de pulsar en este fotograma bool just_pressed; // Se acaba de pulsar en este fotograma
KeyState(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false) KeyState(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false)
: scancode(scancode), is_held(is_held), just_pressed(just_pressed) {} : scancode(scancode),
is_held(is_held),
just_pressed(just_pressed) {}
}; };
struct ButtonState { struct ButtonState {
@@ -43,7 +45,10 @@ class Input {
bool trigger_active{false}; // Estado del trigger como botón digital bool trigger_active{false}; // Estado del trigger como botón digital
ButtonState(int btn = static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID), bool is_held = false, bool just_pressed = false, bool axis_act = false) ButtonState(int btn = static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID), bool is_held = false, bool just_pressed = false, bool axis_act = false)
: button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act) {} : button(btn),
is_held(is_held),
just_pressed(just_pressed),
axis_active(axis_act) {}
}; };
struct Keyboard { struct Keyboard {

View File

@@ -9,7 +9,9 @@
class Texture; // lines 6-6 class Texture; // lines 6-6
Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation) Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation)
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)), play_area_(play_area), type_(type) { : sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
play_area_(play_area),
type_(type) {
switch (type) { switch (type) {
case ItemType::COFFEE_MACHINE: { case ItemType::COFFEE_MACHINE: {
width_ = COFFEE_MACHINE_WIDTH; width_ = COFFEE_MACHINE_WIDTH;

View File

@@ -19,7 +19,9 @@ struct Language {
std::string file_name; // Nombre del fichero con los textos std::string file_name; // Nombre del fichero con los textos
Language(Code c, std::string n, std::string fn) Language(Code c, std::string n, std::string fn)
: code(c), name(std::move(n)), file_name(std::move(fn)) {} : code(c),
name(std::move(n)),
file_name(std::move(fn)) {}
}; };
// --- Funciones --- // --- Funciones ---

View File

@@ -10,8 +10,10 @@ struct HiScoreEntry {
bool one_credit_complete; // Indica si se ha conseguido 1CC bool one_credit_complete; // Indica si se ha conseguido 1CC
// Constructor // Constructor
explicit HiScoreEntry(const std::string &n = "", int s = 0, bool occ = false) explicit HiScoreEntry(const std::string &name = "", int score = 0, bool one_credit_complete = false)
: name(n.substr(0, 6)), score(s), one_credit_complete(occ) {} : name(name.substr(0, 6)),
score(score),
one_credit_complete(one_credit_complete) {}
}; };
// --- Tipos --- // --- Tipos ---

View File

@@ -53,7 +53,7 @@ void MovingSprite::stop() {
flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite
} }
// Mueve el sprite // Mueve el sprite (frame-based)
void MovingSprite::move() { void MovingSprite::move() {
x_ += vx_; x_ += vx_;
y_ += vy_; y_ += vy_;
@@ -65,16 +65,39 @@ void MovingSprite::move() {
pos_.y = static_cast<int>(y_); pos_.y = static_cast<int>(y_);
} }
// Actualiza las variables internas del objeto // Mueve el sprite (time-based)
void MovingSprite::move(float deltaTime) {
// Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
float frameFactor = deltaTime / (1000.0f / 60.0f);
x_ += vx_ * frameFactor;
y_ += vy_ * frameFactor;
vx_ += ax_ * frameFactor;
vy_ += ay_ * frameFactor;
pos_.x = static_cast<int>(x_);
pos_.y = static_cast<int>(y_);
}
// Actualiza las variables internas del objeto (frame-based)
void MovingSprite::update() { void MovingSprite::update() {
move(); move();
rotate(); rotate();
} }
// Muestra el sprite por pantalla // Actualiza las variables internas del objeto (time-based)
void MovingSprite::render() { getTexture()->render(pos_.x, pos_.y, &sprite_clip_, horizontal_zoom_, vertical_zoom_, rotate_.angle, &rotate_.center, flip_); } void MovingSprite::update(float deltaTime) {
move(deltaTime);
rotate(deltaTime);
}
// Establece la rotacion // Muestra el sprite por pantalla
void MovingSprite::render() {
getTexture()->render(pos_.x, pos_.y, &sprite_clip_, horizontal_zoom_, vertical_zoom_, rotate_.angle, &rotate_.center, flip_);
}
// Establece la rotacion (frame-based)
void MovingSprite::rotate() { void MovingSprite::rotate() {
if (rotate_.enabled) { if (rotate_.enabled) {
++rotate_.counter; ++rotate_.counter;
@@ -85,6 +108,15 @@ void MovingSprite::rotate() {
} }
} }
// Establece la rotacion (time-based)
void MovingSprite::rotate(float deltaTime) {
if (rotate_.enabled) {
// Convertir speed (frames) a tiempo: speed frames = speed * (1000ms/60fps) milisegundos
float rotationFrameTime = static_cast<float>(rotate_.speed) * (1000.0f / 60.0f);
rotate_.angle += rotate_.amount * (deltaTime / rotationFrameTime);
}
}
// Activa o desactiva el efecto de rotación // Activa o desactiva el efecto de rotación
void MovingSprite::setRotate(bool enable) { void MovingSprite::setRotate(bool enable) {
rotate_.enabled = enable; rotate_.enabled = enable;

View File

@@ -29,7 +29,8 @@ class MovingSprite : public Sprite {
~MovingSprite() override = default; ~MovingSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
virtual void update(); // Actualiza las variables internas del objeto virtual void update(); // Actualiza las variables internas del objeto (frame-based)
virtual void update(float deltaTime); // Actualiza las variables internas del objeto (time-based)
void clear() override; // Reinicia todas las variables a cero void clear() override; // Reinicia todas las variables a cero
void stop(); // Elimina el movimiento del sprite void stop(); // Elimina el movimiento del sprite
void render() override; // Muestra el sprite por pantalla void render() override; // Muestra el sprite por pantalla
@@ -79,6 +80,8 @@ class MovingSprite : public Sprite {
// --- Métodos internos --- // --- Métodos internos ---
void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo
void move(); // Mueve el sprite según velocidad y aceleración void move(); // Mueve el sprite según velocidad y aceleración (frame-based)
void rotate(); // Rota el sprite según los parámetros de rotación void move(float deltaTime); // Mueve el sprite según velocidad y aceleración (time-based)
void rotate(); // Rota el sprite según los parámetros de rotación (frame-based)
void rotate(float deltaTime); // Rota el sprite según los parámetros de rotación (time-based)
}; };

View File

@@ -106,7 +106,6 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
{"scoreboard.rect.h", [](const std::string& v) { param.scoreboard.rect.h = std::stoi(v); }}, {"scoreboard.rect.h", [](const std::string& v) { param.scoreboard.rect.h = std::stoi(v); }},
{"scoreboard.skip_countdown_value", [](const std::string& v) { param.scoreboard.skip_countdown_value = std::stoi(v); }}, {"scoreboard.skip_countdown_value", [](const std::string& v) { param.scoreboard.skip_countdown_value = std::stoi(v); }},
{"title.press_start_position", [](const std::string& v) { param.title.press_start_position = std::stoi(v); }}, {"title.press_start_position", [](const std::string& v) { param.title.press_start_position = std::stoi(v); }},
{"title.title_duration", [](const std::string& v) { param.title.title_duration = std::stoi(v); }},
{"title.arcade_edition_position", [](const std::string& v) { param.title.arcade_edition_position = std::stoi(v); }}, {"title.arcade_edition_position", [](const std::string& v) { param.title.arcade_edition_position = std::stoi(v); }},
{"title.title_c_c_position", [](const std::string& v) { param.title.title_c_c_position = std::stoi(v); }}, {"title.title_c_c_position", [](const std::string& v) { param.title.title_c_c_position = std::stoi(v); }},
{"intro.text_distance_from_bottom", [](const std::string& v) { param.intro.text_distance_from_bottom = std::stoi(v); }}}; {"intro.text_distance_from_bottom", [](const std::string& v) { param.intro.text_distance_from_bottom = std::stoi(v); }}};
@@ -182,6 +181,7 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
{"balloon.settings[3].grav", [](const std::string& v) { param.balloon.settings.at(3).grav = std::stof(v); }}, {"balloon.settings[3].grav", [](const std::string& v) { param.balloon.settings.at(3).grav = std::stof(v); }},
{"tabe.min_spawn_time", [](const std::string& v) { param.tabe.min_spawn_time = std::stof(v); }}, {"tabe.min_spawn_time", [](const std::string& v) { param.tabe.min_spawn_time = std::stof(v); }},
{"tabe.max_spawn_time", [](const std::string& v) { param.tabe.max_spawn_time = std::stof(v); }}, {"tabe.max_spawn_time", [](const std::string& v) { param.tabe.max_spawn_time = std::stof(v); }},
{"title.title_duration", [](const std::string& v) { param.title.title_duration = std::stof(v); }},
{"service_menu.window_message.padding", [](const std::string& v) { param.service_menu.window_message.padding = std::stof(v); }}, {"service_menu.window_message.padding", [](const std::string& v) { param.service_menu.window_message.padding = std::stof(v); }},
{"service_menu.window_message.line_spacing", [](const std::string& v) { param.service_menu.window_message.line_spacing = std::stof(v); }}, {"service_menu.window_message.line_spacing", [](const std::string& v) { param.service_menu.window_message.line_spacing = std::stof(v); }},
{"service_menu.window_message.title_separator_spacing", [](const std::string& v) { param.service_menu.window_message.title_separator_spacing = std::stof(v); }}, {"service_menu.window_message.title_separator_spacing", [](const std::string& v) { param.service_menu.window_message.title_separator_spacing = std::stof(v); }},
@@ -196,8 +196,10 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
// Colores válidos para globos // Colores válidos para globos
static const std::unordered_map<std::string, bool> VALID_BALLOON_COLORS = { static const std::unordered_map<std::string, bool> VALID_BALLOON_COLORS = {
{"blue", true}, {"orange", true}, {"red", true}, {"green", true} {"blue", true},
}; {"orange", true},
{"red", true},
{"green", true}};
auto validateBalloonColor = [](const std::string& color) -> bool { auto validateBalloonColor = [](const std::string& color) -> bool {
return VALID_BALLOON_COLORS.find(color) != VALID_BALLOON_COLORS.end(); return VALID_BALLOON_COLORS.find(color) != VALID_BALLOON_COLORS.end();

View File

@@ -38,7 +38,7 @@ struct ParamFade {
// --- Parámetros de la pantalla de título --- // --- Parámetros de la pantalla de título ---
struct ParamTitle { struct ParamTitle {
int press_start_position = GameDefaults::Title::PRESS_START_POSITION; int press_start_position = GameDefaults::Title::PRESS_START_POSITION;
int title_duration = GameDefaults::Title::DURATION; float title_duration = GameDefaults::Title::DURATION;
int arcade_edition_position = GameDefaults::Title::ARCADE_EDITION_POSITION; int arcade_edition_position = GameDefaults::Title::ARCADE_EDITION_POSITION;
int title_c_c_position = GameDefaults::Title::TITLE_C_C_POSITION; int title_c_c_position = GameDefaults::Title::TITLE_C_C_POSITION;
Color bg_color = Color::fromHex(GameDefaults::Title::BG_COLOR); Color bg_color = Color::fromHex(GameDefaults::Title::BG_COLOR);
@@ -57,7 +57,8 @@ struct ParamBalloon {
// Constructor por defecto // Constructor por defecto
constexpr Settings(float grav_val = 0.0F, float vel_val = 0.0F) constexpr Settings(float grav_val = 0.0F, float vel_val = 0.0F)
: grav(grav_val), vel(vel_val) {} : grav(grav_val),
vel(vel_val) {}
}; };
// Inicialización con los valores por defecto desde GameDefaults // Inicialización con los valores por defecto desde GameDefaults
@@ -164,7 +165,10 @@ struct ParamPlayer {
// Constructor con tonalidades específicas // Constructor con tonalidades específicas
Shirt(const Color& darkest_tone, const Color& dark_tone, const Color& base_tone, const Color& light_tone) Shirt(const Color& darkest_tone, const Color& dark_tone, const Color& base_tone, const Color& light_tone)
: darkest(darkest_tone), dark(dark_tone), base(base_tone), light(light_tone) {} : darkest(darkest_tone),
dark(dark_tone),
base(base_tone),
light(light_tone) {}
}; };
// Inicialización con valores por defecto // Inicialización con valores por defecto

View File

@@ -4,6 +4,13 @@
#include <functional> // Para function #include <functional> // Para function
#include <utility> // Para move #include <utility> // Para move
// Constructor para paths por puntos (compatibilidad)
Path::Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init)
: spots(spots_init), is_point_path(true) {
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f;
waiting_time_ms = static_cast<float>(waiting_counter_init) * FRAME_TIME_MS;
}
// Devuelve un vector con los puntos que conforman la ruta // Devuelve un vector con los puntos que conforman la ruta
auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easing_function) -> std::vector<SDL_FPoint> { auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easing_function) -> std::vector<SDL_FPoint> {
std::vector<SDL_FPoint> v; std::vector<SDL_FPoint> v;
@@ -32,10 +39,16 @@ auto createPath(float start, float end, PathType type, float fixed_pos, int step
return v; return v;
} }
// Actualiza la posición y comprueba si ha llegado a su destino // Actualiza la posición y comprueba si ha llegado a su destino (compatibilidad)
void PathSprite::update() { void PathSprite::update() {
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f; // 16.67ms por frame a 60 FPS
update(FRAME_TIME_MS);
}
// Actualiza la posición y comprueba si ha llegado a su destino
void PathSprite::update(float delta_time) {
if (enabled_ && !has_finished_) { if (enabled_ && !has_finished_) {
moveThroughCurrentPath(); moveThroughCurrentPath(delta_time);
goToNextPathOrDie(); goToNextPathOrDie();
} }
} }
@@ -79,7 +92,13 @@ void PathSprite::addPath(Path path, bool centered) {
// Añade un recorrido // Añade un recorrido
void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easing_function, int waiting_counter) { void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easing_function, int waiting_counter) {
paths_.emplace_back(createPath(start, end, type, fixed_pos, steps, easing_function), waiting_counter); // Convertir frames a milisegundos
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f;
float duration_ms = static_cast<float>(steps) * FRAME_TIME_MS;
float waiting_ms = static_cast<float>(waiting_counter) * FRAME_TIME_MS;
paths_.emplace_back(static_cast<float>(start), static_cast<float>(end), type, static_cast<float>(fixed_pos),
duration_ms, waiting_ms, easing_function);
} }
// Añade un recorrido // Añade un recorrido
@@ -95,21 +114,32 @@ void PathSprite::enable() {
enabled_ = true; enabled_ = true;
// Establece la posición // Establece la posición inicial
auto &path = paths_.at(current_path_); auto &path = paths_.at(current_path_);
if (path.is_point_path) {
const auto &p = path.spots.at(path.counter); const auto &p = path.spots.at(path.counter);
setPosition(p); setPosition(p);
} else {
// Para paths generados, establecer posición inicial
SDL_FPoint initial_pos;
if (path.type == PathType::HORIZONTAL) {
initial_pos = {path.start_pos, path.fixed_pos};
} else {
initial_pos = {path.fixed_pos, path.start_pos};
}
setPosition(initial_pos);
}
} }
// Coloca el sprite en los diferentes puntos del recorrido // Coloca el sprite en los diferentes puntos del recorrido
void PathSprite::moveThroughCurrentPath() { void PathSprite::moveThroughCurrentPath(float delta_time) {
auto &path = paths_.at(current_path_); auto &path = paths_.at(current_path_);
// Establece la posición if (path.is_point_path) {
// Lógica para paths por puntos (compatibilidad)
const auto &p = path.spots.at(path.counter); const auto &p = path.spots.at(path.counter);
setPosition(p); setPosition(p);
// Comprobar si ha terminado el recorrido
if (!path.on_destination) { if (!path.on_destination) {
++path.counter; ++path.counter;
if (path.counter >= static_cast<int>(path.spots.size())) { if (path.counter >= static_cast<int>(path.spots.size())) {
@@ -118,12 +148,44 @@ void PathSprite::moveThroughCurrentPath() {
} }
} }
// Comprobar si ha terminado la espera
if (path.on_destination) { if (path.on_destination) {
if (path.waiting_counter == 0) { path.waiting_elapsed += delta_time;
if (path.waiting_elapsed >= path.waiting_time_ms) {
path.finished = true; path.finished = true;
}
}
} else { } else {
--path.waiting_counter; // Lógica para paths generados en tiempo real
if (!path.on_destination) {
path.elapsed_time += delta_time;
// Calcular progreso (0.0 a 1.0)
float progress = path.elapsed_time / path.duration_ms;
if (progress >= 1.0f) {
progress = 1.0f;
path.on_destination = true;
}
// Aplicar función de easing
double eased_progress = path.easing_function(progress);
// Calcular posición actual
float current_pos = path.start_pos + (path.end_pos - path.start_pos) * static_cast<float>(eased_progress);
// Establecer posición según el tipo
SDL_FPoint position;
if (path.type == PathType::HORIZONTAL) {
position = {current_pos, path.fixed_pos};
} else {
position = {path.fixed_pos, current_pos};
}
setPosition(position);
} else {
// Esperar en destino
path.waiting_elapsed += delta_time;
if (path.waiting_elapsed >= path.waiting_time_ms) {
path.finished = true;
}
} }
} }
} }

View File

@@ -25,15 +25,30 @@ enum class PathCentered { // Centrado del recorrido
// --- Estructuras --- // --- Estructuras ---
struct Path { // Define un recorrido para el sprite struct Path { // Define un recorrido para el sprite
std::vector<SDL_FPoint> spots; // Puntos por los que se desplazará el sprite float start_pos; // Posición inicial
int waiting_counter; // Tiempo de espera una vez en el destino float end_pos; // Posición final
PathType type; // Tipo de movimiento (horizontal/vertical)
float fixed_pos; // Posición fija en el eje contrario
float duration_ms; // Duración de la animación en milisegundos
float waiting_time_ms; // Tiempo de espera una vez en el destino
std::function<double(double)> easing_function; // Función de easing
float elapsed_time = 0.0f; // Tiempo transcurrido
float waiting_elapsed = 0.0f; // Tiempo de espera transcurrido
bool on_destination = false; // Indica si ha llegado al destino bool on_destination = false; // Indica si ha llegado al destino
bool finished = false; // Indica si ha terminado de esperarse bool finished = false; // Indica si ha terminado de esperarse
int counter = 0; // Contador interno
// Constructor // Constructor para paths generados
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init) Path(float start, float end, PathType path_type, float fixed, float duration, float waiting, std::function<double(double)> easing)
: spots(spots_init), waiting_counter(waiting_counter_init) {} : start_pos(start), end_pos(end), type(path_type), fixed_pos(fixed),
duration_ms(duration), waiting_time_ms(waiting), easing_function(std::move(easing)) {}
// Constructor para paths por puntos (mantenemos compatibilidad)
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init);
// Variables para paths por puntos
std::vector<SDL_FPoint> spots; // Solo para paths por puntos
int counter = 0; // Solo para paths por puntos
bool is_point_path = false; // Indica si es un path por puntos
}; };
// --- Funciones --- // --- Funciones ---
@@ -48,7 +63,8 @@ class PathSprite : public Sprite {
~PathSprite() override = default; ~PathSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la posición del sprite según el recorrido void update(); // Actualiza la posición del sprite según el recorrido (compatibilidad)
void update(float delta_time); // Actualiza la posición del sprite según el recorrido
void render() override; // Muestra el sprite por pantalla void render() override; // Muestra el sprite por pantalla
// --- Gestión de recorridos --- // --- Gestión de recorridos ---
@@ -71,6 +87,6 @@ class PathSprite : public Sprite {
std::vector<Path> paths_; // Caminos a recorrer por el sprite std::vector<Path> paths_; // Caminos a recorrer por el sprite
// --- Métodos internos --- // --- Métodos internos ---
void moveThroughCurrentPath(); // Coloca el sprite en los diferentes puntos del recorrido void moveThroughCurrentPath(float delta_time); // Coloca el sprite en los diferentes puntos del recorrido
void goToNextPathOrDie(); // Cambia de recorrido o finaliza void goToNextPathOrDie(); // Cambia de recorrido o finaliza
}; };

View File

@@ -3,6 +3,7 @@
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_FlipMode #include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_FlipMode
#include <algorithm> // Para clamp, max, min #include <algorithm> // Para clamp, max, min
#include <cmath> // Para fmod
#include <cstdlib> // Para rand #include <cstdlib> // Para rand
#include "animated_sprite.h" // Para AnimatedSprite #include "animated_sprite.h" // Para AnimatedSprite
@@ -22,7 +23,17 @@
// Constructor // Constructor
Player::Player(const Config &config) Player::Player(const Config &config)
: player_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(0), config.animations.at(0))), power_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(4), config.animations.at(1))), enter_name_(std::make_unique<EnterName>()), hi_score_table_(config.hi_score_table), glowing_entry_(config.glowing_entry), stage_info_(config.stage_info), play_area_(*config.play_area), id_(config.id), default_pos_x_(config.x), default_pos_y_(config.y), demo_(config.demo) { : player_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(0), config.animations.at(0))),
power_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(4), config.animations.at(1))),
enter_name_(std::make_unique<EnterName>()),
hi_score_table_(config.hi_score_table),
glowing_entry_(config.glowing_entry),
stage_info_(config.stage_info),
play_area_(*config.play_area),
id_(config.id),
default_pos_x_(config.x),
default_pos_y_(config.y),
demo_(config.demo) {
// Configura objetos // Configura objetos
player_sprite_->addTexture(config.texture.at(1)); player_sprite_->addTexture(config.texture.at(1));
player_sprite_->addTexture(config.texture.at(2)); player_sprite_->addTexture(config.texture.at(2));
@@ -49,17 +60,14 @@ void Player::init() {
power_up_counter_ = POWERUP_COUNTER; power_up_counter_ = POWERUP_COUNTER;
extra_hit_ = false; extra_hit_ = false;
coffees_ = 0; coffees_ = 0;
continue_ticks_ = 0;
continue_counter_ = 10; continue_counter_ = 10;
name_entry_ticks_ = 0; name_entry_idle_time_accumulator_ = 0.0f;
name_entry_idle_counter_ = 0; name_entry_total_time_accumulator_ = 0.0f;
name_entry_total_counter_ = 0;
shiftColliders(); shiftColliders();
vel_x_ = 0; vel_x_ = 0;
vel_y_ = 0; vel_y_ = 0;
score_ = 0; score_ = 0;
score_multiplier_ = 1.0F; score_multiplier_ = 1.0F;
cant_fire_counter_ = 10;
enter_name_->init(last_enter_name_); enter_name_->init(last_enter_name_);
// Establece la posición del sprite // Establece la posición del sprite
@@ -138,7 +146,7 @@ void Player::setInputEnteringName(Input::Action action) {
default: default:
break; break;
} }
name_entry_idle_counter_ = 0; name_entry_idle_time_accumulator_ = 0.0f;
} }
// Mueve el jugador a la posición y animación que le corresponde // Mueve el jugador a la posición y animación que le corresponde
@@ -176,6 +184,41 @@ void Player::move() {
} }
} }
// Fase 1: Sistema de movimiento time-based
void Player::move(float deltaTime) {
switch (playing_state_) {
case State::PLAYING:
handlePlayingMovement(deltaTime);
break;
case State::ROLLING:
handleRollingMovement(); // Usa MovingSprite que ya soporta deltaTime
break;
case State::TITLE_ANIMATION:
handleTitleAnimation(); // Sin cambios - usa solo animaciones
break;
case State::CONTINUE_TIME_OUT:
handleContinueTimeOut(); // Sin cambios - usa MovingSprite que ya soporta deltaTime
break;
case State::LEAVING_SCREEN:
handleLeavingScreen(); // Sin cambios - usa MovingSprite que ya soporta deltaTime
break;
case State::ENTERING_SCREEN:
handleEnteringScreen(); // Sin cambios - usa MovingSprite que ya soporta deltaTime
break;
case State::CREDITS:
handleCreditsMovement(deltaTime); // Fase 4: Sistema de créditos time-based
break;
case State::WAITING:
handleWaitingMovement(deltaTime); // Fase 4: Sistema de waiting time-based
break;
case State::RECOVER:
handleRecoverMovement(); // Sin cambios - usa AnimatedSprite que ya soporta deltaTime
break;
default:
break;
}
}
void Player::handlePlayingMovement() { void Player::handlePlayingMovement() {
// Mueve el jugador a derecha o izquierda // Mueve el jugador a derecha o izquierda
pos_x_ += vel_x_; pos_x_ += vel_x_;
@@ -188,6 +231,19 @@ void Player::handlePlayingMovement() {
shiftSprite(); shiftSprite();
} }
// Fase 1: Movimiento time-based durante el juego
void Player::handlePlayingMovement(float deltaTime) {
// Mueve el jugador a derecha o izquierda (time-based)
pos_x_ += vel_x_ * deltaTime / (1000.0f / 60.0f);
// Si el jugador abandona el area de juego por los laterales, restaura su posición
const float MIN_X = play_area_.x - 5;
const float MAX_X = play_area_.w + 5 - WIDTH;
pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);
shiftSprite();
}
void Player::handleRecoverMovement() { void Player::handleRecoverMovement() {
if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_recover.wav"); } if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_recover.wav"); }
if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); } if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); }
@@ -327,6 +383,20 @@ void Player::handleCreditsMovement() {
shiftSprite(); shiftSprite();
} }
// Fase 4: Movimiento general en la pantalla de créditos (time-based)
void Player::handleCreditsMovement(float deltaTime) {
pos_x_ += (vel_x_ / 2.0F) * deltaTime / (1000.0f / 60.0f); // Convert frame-based movement to time-based
if (vel_x_ > 0) {
handleCreditsRightMovement();
} else {
handleCreditsLeftMovement();
}
updateWalkingStateForCredits();
shiftSprite();
}
void Player::handleCreditsRightMovement() { void Player::handleCreditsRightMovement() {
if (pos_x_ > param.game.game_area.rect.w - WIDTH) { if (pos_x_ > param.game.game_area.rect.w - WIDTH) {
pos_x_ = param.game.game_area.rect.w - WIDTH; pos_x_ = param.game.game_area.rect.w - WIDTH;
@@ -349,6 +419,16 @@ void Player::handleWaitingMovement() {
} }
} }
// Fase 4: Controla la animación del jugador saludando (time-based)
void Player::handleWaitingMovement(float deltaTime) {
waiting_time_accumulator_ += deltaTime;
float waiting_duration = static_cast<float>(WAITING_COUNTER) * (1000.0f / 60.0f); // Convert frames to milliseconds
if (waiting_time_accumulator_ >= waiting_duration) {
waiting_time_accumulator_ = 0.0f;
player_sprite_->resetAnimation();
}
}
void Player::updateWalkingStateForCredits() { void Player::updateWalkingStateForCredits() {
if (pos_x_ > param.game.game_area.center_x - WIDTH / 2) { if (pos_x_ > param.game.game_area.center_x - WIDTH / 2) {
setWalkingState(State::WALKING_LEFT); setWalkingState(State::WALKING_LEFT);
@@ -377,11 +457,35 @@ void Player::updateStepCounter() {
} }
} }
// Fase 4: Incrementa o ajusta el contador de pasos (time-based)
void Player::updateStepCounter(float deltaTime) {
step_time_accumulator_ += deltaTime;
float step_interval = 10.0f / 60.0f; // 10 frames converted to seconds
if (step_time_accumulator_ >= step_interval) {
step_time_accumulator_ = 0.0f;
playSound("walk.wav");
}
}
// Pinta el jugador en pantalla // Pinta el jugador en pantalla
void Player::render() { void Player::render() {
if (power_up_ && isPlaying()) { if (power_up_ && isPlaying()) {
if (power_up_counter_ > (POWERUP_COUNTER / 4) || power_up_counter_ % 20 > 4) { // Convertir lógica de parpadeo a deltaTime
float total_powerup_time_ms = static_cast<float>(POWERUP_COUNTER) / 60.0f * 1000.0f; // Total time in ms
float quarter_time_ms = total_powerup_time_ms / 4.0f; // 25% del tiempo total
if (power_up_time_accumulator_ > quarter_time_ms) {
// En los primeros 75% del tiempo, siempre visible
power_sprite_->render(); power_sprite_->render();
} else {
// En el último 25%, parpadea cada 20 frames (333ms aprox)
constexpr float blink_period_ms = 20.0f / 60.0f * 1000.0f; // 20 frames in ms
constexpr float visible_proportion = 4.0f / 20.0f; // 4 frames visible de 20 total
float cycle_position = fmod(power_up_time_accumulator_, blink_period_ms) / blink_period_ms;
if (cycle_position >= visible_proportion) {
power_sprite_->render();
}
} }
} }
@@ -444,7 +548,7 @@ auto Player::computeAnimation() const -> std::pair<std::string, SDL_FlipMode> {
return {anim_name, flip_mode}; return {anim_name, flip_mode};
} }
// Establece la animación correspondiente al estado // Establece la animación correspondiente al estado (frame-based)
void Player::setAnimation() { void Player::setAnimation() {
switch (playing_state_) { switch (playing_state_) {
case State::PLAYING: case State::PLAYING:
@@ -485,106 +589,70 @@ void Player::setAnimation() {
power_sprite_->update(); power_sprite_->update();
} }
// Actualiza el valor de la variable // Fase 1: Establece la animación correspondiente al estado (time-based)
void Player::updateCooldown() { void Player::setAnimation(float deltaTime) {
if (playing_state_ != State::PLAYING) { switch (playing_state_) {
return; case State::PLAYING:
case State::ENTERING_NAME_GAME_COMPLETED:
case State::ENTERING_SCREEN:
case State::LEAVING_SCREEN:
case State::TITLE_ANIMATION:
case State::CREDITS: {
auto [animName, flipMode] = computeAnimation();
player_sprite_->setCurrentAnimation(animName, false);
player_sprite_->setFlip(flipMode);
break;
}
case State::RECOVER:
player_sprite_->setCurrentAnimation("recover");
break;
case State::WAITING:
case State::GAME_OVER:
player_sprite_->setCurrentAnimation("hello");
break;
case State::ROLLING:
case State::CONTINUE_TIME_OUT:
player_sprite_->setCurrentAnimation("rolling");
break;
case State::LYING_ON_THE_FLOOR_FOREVER:
case State::ENTERING_NAME:
case State::CONTINUE:
player_sprite_->setCurrentAnimation("dizzy");
break;
case State::CELEBRATING:
player_sprite_->setCurrentAnimation("celebration");
break;
default:
break;
} }
if (cant_fire_counter_ > 0) { // La diferencia clave: usa deltaTime para las animaciones
handleFiringCooldown(); player_sprite_->update(deltaTime);
power_sprite_->update(deltaTime);
}
// Actualiza al jugador con deltaTime (time-based)
void Player::update(float deltaTime) {
move(deltaTime); // Sistema de movimiento time-based
setAnimation(deltaTime); // Animaciones time-based
shiftColliders(); // Sin cambios (posicional)
updateFireSystem(deltaTime); // Sistema de disparo de dos líneas
updatePowerUp(deltaTime); // Sistema de power-up time-based
updateInvulnerable(deltaTime); // Sistema de invulnerabilidad time-based
updateScoreboard(); // Sin cambios (no temporal)
updateContinueCounter(deltaTime); // Sistema de continue time-based
updateEnterNameCounter(deltaTime); // Sistema de name entry time-based
updateShowingName(deltaTime); // Sistema de showing name time-based
}
void Player::passShowingName() {
if (game_completed_) {
setPlayingState(State::LEAVING_SCREEN);
} else { } else {
handleRecoilAndCooling(); setPlayingState(State::CONTINUE);
} }
} }
void Player::handleFiringCooldown() {
cooling_state_counter_ = COOLING_DURATION;
// Transition to recoiling state at halfway point
if (cant_fire_counter_ == recoiling_state_duration_ / 2) {
transitionToRecoiling();
}
--cant_fire_counter_;
if (cant_fire_counter_ == 0) {
recoiling_state_counter_ = recoiling_state_duration_;
}
}
void Player::handleRecoilAndCooling() {
if (recoiling_state_counter_ > 0) {
--recoiling_state_counter_;
return;
}
handleCoolingState();
}
void Player::handleCoolingState() {
if (cooling_state_counter_ > COOLING_COMPLETE) {
if (cooling_state_counter_ == COOLING_DURATION) {
transitionToCooling();
}
--cooling_state_counter_;
}
if (cooling_state_counter_ == COOLING_COMPLETE) {
completeCooling();
}
}
void Player::transitionToRecoiling() {
switch (firing_state_) {
case State::FIRING_LEFT:
setFiringState(State::RECOILING_LEFT);
break;
case State::FIRING_RIGHT:
setFiringState(State::RECOILING_RIGHT);
break;
case State::FIRING_UP:
setFiringState(State::RECOILING_UP);
break;
default:
break;
}
}
void Player::transitionToCooling() {
switch (firing_state_) {
case State::RECOILING_LEFT:
setFiringState(State::COOLING_LEFT);
break;
case State::RECOILING_RIGHT:
setFiringState(State::COOLING_RIGHT);
break;
case State::RECOILING_UP:
setFiringState(State::COOLING_UP);
break;
default:
break;
}
}
void Player::completeCooling() {
setFiringState(State::FIRING_NONE);
cooling_state_counter_ = -1;
}
// Actualiza al jugador a su posicion, animación y controla los contadores
void Player::update() {
move();
setAnimation();
shiftColliders();
updateCooldown();
updatePowerUp();
updateInvulnerable();
updateScoreboard();
updateContinueCounter();
updateEnterNameCounter();
updateShowingName();
}
// Incrementa la puntuación del jugador // Incrementa la puntuación del jugador
void Player::addScore(int score, int lowest_hi_score_entry) { void Player::addScore(int score, int lowest_hi_score_entry) {
if (isPlaying()) { if (isPlaying()) {
@@ -624,6 +692,9 @@ void Player::setPlayingState(State state) {
switch (playing_state_) { switch (playing_state_) {
case State::RECOVER: { case State::RECOVER: {
score_ = 0; // Pon los puntos a cero para que no se vea en el marcador
score_multiplier_ = 1.0F;
setScoreboardMode(Scoreboard::Mode::SCORE);
break; break;
} }
case State::RESPAWNING: { case State::RESPAWNING: {
@@ -641,8 +712,8 @@ void Player::setPlayingState(State state) {
} }
case State::CONTINUE: { case State::CONTINUE: {
// Inicializa el contador de continuar // Inicializa el contador de continuar
continue_ticks_ = SDL_GetTicks();
continue_counter_ = 9; continue_counter_ = 9;
continue_time_accumulator_ = 0.0f; // Initialize time accumulator
playSound("continue_clock.wav"); playSound("continue_clock.wav");
setScoreboardMode(Scoreboard::Mode::CONTINUE); setScoreboardMode(Scoreboard::Mode::CONTINUE);
break; break;
@@ -661,6 +732,7 @@ void Player::setPlayingState(State state) {
} }
pos_y_ = default_pos_y_; pos_y_ = default_pos_y_;
waiting_counter_ = 0; waiting_counter_ = 0;
waiting_time_accumulator_ = 0.0f; // Initialize time accumulator
shiftSprite(); shiftSprite();
player_sprite_->setCurrentAnimation("hello"); player_sprite_->setCurrentAnimation("hello");
player_sprite_->animtionPause(); player_sprite_->animtionPause();
@@ -672,7 +744,7 @@ void Player::setPlayingState(State state) {
break; break;
} }
case State::SHOWING_NAME: { case State::SHOWING_NAME: {
showing_name_ticks_ = SDL_GetTicks(); showing_name_time_accumulator_ = 0.0f; // Inicializar acumulador time-based
setScoreboardMode(Scoreboard::Mode::SHOW_NAME); setScoreboardMode(Scoreboard::Mode::SHOW_NAME);
Scoreboard::get()->setRecordName(scoreboard_panel_, last_enter_name_); Scoreboard::get()->setRecordName(scoreboard_panel_, last_enter_name_);
addScoreToScoreBoard(); addScoreToScoreBoard();
@@ -728,12 +800,14 @@ void Player::setPlayingState(State state) {
} }
case State::LEAVING_SCREEN: { case State::LEAVING_SCREEN: {
step_counter_ = 0; step_counter_ = 0;
step_time_accumulator_ = 0.0f; // Initialize time accumulator
setScoreboardMode(Scoreboard::Mode::GAME_COMPLETED); setScoreboardMode(Scoreboard::Mode::GAME_COMPLETED);
break; break;
} }
case State::ENTERING_SCREEN: { case State::ENTERING_SCREEN: {
init(); init();
step_counter_ = 0; step_counter_ = 0;
step_time_accumulator_ = 0.0f; // Initialize time accumulator
setScoreboardMode(Scoreboard::Mode::SCORE); setScoreboardMode(Scoreboard::Mode::SCORE);
switch (id_) { switch (id_) {
case Id::PLAYER1: case Id::PLAYER1:
@@ -774,24 +848,26 @@ void Player::decScoreMultiplier() {
void Player::setInvulnerable(bool value) { void Player::setInvulnerable(bool value) {
invulnerable_ = value; invulnerable_ = value;
invulnerable_counter_ = invulnerable_ ? INVULNERABLE_COUNTER : 0; invulnerable_counter_ = invulnerable_ ? INVULNERABLE_COUNTER : 0;
invulnerable_time_accumulator_ = invulnerable_ ? static_cast<float>(INVULNERABLE_COUNTER) / 60.0f * 1000.0f : 0.0f; // Convert frames to milliseconds
} }
// Monitoriza el estado // Monitoriza el estado (time-based)
void Player::updateInvulnerable() { void Player::updateInvulnerable(float deltaTime) {
if (playing_state_ == State::PLAYING && invulnerable_) { if (playing_state_ == State::PLAYING && invulnerable_) {
if (invulnerable_counter_ > 0) { if (invulnerable_time_accumulator_ > 0) {
--invulnerable_counter_; invulnerable_time_accumulator_ -= deltaTime;
// Frecuencia fija de parpadeo (como el original) // Frecuencia fija de parpadeo adaptada a deltaTime (en milisegundos)
constexpr int blink_speed = 8; constexpr float blink_period_ms = 8.0f / 60.0f * 1000.0f; // 8 frames convertidos a ms
// Calcula proporción decreciente: menos textura blanca hacia el final // Calcula proporción decreciente basada en tiempo restante
// Al inicio: 50-50, hacia el final: 70-30 (menos blanco) float total_invulnerable_time_ms = static_cast<float>(INVULNERABLE_COUNTER) / 60.0f * 1000.0f;
float progress = 1.0f - (static_cast<float>(invulnerable_counter_) / INVULNERABLE_COUNTER); float progress = 1.0f - (invulnerable_time_accumulator_ / total_invulnerable_time_ms);
int white_frames = static_cast<int>((0.5f - progress * 0.2f) * blink_speed); float white_proportion = 0.5f - progress * 0.2f; // Menos blanco hacia el final
// Alterna entre texturas con proporción variable // Calcula si debe mostrar textura de invulnerabilidad basado en el ciclo temporal
bool should_show_invulnerable = (invulnerable_counter_ % blink_speed) < white_frames; float cycle_position = fmod(invulnerable_time_accumulator_, blink_period_ms) / blink_period_ms;
bool should_show_invulnerable = cycle_position < white_proportion;
size_t target_texture = should_show_invulnerable ? INVULNERABLE_TEXTURE : coffees_; size_t target_texture = should_show_invulnerable ? INVULNERABLE_TEXTURE : coffees_;
// Solo cambia textura si es diferente (optimización) // Solo cambia textura si es diferente (optimización)
@@ -800,6 +876,7 @@ void Player::updateInvulnerable() {
} }
} else { } else {
// Fin de invulnerabilidad // Fin de invulnerabilidad
invulnerable_time_accumulator_ = 0;
setInvulnerable(false); setInvulnerable(false);
player_sprite_->setActiveTexture(coffees_); player_sprite_->setActiveTexture(coffees_);
} }
@@ -810,14 +887,18 @@ void Player::updateInvulnerable() {
void Player::setPowerUp() { void Player::setPowerUp() {
power_up_ = true; power_up_ = true;
power_up_counter_ = POWERUP_COUNTER; power_up_counter_ = POWERUP_COUNTER;
power_up_time_accumulator_ = static_cast<float>(POWERUP_COUNTER) / 60.0f * 1000.0f; // Convert frames to milliseconds
} }
// Actualiza el valor de la variable // Actualiza el valor de la variable (time-based)
void Player::updatePowerUp() { void Player::updatePowerUp(float deltaTime) {
if (playing_state_ == State::PLAYING) { if (playing_state_ == State::PLAYING) {
if (power_up_) { if (power_up_) {
--power_up_counter_; power_up_time_accumulator_ -= deltaTime;
power_up_ = power_up_counter_ > 0; power_up_ = power_up_time_accumulator_ > 0;
if (!power_up_) {
power_up_time_accumulator_ = 0;
}
} }
} }
} }
@@ -854,31 +935,36 @@ void Player::setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &text
power_sprite_->setTexture(texture[1]); power_sprite_->setTexture(texture[1]);
} }
// Actualiza el contador de continue // Actualiza el contador de continue (time-based)
void Player::updateContinueCounter() { void Player::updateContinueCounter(float deltaTime) {
if (playing_state_ == State::CONTINUE) { if (playing_state_ == State::CONTINUE) {
constexpr int TICKS_SPEED = 1000; continue_time_accumulator_ += deltaTime;
if (SDL_GetTicks() - continue_ticks_ > TICKS_SPEED) { constexpr float CONTINUE_INTERVAL = 1000.0f; // 1 segundo en milisegundos
if (continue_time_accumulator_ >= CONTINUE_INTERVAL) {
continue_time_accumulator_ -= CONTINUE_INTERVAL;
decContinueCounter(); decContinueCounter();
} }
} }
} }
// Actualiza el contador de entrar nombre // Actualiza el contador de entrar nombre (time-based)
void Player::updateEnterNameCounter() { void Player::updateEnterNameCounter(float deltaTime) {
if (playing_state_ == State::ENTERING_NAME || playing_state_ == State::ENTERING_NAME_GAME_COMPLETED) { if (playing_state_ == State::ENTERING_NAME || playing_state_ == State::ENTERING_NAME_GAME_COMPLETED) {
constexpr int TICKS_SPEED = 1000; name_entry_time_accumulator_ += deltaTime;
if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED) { constexpr float NAME_ENTRY_INTERVAL = 1000.0f; // 1 segundo en milisegundos
if (name_entry_time_accumulator_ >= NAME_ENTRY_INTERVAL) {
name_entry_time_accumulator_ -= NAME_ENTRY_INTERVAL;
decNameEntryCounter(); decNameEntryCounter();
} }
} }
} }
// Actualiza el estado de SHOWING_NAME // Actualiza el estado de SHOWING_NAME (time-based)
void Player::updateShowingName() { void Player::updateShowingName(float deltaTime) {
if (playing_state_ == State::SHOWING_NAME) { if (playing_state_ == State::SHOWING_NAME) {
constexpr int TICKS_SPEED = 5000; showing_name_time_accumulator_ += deltaTime;
if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED) { constexpr float SHOWING_NAME_DURATION = 5000.0f; // 5 segundos en milisegundos
if (showing_name_time_accumulator_ >= SHOWING_NAME_DURATION) {
game_completed_ ? setPlayingState(State::LEAVING_SCREEN) : setPlayingState(State::CONTINUE); game_completed_ ? setPlayingState(State::LEAVING_SCREEN) : setPlayingState(State::CONTINUE);
} }
} }
@@ -886,7 +972,7 @@ void Player::updateShowingName() {
// Decrementa el contador de continuar // Decrementa el contador de continuar
void Player::decContinueCounter() { void Player::decContinueCounter() {
continue_ticks_ = SDL_GetTicks(); continue_time_accumulator_ = 0.0f; // Reset time accumulator
--continue_counter_; --continue_counter_;
if (continue_counter_ < 0) { if (continue_counter_ < 0) {
setPlayingState(State::CONTINUE_TIME_OUT); setPlayingState(State::CONTINUE_TIME_OUT);
@@ -897,17 +983,17 @@ void Player::decContinueCounter() {
// Decrementa el contador de entrar nombre // Decrementa el contador de entrar nombre
void Player::decNameEntryCounter() { void Player::decNameEntryCounter() {
name_entry_ticks_ = SDL_GetTicks(); name_entry_time_accumulator_ = 0.0f; // Reset time accumulator
// Actualiza contadores // Incrementa acumuladores de tiempo (1 segundo = 1000ms)
++name_entry_idle_counter_; name_entry_idle_time_accumulator_ += 1000.0f;
++name_entry_total_counter_; name_entry_total_time_accumulator_ += 1000.0f;
// Comprueba los contadores // Comprueba los acumuladores directamente contra los límites en milisegundos
if ((name_entry_total_counter_ >= param.game.name_entry_total_time) || if ((name_entry_total_time_accumulator_ >= param.game.name_entry_total_time) ||
(name_entry_idle_counter_ >= param.game.name_entry_idle_time)) { (name_entry_idle_time_accumulator_ >= param.game.name_entry_idle_time)) {
name_entry_total_counter_ = 0; name_entry_total_time_accumulator_ = 0.0f;
name_entry_idle_counter_ = 0; name_entry_idle_time_accumulator_ = 0.0f;
if (playing_state_ == State::ENTERING_NAME) { if (playing_state_ == State::ENTERING_NAME) {
last_enter_name_ = getRecordName(); last_enter_name_ = getRecordName();
setPlayingState(State::SHOWING_NAME); setPlayingState(State::SHOWING_NAME);
@@ -968,3 +1054,144 @@ void Player::addCredit() {
++credits_used_; ++credits_used_;
playSound("credit.wav"); playSound("credit.wav");
} }
// ========================================
// SISTEMA DE DISPARO DE DOS LÍNEAS
// ========================================
// Método principal del sistema de disparo
void Player::updateFireSystem(float deltaTime) {
updateFunctionalLine(deltaTime); // Línea 1: CanFire
updateVisualLine(deltaTime); // Línea 2: Animaciones
}
// LÍNEA 1: Sistema Funcional (CanFire)
void Player::updateFunctionalLine(float deltaTime) {
if (fire_cooldown_timer_ > 0) {
fire_cooldown_timer_ -= deltaTime;
can_fire_new_system_ = false;
} else {
fire_cooldown_timer_ = 0; // Evitar valores negativos
can_fire_new_system_ = true;
}
}
// LÍNEA 2: Sistema Visual (Animaciones)
void Player::updateVisualLine(float deltaTime) {
if (visual_fire_state_ == VisualFireState::NORMAL) {
return; // No hay temporizador activo en estado NORMAL
}
visual_state_timer_ -= deltaTime;
switch (visual_fire_state_) {
case VisualFireState::AIMING:
if (visual_state_timer_ <= 0) {
transitionToRecoilingNew();
}
break;
case VisualFireState::RECOILING:
if (visual_state_timer_ <= 0) {
transitionToThreatPose();
}
break;
case VisualFireState::THREAT_POSE:
if (visual_state_timer_ <= 0) {
transitionToNormalNew();
}
break;
case VisualFireState::NORMAL:
// Ya manejado arriba
break;
}
}
// Inicia un disparo en ambas líneas
void Player::startFiringSystem(int cooldown_frames) {
// LÍNEA 1: Inicia cooldown funcional
fire_cooldown_timer_ = static_cast<float>(cooldown_frames) / 60.0f * 1000.0f; // Convertir frames a ms
can_fire_new_system_ = false;
// LÍNEA 2: Resetea completamente el estado visual
aiming_duration_ = fire_cooldown_timer_ * AIMING_DURATION_FACTOR; // 50% del cooldown
recoiling_duration_ = aiming_duration_ * RECOILING_DURATION_MULTIPLIER; // 4 veces la duración de aiming
visual_fire_state_ = VisualFireState::AIMING;
visual_state_timer_ = aiming_duration_;
updateFiringStateFromVisual(); // Sincroniza firing_state_ para animaciones
}
// Sincroniza firing_state_ con visual_fire_state_
void Player::updateFiringStateFromVisual() {
// Mantener la dirección actual del disparo
State base_state = State::FIRING_NONE;
if (firing_state_ == State::FIRING_LEFT || firing_state_ == State::RECOILING_LEFT || firing_state_ == State::COOLING_LEFT) {
base_state = State::FIRING_LEFT;
} else if (firing_state_ == State::FIRING_RIGHT || firing_state_ == State::RECOILING_RIGHT || firing_state_ == State::COOLING_RIGHT) {
base_state = State::FIRING_RIGHT;
} else if (firing_state_ == State::FIRING_UP || firing_state_ == State::RECOILING_UP || firing_state_ == State::COOLING_UP) {
base_state = State::FIRING_UP;
}
switch (visual_fire_state_) {
case VisualFireState::NORMAL:
firing_state_ = State::FIRING_NONE;
break;
case VisualFireState::AIMING:
firing_state_ = base_state; // FIRING_LEFT/RIGHT/UP
break;
case VisualFireState::RECOILING:
switch (base_state) {
case State::FIRING_LEFT: firing_state_ = State::RECOILING_LEFT; break;
case State::FIRING_RIGHT: firing_state_ = State::RECOILING_RIGHT; break;
case State::FIRING_UP: firing_state_ = State::RECOILING_UP; break;
default: firing_state_ = State::RECOILING_UP; break;
}
break;
case VisualFireState::THREAT_POSE:
switch (base_state) {
case State::FIRING_LEFT: firing_state_ = State::COOLING_LEFT; break;
case State::FIRING_RIGHT: firing_state_ = State::COOLING_RIGHT; break;
case State::FIRING_UP: firing_state_ = State::COOLING_UP; break;
default: firing_state_ = State::COOLING_UP; break;
}
break;
}
}
// Transiciones del sistema visual
void Player::transitionToRecoilingNew() {
visual_fire_state_ = VisualFireState::RECOILING;
visual_state_timer_ = recoiling_duration_;
updateFiringStateFromVisual();
}
void Player::transitionToThreatPose() {
visual_fire_state_ = VisualFireState::THREAT_POSE;
// Calcular threat_pose_duration ajustada:
// Duración original (833ms) menos el tiempo extra que ahora dura recoiling
float original_recoiling_duration = fire_cooldown_timer_; // Era 100% del cooldown
float new_recoiling_duration = aiming_duration_ * RECOILING_DURATION_MULTIPLIER; // Ahora es más del cooldown
float extra_recoiling_time = new_recoiling_duration - original_recoiling_duration;
float adjusted_threat_duration = THREAT_POSE_DURATION - extra_recoiling_time;
// Asegurar que no sea negativo
visual_state_timer_ = std::max(adjusted_threat_duration, MIN_THREAT_POSE_DURATION);
updateFiringStateFromVisual();
}
void Player::transitionToNormalNew() {
visual_fire_state_ = VisualFireState::NORMAL;
visual_state_timer_ = 0;
updateFiringStateFromVisual();
}

View File

@@ -17,7 +17,21 @@
class Texture; class Texture;
// --- Clase Player --- // --- Clase Player: jugador principal del juego ---
//
// Esta clase gestiona todos los aspectos de un jugador durante el juego,
// incluyendo movimiento, disparos, animaciones y estados especiales.
//
// Funcionalidades principales:
// • Sistema de disparo de dos líneas: funcional (cooldown) + visual (animaciones)
// • Estados de animación: normal → aiming → recoiling → threat_pose → normal
// • Movimiento time-based: compatibilidad con deltaTime para fluidez variable
// • Power-ups e invulnerabilidad: coffee machine, extra hits, parpadeos
// • Sistema de puntuación: multipliers, high scores, entrada de nombres
// • Estados de juego: playing, rolling, continue, entering_name, etc.
//
// El sistema de disparo utiliza duraciones configurables mediante constantes
// para facilitar el ajuste del gameplay y la sensación de disparo.
class Player { class Player {
public: public:
// --- Constantes --- // --- Constantes ---
@@ -95,7 +109,7 @@ class Player {
// --- Inicialización y ciclo de vida --- // --- Inicialización y ciclo de vida ---
void init(); // Inicializa el jugador void init(); // Inicializa el jugador
void update(); // Actualiza estado, animación y contadores void update(float deltaTime); // Actualiza estado, animación y contadores (time-based)
void render(); // Dibuja el jugador en pantalla void render(); // Dibuja el jugador en pantalla
// --- Entrada y control --- // --- Entrada y control ---
@@ -104,14 +118,15 @@ class Player {
void setInputEnteringName(Input::Action action); // Procesa entrada al introducir nombre void setInputEnteringName(Input::Action action); // Procesa entrada al introducir nombre
// --- Movimiento y animación --- // --- Movimiento y animación ---
void move(); // Mueve el jugador void move(); // Mueve el jugador (frame-based)
void setAnimation(); // Establece la animación según el estado void move(float deltaTime); // Mueve el jugador (time-based)
void setAnimation(); // Establece la animación según el estado (frame-based)
void setAnimation(float deltaTime); // Establece la animación según el estado (time-based)
// --- Texturas y animaciones --- // --- Texturas y animaciones ---
void setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture); // Cambia las texturas del jugador void setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture); // Cambia las texturas del jugador
// --- Estados y contadores --- // --- Estados y contadores ---
void updateCooldown(); // Actualiza el cooldown de disparo
// --- Puntuación y marcador --- // --- Puntuación y marcador ---
void addScore(int score, int lowest_hi_score_entry); // Añade puntos void addScore(int score, int lowest_hi_score_entry); // Añade puntos
@@ -122,7 +137,7 @@ class Player {
void setPlayingState(State state); // Cambia el estado de juego void setPlayingState(State state); // Cambia el estado de juego
void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad
void setPowerUp(); // Activa el modo PowerUp void setPowerUp(); // Activa el modo PowerUp
void updatePowerUp(); // Actualiza el valor de PowerUp void updatePowerUp(float deltaTime); // Actualiza el valor de PowerUp (time-based)
void giveExtraHit(); // Concede un toque extra al jugador void giveExtraHit(); // Concede un toque extra al jugador
void removeExtraHit(); // Quita el toque extra al jugador void removeExtraHit(); // Quita el toque extra al jugador
void decContinueCounter(); // Decrementa el contador de continuar void decContinueCounter(); // Decrementa el contador de continuar
@@ -145,7 +160,7 @@ class Player {
[[nodiscard]] auto isTitleHidden() const -> bool { return playing_state_ == State::TITLE_HIDDEN; } [[nodiscard]] auto isTitleHidden() const -> bool { return playing_state_ == State::TITLE_HIDDEN; }
// Getters // Getters
[[nodiscard]] auto canFire() const -> bool { return cant_fire_counter_ <= 0; } [[nodiscard]] auto canFire() const -> bool { return can_fire_new_system_; } // Usa nuevo sistema
[[nodiscard]] auto hasExtraHit() const -> bool { return extra_hit_; } [[nodiscard]] auto hasExtraHit() const -> bool { return extra_hit_; }
[[nodiscard]] auto isCooling() const -> bool { return firing_state_ == State::COOLING_LEFT || firing_state_ == State::COOLING_UP || firing_state_ == State::COOLING_RIGHT; } [[nodiscard]] auto isCooling() const -> bool { return firing_state_ == State::COOLING_LEFT || firing_state_ == State::COOLING_UP || firing_state_ == State::COOLING_RIGHT; }
[[nodiscard]] auto isRecoiling() const -> bool { return firing_state_ == State::RECOILING_LEFT || firing_state_ == State::RECOILING_UP || firing_state_ == State::RECOILING_RIGHT; } [[nodiscard]] auto isRecoiling() const -> bool { return firing_state_ == State::RECOILING_LEFT || firing_state_ == State::RECOILING_UP || firing_state_ == State::RECOILING_RIGHT; }
@@ -170,12 +185,12 @@ class Player {
[[nodiscard]] static auto getWidth() -> int { return WIDTH; } [[nodiscard]] static auto getWidth() -> int { return WIDTH; }
[[nodiscard]] auto getPlayingState() const -> State { return playing_state_; } [[nodiscard]] auto getPlayingState() const -> State { return playing_state_; }
[[nodiscard]] auto getName() const -> const std::string & { return name_; } [[nodiscard]] auto getName() const -> const std::string & { return name_; }
[[nodiscard]] auto get1CC() const -> bool { return game_completed_ && credits_used_ == 1; } [[nodiscard]] auto get1CC() const -> bool { return game_completed_ && credits_used_ <= 1; }
[[nodiscard]] auto getEnterNamePositionOverflow() const -> bool { return enter_name_ ? enter_name_->getPositionOverflow() : false; } [[nodiscard]] auto getEnterNamePositionOverflow() const -> bool { return enter_name_ ? enter_name_->getPositionOverflow() : false; }
// Setters inline // Setters inline
void setController(int index) { controller_index_ = index; } void setController(int index) { controller_index_ = index; }
void setCantFireCounter(int counter) { recoiling_state_duration_ = cant_fire_counter_ = counter; } void startFiringSystem(int cooldown_frames); // Método público para iniciar disparo
void setFiringState(State state) { firing_state_ = state; } void setFiringState(State state) { firing_state_ = state; }
void setInvulnerableCounter(int value) { invulnerable_counter_ = value; } void setInvulnerableCounter(int value) { invulnerable_counter_ = value; }
void setName(const std::string &name) { name_ = name; } void setName(const std::string &name) { name_ = name; }
@@ -186,21 +201,34 @@ class Player {
void setWalkingState(State state) { walking_state_ = state; } void setWalkingState(State state) { walking_state_ = state; }
void addCredit(); void addCredit();
void passShowingName();
void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = std::move(gamepad); } void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = std::move(gamepad); }
[[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; } [[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; }
void setUsesKeyboard(bool value) { uses_keyboard_ = value; } void setUsesKeyboard(bool value) { uses_keyboard_ = value; }
[[nodiscard]] auto getUsesKeyboard() const -> bool { return uses_keyboard_; } [[nodiscard]] auto getUsesKeyboard() const -> bool { return uses_keyboard_; }
private: private:
// --- Constantes --- // --- Constantes de física y movimiento ---
static constexpr int POWERUP_COUNTER = 1500; // Duración del estado PowerUp
static constexpr int INVULNERABLE_COUNTER = 200; // Duración del estado invulnerable
static constexpr size_t INVULNERABLE_TEXTURE = 3; // Textura usada durante invulnerabilidad
static constexpr float BASE_SPEED = 1.5F; // Velocidad base del jugador static constexpr float BASE_SPEED = 1.5F; // Velocidad base del jugador
// --- Constantes de power-ups y estados especiales ---
static constexpr int POWERUP_COUNTER = 1500; // Duración del estado PowerUp (frames)
static constexpr int INVULNERABLE_COUNTER = 200; // Duración del estado invulnerable (frames)
static constexpr size_t INVULNERABLE_TEXTURE = 3; // Textura usada durante invulnerabilidad
// --- Constantes del sistema de disparo (obsoletas - usar nuevo sistema) ---
static constexpr int COOLING_DURATION = 50; // Duración del enfriamiento tras disparar static constexpr int COOLING_DURATION = 50; // Duración del enfriamiento tras disparar
static constexpr int COOLING_COMPLETE = 0; // Valor que indica enfriamiento completado static constexpr int COOLING_COMPLETE = 0; // Valor que indica enfriamiento completado
// --- Constantes de estados de espera ---
static constexpr int WAITING_COUNTER = 1000; // Tiempo de espera en estado de espera static constexpr int WAITING_COUNTER = 1000; // Tiempo de espera en estado de espera
// --- Constantes del nuevo sistema de disparo de dos líneas ---
static constexpr float AIMING_DURATION_FACTOR = 0.5f; // 50% del cooldown funcional
static constexpr float RECOILING_DURATION_MULTIPLIER = 4.0f; // 4 veces la duración de aiming
static constexpr float THREAT_POSE_DURATION = 833.33f; // 50 frames = ~833ms (duración base)
static constexpr float MIN_THREAT_POSE_DURATION = 100.0f; // Duración mínima para threat pose
// --- Objetos y punteros --- // --- Objetos y punteros ---
std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador
std::unique_ptr<AnimatedSprite> power_sprite_; // Sprite para dibujar el aura del jugador con el poder a tope std::unique_ptr<AnimatedSprite> power_sprite_; // Sprite para dibujar el aura del jugador con el poder a tope
@@ -221,9 +249,6 @@ class Player {
State firing_state_ = State::FIRING_NONE; // Estado del jugador al disparar State firing_state_ = State::FIRING_NONE; // Estado del jugador al disparar
State playing_state_ = State::WAITING; // Estado del jugador en el juego State playing_state_ = State::WAITING; // Estado del jugador en el juego
Uint32 continue_ticks_ = 0; // Variable para poder cambiar el contador de continue en función del tiempo
Uint32 name_entry_ticks_ = 0; // Variable para poder cambiar el contador de poner nombre en función del tiempo
Uint32 showing_name_ticks_ = 0; // Tiempo en el que se entra al estado SHOWING_NAME
float pos_x_ = 0.0F; // Posición en el eje X float pos_x_ = 0.0F; // Posición en el eje X
float default_pos_x_; // Posición inicial para el jugador float default_pos_x_; // Posición inicial para el jugador
float vel_x_ = 0.0F; // Cantidad de píxeles a desplazarse en el eje X float vel_x_ = 0.0F; // Cantidad de píxeles a desplazarse en el eje X
@@ -231,10 +256,36 @@ class Player {
int pos_y_ = 0; // Posición en el eje Y int pos_y_ = 0; // Posición en el eje Y
int default_pos_y_; // Posición inicial para el jugador int default_pos_y_; // Posición inicial para el jugador
int vel_y_ = 0; // Cantidad de píxeles a desplazarse en el eje Y int vel_y_ = 0; // Cantidad de píxeles a desplazarse en el eje Y
int cant_fire_counter_ = 0; // Contador durante el cual no puede disparar float invulnerable_time_accumulator_ = 0.0f; // Acumulador de tiempo para invulnerabilidad (time-based)
int recoiling_state_counter_ = 0; // Contador para la animación del estado de retroceso float power_up_time_accumulator_ = 0.0f; // Acumulador de tiempo para power-up (time-based)
int recoiling_state_duration_ = 0; // Número de frames que dura el estado de retroceso float continue_time_accumulator_ = 0.0f; // Acumulador de tiempo para continue counter (time-based)
int cooling_state_counter_ = 0; // Contador para la animación del estado cooling float name_entry_time_accumulator_ = 0.0f; // Acumulador de tiempo para name entry counter (time-based)
float showing_name_time_accumulator_ = 0.0f; // Acumulador de tiempo para showing name (time-based)
float waiting_time_accumulator_ = 0.0f; // Acumulador de tiempo para waiting movement (time-based)
float step_time_accumulator_ = 0.0f; // Acumulador de tiempo para step counter (time-based)
// ========================================
// NUEVO SISTEMA DE DISPARO DE DOS LÍNEAS
// ========================================
// LÍNEA 1: SISTEMA FUNCIONAL (CanFire)
float fire_cooldown_timer_ = 0.0f; // Tiempo restante hasta poder disparar otra vez
bool can_fire_new_system_ = true; // true si puede disparar ahora mismo
// LÍNEA 2: SISTEMA VISUAL (Animaciones)
enum class VisualFireState {
NORMAL, // Brazo en posición neutral
AIMING, // Brazo alzado (disparando)
RECOILING, // Brazo en retroceso
THREAT_POSE // Posición amenazante
};
VisualFireState visual_fire_state_ = VisualFireState::NORMAL;
float visual_state_timer_ = 0.0f; // Tiempo en el estado visual actual
float aiming_duration_ = 0.0f; // Duración del estado AIMING
float recoiling_duration_ = 0.0f; // Duración del estado RECOILING
int invulnerable_counter_ = INVULNERABLE_COUNTER; // Contador para la invulnerabilidad int invulnerable_counter_ = INVULNERABLE_COUNTER; // Contador para la invulnerabilidad
int score_ = 0; // Puntos del jugador int score_ = 0; // Puntos del jugador
int coffees_ = 0; // Indica cuántos cafés lleva acumulados int coffees_ = 0; // Indica cuántos cafés lleva acumulados
@@ -242,8 +293,8 @@ class Player {
int power_up_x_offset_ = 0; // Desplazamiento del sprite de PowerUp respecto al sprite del jugador int power_up_x_offset_ = 0; // Desplazamiento del sprite de PowerUp respecto al sprite del jugador
int continue_counter_ = 10; // Contador para poder continuar int continue_counter_ = 10; // Contador para poder continuar
int controller_index_ = 0; // Índice del array de mandos que utilizará para moverse int controller_index_ = 0; // Índice del array de mandos que utilizará para moverse
int name_entry_idle_counter_ = 0; // Contador para poner nombre float name_entry_idle_time_accumulator_ = 0.0f; // Tiempo idle acumulado para poner nombre (milisegundos)
int name_entry_total_counter_ = 0; // Segundos totales que lleva acumulados poniendo nombre float name_entry_total_time_accumulator_ = 0.0f; // Tiempo total acumulado poniendo nombre (milisegundos)
int step_counter_ = 0; // Cuenta los pasos para los estados en los que camina automáticamente int step_counter_ = 0; // Cuenta los pasos para los estados en los que camina automáticamente
int credits_used_ = 0; // Indica el número de veces que ha continuado int credits_used_ = 0; // Indica el número de veces que ha continuado
int waiting_counter_ = 0; // Contador para el estado de espera int waiting_counter_ = 0; // Contador para el estado de espera
@@ -258,23 +309,39 @@ class Player {
// --- Métodos internos --- // --- Métodos internos ---
void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador
void shiftSprite(); // Recoloca el sprite void shiftSprite(); // Recoloca el sprite
void updateInvulnerable(); // Monitoriza el estado de invulnerabilidad void updateInvulnerable(float deltaTime); // Monitoriza el estado de invulnerabilidad (time-based)
void updateContinueCounter(); // Actualiza el contador de continue void updateContinueCounter(float deltaTime); // Actualiza el contador de continue (time-based)
void updateEnterNameCounter(); // Actualiza el contador de entrar nombre void updateEnterNameCounter(float deltaTime); // Actualiza el contador de entrar nombre (time-based)
void updateShowingName(); // Actualiza el estado SHOWING_NAME void updateShowingName(float deltaTime); // Actualiza el estado SHOWING_NAME (time-based)
void decNameEntryCounter(); // Decrementa el contador de entrar nombre void decNameEntryCounter(); // Decrementa el contador de entrar nombre
void updateScoreboard(); // Actualiza el panel del marcador void updateScoreboard(); // Actualiza el panel del marcador
void setScoreboardMode(Scoreboard::Mode mode) const; // Cambia el modo del marcador void setScoreboardMode(Scoreboard::Mode mode) const; // Cambia el modo del marcador
void playSound(const std::string &name) const; // Hace sonar un sonido void playSound(const std::string &name) const; // Hace sonar un sonido
[[nodiscard]] auto isRenderable() const -> bool; // Indica si se puede dibujar el objeto [[nodiscard]] auto isRenderable() const -> bool; // Indica si se puede dibujar el objeto
void addScoreToScoreBoard() const; // Añade una puntuación a la tabla de records void addScoreToScoreBoard() const; // Añade una puntuación a la tabla de records
void handleFiringCooldown(); // Gestiona el tiempo de espera después de disparar antes de permitir otro disparo
void handleRecoilAndCooling(); // Procesa simultáneamente el retroceso del arma y la transición al estado de enfriamiento si aplica // --- Métodos del sistema de disparo de dos líneas ---
void handleCoolingState(); // Actualiza la lógica interna mientras el sistema está en estado de enfriamiento void updateFireSystem(float deltaTime); // Método principal del nuevo sistema de disparo
void transitionToRecoiling(); // Cambia el estado actual al de retroceso después de disparar void updateFunctionalLine(float deltaTime); // Actualiza la línea funcional (CanFire)
void transitionToCooling(); // Cambia el estado actual al de enfriamiento (por ejemplo, tras una ráfaga o sobrecalentamiento) void updateVisualLine(float deltaTime); // Actualiza la línea visual (Animaciones)
void completeCooling(); // Finaliza el proceso de enfriamiento y restablece el estado listo para disparar void startFiring(int cooldown_frames); // Inicia un nuevo disparo en ambas líneas
void handlePlayingMovement(); // Gestiona el movimiento del personaje u objeto durante el estado de juego activo void updateFiringStateFromVisual(); // Sincroniza firing_state_ con visual_fire_state_
void transitionToRecoilingNew(); // Transición AIMING → RECOILING
void transitionToThreatPose(); // Transición RECOILING → THREAT_POSE
void transitionToNormalNew(); // Transición THREAT_POSE → NORMAL
// --- Métodos del sistema de disparo obsoleto ---
void handleFiringCooldown(); // Gestiona el tiempo de espera después de disparar (frame-based)
void handleFiringCooldown(float deltaTime); // Gestiona el tiempo de espera después de disparar (time-based)
void handleRecoilAndCooling(); // Procesa retroceso y enfriamiento (frame-based)
void handleRecoilAndCooling(float deltaTime); // Procesa retroceso y enfriamiento (time-based)
void handleCoolingState(); // Actualiza estado de enfriamiento (frame-based)
void handleCoolingState(float deltaTime); // Actualiza estado de enfriamiento (time-based)
void transitionToRecoiling(); // Transición a retroceso (sistema obsoleto)
void transitionToCooling(); // Transición a enfriamiento (sistema obsoleto)
void completeCooling(); // Finaliza enfriamiento (sistema obsoleto)
void handlePlayingMovement(); // Gestiona el movimiento del personaje u objeto durante el estado de juego activo (frame-based)
void handlePlayingMovement(float deltaTime); // Gestiona el movimiento del personaje u objeto durante el estado de juego activo (time-based)
void handleRecoverMovement(); // Comprueba si ha acabado la animación void handleRecoverMovement(); // Comprueba si ha acabado la animación
void handleRollingMovement(); // Actualiza la lógica de movimiento de "rodar" (posiblemente tras impacto o acción especial) void handleRollingMovement(); // Actualiza la lógica de movimiento de "rodar" (posiblemente tras impacto o acción especial)
void handleRollingBoundaryCollision(); // Detecta y maneja colisiones del objeto rodante con los límites de la pantalla void handleRollingBoundaryCollision(); // Detecta y maneja colisiones del objeto rodante con los límites de la pantalla
@@ -287,12 +354,15 @@ class Player {
void handleEnteringScreen(); // Lógica para entrar en una nueva pantalla, posiblemente con animación o retraso void handleEnteringScreen(); // Lógica para entrar en una nueva pantalla, posiblemente con animación o retraso
void handlePlayer1Entering(); // Controla la animación o posición de entrada del Jugador 1 en pantalla void handlePlayer1Entering(); // Controla la animación o posición de entrada del Jugador 1 en pantalla
void handlePlayer2Entering(); // Controla la animación o posición de entrada del Jugador 2 en pantalla void handlePlayer2Entering(); // Controla la animación o posición de entrada del Jugador 2 en pantalla
void handleCreditsMovement(); // Movimiento general en la pantalla de créditos (desplazamiento vertical u horizontal) void handleCreditsMovement(); // Movimiento general en la pantalla de créditos (frame-based)
void handleCreditsMovement(float deltaTime); // Movimiento general en la pantalla de créditos (time-based)
void handleCreditsRightMovement(); // Lógica específica para mover los créditos hacia la derecha void handleCreditsRightMovement(); // Lógica específica para mover los créditos hacia la derecha
void handleCreditsLeftMovement(); // Lógica específica para mover los créditos hacia la izquierda void handleCreditsLeftMovement(); // Lógica específica para mover los créditos hacia la izquierda
void handleWaitingMovement(); // Controla la animación del jugador saludando void handleWaitingMovement(); // Controla la animación del jugador saludando (frame-based)
void handleWaitingMovement(float deltaTime); // Controla la animación del jugador saludando (time-based)
void updateWalkingStateForCredits(); // Actualiza el estado de caminata de algún personaje u elemento animado en los créditos void updateWalkingStateForCredits(); // Actualiza el estado de caminata de algún personaje u elemento animado en los créditos
void setInputBasedOnPlayerId(); // Asocia las entradas de control en función del identificador del jugador (teclas, mando, etc.) void setInputBasedOnPlayerId(); // Asocia las entradas de control en función del identificador del jugador (teclas, mando, etc.)
void updateStepCounter(); // Incrementa o ajusta el contador de pasos para animaciones o mecánicas relacionadas con movimiento void updateStepCounter(); // Incrementa o ajusta el contador de pasos (frame-based)
void updateStepCounter(float deltaTime); // Incrementa o ajusta el contador de pasos (time-based)
[[nodiscard]] auto computeAnimation() const -> std::pair<std::string, SDL_FlipMode>; // Calcula la animacion de moverse y disparar del jugador [[nodiscard]] auto computeAnimation() const -> std::pair<std::string, SDL_FlipMode>; // Calcula la animacion de moverse y disparar del jugador
}; };

View File

@@ -17,16 +17,16 @@
#endif #endif
#include "lang.h" // Para getText #include "lang.h" // Para getText
#include "param.h" // Para Param, param, ParamResource, ParamGame #include "param.h" // Para Param, param, ParamResource, ParamGame
#include "resource_helper.h" // Para ResourceHelper
#include "screen.h" // Para Screen #include "screen.h" // Para Screen
#include "text.h" // Para Text #include "text.h" // Para Text
#include "resource_helper.h" // Para ResourceHelper
struct JA_Music_t; // lines 11-11 struct JA_Music_t; // lines 11-11
struct JA_Sound_t; // lines 12-12 struct JA_Sound_t; // lines 12-12
// Helper para cargar archivos de audio desde pack o filesystem // Helper para cargar archivos de audio desde pack o filesystem
namespace { namespace {
std::string createTempAudioFile(const std::string& file_path, std::vector<std::string>& temp_files_tracker) { std::string createTempAudioFile(const std::string &file_path, std::vector<std::string> &temp_files_tracker) {
auto resource_data = ResourceHelper::loadFile(file_path); auto resource_data = ResourceHelper::loadFile(file_path);
if (!resource_data.empty()) { if (!resource_data.empty()) {
// Crear archivo temporal // Crear archivo temporal
@@ -42,7 +42,7 @@ namespace {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot create temp file %s", temp_path.c_str()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot create temp file %s", temp_path.c_str());
return file_path; return file_path;
} }
temp_file.write(reinterpret_cast<const char*>(resource_data.data()), resource_data.size()); temp_file.write(reinterpret_cast<const char *>(resource_data.data()), resource_data.size());
temp_file.close(); temp_file.close();
// Agregar a la lista de archivos temporales para limpieza posterior // Agregar a la lista de archivos temporales para limpieza posterior
@@ -51,8 +51,8 @@ namespace {
return temp_path; return temp_path;
} }
return file_path; // Usar ruta original si no está en pack return file_path; // Usar ruta original si no está en pack
}
} }
} // namespace
// Declaraciones de funciones que necesitas implementar en otros archivos // Declaraciones de funciones que necesitas implementar en otros archivos
@@ -75,7 +75,8 @@ auto Resource::get() -> Resource * { return Resource::instance; }
// Constructor con modo de carga // Constructor con modo de carga
Resource::Resource(LoadingMode mode) Resource::Resource(LoadingMode mode)
: loading_mode_(mode), loading_text_(nullptr) { : loading_mode_(mode),
loading_text_(nullptr) {
if (loading_mode_ == LoadingMode::PRELOAD) { if (loading_mode_ == LoadingMode::PRELOAD) {
loading_text_ = Screen::get()->getText(); loading_text_ = Screen::get()->getText();
load(); load();
@@ -649,7 +650,8 @@ void Resource::createTextTextures() {
std::string text; std::string text;
NameAndText(std::string name_init, std::string text_init) NameAndText(std::string name_init, std::string text_init)
: name(std::move(name_init)), text(std::move(text_init)) {} : name(std::move(name_init)),
text(std::move(text_init)) {}
}; };
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXTURES"); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXTURES");
@@ -661,7 +663,8 @@ void Resource::createTextTextures() {
{"game_text_5000_points", "5.000"}, {"game_text_5000_points", "5.000"},
{"game_text_powerup", Lang::getText("[GAME_TEXT] 4")}, {"game_text_powerup", Lang::getText("[GAME_TEXT] 4")},
{"game_text_one_hit", Lang::getText("[GAME_TEXT] 5")}, {"game_text_one_hit", Lang::getText("[GAME_TEXT] 5")},
{"game_text_stop", Lang::getText("[GAME_TEXT] 6")}}; {"game_text_stop", Lang::getText("[GAME_TEXT] 6")},
{"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}};
auto text1 = getText("04b_25_enhanced"); auto text1 = getText("04b_25_enhanced");
for (const auto &s : strings1) { for (const auto &s : strings1) {
@@ -669,27 +672,17 @@ void Resource::createTextTextures() {
printWithDots("Texture : ", s.name, "[ DONE ]"); printWithDots("Texture : ", s.name, "[ DONE ]");
} }
// Texturas de tamaño normal
std::vector<NameAndText> strings2 = {
{"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}};
auto text2 = getText("04b_25");
for (const auto &s : strings2) {
textures_.emplace_back(s.name, text2->writeDXToTexture(Text::STROKE, s.text, -2, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
printWithDots("Texture : ", s.name, "[ DONE ]");
}
// Texturas de tamaño doble // Texturas de tamaño doble
std::vector<NameAndText> strings3 = { std::vector<NameAndText> strings2 = {
{"game_text_100000_points", "100.000"}, {"game_text_100000_points", "100.000"},
{"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")}, {"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")},
{"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")}, {"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")},
{"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")}, {"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")},
{"game_text_game_over", "Game Over"}}; {"game_text_game_over", "Game Over"}};
auto text3 = getText("04b_25_2x"); auto text2 = getText("04b_25_2x_enhanced");
for (const auto &s : strings3) { for (const auto &s : strings2) {
textures_.emplace_back(s.name, text3->writeToTexture(s.text, 1, -4)); textures_.emplace_back(s.name, text2->writeDXToTexture(Text::STROKE, s.text, -4, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
printWithDots("Texture : ", s.name, "[ DONE ]"); printWithDots("Texture : ", s.name, "[ DONE ]");
} }
} }
@@ -703,7 +696,10 @@ void Resource::createText() {
std::string white_texture_file; // Textura blanca opcional std::string white_texture_file; // Textura blanca opcional
ResourceInfo(std::string k, std::string t_file, std::string txt_file, std::string w_file = "") ResourceInfo(std::string k, std::string t_file, std::string txt_file, std::string w_file = "")
: key(std::move(k)), texture_file(std::move(t_file)), text_file(std::move(txt_file)), white_texture_file(std::move(w_file)) {} : key(std::move(k)),
texture_file(std::move(t_file)),
text_file(std::move(txt_file)),
white_texture_file(std::move(w_file)) {}
}; };
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXT OBJECTS"); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXT OBJECTS");
@@ -713,6 +709,7 @@ void Resource::createText() {
{"04b_25_enhanced", "04b_25.png", "04b_25.txt", "04b_25_white.png"}, // Nueva fuente con textura blanca {"04b_25_enhanced", "04b_25.png", "04b_25.txt", "04b_25_white.png"}, // Nueva fuente con textura blanca
{"04b_25_white", "04b_25_white.png", "04b_25.txt"}, {"04b_25_white", "04b_25_white.png", "04b_25.txt"},
{"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"}, {"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"},
{"04b_25_2x_enhanced", "04b_25_2x.png", "04b_25_2x.txt", "04b_25_2x_white.png"}, // Nueva fuente con textura blanca
{"04b_25_metal", "04b_25_metal.png", "04b_25.txt"}, {"04b_25_metal", "04b_25_metal.png", "04b_25.txt"},
{"04b_25_grey", "04b_25_grey.png", "04b_25.txt"}, {"04b_25_grey", "04b_25_grey.png", "04b_25.txt"},
{"04b_25_flat", "04b_25_flat.png", "04b_25.txt"}, {"04b_25_flat", "04b_25_flat.png", "04b_25.txt"},
@@ -874,13 +871,13 @@ void Resource::updateProgressBar() {
// Limpia archivos temporales de audio // Limpia archivos temporales de audio
void Resource::cleanupTempAudioFiles() { void Resource::cleanupTempAudioFiles() {
for (const auto& temp_path : temp_audio_files_) { for (const auto &temp_path : temp_audio_files_) {
try { try {
if (std::filesystem::exists(temp_path)) { if (std::filesystem::exists(temp_path)) {
std::filesystem::remove(temp_path); std::filesystem::remove(temp_path);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Removed temp audio file: %s", temp_path.c_str()); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Removed temp audio file: %s", temp_path.c_str());
} }
} catch (const std::exception& e) { } catch (const std::exception &e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to remove temp audio file %s: %s", temp_path.c_str(), e.what()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to remove temp audio file %s: %s", temp_path.c_str(), e.what());
} }
} }

View File

@@ -52,7 +52,8 @@ class Resource {
JA_Sound_t *sound; // Objeto con el sonido JA_Sound_t *sound; // Objeto con el sonido
ResourceSound(std::string name, JA_Sound_t *sound = nullptr) ResourceSound(std::string name, JA_Sound_t *sound = nullptr)
: name(std::move(name)), sound(sound) {} : name(std::move(name)),
sound(sound) {}
}; };
struct ResourceMusic { struct ResourceMusic {
@@ -60,7 +61,8 @@ class Resource {
JA_Music_t *music; // Objeto con la música JA_Music_t *music; // Objeto con la música
ResourceMusic(std::string name, JA_Music_t *music = nullptr) ResourceMusic(std::string name, JA_Music_t *music = nullptr)
: name(std::move(name)), music(music) {} : name(std::move(name)),
music(music) {}
}; };
struct ResourceTexture { struct ResourceTexture {
@@ -68,7 +70,8 @@ class Resource {
std::shared_ptr<Texture> texture; // Objeto con la textura std::shared_ptr<Texture> texture; // Objeto con la textura
ResourceTexture(std::string name, std::shared_ptr<Texture> texture = nullptr) ResourceTexture(std::string name, std::shared_ptr<Texture> texture = nullptr)
: name(std::move(name)), texture(std::move(texture)) {} : name(std::move(name)),
texture(std::move(texture)) {}
}; };
struct ResourceTextFile { struct ResourceTextFile {
@@ -76,7 +79,8 @@ class Resource {
std::shared_ptr<Text::File> text_file; // Objeto con los descriptores de la fuente de texto std::shared_ptr<Text::File> text_file; // Objeto con los descriptores de la fuente de texto
ResourceTextFile(std::string name, std::shared_ptr<Text::File> text_file = nullptr) ResourceTextFile(std::string name, std::shared_ptr<Text::File> text_file = nullptr)
: name(std::move(name)), text_file(std::move(text_file)) {} : name(std::move(name)),
text_file(std::move(text_file)) {}
}; };
struct ResourceText { struct ResourceText {
@@ -84,7 +88,8 @@ class Resource {
std::shared_ptr<Text> text; // Objeto de texto std::shared_ptr<Text> text; // Objeto de texto
ResourceText(std::string name, std::shared_ptr<Text> text = nullptr) ResourceText(std::string name, std::shared_ptr<Text> text = nullptr)
: name(std::move(name)), text(std::move(text)) {} : name(std::move(name)),
text(std::move(text)) {}
}; };
struct ResourceAnimation { struct ResourceAnimation {
@@ -92,7 +97,8 @@ class Resource {
AnimationsFileBuffer animation; // Objeto con las animaciones AnimationsFileBuffer animation; // Objeto con las animaciones
ResourceAnimation(std::string name, AnimationsFileBuffer animation = {}) ResourceAnimation(std::string name, AnimationsFileBuffer animation = {})
: name(std::move(name)), animation(std::move(animation)) {} : name(std::move(name)),
animation(std::move(animation)) {}
}; };
// --- Estructura para el progreso de carga --- // --- Estructura para el progreso de carga ---
@@ -100,8 +106,12 @@ class Resource {
size_t total; // Número total de recursos size_t total; // Número total de recursos
size_t loaded; // Número de recursos cargados size_t loaded; // Número de recursos cargados
ResourceCount() : total(0), loaded(0) {} ResourceCount()
ResourceCount(size_t total) : total(total), loaded(0) {} : total(0),
loaded(0) {}
ResourceCount(size_t total)
: total(total),
loaded(0) {}
void add(size_t amount) { loaded += amount; } void add(size_t amount) { loaded += amount; }
void increase() { loaded++; } void increase() { loaded++; }

View File

@@ -1,13 +1,14 @@
#include "resource_helper.h" #include "resource_helper.h"
#include <algorithm>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <algorithm>
namespace ResourceHelper { namespace ResourceHelper {
static bool resource_system_initialized = false; static bool resource_system_initialized = false;
bool initializeResourceSystem(const std::string& pack_file) { bool initializeResourceSystem(const std::string& pack_file) {
auto& loader = ResourceLoader::getInstance(); auto& loader = ResourceLoader::getInstance();
resource_system_initialized = loader.initialize(pack_file, true); resource_system_initialized = loader.initialize(pack_file, true);
@@ -18,16 +19,16 @@ namespace ResourceHelper {
} }
return true; // Always return true as fallback is acceptable return true; // Always return true as fallback is acceptable
} }
void shutdownResourceSystem() { void shutdownResourceSystem() {
if (resource_system_initialized) { if (resource_system_initialized) {
ResourceLoader::getInstance().shutdown(); ResourceLoader::getInstance().shutdown();
resource_system_initialized = false; resource_system_initialized = false;
} }
} }
std::vector<uint8_t> loadFile(const std::string& filepath) { std::vector<uint8_t> loadFile(const std::string& filepath) {
if (resource_system_initialized && shouldUseResourcePack(filepath)) { if (resource_system_initialized && shouldUseResourcePack(filepath)) {
auto& loader = ResourceLoader::getInstance(); auto& loader = ResourceLoader::getInstance();
std::string pack_path = getPackPath(filepath); std::string pack_path = getPackPath(filepath);
@@ -53,9 +54,9 @@ namespace ResourceHelper {
} }
return data; return data;
} }
bool shouldUseResourcePack(const std::string& filepath) { bool shouldUseResourcePack(const std::string& filepath) {
// Archivos que NO van al pack: // Archivos que NO van al pack:
// - config/ (ahora está fuera de data/) // - config/ (ahora está fuera de data/)
// - archivos absolutos del sistema // - archivos absolutos del sistema
@@ -70,9 +71,9 @@ namespace ResourceHelper {
} }
return false; return false;
} }
std::string getPackPath(const std::string& asset_path) { std::string getPackPath(const std::string& asset_path) {
std::string pack_path = asset_path; std::string pack_path = asset_path;
// Normalizar separadores de path a '/' // Normalizar separadores de path a '/'
@@ -91,5 +92,5 @@ namespace ResourceHelper {
} }
return pack_path; return pack_path;
}
} }
} // namespace ResourceHelper

View File

@@ -1,32 +1,33 @@
#pragma once #pragma once
#include "resource_loader.h"
#include <string>
#include <vector>
#include <memory>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <memory>
#include <string>
#include <vector>
#include "resource_loader.h"
// Helper functions para integrar ResourceLoader con el sistema existente // Helper functions para integrar ResourceLoader con el sistema existente
namespace ResourceHelper { namespace ResourceHelper {
// Inicializa ResourceLoader (llamar al inicio del programa) // Inicializa ResourceLoader (llamar al inicio del programa)
bool initializeResourceSystem(const std::string& pack_file = "resources.pack"); bool initializeResourceSystem(const std::string& pack_file = "resources.pack");
// Cierra ResourceLoader // Cierra ResourceLoader
void shutdownResourceSystem(); void shutdownResourceSystem();
// Carga un archivo usando ResourceLoader o fallback a filesystem // Carga un archivo usando ResourceLoader o fallback a filesystem
std::vector<uint8_t> loadFile(const std::string& filepath); std::vector<uint8_t> loadFile(const std::string& filepath);
// Verifica si un archivo debería cargarse del pack vs filesystem // Verifica si un archivo debería cargarse del pack vs filesystem
bool shouldUseResourcePack(const std::string& filepath); bool shouldUseResourcePack(const std::string& filepath);
// Convierte ruta Asset a ruta relativa para ResourceLoader // Convierte ruta Asset a ruta relativa para ResourceLoader
std::string getPackPath(const std::string& asset_path); std::string getPackPath(const std::string& asset_path);
// Wrappea la carga de archivos para mantener compatibilidad // Wrappea la carga de archivos para mantener compatibilidad
template<typename T> template <typename T>
T* loadResourceFile(const std::string& asset_path, T* (*loader_func)(const char*)) { T* loadResourceFile(const std::string& asset_path, T* (*loader_func)(const char*)) {
auto data = loadFile(asset_path); auto data = loadFile(asset_path);
if (data.empty()) { if (data.empty()) {
return loader_func(asset_path.c_str()); return loader_func(asset_path.c_str());
@@ -42,5 +43,5 @@ namespace ResourceHelper {
std::filesystem::remove(temp_path); std::filesystem::remove(temp_path);
return result; return result;
}
} }
} // namespace ResourceHelper

View File

@@ -1,13 +1,15 @@
#include "resource_loader.h" #include "resource_loader.h"
#include <algorithm>
#include <filesystem>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <filesystem>
#include <algorithm>
std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr; std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr;
ResourceLoader::ResourceLoader() ResourceLoader::ResourceLoader()
: resourcePack(nullptr), fallbackToFiles(true) {} : resourcePack(nullptr),
fallbackToFiles(true) {}
ResourceLoader& ResourceLoader::getInstance() { ResourceLoader& ResourceLoader::getInstance() {
if (!instance) { if (!instance) {

View File

@@ -1,11 +1,12 @@
#ifndef RESOURCE_LOADER_H #ifndef RESOURCE_LOADER_H
#define RESOURCE_LOADER_H #define RESOURCE_LOADER_H
#include "resource_pack.h"
#include <memory> #include <memory>
#include "resource_pack.h"
class ResourceLoader { class ResourceLoader {
private: private:
static std::unique_ptr<ResourceLoader> instance; static std::unique_ptr<ResourceLoader> instance;
ResourcePack* resourcePack; ResourcePack* resourcePack;
std::string packPath; std::string packPath;
@@ -13,7 +14,7 @@ private:
ResourceLoader(); ResourceLoader();
public: public:
static ResourceLoader& getInstance(); static ResourceLoader& getInstance();
~ResourceLoader(); ~ResourceLoader();
@@ -29,7 +30,7 @@ public:
size_t getLoadedResourceCount() const; size_t getLoadedResourceCount() const;
std::vector<std::string> getAvailableResources() const; std::vector<std::string> getAvailableResources() const;
private: private:
std::vector<uint8_t> loadFromFile(const std::string& filename); std::vector<uint8_t> loadFromFile(const std::string& filename);
std::string getDataPath(const std::string& filename); std::string getDataPath(const std::string& filename);
}; };

View File

@@ -1,12 +1,14 @@
#include "resource_pack.h" #include "resource_pack.h"
#include <algorithm>
#include <filesystem>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <filesystem>
#include <algorithm>
const std::string ResourcePack::DEFAULT_ENCRYPT_KEY = "CCAE_RESOURCES_2024"; const std::string ResourcePack::DEFAULT_ENCRYPT_KEY = "CCAE_RESOURCES_2024";
ResourcePack::ResourcePack() : loaded(false) {} ResourcePack::ResourcePack()
: loaded(false) {}
ResourcePack::~ResourcePack() { ResourcePack::~ResourcePack() {
clear(); clear();

View File

@@ -1,10 +1,10 @@
#ifndef RESOURCE_PACK_H #ifndef RESOURCE_PACK_H
#define RESOURCE_PACK_H #define RESOURCE_PACK_H
#include <cstdint>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <cstdint>
struct ResourceEntry { struct ResourceEntry {
std::string filename; std::string filename;
@@ -14,7 +14,7 @@ struct ResourceEntry {
}; };
class ResourcePack { class ResourcePack {
private: private:
std::unordered_map<std::string, ResourceEntry> resources; std::unordered_map<std::string, ResourceEntry> resources;
std::vector<uint8_t> data; std::vector<uint8_t> data;
bool loaded; bool loaded;
@@ -23,7 +23,7 @@ private:
void encryptData(std::vector<uint8_t>& data, const std::string& key); void encryptData(std::vector<uint8_t>& data, const std::string& key);
void decryptData(std::vector<uint8_t>& data, const std::string& key); void decryptData(std::vector<uint8_t>& data, const std::string& key);
public: public:
ResourcePack(); ResourcePack();
~ResourcePack(); ~ResourcePack();

View File

@@ -40,7 +40,8 @@ Scoreboard::Scoreboard()
: renderer_(Screen::get()->getRenderer()), : renderer_(Screen::get()->getRenderer()),
game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")), game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)), power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
text_scoreboard_(Resource::get()->getText("8bithud")) { text_(Resource::get()->getText("8bithud")),
enter_name_text_(Resource::get()->getText("smb2")) {
// Inicializa variables // Inicializa variables
for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) { for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) {
name_.at(i).clear(); name_.at(i).clear();
@@ -198,50 +199,50 @@ void Scoreboard::renderPanelContent(size_t panel_index) {
void Scoreboard::renderScoreMode(size_t panel_index) { void Scoreboard::renderScoreMode(size_t panel_index) {
// SCORE // SCORE
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_); text_->writeDX(Text::COLOR | Text::CENTER, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_); text_->writeDX(Text::COLOR | Text::CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
// MULT // MULT
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 3"), 1, text_color1_); text_->writeDX(Text::COLOR | Text::CENTER, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 3"), 1, text_color1_);
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_.at(panel_index)).substr(0, 3), 1, text_color2_); text_->writeDX(Text::COLOR | Text::CENTER, slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_.at(panel_index)).substr(0, 3), 1, text_color2_);
} }
void Scoreboard::renderDemoMode() { void Scoreboard::renderDemoMode() {
// DEMO MODE // DEMO MODE
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_);
// PRESS START TO PLAY // PRESS START TO PLAY
if (time_counter_ % 10 < 8) { if (time_counter_ % 10 < 8) {
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
} }
} }
void Scoreboard::renderWaitingMode() { void Scoreboard::renderWaitingMode() {
// GAME OVER // GAME OVER
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
// PRESS START TO PLAY // PRESS START TO PLAY
if (time_counter_ % 10 < 8) { if (time_counter_ % 10 < 8) {
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
} }
} }
void Scoreboard::renderGameOverMode() { void Scoreboard::renderGameOverMode() {
// GAME OVER // GAME OVER
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
// PLEASE WAIT // PLEASE WAIT
if (time_counter_ % 10 < 8) { if (time_counter_ % 10 < 8) {
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 12"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 12"), 1, text_color1_);
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_);
} }
} }
void Scoreboard::renderStageInfoMode() { void Scoreboard::renderStageInfoMode() {
// STAGE // STAGE
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + " " + std::to_string(stage_), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + " " + std::to_string(stage_), 1, text_color1_);
// POWERMETER // POWERMETER
power_meter_sprite_->setSpriteClip(0, 0, 40, 7); power_meter_sprite_->setSpriteClip(0, 0, 40, 7);
@@ -250,76 +251,75 @@ void Scoreboard::renderStageInfoMode() {
power_meter_sprite_->render(); power_meter_sprite_->render();
// HI-SCORE // HI-SCORE
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 4"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 4"), 1, text_color1_);
const std::string NAME = hi_score_name_.empty() ? "" : hi_score_name_ + " - "; const std::string NAME = hi_score_name_.empty() ? "" : hi_score_name_ + " - ";
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, NAME + updateScoreText(hi_score_), 1, text_color2_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, NAME + updateScoreText(hi_score_), 1, text_color2_);
} }
void Scoreboard::renderContinueMode(size_t panel_index) { void Scoreboard::renderContinueMode(size_t panel_index) {
// SCORE // SCORE
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
// CONTINUE // CONTINUE
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 10"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 10"), 1, text_color1_);
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_.at(panel_index)), 1, text_color2_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_.at(panel_index)), 1, text_color2_);
} }
void Scoreboard::renderEnterNameMode(size_t panel_index) { void Scoreboard::renderEnterNameMode(size_t panel_index) {
// SCORE // SCORE
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
// ENTER NAME // ENTER NAME
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
renderNameInputField(panel_index); renderNameInputField(panel_index);
} }
void Scoreboard::renderNameInputField(size_t panel_index) { void Scoreboard::renderNameInputField(size_t panel_index) {
SDL_FRect rect = {enter_name_pos_.x, enter_name_pos_.y, 5.0F, 7.0F}; SDL_FRect rect = {
.x = enter_name_pos_.x,
.y = enter_name_pos_.y,
.w = static_cast<float>(enter_name_text_->getCharacterSize() - 2),
.h = static_cast<float>(enter_name_text_->getCharacterSize())};
// Recorre todos los slots de letras del nombre // Recorre todos los slots de letras del nombre
for (size_t j = 0; j < NAME_SIZE; ++j) { for (size_t j = 0; j < NAME_SIZE; ++j) {
// Selecciona el color // Dibuja la linea. Si coincide con el selector solo se dibuja 2 de cada 4 veces
const Color COLOR = j < selector_pos_.at(panel_index) ? text_color2_ : text_color1_; if (j != selector_pos_.at(panel_index) || time_counter_ % 4 >= 2) {
SDL_SetRenderDrawColor(renderer_, text_color1_.r, text_color1_.g, text_color1_.b, 255);
if (j != selector_pos_.at(panel_index) || time_counter_ % 3 == 0) {
// Dibuja la linea
if (j >= selector_pos_.at(panel_index)) {
SDL_SetRenderDrawColor(renderer_, COLOR.r, COLOR.g, COLOR.b, 255);
SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h); SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h);
} }
// Dibuja la letra // Dibuja la letra
if (j < record_name_.at(panel_index).size()) { if (j < record_name_.at(panel_index).size()) {
text_scoreboard_->writeColored(rect.x, rect.y, record_name_.at(panel_index).substr(j, 1), COLOR); enter_name_text_->writeColored(rect.x, rect.y, record_name_.at(panel_index).substr(j, 1), text_color2_);
} }
} rect.x += enter_name_text_->getCharacterSize();
rect.x += 7;
} }
} }
void Scoreboard::renderShowNameMode(size_t panel_index) { void Scoreboard::renderShowNameMode(size_t panel_index) {
// SCORE // SCORE
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
// NAME // NAME
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
/* TEXTO CENTRADO */ // NOMBRE INTRODUCIDO
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, record_name_.at(panel_index), 1, Colors::getColorLikeKnightRider(name_colors_, loop_counter_ / 5)); enter_name_text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, record_name_.at(panel_index), 1, Colors::getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
} }
void Scoreboard::renderGameCompletedMode(size_t panel_index) { void Scoreboard::renderGameCompletedMode(size_t panel_index) {
// GAME OVER // GAME OVER
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
// SCORE // SCORE
if (time_counter_ % 10 < 8) { if (time_counter_ % 10 < 8) {
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 14"), 1, text_color1_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 14"), 1, text_color1_);
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_.at(panel_index)), 1, text_color2_); text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_.at(panel_index)), 1, text_color2_);
} }
} }
@@ -379,7 +379,7 @@ void Scoreboard::recalculateAnchors() {
slot4_4_ = {.x = COL, .y = ROW4}; slot4_4_ = {.x = COL, .y = ROW4};
// Primer cuadrado para poner el nombre de record // Primer cuadrado para poner el nombre de record
const int ENTER_NAME_LENGTH = text_scoreboard_->length(std::string(NAME_SIZE, 'A')); const int ENTER_NAME_LENGTH = enter_name_text_->length(std::string(NAME_SIZE, 'A'));
enter_name_pos_.x = COL - (ENTER_NAME_LENGTH / 2); enter_name_pos_.x = COL - (ENTER_NAME_LENGTH / 2);
enter_name_pos_.y = ROW4; enter_name_pos_.y = ROW4;

View File

@@ -73,7 +73,8 @@ class Scoreboard {
SDL_Renderer *renderer_; // El renderizador de la ventana SDL_Renderer *renderer_; // El renderizador de la ventana
std::shared_ptr<Texture> game_power_meter_texture_; // Textura con el marcador de poder de la fase std::shared_ptr<Texture> game_power_meter_texture_; // Textura con el marcador de poder de la fase
std::unique_ptr<Sprite> power_meter_sprite_; // Sprite para el medidor de poder de la fase std::unique_ptr<Sprite> power_meter_sprite_; // Sprite para el medidor de poder de la fase
std::shared_ptr<Text> text_scoreboard_; // Fuente para el marcador del juego std::shared_ptr<Text> text_; // Fuente para el marcador del juego
std::shared_ptr<Text> enter_name_text_; // Fuente para la introducción de nombre del jugador
SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador
std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel

View File

@@ -91,7 +91,11 @@ class Screen {
Color color; // Color del flash Color color; // Color del flash
explicit FlashEffect(bool enabled = false, int length = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF)) explicit FlashEffect(bool enabled = false, int length = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF))
: enabled(enabled), length(length), delay(delay), counter(length), color(color) {} : enabled(enabled),
length(length),
delay(delay),
counter(length),
color(color) {}
void update() { (enabled && counter > 0) ? counter-- : static_cast<int>(enabled = false); } void update() { (enabled && counter > 0) ? counter-- : static_cast<int>(enabled = false); }
[[nodiscard]] auto isRendarable() const -> bool { return enabled && counter < length - delay; } [[nodiscard]] auto isRendarable() const -> bool { return enabled && counter < length - delay; }
@@ -109,7 +113,14 @@ class Screen {
bool enabled; // Indica si el efecto está activo bool enabled; // Indica si el efecto está activo
explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int orig_pos = 0, int orig_width = 800) explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int orig_pos = 0, int orig_width = 800)
: desp(dp), delay(dl), counter(cnt), length(len), remaining(rem), original_pos(orig_pos), original_width(orig_width), enabled(en) {} : desp(dp),
delay(dl),
counter(cnt),
length(len),
remaining(rem),
original_pos(orig_pos),
original_width(orig_width),
enabled(en) {}
// Activa el efecto de sacudida y guarda la posición y tamaño originales // Activa el efecto de sacudida y guarda la posición y tamaño originales
void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_length = -1) { void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_length = -1) {

View File

@@ -35,7 +35,12 @@ constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner";
// Constructor // Constructor
Credits::Credits() Credits::Credits()
: balloon_manager_(std::make_unique<BalloonManager>(nullptr)), tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::DIAGONAL)), fade_in_(std::make_unique<Fade>()), fade_out_(std::make_unique<Fade>()), text_texture_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, static_cast<int>(param.game.width), static_cast<int>(param.game.height))), canvas_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, static_cast<int>(param.game.width), static_cast<int>(param.game.height))) { : balloon_manager_(std::make_unique<BalloonManager>(nullptr)),
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::DIAGONAL)),
fade_in_(std::make_unique<Fade>()),
fade_out_(std::make_unique<Fade>()),
text_texture_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, static_cast<int>(param.game.width), static_cast<int>(param.game.height))),
canvas_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, static_cast<int>(param.game.width), static_cast<int>(param.game.height))) {
if (text_texture_ == nullptr) { if (text_texture_ == nullptr) {
throw std::runtime_error("Failed to create SDL texture for text."); throw std::runtime_error("Failed to create SDL texture for text.");
} }
@@ -73,36 +78,46 @@ Credits::~Credits() {
Options::gamepad_manager.clearPlayers(); Options::gamepad_manager.clearPlayers();
} }
// Calcula el deltatime
auto Credits::calculateDeltaTime() -> float {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle principal // Bucle principal
void Credits::run() { void Credits::run() {
last_time_ = SDL_GetTicks();
while (Section::name == Section::Name::CREDITS) { while (Section::name == Section::Name::CREDITS) {
checkInput(); checkInput();
update(); const float delta_time = calculateDeltaTime();
update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
} }
// Actualiza las variables // Actualiza las variables (time-based)
void Credits::update() { void Credits::update(float deltaTime) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { const float multiplier = want_to_pass_ ? 4.0f : 1.0f;
ticks_ = SDL_GetTicks(); const float adjusted_delta_time = deltaTime * multiplier;
const int REPEAT = want_to_pass_ ? 4 : 1;
for (int i = 0; i < REPEAT; ++i) { tiled_bg_->update(adjusted_delta_time);
tiled_bg_->update();
cycleColors(); cycleColors();
balloon_manager_->update(); balloon_manager_->update(adjusted_delta_time);
updateTextureDstRects(); updateTextureDstRects(adjusted_delta_time);
throwBalloons(); throwBalloons(adjusted_delta_time);
updatePlayers(); updatePlayers(adjusted_delta_time);
updateAllFades(); updateAllFades(adjusted_delta_time);
++counter_;
} // Convertir deltaTime a factor de frame (asumiendo 60fps)
const float frameFactor = adjusted_delta_time / (1000.0f / 60.0f);
counter_ += frameFactor;
Screen::get()->update(); Screen::get()->update();
fillCanvas(); fillCanvas();
}
Audio::update(); Audio::update();
} }
@@ -272,9 +287,9 @@ void Credits::fillCanvas() {
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
} }
// Actualiza el destino de los rectangulos de las texturas // Actualiza el destino de los rectangulos de las texturas (frame-based)
void Credits::updateTextureDstRects() { void Credits::updateTextureDstRects() {
if (counter_ % 10 == 0) { if (static_cast<int>(counter_) % 10 == 0) {
// Comprueba la posición de la textura con los titulos de credito // Comprueba la posición de la textura con los titulos de credito
if (credits_rect_dst_.y + credits_rect_dst_.h > play_area_.y) { if (credits_rect_dst_.y + credits_rect_dst_.h > play_area_.y) {
--credits_rect_dst_.y; --credits_rect_dst_.y;
@@ -301,7 +316,42 @@ void Credits::updateTextureDstRects() {
} }
} }
// Tira globos al escenario // Actualiza el destino de los rectangulos de las texturas (time-based)
void Credits::updateTextureDstRects(float deltaTime) {
constexpr float TEXTURE_UPDATE_INTERVAL = 10 * (1000.0f / 60.0f); // 166.67ms (cada 10 frames)
static float texture_accumulator = 0.0f;
texture_accumulator += deltaTime;
if (texture_accumulator >= TEXTURE_UPDATE_INTERVAL) {
texture_accumulator -= TEXTURE_UPDATE_INTERVAL;
// Comprueba la posición de la textura con los titulos de credito
if (credits_rect_dst_.y + credits_rect_dst_.h > play_area_.y) {
--credits_rect_dst_.y;
}
// Comprueba la posición de la textura con el mini_logo
if (mini_logo_rect_dst_.y == mini_logo_final_pos_) {
mini_logo_on_position_ = true;
// Si el jugador quiere pasar los titulos de credito, el fade se inicia solo
if (want_to_pass_) {
fading_ = true;
}
// Se activa el contador para evitar que la sección sea infinita
if (counter_prevent_endless_ == 1000) {
fading_ = true;
} else {
++counter_prevent_endless_;
}
} else {
--mini_logo_rect_dst_.y;
}
}
}
// Tira globos al escenario (frame-based)
void Credits::throwBalloons() { void Credits::throwBalloons() {
constexpr int SPEED = 200; constexpr int SPEED = 200;
const std::vector<int> SETS = {0, 63, 25, 67, 17, 75, 13, 50}; const std::vector<int> SETS = {0, 63, 25, 67, 17, 75, 13, 50};
@@ -310,12 +360,41 @@ void Credits::throwBalloons() {
return; return;
} }
if (counter_ % SPEED == 0) { if (static_cast<int>(counter_) % SPEED == 0) {
const int INDEX = (counter_ / SPEED) % SETS.size(); const int INDEX = (static_cast<int>(counter_) / SPEED) % SETS.size();
balloon_manager_->deployFormation(SETS.at(INDEX), -60); balloon_manager_->deployFormation(SETS.at(INDEX), -60);
} }
if (counter_ % (SPEED * 4) == 0 && counter_ > 0) { if (static_cast<int>(counter_) % (SPEED * 4) == 0 && counter_ > 0) {
balloon_manager_->createPowerBall();
}
}
// Tira globos al escenario (time-based)
void Credits::throwBalloons(float deltaTime) {
constexpr int SPEED = 200;
const std::vector<int> SETS = {0, 63, 25, 67, 17, 75, 13, 50};
constexpr float BALLOON_INTERVAL = SPEED * (1000.0f / 60.0f); // 3333.33ms (cada 200 frames)
constexpr float POWERBALL_INTERVAL = (SPEED * 4) * (1000.0f / 60.0f); // 13333.33ms (cada 800 frames)
if (counter_ > ((SETS.size() - 1) * SPEED) * 3) {
return;
}
static float balloon_accumulator = 0.0f;
static float powerball_accumulator = 0.0f;
balloon_accumulator += deltaTime;
powerball_accumulator += deltaTime;
if (balloon_accumulator >= BALLOON_INTERVAL) {
balloon_accumulator -= BALLOON_INTERVAL;
const int INDEX = (static_cast<int>(counter_ / SPEED)) % SETS.size();
balloon_manager_->deployFormation(SETS.at(INDEX), -60);
}
if (powerball_accumulator >= POWERBALL_INTERVAL && counter_ > 0) {
powerball_accumulator -= POWERBALL_INTERVAL;
balloon_manager_->createPowerBall(); balloon_manager_->createPowerBall();
} }
} }
@@ -387,12 +466,12 @@ void Credits::initPlayers() {
} }
} }
// Actualiza los rectangulos negros // Actualiza los rectangulos negros (frame-based)
void Credits::updateBlackRects() { void Credits::updateBlackRects() {
static int current_step_ = steps_; static int current_step_ = steps_;
if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1) { if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1) {
// Si los rectangulos superior e inferior no han llegado al centro // Si los rectangulos superior e inferior no han llegado al centro
if (counter_ % 4 == 0) { if (static_cast<int>(counter_) % 4 == 0) {
// Incrementa la altura del rectangulo superior // Incrementa la altura del rectangulo superior
top_black_rect_.h = std::min(top_black_rect_.h + 1, param.game.game_area.center_y - 1); top_black_rect_.h = std::min(top_black_rect_.h + 1, param.game.game_area.center_y - 1);
@@ -430,6 +509,57 @@ void Credits::updateBlackRects() {
} }
} }
// Actualiza los rectangulos negros (time-based)
void Credits::updateBlackRects(float deltaTime) {
static float current_step_ = static_cast<float>(steps_);
constexpr float BLACK_RECT_INTERVAL = 4 * (1000.0f / 60.0f); // 66.67ms (cada 4 frames)
static float black_rect_accumulator = 0.0f;
if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1) {
// Si los rectangulos superior e inferior no han llegado al centro
black_rect_accumulator += deltaTime;
if (black_rect_accumulator >= BLACK_RECT_INTERVAL) {
black_rect_accumulator -= BLACK_RECT_INTERVAL;
// Incrementa la altura del rectangulo superior
top_black_rect_.h = std::min(top_black_rect_.h + 1, param.game.game_area.center_y - 1);
// Incrementa la altura y modifica la posición del rectangulo inferior
++bottom_black_rect_.h;
bottom_black_rect_.y = std::max(bottom_black_rect_.y - 1, param.game.game_area.center_y + 1);
--current_step_;
setVolume(static_cast<int>(initial_volume_ * current_step_ / steps_));
}
} else {
// Si los rectangulos superior e inferior han llegado al centro
if (left_black_rect_.w != param.game.game_area.center_x && right_black_rect_.x != param.game.game_area.center_x) {
constexpr int SPEED = 2;
// Si los rectangulos izquierdo y derecho no han llegado al centro
// Incrementa la anchura del rectangulo situado a la izquierda
left_black_rect_.w = std::min(left_black_rect_.w + SPEED, param.game.game_area.center_x);
// Incrementa la anchura y modifica la posición del rectangulo situado a la derecha
right_black_rect_.w += SPEED;
right_black_rect_.x = std::max(right_black_rect_.x - SPEED, param.game.game_area.center_x);
--current_step_;
setVolume(static_cast<int>(initial_volume_ * current_step_ / steps_));
} else {
// Si los rectangulos izquierdo y derecho han llegado al centro
setVolume(0);
Audio::get()->stopMusic();
if (counter_pre_fade_ == 400) {
fade_out_->activate();
} else {
// Convertir deltaTime a factor de frame
const float frameFactor = deltaTime / (1000.0f / 60.0f);
counter_pre_fade_ += frameFactor;
}
}
}
}
// Actualiza el rectangulo rojo // Actualiza el rectangulo rojo
void Credits::updateRedRect() { void Credits::updateRedRect() {
border_rect_.x = left_black_rect_.x + left_black_rect_.w; border_rect_.x = left_black_rect_.x + left_black_rect_.w;
@@ -438,7 +568,7 @@ void Credits::updateRedRect() {
border_rect_.h = bottom_black_rect_.y - border_rect_.y + 1; border_rect_.h = bottom_black_rect_.y - border_rect_.y + 1;
} }
// Actualiza el estado de fade // Actualiza el estado de fade (frame-based)
void Credits::updateAllFades() { void Credits::updateAllFades() {
if (fading_) { if (fading_) {
updateBlackRects(); updateBlackRects();
@@ -456,6 +586,24 @@ void Credits::updateAllFades() {
} }
} }
// Actualiza el estado de fade (time-based)
void Credits::updateAllFades(float deltaTime) {
if (fading_) {
updateBlackRects(deltaTime);
updateRedRect();
}
fade_in_->update(); // Fade ya usa tiempo interno
if (fade_in_->hasEnded()) {
Audio::get()->playMusic("credits.ogg");
}
fade_out_->update(); // Fade ya usa tiempo interno
if (fade_out_->hasEnded()) {
Section::name = Section::Name::HI_SCORE_TABLE;
}
}
// Establece el nivel de volumen // Establece el nivel de volumen
void Credits::setVolume(int amount) { void Credits::setVolume(int amount) {
Options::audio.music.volume = std::clamp(amount, 0, 100); Options::audio.music.volume = std::clamp(amount, 0, 100);
@@ -503,10 +651,10 @@ void Credits::cycleColors() {
tiled_bg_->setColor(color_); tiled_bg_->setColor(color_);
} }
// Actualza los jugadores // Actualza los jugadores (time-based)
void Credits::updatePlayers() { void Credits::updatePlayers(float deltaTime) {
for (auto &player : players_) { for (auto &player : players_) {
player->update(); player->update(deltaTime);
} }
} }

View File

@@ -25,6 +25,11 @@ class Credits {
// --- Bucle principal --- // --- Bucle principal ---
void run(); void run();
private:
// --- Métodos del bucle principal ---
void update(float deltaTime); // Actualización principal de la lógica (time-based)
auto calculateDeltaTime() -> float; // Calcula el deltatime
private: private:
// --- Constantes de clase --- // --- Constantes de clase ---
static constexpr int PLAY_AREA_HEIGHT = 200; static constexpr int PLAY_AREA_HEIGHT = 200;
@@ -41,10 +46,10 @@ class Credits {
SDL_Texture *canvas_; // Textura donde se dibuja todo SDL_Texture *canvas_; // Textura donde se dibuja todo
// --- Temporización y contadores --- // --- Temporización y contadores ---
Uint64 ticks_ = 0; // Control de velocidad del programa Uint64 last_time_ = 0; // Último tiempo registrado para deltaTime
Uint32 counter_ = 0; // Contador principal de lógica float counter_ = 0; // Contador principal de lógica
Uint32 counter_pre_fade_ = 0; // Activación del fundido final float counter_pre_fade_ = 0; // Activación del fundido final
Uint32 counter_prevent_endless_ = 0; // Prevención de bucle infinito float counter_prevent_endless_ = 0; // Prevención de bucle infinito
// --- Variables de estado --- // --- Variables de estado ---
bool fading_ = false; // Estado del fade final bool fading_ = false; // Estado del fade final
@@ -101,8 +106,6 @@ class Credits {
// Borde para la ventana // Borde para la ventana
SDL_FRect border_rect_ = play_area_; // Delimitador de ventana SDL_FRect border_rect_ = play_area_; // Delimitador de ventana
// --- Métodos del bucle principal ---
void update(); // Actualización principal de la lógica
void render(); // Renderizado de la escena void render(); // Renderizado de la escena
static void checkEvents(); // Manejo de eventos static void checkEvents(); // Manejo de eventos
void checkInput(); // Procesamiento de entrada void checkInput(); // Procesamiento de entrada
@@ -110,19 +113,23 @@ class Credits {
// --- Métodos de renderizado --- // --- Métodos de renderizado ---
void fillTextTexture(); // Crear textura de texto de créditos void fillTextTexture(); // Crear textura de texto de créditos
void fillCanvas(); // Renderizar todos los sprites y fondos void fillCanvas(); // Renderizar todos los sprites y fondos
void updateTextureDstRects(); // Actualizar destinos de texturas
void renderPlayers(); // Renderiza los jugadores void renderPlayers(); // Renderiza los jugadores
// --- Métodos de lógica del juego --- // --- Métodos de lógica del juego ---
void throwBalloons(); // Lanzar globos al escenario void throwBalloons(); // Lanzar globos al escenario (frame-based)
void throwBalloons(float deltaTime); // Lanzar globos al escenario (time-based)
void initPlayers(); // Inicializar jugadores void initPlayers(); // Inicializar jugadores
void updateAllFades(); // Actualizar estados de fade void updateAllFades(); // Actualizar estados de fade (frame-based)
void updateAllFades(float deltaTime); // Actualizar estados de fade (time-based)
void cycleColors(); // Cambiar colores de fondo void cycleColors(); // Cambiar colores de fondo
void updatePlayers(); // Actualza los jugadores void updatePlayers(float deltaTime); // Actualza los jugadores (time-based)
// --- Métodos de interfaz --- // --- Métodos de interfaz ---
void updateBlackRects(); // Actualizar rectángulos negros (letterbox) void updateBlackRects(); // Actualizar rectángulos negros (letterbox) (frame-based)
void updateBlackRects(float deltaTime); // Actualizar rectángulos negros (letterbox) (time-based)
void updateRedRect(); // Actualizar rectángulo rojo (borde) void updateRedRect(); // Actualizar rectángulo rojo (borde)
void updateTextureDstRects(); // Actualizar destinos de texturas (frame-based)
void updateTextureDstRects(float deltaTime); // Actualizar destinos de texturas (time-based)
// --- Métodos de audio --- // --- Métodos de audio ---
static void setVolume(int amount); // Establecer volumen static void setVolume(int amount); // Establecer volumen

View File

@@ -49,7 +49,18 @@
// Constructor // Constructor
Game::Game(Player::Id player_id, int current_stage, bool demo) Game::Game(Player::Id player_id, int current_stage, bool demo)
: renderer_(Screen::get()->getRenderer()), screen_(Screen::get()), input_(Input::get()), canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)), pause_manager_(std::make_unique<PauseManager>([this](bool is_paused) { onPauseStateChanged(is_paused); })), stage_manager_(std::make_unique<StageManager>()), balloon_manager_(std::make_unique<BalloonManager>(stage_manager_.get())), background_(std::make_unique<Background>(stage_manager_->getPowerNeededToReachStage(stage_manager_->getTotalStages() - 1))), fade_in_(std::make_unique<Fade>()), fade_out_(std::make_unique<Fade>()), tabe_(std::make_unique<Tabe>()), hit_(Hit(Resource::get()->getTexture("hit.png"))) { : renderer_(Screen::get()->getRenderer()),
screen_(Screen::get()),
input_(Input::get()),
canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)),
pause_manager_(std::make_unique<PauseManager>([this](bool is_paused) { onPauseStateChanged(is_paused); })),
stage_manager_(std::make_unique<StageManager>()),
balloon_manager_(std::make_unique<BalloonManager>(stage_manager_.get())),
background_(std::make_unique<Background>(stage_manager_->getPowerNeededToReachStage(stage_manager_->getTotalStages() - 1))),
fade_in_(std::make_unique<Fade>()),
fade_out_(std::make_unique<Fade>()),
tabe_(std::make_unique<Tabe>()),
hit_(Hit(Resource::get()->getTexture("hit.png"))) {
// Pasa variables // Pasa variables
demo_.enabled = demo; demo_.enabled = demo;
@@ -68,7 +79,7 @@ Game::Game(Player::Id player_id, int current_stage, bool demo)
scoreboard_ = Scoreboard::get(); scoreboard_ = Scoreboard::get();
fade_in_->setColor(param.fade.color); fade_in_->setColor(param.fade.color);
fade_in_->setPreDuration(demo_.enabled ? 500 : 0); fade_in_->setPreDuration(demo_.enabled ? DEMO_FADE_PRE_DURATION_MS : 0);
fade_in_->setPostDuration(0); fade_in_->setPostDuration(0);
fade_in_->setType(Fade::Type::RANDOM_SQUARE2); fade_in_->setType(Fade::Type::RANDOM_SQUARE2);
fade_in_->setMode(Fade::Mode::IN); fade_in_->setMode(Fade::Mode::IN);
@@ -200,9 +211,9 @@ void Game::updateHiScore() {
} }
// Actualiza las variables del jugador // Actualiza las variables del jugador
void Game::updatePlayers() { void Game::updatePlayers(float deltaTime) {
for (auto &player : players_) { for (auto &player : players_) {
player->update(); player->update(deltaTime);
if (player->isPlaying()) { if (player->isPlaying()) {
// Comprueba la colisión entre el jugador y los globos // Comprueba la colisión entre el jugador y los globos
@@ -211,7 +222,7 @@ void Game::updatePlayers() {
// Si hay colisión // Si hay colisión
if (balloon) { if (balloon) {
// Si el globo está parado y el temporizador activo, lo explota // Si el globo está parado y el temporizador activo, lo explota
if (balloon->isStopped() && time_stopped_counter_ > 0) { if (balloon->isStopped() && time_stopped_timer_ > 0) {
balloon_manager_->popBalloon(balloon); balloon_manager_->popBalloon(balloon);
} }
// En caso contrario, el jugador ha sido golpeado por un globo activo // En caso contrario, el jugador ha sido golpeado por un globo activo
@@ -285,11 +296,11 @@ void Game::updateStage() {
if (current_stage_index == total_stages - 1) { // Penúltima fase (será la última) if (current_stage_index == total_stages - 1) { // Penúltima fase (será la última)
createMessage(paths, Resource::get()->getTexture("game_text_last_stage")); createMessage(paths, Resource::get()->getTexture("game_text_last_stage"));
} else { } else {
auto text = Resource::get()->getText("04b_25_2x"); auto text = Resource::get()->getText("04b_25_2x_enhanced");
const std::string CAPTION = Lang::getText("[GAME_TEXT] 2") + const std::string CAPTION = Lang::getText("[GAME_TEXT] 2") +
std::to_string(total_stages - current_stage_index) + std::to_string(total_stages - current_stage_index) +
Lang::getText("[GAME_TEXT] 2A"); Lang::getText("[GAME_TEXT] 2A");
createMessage(paths, text->writeToTexture(CAPTION, 1, -4)); createMessage(paths, text->writeDXToTexture(Text::STROKE, CAPTION, -4, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
} }
} }
@@ -301,9 +312,9 @@ void Game::updateStage() {
} }
// Actualiza el estado de fin de la partida // Actualiza el estado de fin de la partida
void Game::updateGameStateGameOver() { void Game::updateGameStateGameOver(float deltaTime) {
fade_out_->update(); fade_out_->update();
updatePlayers(); updatePlayers(deltaTime);
updateScoreboard(); updateScoreboard();
updateBackground(); updateBackground();
balloon_manager_->update(); balloon_manager_->update();
@@ -312,20 +323,17 @@ void Game::updateGameStateGameOver() {
updateItems(); updateItems();
updateSmartSprites(); updateSmartSprites();
updatePathSprites(); updatePathSprites();
updateTimeStopped(); updateTimeStopped(deltaTime);
checkBulletCollision(); checkBulletCollision();
cleanVectors(); cleanVectors();
if (game_over_counter_ > 0) { if (game_over_timer_ < GAME_OVER_DURATION_MS) {
if (game_over_counter_ == GAME_OVER_COUNTER) { handleGameOverEvents(); // Maneja eventos al inicio
createMessage({paths_.at(2), paths_.at(3)}, Resource::get()->getTexture("game_text_game_over"));
Audio::get()->fadeOutMusic(1000);
balloon_manager_->setBouncingSounds(true);
}
game_over_counter_--; game_over_timer_ += deltaTime; // Incremento time-based
if (game_over_counter_ == 150) { constexpr float FADE_TRIGGER_MS = GAME_OVER_DURATION_MS - (150.0f * (1000.0f / 60.0f)); // 2500ms antes del final
if (game_over_timer_ >= FADE_TRIGGER_MS && !fade_out_->isEnabled()) {
fade_out_->activate(); fade_out_->activate();
} }
} }
@@ -338,7 +346,7 @@ void Game::updateGameStateGameOver() {
} }
if (fade_out_->hasEnded()) { if (fade_out_->hasEnded()) {
if (game_completed_counter_ > 0) { if (game_completed_timer_ > 0) {
Section::name = Section::Name::CREDITS; // Los jugadores han completado el juego Section::name = Section::Name::CREDITS; // Los jugadores han completado el juego
} else { } else {
Section::name = Section::Name::HI_SCORE_TABLE; // La partida ha terminado con la derrota de los jugadores Section::name = Section::Name::HI_SCORE_TABLE; // La partida ha terminado con la derrota de los jugadores
@@ -352,11 +360,8 @@ void Game::updateGameStateGameOver() {
} }
// Gestiona eventos para el estado del final del juego // Gestiona eventos para el estado del final del juego
void Game::updateGameStateCompleted() { void Game::updateGameStateCompleted(float deltaTime) {
constexpr int START_CELEBRATIONS = 400; updatePlayers(deltaTime);
constexpr int END_CELEBRATIONS = START_CELEBRATIONS + 300;
updatePlayers();
updateScoreboard(); updateScoreboard();
updateBackground(); updateBackground();
balloon_manager_->update(); balloon_manager_->update();
@@ -367,49 +372,16 @@ void Game::updateGameStateCompleted() {
updatePathSprites(); updatePathSprites();
cleanVectors(); cleanVectors();
// Para la música y elimina todos los globos e items // Maneja eventos del juego completado
if (game_completed_counter_ == 0) { handleGameCompletedEvents();
stopMusic(); // Detiene la música
balloon_manager_->destroyAllBalloons(); // Destruye a todos los globos
playSound("power_ball_explosion.wav"); // Sonido de destruir todos los globos
destroyAllItems(); // Destruye todos los items
background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas
}
// Comienza las celebraciones
// Muestra el mensaje de felicitación y da los puntos a los jugadores
if (game_completed_counter_ == START_CELEBRATIONS) {
createMessage({paths_.at(4), paths_.at(5)}, Resource::get()->getTexture("game_text_congratulations"));
createMessage({paths_.at(6), paths_.at(7)}, Resource::get()->getTexture("game_text_1000000_points"));
for (auto &player : players_) {
if (player->isPlaying()) {
player->addScore(1000000, Options::settings.hi_score_table.back().score);
player->setPlayingState(Player::State::CELEBRATING);
} else {
player->setPlayingState(Player::State::GAME_OVER);
}
}
updateHiScore();
}
// Termina las celebraciones
if (game_completed_counter_ == END_CELEBRATIONS) {
for (auto &player : players_) {
if (player->isCelebrating()) {
player->setPlayingState(player->qualifiesForHighScore() ? Player::State::ENTERING_NAME_GAME_COMPLETED : Player::State::LEAVING_SCREEN);
}
}
}
// Si los jugadores ya no estan y no quedan mensajes en pantalla // Si los jugadores ya no estan y no quedan mensajes en pantalla
if (allPlayersAreGameOver() && path_sprites_.empty()) { if (allPlayersAreGameOver() && path_sprites_.empty()) {
setState(State::GAME_OVER); setState(State::GAME_OVER);
} }
// Incrementa el contador al final // Incrementa el acumulador al final
++game_completed_counter_; game_completed_timer_ += deltaTime;
} }
// Comprueba el estado del juego // Comprueba el estado del juego
@@ -606,6 +578,7 @@ void Game::handleItemDrop(const std::shared_ptr<Balloon> &balloon, const std::sh
if (DROPPED_ITEM != ItemType::COFFEE_MACHINE) { if (DROPPED_ITEM != ItemType::COFFEE_MACHINE) {
createItem(DROPPED_ITEM, balloon->getPosX(), balloon->getPosY()); createItem(DROPPED_ITEM, balloon->getPosX(), balloon->getPosY());
playSound("item_drop.wav");
} else { } else {
createItem(DROPPED_ITEM, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT); createItem(DROPPED_ITEM, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT);
coffee_machine_enabled_ = true; coffee_machine_enabled_ = true;
@@ -733,7 +706,6 @@ auto Game::dropItem() -> ItemType {
// Crea un objeto item // Crea un objeto item
void Game::createItem(ItemType type, float x, float y) { 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])); 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]));
playSound("item_drop.wav");
} }
// Vacia el vector de items // Vacia el vector de items
@@ -885,21 +857,35 @@ void Game::handlePlayerCollision(std::shared_ptr<Player> &player, std::shared_pt
} }
} }
// Actualiza y comprueba el valor de la variable // Actualiza el estado del tiempo detenido
void Game::updateTimeStopped() { void Game::updateTimeStopped(float deltaTime) {
if (time_stopped_counter_ > 0) { static constexpr float WARNING_THRESHOLD_MS = 2000.0f; // 120 frames a 60fps
time_stopped_counter_--; static constexpr float CLOCK_SOUND_INTERVAL_MS = 500.0f; // 30 frames a 60fps
if (time_stopped_counter_ > 120) { static constexpr float COLOR_FLASH_INTERVAL_MS = 250.0f; // 15 frames a 60fps
if (time_stopped_counter_ % 30 == 0) {
if (time_stopped_timer_ > 0) {
time_stopped_timer_ -= deltaTime;
// Fase de advertencia (últimos 2 segundos)
if (time_stopped_timer_ <= WARNING_THRESHOLD_MS) {
static float last_sound_time = 0.0f;
last_sound_time += deltaTime;
if (last_sound_time >= CLOCK_SOUND_INTERVAL_MS) {
balloon_manager_->normalColorsToAllBalloons();
playSound("clock.wav");
last_sound_time = 0.0f;
} else if (last_sound_time >= COLOR_FLASH_INTERVAL_MS) {
balloon_manager_->reverseColorsToAllBalloons();
playSound("clock.wav"); playSound("clock.wav");
} }
} else { } else {
if (time_stopped_counter_ % 30 == 0) { // Fase normal - solo sonido ocasional
balloon_manager_->normalColorsToAllBalloons(); static float sound_timer = 0.0f;
playSound("clock.wav"); sound_timer += deltaTime;
} else if (time_stopped_counter_ % 30 == 15) { if (sound_timer >= CLOCK_SOUND_INTERVAL_MS) {
balloon_manager_->reverseColorsToAllBalloons();
playSound("clock.wav"); playSound("clock.wav");
sound_timer = 0.0f;
} }
} }
} else { } else {
@@ -907,18 +893,16 @@ void Game::updateTimeStopped() {
} }
} }
void Game::update() { // Actualiza toda la lógica del juego
if (SDL_GetTicks() - ticks_ > param.game.speed) { void Game::update(float deltaTime) {
ticks_ = SDL_GetTicks();
screen_->update(); screen_->update();
updateDemo(); updateDemo();
#ifdef RECORDING #ifdef RECORDING
updateRecording(); updateRecording();
#endif #endif
updateGameStates(); updateGameStates(deltaTime);
fillCanvas(); fillCanvas();
}
Audio::update(); Audio::update();
} }
@@ -936,26 +920,26 @@ void Game::render() {
} }
// Actualiza los estados del juego // Actualiza los estados del juego
void Game::updateGameStates() { void Game::updateGameStates(float deltaTime) {
if (!pause_manager_->isPaused()) { if (!pause_manager_->isPaused()) {
switch (state_) { switch (state_) {
case State::FADE_IN: case State::FADE_IN:
updateGameStateFadeIn(); updateGameStateFadeIn();
break; break;
case State::ENTERING_PLAYER: case State::ENTERING_PLAYER:
updateGameStateEnteringPlayer(); updateGameStateEnteringPlayer(deltaTime);
break; break;
case State::SHOWING_GET_READY_MESSAGE: case State::SHOWING_GET_READY_MESSAGE:
updateGameStateShowingGetReadyMessage(); updateGameStateShowingGetReadyMessage(deltaTime);
break; break;
case State::PLAYING: case State::PLAYING:
updateGameStatePlaying(); updateGameStatePlaying(deltaTime);
break; break;
case State::COMPLETED: case State::COMPLETED:
updateGameStateCompleted(); updateGameStateCompleted(deltaTime);
break; break;
case State::GAME_OVER: case State::GAME_OVER:
updateGameStateGameOver(); updateGameStateGameOver(deltaTime);
break; break;
default: default:
break; break;
@@ -994,23 +978,34 @@ void Game::fillCanvas() {
void Game::enableTimeStopItem() { void Game::enableTimeStopItem() {
balloon_manager_->stopAllBalloons(); balloon_manager_->stopAllBalloons();
balloon_manager_->reverseColorsToAllBalloons(); balloon_manager_->reverseColorsToAllBalloons();
time_stopped_counter_ = TIME_STOPPED_COUNTER; time_stopped_timer_ = TIME_STOPPED_DURATION_MS;
} }
// Deshabilita el efecto del item de detener el tiempo // Deshabilita el efecto del item de detener el tiempo
void Game::disableTimeStopItem() { void Game::disableTimeStopItem() {
time_stopped_counter_ = 0; time_stopped_timer_ = 0;
balloon_manager_->startAllBalloons(); balloon_manager_->startAllBalloons();
balloon_manager_->normalColorsToAllBalloons(); balloon_manager_->normalColorsToAllBalloons();
} }
// Calcula el deltatime
auto Game::calculateDeltaTime() -> float {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle para el juego // Bucle para el juego
void Game::run() { void Game::run() {
last_time_ = SDL_GetTicks();
while (Section::name == Section::Name::GAME) { while (Section::name == Section::Name::GAME) {
#ifndef RECORDING #ifndef RECORDING
checkInput(); checkInput();
#endif #endif
update(); const float delta_time = calculateDeltaTime();
update(delta_time);
handleEvents(); // Tiene que ir antes del render handleEvents(); // Tiene que ir antes del render
render(); render();
} }
@@ -1327,7 +1322,7 @@ void Game::handleFireInput(const std::shared_ptr<Player> &player, BulletType bul
cant_fire_counter = NORMAL_COOLDOWN; cant_fire_counter = NORMAL_COOLDOWN;
} }
player->setCantFireCounter(cant_fire_counter); player->startFiringSystem(cant_fire_counter); // Sistema de disparo de dos líneas
} }
} }
@@ -1371,17 +1366,27 @@ void Game::handleNormalPlayerInput(const std::shared_ptr<Player> &player) {
// Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado. // 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) { void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire) {
if (!player) {
return;
}
if (input_->checkAction(Input::Action::FIRE_CENTER, autofire, player->getUsesKeyboard(), player->getGamepad())) { if (input_->checkAction(Input::Action::FIRE_CENTER, autofire, player->getUsesKeyboard(), player->getGamepad())) {
handleFireInput(player, BulletType::UP); handleFireInput(player, BulletType::UP);
#ifdef RECORDING #ifdef RECORDING
demo_.keys.fire = 1; demo_.keys.fire = 1;
#endif #endif
} else if (input_->checkAction(Input::Action::FIRE_LEFT, autofire, player->getUsesKeyboard(), player->getGamepad())) { return;
}
if (input_->checkAction(Input::Action::FIRE_LEFT, autofire, player->getUsesKeyboard(), player->getGamepad())) {
handleFireInput(player, BulletType::LEFT); handleFireInput(player, BulletType::LEFT);
#ifdef RECORDING #ifdef RECORDING
demo_.keys.fire_left = 1; demo_.keys.fire_left = 1;
#endif #endif
} else if (input_->checkAction(Input::Action::FIRE_RIGHT, autofire, player->getUsesKeyboard(), player->getGamepad())) { return;
}
if (input_->checkAction(Input::Action::FIRE_RIGHT, autofire, player->getUsesKeyboard(), player->getGamepad())) {
handleFireInput(player, BulletType::RIGHT); handleFireInput(player, BulletType::RIGHT);
#ifdef RECORDING #ifdef RECORDING
demo_.keys.fire_right = 1; demo_.keys.fire_right = 1;
@@ -1395,6 +1400,7 @@ void Game::handlePlayerContinueInput(const std::shared_ptr<Player> &player) {
player->setPlayingState(Player::State::RECOVER); player->setPlayingState(Player::State::RECOVER);
player->addCredit(); player->addCredit();
sendPlayerToTheFront(player); sendPlayerToTheFront(player);
return;
} }
// Disminuye el contador de continuación si se presiona cualquier botón de disparo. // Disminuye el contador de continuación si se presiona cualquier botón de disparo.
@@ -1420,34 +1426,54 @@ void Game::handlePlayerWaitingInput(const std::shared_ptr<Player> &player) {
void Game::handleNameInput(const std::shared_ptr<Player> &player) { void Game::handleNameInput(const std::shared_ptr<Player> &player) {
if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) { if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
if (player->isShowingName()) { if (player->isShowingName()) {
player->setPlayingState(Player::State::CONTINUE); player->passShowingName();
} else if (player->getEnterNamePositionOverflow()) { return;
}
if (player->getEnterNamePositionOverflow()) {
player->setInput(Input::Action::START); player->setInput(Input::Action::START);
player->setPlayingState(Player::State::SHOWING_NAME); player->setPlayingState(Player::State::SHOWING_NAME);
playSound("service_menu_select.wav");
updateHiScoreName(); updateHiScoreName();
} else { return;
player->setInput(Input::Action::RIGHT);
} }
} else if (input_->checkAction(Input::Action::FIRE_CENTER, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad()) || player->setInput(Input::Action::RIGHT);
playSound("service_menu_select.wav");
return;
}
if (input_->checkAction(Input::Action::FIRE_CENTER, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad()) ||
input_->checkAction(Input::Action::FIRE_RIGHT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) { input_->checkAction(Input::Action::FIRE_RIGHT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
if (player->isShowingName()) { if (player->isShowingName()) {
player->setPlayingState(Player::State::CONTINUE); player->passShowingName();
} else { return;
player->setInput(Input::Action::LEFT);
} }
} else if (input_->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) { player->setInput(Input::Action::LEFT);
playSound("service_menu_back.wav");
return;
}
if (input_->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
player->setInput(Input::Action::UP); player->setInput(Input::Action::UP);
} else if (input_->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) { playSound("service_menu_move.wav");
return;
}
if (input_->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
player->setInput(Input::Action::DOWN); player->setInput(Input::Action::DOWN);
} else if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) { playSound("service_menu_move.wav");
return;
}
if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
if (player->isShowingName()) { if (player->isShowingName()) {
player->setPlayingState(Player::State::CONTINUE); player->passShowingName();
} else { return;
}
player->setInput(Input::Action::START); player->setInput(Input::Action::START);
player->setPlayingState(Player::State::SHOWING_NAME); player->setPlayingState(Player::State::SHOWING_NAME);
playSound("service_menu_select.wav");
updateHiScoreName(); updateHiScoreName();
} }
}
} }
// Inicializa las variables para el modo DEMO // Inicializa las variables para el modo DEMO
@@ -1457,20 +1483,16 @@ void Game::initDemo(Player::Id player_id) {
setState(State::PLAYING); setState(State::PLAYING);
// Aleatoriza la asignación del fichero con los datos del modo demostracion // Aleatoriza la asignación del fichero con los datos del modo demostracion
{
const auto DEMO1 = rand() % 2; const auto DEMO1 = rand() % 2;
const auto DEMO2 = (DEMO1 == 0) ? 1 : 0; const auto DEMO2 = (DEMO1 == 0) ? 1 : 0;
demo_.data.emplace_back(Resource::get()->getDemoData(DEMO1)); demo_.data.emplace_back(Resource::get()->getDemoData(DEMO1));
demo_.data.emplace_back(Resource::get()->getDemoData(DEMO2)); demo_.data.emplace_back(Resource::get()->getDemoData(DEMO2));
}
// Selecciona una pantalla al azar // Selecciona una pantalla al azar
{
constexpr auto NUM_DEMOS = 3; constexpr auto NUM_DEMOS = 3;
const auto DEMO = rand() % NUM_DEMOS; const auto DEMO = rand() % NUM_DEMOS;
constexpr std::array<int, NUM_DEMOS> STAGES = {0, 3, 5}; constexpr std::array<int, NUM_DEMOS> STAGES = {0, 3, 5};
stage_manager_->jumpToStage(STAGES.at(DEMO)); stage_manager_->jumpToStage(STAGES.at(DEMO));
}
// Activa o no al otro jugador // Activa o no al otro jugador
if (rand() % 3 != 0) { if (rand() % 3 != 0) {
@@ -1554,60 +1576,51 @@ void Game::initDifficultyVars() {
// Inicializa los jugadores // Inicializa los jugadores
void Game::initPlayers(Player::Id player_id) { void Game::initPlayers(Player::Id player_id) {
const int Y = param.game.play_area.rect.h - Player::HEIGHT + 1; // Se hunde un pixel para esconder el outline de los pies const int Y = param.game.play_area.rect.h - Player::HEIGHT + 1; // Se hunde un pixel para esconder el outline de los pies
const Player::State STATE = demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN;
// Crea al jugador uno y lo pone en modo espera // Crea al jugador uno y lo pone en modo espera
Player::Config config_player1; Player::Config config_player1{
config_player1.id = Player::Id::PLAYER1; .id = Player::Id::PLAYER1,
config_player1.x = param.game.play_area.first_quarter_x - (Player::WIDTH / 2); .x = param.game.play_area.first_quarter_x - (Player::WIDTH / 2),
config_player1.y = Y; .y = Y,
config_player1.demo = demo_.enabled; .demo = demo_.enabled,
config_player1.play_area = &param.game.play_area.rect; .play_area = &param.game.play_area.rect,
config_player1.texture = player_textures_.at(0); .texture = player_textures_.at(0),
config_player1.animations = player_animations_; .animations = player_animations_,
config_player1.hi_score_table = &Options::settings.hi_score_table; .hi_score_table = &Options::settings.hi_score_table,
config_player1.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER1) - 1); .glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER1) - 1),
config_player1.stage_info = stage_manager_.get(); .stage_info = stage_manager_.get()};
auto player1 = std::make_unique<Player>(config_player1); auto player1 = std::make_unique<Player>(config_player1);
player1->setScoreBoardPanel(Scoreboard::Id::LEFT); player1->setScoreBoardPanel(Scoreboard::Id::LEFT);
player1->setName(Lang::getText("[SCOREBOARD] 1")); player1->setName(Lang::getText("[SCOREBOARD] 1"));
player1->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER1).instance); player1->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER1).instance);
player1->setUsesKeyboard(Player::Id::PLAYER1 == Options::keyboard.player_id); player1->setUsesKeyboard(Player::Id::PLAYER1 == Options::keyboard.player_id);
player1->setPlayingState(Player::State::WAITING); player1->setPlayingState((player_id == Player::Id::BOTH_PLAYERS || player_id == Player::Id::PLAYER1) ? STATE : Player::State::WAITING);
players_.push_back(std::move(player1));
// Crea al jugador dos y lo pone en modo espera // Crea al jugador dos y lo pone en modo espera
Player::Config config_player2; Player::Config config_player2{
config_player2.id = Player::Id::PLAYER2; .id = Player::Id::PLAYER2,
config_player2.x = param.game.play_area.third_quarter_x - (Player::WIDTH / 2); .x = param.game.play_area.third_quarter_x - (Player::WIDTH / 2),
config_player2.y = Y; .y = Y,
config_player2.demo = demo_.enabled; .demo = demo_.enabled,
config_player2.play_area = &param.game.play_area.rect; .play_area = &param.game.play_area.rect,
config_player2.texture = player_textures_.at(1); .texture = player_textures_.at(1),
config_player2.animations = player_animations_; .animations = player_animations_,
config_player2.hi_score_table = &Options::settings.hi_score_table; .hi_score_table = &Options::settings.hi_score_table,
config_player2.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER2) - 1); .glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER2) - 1),
config_player2.stage_info = stage_manager_.get(); .stage_info = stage_manager_.get()};
auto player2 = std::make_unique<Player>(config_player2); auto player2 = std::make_unique<Player>(config_player2);
player2->setScoreBoardPanel(Scoreboard::Id::RIGHT); player2->setScoreBoardPanel(Scoreboard::Id::RIGHT);
player2->setName(Lang::getText("[SCOREBOARD] 2")); player2->setName(Lang::getText("[SCOREBOARD] 2"));
player2->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER2).instance); player2->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER2).instance);
player2->setUsesKeyboard(Player::Id::PLAYER2 == Options::keyboard.player_id); player2->setUsesKeyboard(Player::Id::PLAYER2 == Options::keyboard.player_id);
player2->setPlayingState(Player::State::WAITING); player2->setPlayingState((player_id == Player::Id::BOTH_PLAYERS || player_id == Player::Id::PLAYER2) ? STATE : Player::State::WAITING);
players_.push_back(std::move(player2));
// Activa el jugador que coincide con el "player_id" o ambos si es "0" // Añade los jugadores al vector de forma que el jugador 1 se pinte por delante del jugador 2
if (player_id == Player::Id::BOTH_PLAYERS) { players_.push_back(std::move(player2));
// Activa ambos jugadores players_.push_back(std::move(player1));
getPlayer(Player::Id::PLAYER1)->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
getPlayer(Player::Id::PLAYER2)->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
} else {
// Activa el jugador elegido
auto player = getPlayer(player_id);
player->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
sendPlayerToTheFront(player);
}
// Registra los jugadores en Options // Registra los jugadores en Options
for (const auto &player : players_) { for (const auto &player : players_) {
@@ -1698,9 +1711,9 @@ void Game::updateGameStateFadeIn() {
} }
// Actualiza las variables durante dicho estado // Actualiza las variables durante dicho estado
void Game::updateGameStateEnteringPlayer() { void Game::updateGameStateEnteringPlayer(float deltaTime) {
balloon_manager_->update(); balloon_manager_->update();
updatePlayers(); updatePlayers(deltaTime);
updateScoreboard(); updateScoreboard();
updateBackground(); updateBackground();
for (const auto &player : players_) { for (const auto &player : players_) {
@@ -1713,8 +1726,8 @@ void Game::updateGameStateEnteringPlayer() {
} }
// Actualiza las variables durante dicho estado // Actualiza las variables durante dicho estado
void Game::updateGameStateShowingGetReadyMessage() { void Game::updateGameStateShowingGetReadyMessage(float deltaTime) {
updateGameStatePlaying(); updateGameStatePlaying(deltaTime);
if (path_sprites_.empty()) { if (path_sprites_.empty()) {
setState(State::PLAYING); setState(State::PLAYING);
} }
@@ -1725,13 +1738,13 @@ void Game::updateGameStateShowingGetReadyMessage() {
} }
// Actualiza las variables durante el transcurso normal del juego // Actualiza las variables durante el transcurso normal del juego
void Game::updateGameStatePlaying() { void Game::updateGameStatePlaying(float deltaTime) {
#ifdef _DEBUG #ifdef _DEBUG
if (auto_pop_balloons_) { if (auto_pop_balloons_) {
stage_manager_->addPower(5); stage_manager_->addPower(5);
} }
#endif #endif
updatePlayers(); updatePlayers(deltaTime);
checkPlayersStatusPlaying(); checkPlayersStatusPlaying();
updateScoreboard(); updateScoreboard();
updateBackground(); updateBackground();
@@ -1742,7 +1755,7 @@ void Game::updateGameStatePlaying() {
updateStage(); updateStage();
updateSmartSprites(); updateSmartSprites();
updatePathSprites(); updatePathSprites();
updateTimeStopped(); updateTimeStopped(deltaTime);
updateHelper(); updateHelper();
checkBulletCollision(); checkBulletCollision();
updateMenace(); updateMenace();
@@ -1812,6 +1825,19 @@ void Game::checkAndUpdateBalloonSpeed() {
void Game::setState(State state) { void Game::setState(State state) {
state_ = state; state_ = state;
counter_ = 0; counter_ = 0;
switch (state) {
case State::COMPLETED: // Para la música y elimina todos los globos e items
stopMusic(); // Detiene la música
balloon_manager_->destroyAllBalloons(); // Destruye a todos los globos
playSound("power_ball_explosion.wav"); // Sonido de destruir todos los globos
destroyAllItems(); // Destruye todos los items
background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas
tabe_->disableSpawning(); // Deshabilita la creacion de Tabes
break;
default:
break;
}
} }
void Game::playSound(const std::string &name) const { void Game::playSound(const std::string &name) const {
@@ -1867,26 +1893,72 @@ void Game::onPauseStateChanged(bool is_paused) {
tabe_->pauseTimer(is_paused); tabe_->pauseTimer(is_paused);
} }
// Maneja eventos del juego completado usando flags para triggers únicos
void Game::handleGameCompletedEvents() {
constexpr float START_CELEBRATIONS_MS = 6667.0f; // 400 frames a 60fps
constexpr float END_CELEBRATIONS_MS = 11667.0f; // 700 frames a 60fps
// Inicio de celebraciones
static bool start_celebrations_triggered = false;
if (!start_celebrations_triggered && game_completed_timer_ >= START_CELEBRATIONS_MS) {
createMessage({paths_.at(4), paths_.at(5)}, Resource::get()->getTexture("game_text_congratulations"));
createMessage({paths_.at(6), paths_.at(7)}, Resource::get()->getTexture("game_text_1000000_points"));
for (auto &player : players_) {
if (player->isPlaying()) {
player->addScore(1000000, Options::settings.hi_score_table.back().score);
player->setPlayingState(Player::State::CELEBRATING);
} else {
player->setPlayingState(Player::State::GAME_OVER);
}
}
updateHiScore();
start_celebrations_triggered = true;
}
// Fin de celebraciones
static bool end_celebrations_triggered = false;
if (!end_celebrations_triggered && game_completed_timer_ >= END_CELEBRATIONS_MS) {
for (auto &player : players_) {
if (player->isCelebrating()) {
player->setPlayingState(player->qualifiesForHighScore() ? Player::State::ENTERING_NAME_GAME_COMPLETED : Player::State::LEAVING_SCREEN);
}
}
fade_out_->activate();
end_celebrations_triggered = true;
}
}
// Maneja eventos de game over usando flag para trigger único
void Game::handleGameOverEvents() {
static bool game_over_triggered = false;
if (!game_over_triggered && game_over_timer_ == 0.0f) {
createMessage({paths_.at(2), paths_.at(3)}, Resource::get()->getTexture("game_text_game_over"));
Audio::get()->fadeOutMusic(1000);
balloon_manager_->setBouncingSounds(true);
game_over_triggered = true;
}
}
#ifdef _DEBUG #ifdef _DEBUG
// Comprueba los eventos en el modo DEBUG // Comprueba los eventos en el modo DEBUG
void Game::handleDebugEvents(const SDL_Event &event) { void Game::handleDebugEvents(const SDL_Event &event) {
static int formation_id_ = 0; static int formation_id_ = 0;
if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) { if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) {
switch (event.key.key) { switch (event.key.key) {
case SDLK_1: // Crea una powerball case SDLK_1: { // Crea una powerball
{
balloon_manager_->createPowerBall(); balloon_manager_->createPowerBall();
break; break;
} }
case SDLK_2: // Activa o desactiva la aparición de globos case SDLK_2: { // Activa o desactiva la aparición de globos
{
static bool deploy_balloons_ = true; static bool deploy_balloons_ = true;
deploy_balloons_ = !deploy_balloons_; deploy_balloons_ = !deploy_balloons_;
balloon_manager_->enableBalloonDeployment(deploy_balloons_); balloon_manager_->enableBalloonDeployment(deploy_balloons_);
break; break;
} }
case SDLK_3: // Activa el modo para pasar el juego automaticamente case SDLK_3: { // Activa el modo para pasar el juego automaticamente
{
auto_pop_balloons_ = !auto_pop_balloons_; auto_pop_balloons_ = !auto_pop_balloons_;
Notifier::get()->show({"auto advance: " + boolToString(auto_pop_balloons_)}); Notifier::get()->show({"auto advance: " + boolToString(auto_pop_balloons_)});
if (auto_pop_balloons_) { if (auto_pop_balloons_) {
@@ -1896,24 +1968,20 @@ void Game::handleDebugEvents(const SDL_Event &event) {
balloon_manager_->enableBalloonDeployment(!auto_pop_balloons_); balloon_manager_->enableBalloonDeployment(!auto_pop_balloons_);
break; break;
} }
case SDLK_4: // Suelta un item case SDLK_4: { // Suelta un item
{
createItem(ItemType::CLOCK, players_.at(0)->getPosX(), players_.at(0)->getPosY() - 40); createItem(ItemType::CLOCK, players_.at(0)->getPosX(), players_.at(0)->getPosY() - 40);
break; break;
} }
case SDLK_5: // 5.000 case SDLK_5: { // 5.000
{
const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2); const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
createItemText(X, game_text_textures_.at(2)); createItemText(X, game_text_textures_.at(2));
break; break;
} }
case SDLK_6: // Crea un mensaje case SDLK_6: { // Crea un mensaje
{
createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready")); createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready"));
break; break;
} }
case SDLK_7: // 100.000 case SDLK_7: { // 100.000
{
const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2); const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
createItemText(X, game_text_textures_.at(6)); createItemText(X, game_text_textures_.at(6));
break; break;

View File

@@ -33,7 +33,22 @@ namespace Difficulty {
enum class Code; enum class Code;
} // namespace Difficulty } // namespace Difficulty
// --- Clase Game: gestor principal del juego --- // --- Clase Game: núcleo principal del gameplay ---
//
// Esta clase gestiona toda la lógica del juego durante las partidas activas,
// incluyendo mecánicas de juego, estados, objetos y sistemas de puntuación.
//
// Funcionalidades principales:
// • Gestión de jugadores: soporte para 1 o 2 jugadores simultáneos
// • Sistema de estados: fade-in, entrada, jugando, completado, game-over
// • Mecánicas de juego: globos, balas, ítems, power-ups y efectos especiales
// • Sistema de puntuación: scoreboard y tabla de récords
// • Efectos temporales: tiempo detenido, ayudas automáticas
// • Modo demo: reproducción automática para attract mode
// • Gestión de fases: progresión entre niveles y dificultad
//
// Utiliza un sistema de tiempo basado en milisegundos para garantizar
// comportamiento consistente independientemente del framerate.
class Game { class Game {
public: public:
// --- Constantes --- // --- Constantes ---
@@ -58,12 +73,13 @@ class Game {
GAME_OVER, // Fin del juego GAME_OVER, // Fin del juego
}; };
// --- Constantes internas --- // --- Constantes de tiempo (en milisegundos) ---
static constexpr int HELP_COUNTER = 1000; static constexpr float HELP_COUNTER_MS = 16667.0f; // Contador de ayuda (1000 frames a 60fps)
static constexpr int GAME_COMPLETED_START_FADE = 500; static constexpr float GAME_COMPLETED_START_FADE_MS = 8333.0f; // Inicio del fade al completar (500 frames)
static constexpr int GAME_COMPLETED_END = 700; static constexpr float GAME_COMPLETED_END_MS = 11667.0f; // Fin del juego completado (700 frames)
static constexpr int GAME_OVER_COUNTER = 350; static constexpr float GAME_OVER_DURATION_MS = 5833.0f; // Duración game over (350 frames)
static constexpr int TIME_STOPPED_COUNTER = 360; static constexpr float TIME_STOPPED_DURATION_MS = 6000.0f; // Duración del tiempo detenido (360 frames)
static constexpr int DEMO_FADE_PRE_DURATION_MS = 500; // Pre-duración del fade en modo demo
static constexpr int ITEM_POINTS_1_DISK_ODDS = 10; static constexpr int ITEM_POINTS_1_DISK_ODDS = 10;
static constexpr int ITEM_POINTS_2_GAVINA_ODDS = 6; static constexpr int ITEM_POINTS_2_GAVINA_ODDS = 6;
static constexpr int ITEM_POINTS_3_PACMAR_ODDS = 3; static constexpr int ITEM_POINTS_3_PACMAR_ODDS = 3;
@@ -77,7 +93,7 @@ class Game {
bool need_coffee{false}; // Indica si se necesitan cafes bool need_coffee{false}; // Indica si se necesitan cafes
bool need_coffee_machine{false}; // Indica si se necesita PowerUp bool need_coffee_machine{false}; // Indica si se necesita PowerUp
bool need_power_ball{false}; // Indica si se necesita una PowerBall bool need_power_ball{false}; // Indica si se necesita una PowerBall
int counter; // Contador para no dar ayudas consecutivas float counter; // Contador para no dar ayudas consecutivas
int item_disk_odds; // Probabilidad de aparición del objeto int item_disk_odds; // Probabilidad de aparición del objeto
int item_gavina_odds; // Probabilidad de aparición del objeto int item_gavina_odds; // Probabilidad de aparición del objeto
int item_pacmar_odds; // Probabilidad de aparición del objeto int item_pacmar_odds; // Probabilidad de aparición del objeto
@@ -86,7 +102,7 @@ class Game {
int item_coffee_machine_odds; // Probabilidad de aparición del objeto int item_coffee_machine_odds; // Probabilidad de aparición del objeto
Helper() Helper()
: counter(HELP_COUNTER), : counter(HELP_COUNTER_MS),
item_disk_odds(ITEM_POINTS_1_DISK_ODDS), item_disk_odds(ITEM_POINTS_1_DISK_ODDS),
item_gavina_odds(ITEM_POINTS_2_GAVINA_ODDS), item_gavina_odds(ITEM_POINTS_2_GAVINA_ODDS),
item_pacmar_odds(ITEM_POINTS_3_PACMAR_ODDS), item_pacmar_odds(ITEM_POINTS_3_PACMAR_ODDS),
@@ -134,14 +150,14 @@ class Game {
Demo demo_; // Variable con todas las variables relacionadas con el modo demo Demo demo_; // Variable con todas las variables relacionadas con el modo demo
Difficulty::Code difficulty_ = Options::settings.difficulty; // Dificultad del juego Difficulty::Code difficulty_ = Options::settings.difficulty; // Dificultad del juego
Helper helper_; // Variable para gestionar las ayudas Helper helper_; // Variable para gestionar las ayudas
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último tiempo registrado para deltaTime
bool coffee_machine_enabled_ = false; // Indica si hay una máquina de café en el terreno de juego bool coffee_machine_enabled_ = false; // Indica si hay una máquina de café en el terreno de juego
bool hi_score_achieved_ = false; // Indica si se ha superado la puntuación máxima bool hi_score_achieved_ = false; // Indica si se ha superado la puntuación máxima
float difficulty_score_multiplier_; // Multiplicador de puntos en función de la dificultad float difficulty_score_multiplier_ = 1.0f; // Multiplicador de puntos en función de la dificultad
int counter_ = 0; // Contador para el juego float counter_ = 0.0f; // Contador para el juego
int game_completed_counter_ = 0; // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más globos float game_completed_timer_ = 0.0f; // Acumulador de tiempo para el tramo final (milisegundos)
int game_over_counter_ = GAME_OVER_COUNTER; // Contador para el estado de fin de partida float game_over_timer_ = 0.0f; // Timer para el estado de fin de partida (milisegundos)
int time_stopped_counter_ = 0; // Temporizador para llevar la cuenta del tiempo detenido float time_stopped_timer_ = 0.0f; // Temporizador para llevar la cuenta del tiempo detenido
int menace_ = 0; // Nivel de amenaza actual int menace_ = 0; // Nivel de amenaza actual
int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos
State state_ = State::FADE_IN; // Estado State state_ = State::FADE_IN; // Estado
@@ -154,7 +170,8 @@ class Game {
#endif #endif
// --- Ciclo principal del juego --- // --- Ciclo principal del juego ---
void update(); // Actualiza la lógica principal del juego void update(float deltaTime); // Actualiza la lógica principal del juego
auto calculateDeltaTime() -> float; // Calcula el deltatime
void render(); // Renderiza todos los elementos del juego void render(); // Renderiza todos los elementos del juego
void handleEvents(); // Procesa los eventos del sistema en cola void handleEvents(); // Procesa los eventos del sistema en cola
void checkState(); // Verifica y actualiza el estado actual del juego void checkState(); // Verifica y actualiza el estado actual del juego
@@ -162,17 +179,17 @@ class Game {
void cleanVectors(); // Limpia vectores de elementos deshabilitados void cleanVectors(); // Limpia vectores de elementos deshabilitados
// --- Gestión de estados del juego --- // --- Gestión de estados del juego ---
void updateGameStates(); // Actualiza todos los estados del juego void updateGameStates(float deltaTime); // Actualiza todos los estados del juego
void updateGameStateFadeIn(); // Gestiona el estado de transición de entrada void updateGameStateFadeIn(); // Gestiona el estado de transición de entrada
void updateGameStateEnteringPlayer(); // Gestiona el estado de entrada de jugador void updateGameStateEnteringPlayer(float deltaTime); // Gestiona el estado de entrada de jugador
void updateGameStateShowingGetReadyMessage(); // Gestiona el estado de mensaje "preparado" void updateGameStateShowingGetReadyMessage(float deltaTime); // Gestiona el estado de mensaje "preparado"
void updateGameStatePlaying(); // Gestiona el estado de juego activo void updateGameStatePlaying(float deltaTime); // Gestiona el estado de juego activo
void updateGameStateCompleted(); // Gestiona el estado de juego completado void updateGameStateCompleted(float deltaTime); // Gestiona el estado de juego completado
void updateGameStateGameOver(); // Gestiona el estado de fin de partida void updateGameStateGameOver(float deltaTime); // Gestiona el estado de fin de partida
// --- Gestión de jugadores --- // --- Gestión de jugadores ---
void initPlayers(Player::Id player_id); // Inicializa los datos de los jugadores void initPlayers(Player::Id player_id); // Inicializa los datos de los jugadores
void updatePlayers(); // Actualiza las variables y estados de los jugadores void updatePlayers(float deltaTime); // Actualiza las variables y estados de los jugadores
void renderPlayers(); // Renderiza todos los jugadores en pantalla void renderPlayers(); // Renderiza todos los jugadores en pantalla
void sortPlayersByZOrder(); // Reorganiza el orden de dibujado de jugadores void sortPlayersByZOrder(); // Reorganiza el orden de dibujado de jugadores
auto getPlayer(Player::Id id) -> std::shared_ptr<Player>; // Obtiene un jugador por su identificador auto getPlayer(Player::Id id) -> std::shared_ptr<Player>; // Obtiene un jugador por su identificador
@@ -231,7 +248,9 @@ class Game {
// --- ítems especiales --- // --- ítems especiales ---
void enableTimeStopItem(); // Activa el efecto de detener el tiempo void enableTimeStopItem(); // Activa el efecto de detener el tiempo
void disableTimeStopItem(); // Desactiva el efecto de detener el tiempo void disableTimeStopItem(); // Desactiva el efecto de detener el tiempo
void updateTimeStopped(); // Actualiza el estado del tiempo detenido void updateTimeStopped(float deltaTime); // Actualiza el estado del tiempo detenido
void handleGameCompletedEvents(); // Maneja eventos del juego completado
void handleGameOverEvents(); // Maneja eventos de game over
void throwCoffee(int x, int y); // Crea efecto de café arrojado al ser golpeado void throwCoffee(int x, int y); // Crea efecto de café arrojado al ser golpeado
// --- Gestión de caída de ítems --- // --- Gestión de caída de ítems ---

View File

@@ -29,7 +29,14 @@
// Constructor // Constructor
HiScoreTable::HiScoreTable() HiScoreTable::HiScoreTable()
: renderer_(Screen::get()->getRenderer()), backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), fade_(std::make_unique<Fade>()), background_(std::make_unique<Background>()), ticks_(0), view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}), fade_mode_(Fade::Mode::IN), background_fade_color_(Color(0, 0, 0)) { : renderer_(Screen::get()->getRenderer()),
backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
fade_(std::make_unique<Fade>()),
background_(std::make_unique<Background>()),
last_time_(0),
view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}),
fade_mode_(Fade::Mode::IN),
background_fade_color_(Color(0, 0, 0)) {
// Inicializa el resto // Inicializa el resto
Section::name = Section::Name::HI_SCORE_TABLE; Section::name = Section::Name::HI_SCORE_TABLE;
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
@@ -46,17 +53,14 @@ HiScoreTable::~HiScoreTable() {
} }
// Actualiza las variables // Actualiza las variables
void HiScoreTable::update() { void HiScoreTable::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) {
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen Screen::get()->update(); // Actualiza el objeto screen
updateSprites(); // Actualiza las posiciones de los sprites de texto updateSprites(delta_time); // Actualiza las posiciones de los sprites de texto
background_->update(); // Actualiza el fondo background_->update(delta_time); // Actualiza el fondo
updateFade(); // Gestiona el fade updateFade(delta_time); // Gestiona el fade
updateCounter(); // Gestiona el contador y sus eventos updateCounter(); // Gestiona el contador y sus eventos
fillTexture(); // Dibuja los sprites en la textura fillTexture(); // Dibuja los sprites en la textura
}
Audio::update(); Audio::update();
} }
@@ -110,20 +114,32 @@ void HiScoreTable::checkInput() {
GlobalInputs::check(); GlobalInputs::check();
} }
// Calcula el tiempo transcurrido desde el último frame
float HiScoreTable::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle para la pantalla de instrucciones // Bucle para la pantalla de instrucciones
void HiScoreTable::run() { void HiScoreTable::run() {
last_time_ = SDL_GetTicks();
Audio::get()->playMusic("title.ogg"); Audio::get()->playMusic("title.ogg");
while (Section::name == Section::Name::HI_SCORE_TABLE) { while (Section::name == Section::Name::HI_SCORE_TABLE) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
} }
// Gestiona el fade // Gestiona el fade
void HiScoreTable::updateFade() { void HiScoreTable::updateFade(float delta_time) {
fade_->update(); fade_->update(delta_time);
if (fade_->hasEnded() && fade_mode_ == Fade::Mode::IN) { if (fade_->hasEnded() && fade_mode_ == Fade::Mode::IN) {
fade_->reset(); fade_->reset();
@@ -242,7 +258,7 @@ void HiScoreTable::createSprites() {
} }
// Actualiza las posiciones de los sprites de texto // Actualiza las posiciones de los sprites de texto
void HiScoreTable::updateSprites() { void HiScoreTable::updateSprites(float delta_time) {
constexpr int INIT_COUNTER = 190; constexpr int INIT_COUNTER = 190;
const int COUNTER_BETWEEN_ENTRIES = 16; const int COUNTER_BETWEEN_ENTRIES = 16;
if (counter_ >= INIT_COUNTER) { if (counter_ >= INIT_COUNTER) {
@@ -255,7 +271,7 @@ void HiScoreTable::updateSprites() {
} }
} }
for (auto const &entry : entry_names_) { for (auto const &entry : entry_names_) {
entry->update(); entry->update(delta_time);
} }
glowEntryNames(); glowEntryNames();

View File

@@ -45,26 +45,27 @@ class HiScoreTable {
// --- Variables --- // --- Variables ---
Uint16 counter_ = 0; // Contador Uint16 counter_ = 0; // Contador
Uint64 ticks_; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla
Fade::Mode fade_mode_; // Modo de fade a utilizar Fade::Mode fade_mode_; // Modo de fade a utilizar
Color background_fade_color_; // Color de atenuación del fondo Color background_fade_color_; // Color de atenuación del fondo
std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables void update(float delta_time); // Actualiza las variables
void render(); // Pinta en pantalla void render(); // Pinta en pantalla
static void checkEvents(); // Comprueba los eventos static void checkEvents(); // Comprueba los eventos
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
static auto format(int number) -> std::string; // Convierte un entero a un string con separadores de miles static auto format(int number) -> std::string; // Convierte un entero a un string con separadores de miles
void fillTexture(); // Dibuja los sprites en la textura void fillTexture(); // Dibuja los sprites en la textura
void updateFade(); // Gestiona el fade void updateFade(float delta_time); // Gestiona el fade
void createSprites(); // Crea los sprites con los textos void createSprites(); // Crea los sprites con los textos
void updateSprites(); // Actualiza las posiciones de los sprites de texto void updateSprites(float delta_time); // Actualiza las posiciones de los sprites de texto
void initFade(); // Inicializa el fade void initFade(); // Inicializa el fade
void initBackground(); // Inicializa el fondo void initBackground(); // Inicializa el fondo
auto getEntryColor(int counter) -> Color; // Obtiene un color del vector de colores de entradas auto getEntryColor(int counter) -> Color; // Obtiene un color del vector de colores de entradas
void iniEntryColors(); // Inicializa los colores de las entradas void iniEntryColors(); // Inicializa los colores de las entradas
void glowEntryNames(); // Hace brillar los nombres de la tabla de records void glowEntryNames(); // Hace brillar los nombres de la tabla de records
void updateCounter(); // Gestiona el contador void updateCounter(); // Gestiona el contador
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
}; };

View File

@@ -26,7 +26,12 @@
// Constructor // Constructor
Instructions::Instructions() 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>()) { : 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 // Configura las texturas
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND);
@@ -199,18 +204,15 @@ void Instructions::fillBackbuffer() {
} }
// Actualiza las variables // Actualiza las variables
void Instructions::update() { void Instructions::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) {
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen Screen::get()->update(); // Actualiza el objeto screen
counter_++; // Incrementa el contador counter_++; // Incrementa el contador
updateSprites(); // Actualiza los sprites updateSprites(); // Actualiza los sprites
updateBackbuffer(); // Gestiona la textura con los graficos updateBackbuffer(); // Gestiona la textura con los graficos
tiled_bg_->update(); // Actualiza el mosaico de fondo tiled_bg_->update(delta_time); // Actualiza el mosaico de fondo
fade_->update(); // Actualiza el objeto "fade" fade_->update(delta_time); // Actualiza el objeto "fade"
fillBackbuffer(); // Rellena el backbuffer fillBackbuffer(); // Rellena el backbuffer
}
Audio::update(); Audio::update();
} }
@@ -250,12 +252,24 @@ void Instructions::checkInput() {
GlobalInputs::check(); GlobalInputs::check();
} }
// Calcula el tiempo transcurrido desde el último frame
float Instructions::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle para la pantalla de instrucciones // Bucle para la pantalla de instrucciones
void Instructions::run() { void Instructions::run() {
last_time_ = SDL_GetTicks();
Audio::get()->playMusic("title.ogg"); Audio::get()->playMusic("title.ogg");
while (Section::name == Section::Name::INSTRUCTIONS) { while (Section::name == Section::Name::INSTRUCTIONS) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }

View File

@@ -34,7 +34,9 @@ struct Line { // Almacena información de línea animada
// Constructor de Line // Constructor de Line
Line(int y, float x, int direction) Line(int y, float x, int direction)
: y(y), x(x), direction(direction) {} : y(y),
x(x),
direction(direction) {}
}; };
// Clase Instructions // Clase Instructions
@@ -61,7 +63,7 @@ class Instructions {
// --- Variables --- // --- Variables ---
int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla
SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista
float item_space_ = 2.0; // Espacio entre los items en pantalla float item_space_ = 2.0; // Espacio entre los items en pantalla
@@ -71,7 +73,7 @@ class Instructions {
bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables void update(float delta_time); // Actualiza las variables
void render(); // Pinta en pantalla void render(); // Pinta en pantalla
static void checkEvents(); // Comprueba los eventos static void checkEvents(); // Comprueba los eventos
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
@@ -80,7 +82,8 @@ class Instructions {
void iniSprites(); // Inicializa los sprites de los items void iniSprites(); // Inicializa los sprites de los items
void updateSprites(); // Actualiza los sprites void updateSprites(); // Actualiza los sprites
static auto initializeLines(int height) -> std::vector<Line>; // Inicializa las líneas animadas static auto initializeLines(int height) -> std::vector<Line>; // Inicializa las líneas animadas
static auto moveLines(std::vector<Line> &lines, int width, float duration, Uint32 start_delay) -> bool; // Mueve las líneas static auto moveLines(std::vector<Line> &lines, int width, float duration, Uint32 start_delay) -> bool; // Mueve las líneas (ya usa tiempo real)
static void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas static void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas
void updateBackbuffer(); // Gestiona la textura con los gráficos void updateBackbuffer(); // Gestiona la textura con los gráficos
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
}; };

View File

@@ -207,17 +207,15 @@ void Intro::switchText(int from_index, int to_index) {
} }
// Actualiza las variables del objeto // Actualiza las variables del objeto
void Intro::update() { void Intro::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) {
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen Screen::get()->update(); // Actualiza el objeto screen
tiled_bg_->update(); // Actualiza el fondo tiled_bg_->update(delta_time); // Actualiza el fondo
switch (state_) { switch (state_) {
case State::SCENES: case State::SCENES:
updateSprites(); updateSprites(delta_time);
updateTexts(); updateTexts(delta_time);
updateScenes(); updateScenes();
break; break;
@@ -225,7 +223,6 @@ void Intro::update() {
updatePostState(); updatePostState();
break; break;
} }
}
Audio::update(); Audio::update();
} }
@@ -253,12 +250,24 @@ void Intro::render() {
SCREEN->render(); // Vuelca el contenido del renderizador en pantalla SCREEN->render(); // Vuelca el contenido del renderizador en pantalla
} }
// Calcula el tiempo transcurrido desde el último frame
float Intro::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle principal // Bucle principal
void Intro::run() { void Intro::run() {
last_time_ = SDL_GetTicks();
Audio::get()->playMusic("intro.ogg", 0); Audio::get()->playMusic("intro.ogg", 0);
while (Section::name == Section::Name::INTRO) { while (Section::name == Section::Name::INTRO) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
@@ -277,7 +286,7 @@ void Intro::initSprites() {
// Constantes // Constantes
constexpr int TOTAL_SPRITES = TEXTURE_LIST.size(); constexpr int TOTAL_SPRITES = TEXTURE_LIST.size();
const float BORDER = 2.0F; const float BORDER = CARD_BORDER_SIZE;
auto texture = Resource::get()->getTexture(TEXTURE_LIST.front()); auto texture = Resource::get()->getTexture(TEXTURE_LIST.front());
const float CARD_WIDTH = texture->getWidth() + (BORDER * 2); const float CARD_WIDTH = texture->getWidth() + (BORDER * 2);
@@ -329,16 +338,16 @@ void Intro::initSprites() {
const float X_DEST = param.game.game_area.center_x - (CARD_WIDTH / 2); 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); const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4);
card_sprites_.at(0)->addPath(-CARD_WIDTH - 10, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeInOutExpo, 0); card_sprites_.at(0)->addPath(-CARD_WIDTH - CARD_OFFSET_MARGIN, X_DEST, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_NORMAL, easeInOutExpo, 0);
card_sprites_.at(1)->addPath(param.game.width, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeOutBounce, 0); card_sprites_.at(1)->addPath(param.game.width, X_DEST, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_NORMAL, easeOutBounce, 0);
card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 40, easeOutQuint, 0); card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0);
card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, 300, easeInOutExpo, 0); card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0);
card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 70, easeOutElastic, 0); card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0);
card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 250, easeOutQuad, 450); card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG);
card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, 80, easeInElastic, 0); card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0);
// Constantes // Constantes
const float DESP = 8; const float DESP = SHADOW_OFFSET;
const float SHADOW_SPRITE_WIDTH = CARD_WIDTH; const float SHADOW_SPRITE_WIDTH = CARD_WIDTH;
const float SHADOW_SPRITE_HEIGHT = CARD_HEIGHT; const float SHADOW_SPRITE_HEIGHT = CARD_HEIGHT;
@@ -380,13 +389,13 @@ void Intro::initSprites() {
const float S_X_DEST = X_DEST + DESP; const float S_X_DEST = X_DEST + DESP;
const float S_Y_DEST = Y_DEST + DESP; const float S_Y_DEST = Y_DEST + DESP;
shadow_sprites_.at(0)->addPath(param.game.height + 10, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 100, easeInOutExpo, 0); 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);
shadow_sprites_.at(1)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 100, easeOutBounce, 0); shadow_sprites_.at(1)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_NORMAL, easeOutBounce, 0);
shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, 40, easeOutQuint, 0); shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0);
shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 300, easeInOutExpo, 0); shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0);
shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 70, easeOutElastic, 0); shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0);
shadow_sprites_.at(5)->addPath(param.game.width, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, 250, easeOutQuad, 450); 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);
shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, 80, easeInElastic, 0); shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0);
} }
// Inicializa los textos // Inicializa los textos
@@ -396,47 +405,47 @@ void Intro::initTexts() {
auto writer = std::make_unique<Writer>(Resource::get()->getText("04b_25_metal")); auto writer = std::make_unique<Writer>(Resource::get()->getText("04b_25_metal"));
writer->setPosX(0); writer->setPosX(0);
writer->setPosY(param.game.height - param.intro.text_distance_from_bottom); writer->setPosY(param.game.height - param.intro.text_distance_from_bottom);
writer->setKerning(-2); writer->setKerning(TEXT_KERNING);
writer->setEnabled(false); writer->setEnabled(false);
writer->setFinishedCounter(180); writer->setFinishedTimerMs(TEXT_DISPLAY_DURATION_MS);
texts_.push_back(std::move(writer)); texts_.push_back(std::move(writer));
} }
// Un dia qualsevol de l'any 2000 // Un dia qualsevol de l'any 2000
texts_.at(0)->setCaption(Lang::getText("[INTRO] 1")); texts_.at(0)->setCaption(Lang::getText("[INTRO] 1"));
texts_.at(0)->setSpeed(8); texts_.at(0)->setSpeed(TEXT_SPEED_NORMAL);
// Tot esta tranquil a la UPV // Tot esta tranquil a la UPV
texts_.at(1)->setCaption(Lang::getText("[INTRO] 2")); texts_.at(1)->setCaption(Lang::getText("[INTRO] 2"));
texts_.at(1)->setSpeed(8); texts_.at(1)->setSpeed(TEXT_SPEED_NORMAL);
// Fins que un desaprensiu... // Fins que un desaprensiu...
texts_.at(2)->setCaption(Lang::getText("[INTRO] 3")); texts_.at(2)->setCaption(Lang::getText("[INTRO] 3"));
texts_.at(2)->setSpeed(12); texts_.at(2)->setSpeed(TEXT_SPEED_FAST);
// HEY! ME ANE A FERME UN CORTAET... // HEY! ME ANE A FERME UN CORTAET...
texts_.at(3)->setCaption(Lang::getText("[INTRO] 4")); texts_.at(3)->setCaption(Lang::getText("[INTRO] 4"));
texts_.at(3)->setSpeed(8); texts_.at(3)->setSpeed(TEXT_SPEED_NORMAL);
// UAAAAAAAAAAAAA!!! // UAAAAAAAAAAAAA!!!
texts_.at(4)->setCaption(Lang::getText("[INTRO] 5")); texts_.at(4)->setCaption(Lang::getText("[INTRO] 5"));
texts_.at(4)->setSpeed(1); texts_.at(4)->setSpeed(TEXT_SPEED_VERY_SLOW);
// Espera un moment... // Espera un moment...
texts_.at(5)->setCaption(Lang::getText("[INTRO] 6")); texts_.at(5)->setCaption(Lang::getText("[INTRO] 6"));
texts_.at(5)->setSpeed(16); texts_.at(5)->setSpeed(TEXT_SPEED_VERY_FAST);
// Si resulta que no tinc solt! // Si resulta que no tinc solt!
texts_.at(6)->setCaption(Lang::getText("[INTRO] 7")); texts_.at(6)->setCaption(Lang::getText("[INTRO] 7"));
texts_.at(6)->setSpeed(2); texts_.at(6)->setSpeed(TEXT_SPEED_SLOW);
// MERDA DE MAQUINA! // MERDA DE MAQUINA!
texts_.at(7)->setCaption(Lang::getText("[INTRO] 8")); texts_.at(7)->setCaption(Lang::getText("[INTRO] 8"));
texts_.at(7)->setSpeed(3); texts_.at(7)->setSpeed(TEXT_SPEED_MEDIUM_SLOW);
// Blop... blop... blop... // Blop... blop... blop...
texts_.at(8)->setCaption(Lang::getText("[INTRO] 9")); texts_.at(8)->setCaption(Lang::getText("[INTRO] 9"));
texts_.at(8)->setSpeed(20); texts_.at(8)->setSpeed(TEXT_SPEED_ULTRA_FAST);
for (auto &text : texts_) { for (auto &text : texts_) {
text->center(param.game.game_area.center_x); text->center(param.game.game_area.center_x);
@@ -444,20 +453,20 @@ void Intro::initTexts() {
} }
// Actualiza los sprites // Actualiza los sprites
void Intro::updateSprites() { void Intro::updateSprites(float delta_time) {
for (auto &sprite : card_sprites_) { for (auto &sprite : card_sprites_) {
sprite->update(); sprite->update(delta_time);
} }
for (auto &sprite : shadow_sprites_) { for (auto &sprite : shadow_sprites_) {
sprite->update(); sprite->update(delta_time);
} }
} }
// Actualiza los textos // Actualiza los textos
void Intro::updateTexts() { void Intro::updateTexts(float delta_time) {
for (auto &text : texts_) { for (auto &text : texts_) {
text->update(); text->update(delta_time);
} }
} }
@@ -480,8 +489,8 @@ void Intro::updatePostState() {
switch (post_state_) { switch (post_state_) {
case PostState::STOP_BG: case PostState::STOP_BG:
// EVENTO: Detiene el fondo después de 1 segundo // EVENTO: Detiene el fondo después del tiempo especificado
if (ELAPSED_TIME >= 1000) { if (ELAPSED_TIME >= POST_BG_STOP_DELAY_MS) {
tiled_bg_->stopGracefully(); tiled_bg_->stopGracefully();
if (!bg_color_.IS_EQUAL_TO(param.title.bg_color)) { if (!bg_color_.IS_EQUAL_TO(param.title.bg_color)) {
@@ -499,8 +508,8 @@ void Intro::updatePostState() {
break; break;
case PostState::END: case PostState::END:
// Finaliza la intro después de 1 segundo // Finaliza la intro después del tiempo especificado
if (ELAPSED_TIME >= 1000) { if (ELAPSED_TIME >= POST_END_DELAY_MS) {
Audio::get()->stopMusic(); Audio::get()->stopMusic();
Section::name = Section::Name::TITLE; Section::name = Section::Name::TITLE;
Section::options = Section::Options::TITLE_1; Section::options = Section::Options::TITLE_1;

View File

@@ -11,9 +11,22 @@
#include "tiled_bg.h" // Para TiledBG #include "tiled_bg.h" // Para TiledBG
#include "writer.h" // Para Writer #include "writer.h" // Para Writer
// --- Clase Intro: muestra la secuencia de introducción --- // --- Clase Intro: secuencia cinemática de introducción del juego ---
// Esta clase gestiona un estado del programa. Se encarga de mostrar la secuencia //
// de introducción. // Esta clase gestiona la secuencia de introducción narrativa del juego, mostrando
// una serie de escenas con imágenes, texto y efectos visuales sincronizados.
//
// Funcionalidades principales:
// • Sistema de escenas secuencial: 6 escenas con transiciones automáticas
// • Animaciones de tarjetas: efectos de entrada con diferentes tipos de easing
// • 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 { class Intro {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
@@ -24,6 +37,35 @@ class Intro {
void run(); void run();
private: private:
// --- Constantes de tiempo (en milisegundos) ---
static constexpr float TEXT_DISPLAY_DURATION_MS = 3000.0f; // Duración de visualización de texto (180 frames a 60fps)
static constexpr Uint32 POST_BG_STOP_DELAY_MS = 1000; // Retraso antes de detener el fondo
static constexpr Uint32 POST_END_DELAY_MS = 1000; // 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 int TEXT_KERNING = -2; // Espaciado entre caracteres
// --- Constantes de velocidades de texto ---
static constexpr int TEXT_SPEED_NORMAL = 8; // Velocidad normal de escritura
static constexpr int TEXT_SPEED_FAST = 12; // Velocidad rápida
static constexpr int TEXT_SPEED_VERY_SLOW = 1; // Velocidad muy lenta (grito)
static constexpr int TEXT_SPEED_VERY_FAST = 16; // Velocidad muy rápida
static constexpr int TEXT_SPEED_SLOW = 2; // Velocidad lenta
static constexpr int TEXT_SPEED_MEDIUM_SLOW = 3; // Velocidad medio-lenta
static constexpr int TEXT_SPEED_ULTRA_FAST = 20; // Velocidad ultra rápida
// --- Constantes de animaciones de tarjetas (duraciones en ms) ---
static constexpr int CARD_ANIM_DURATION_NORMAL = 100; // Duración estándar (100ms)
static constexpr int CARD_ANIM_DURATION_FAST = 40; // Duración rápida (40ms)
static constexpr int CARD_ANIM_DURATION_MEDIUM = 70; // Duración media (70ms)
static constexpr int CARD_ANIM_DURATION_SHORT = 80; // Duración corta (80ms)
static constexpr int CARD_ANIM_DURATION_SLOW = 250; // Duración lenta (250ms)
static constexpr int CARD_ANIM_DURATION_VERY_SLOW = 300; // Duración muy lenta (300ms)
static constexpr int CARD_ANIM_DELAY_LONG = 450; // Retraso largo antes de animación
static constexpr float CARD_OFFSET_MARGIN = 10.0f; // Margen fuera de pantalla
// --- Estados internos --- // --- Estados internos ---
enum class State { enum class State {
SCENES, SCENES,
@@ -42,7 +84,7 @@ class Intro {
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
// --- Variables --- // --- Variables ---
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
int scene_ = 0; // Indica qué escena está activa int scene_ = 0; // Indica qué escena está activa
State state_ = State::SCENES; // Estado principal de la intro State state_ = State::SCENES; // Estado principal de la intro
PostState post_state_ = PostState::STOP_BG; // Estado POST PostState post_state_ = PostState::STOP_BG; // Estado POST
@@ -50,19 +92,20 @@ class Intro {
Color bg_color_ = param.intro.bg_color; // Color de fondo Color bg_color_ = param.intro.bg_color; // Color de fondo
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables del objeto void update(float delta_time); // Actualiza las variables del objeto
void render(); // Dibuja el objeto en pantalla void render(); // Dibuja el objeto en pantalla
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
static void checkEvents(); // Comprueba los eventos static void checkEvents(); // Comprueba los eventos
void updateScenes(); // Actualiza las escenas de la intro void updateScenes(); // Actualiza las escenas de la intro
void initSprites(); // Inicializa las imágenes void initSprites(); // Inicializa las imágenes
void initTexts(); // Inicializa los textos void initTexts(); // Inicializa los textos
void updateSprites(); // Actualiza los sprites void updateSprites(float delta_time); // Actualiza los sprites
void updateTexts(); // Actualiza los textos void updateTexts(float delta_time); // Actualiza los textos
void renderSprites(); // Dibuja los sprites void renderSprites(); // Dibuja los sprites
void renderTexts(); // Dibuja los textos void renderTexts(); // Dibuja los textos
static void renderTextRect(); // Dibuja el rectangulo de fondo del texto; static void renderTextRect(); // Dibuja el rectangulo de fondo del texto;
void updatePostState(); // Actualiza el estado POST void updatePostState(); // Actualiza el estado POST
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
// --- Métodos para manejar cada escena individualmente --- // --- Métodos para manejar cada escena individualmente ---
void updateScene0(); void updateScene0();

View File

@@ -28,38 +28,38 @@ Logo::Logo()
dest_.y = param.game.game_area.center_y - jail_texture_->getHeight() / 2; dest_.y = param.game.game_area.center_y - jail_texture_->getHeight() / 2;
since_sprite_->setPosition(SDL_FRect{ since_sprite_->setPosition(SDL_FRect{
static_cast<float>((param.game.width - since_texture_->getWidth()) / 2), static_cast<float>((param.game.width - since_texture_->getWidth()) / 2),
static_cast<float>(83 + jail_texture_->getHeight() + 5), static_cast<float>(SINCE_SPRITE_Y_OFFSET + jail_texture_->getHeight() + LOGO_SPACING),
static_cast<float>(since_texture_->getWidth()), static_cast<float>(since_texture_->getWidth()),
static_cast<float>(since_texture_->getHeight())}); static_cast<float>(since_texture_->getHeight())});
since_sprite_->setY(dest_.y + jail_texture_->getHeight() + 5); since_sprite_->setY(dest_.y + jail_texture_->getHeight() + LOGO_SPACING);
since_sprite_->setSpriteClip(0, 0, since_texture_->getWidth(), since_texture_->getHeight()); since_sprite_->setSpriteClip(0, 0, since_texture_->getWidth(), since_texture_->getHeight());
since_texture_->setColor(0x00, 0x00, 0x00); since_texture_->setColor(SPECTRUM_BLACK.r, SPECTRUM_BLACK.g, SPECTRUM_BLACK.b);
// Crea los sprites de cada linea // Crea los sprites de cada linea
for (int i = 0; i < jail_texture_->getHeight(); ++i) { for (int i = 0; i < jail_texture_->getHeight(); ++i) {
auto temp = std::make_unique<Sprite>(jail_texture_, 0, i, jail_texture_->getWidth(), 1); auto temp = std::make_unique<Sprite>(jail_texture_, 0, i, jail_texture_->getWidth(), SPRITE_LINE_HEIGHT);
temp->setSpriteClip(0, i, jail_texture_->getWidth(), 1); temp->setSpriteClip(0, i, jail_texture_->getWidth(), SPRITE_LINE_HEIGHT);
const int POS_X = (i % 2 == 0) ? param.game.width + (i * 3) : -jail_texture_->getWidth() - (i * 3); const int POS_X = (i % 2 == 0) ? param.game.width + (i * LINE_OFFSET_FACTOR) : -jail_texture_->getWidth() - (i * LINE_OFFSET_FACTOR);
temp->setX(POS_X); temp->setX(POS_X);
temp->setY(dest_.y + i); temp->setY(dest_.y + i);
jail_sprite_.push_back(std::move(temp)); jail_sprite_.push_back(std::move(temp));
} }
// Inicializa el vector de colores // Inicializa el vector de colores con la paleta ZX Spectrum
color_.emplace_back(0x00, 0x00, 0x00); // Black color_.emplace_back(SPECTRUM_BLACK);
color_.emplace_back(0x00, 0x00, 0xd8); // Blue color_.emplace_back(SPECTRUM_BLUE);
color_.emplace_back(0xd8, 0x00, 0x00); // Red color_.emplace_back(SPECTRUM_RED);
color_.emplace_back(0xd8, 0x00, 0xd8); // Magenta color_.emplace_back(SPECTRUM_MAGENTA);
color_.emplace_back(0x00, 0xd8, 0x00); // Green color_.emplace_back(SPECTRUM_GREEN);
color_.emplace_back(0x00, 0xd8, 0xd8); // Cyan color_.emplace_back(SPECTRUM_CYAN);
color_.emplace_back(0xd8, 0xd8, 0x00); // Yellow color_.emplace_back(SPECTRUM_YELLOW);
color_.emplace_back(0xFF, 0xFF, 0xFF); // Bright white color_.emplace_back(SPECTRUM_WHITE);
} }
// Destructor // Destructor
Logo::~Logo() { Logo::~Logo() {
jail_texture_->setColor(255, 255, 255); jail_texture_->setColor(RESET_COLOR.r, RESET_COLOR.g, RESET_COLOR.b);
since_texture_->setColor(255, 255, 255); since_texture_->setColor(RESET_COLOR.r, RESET_COLOR.g, RESET_COLOR.b);
Audio::get()->stopAllSounds(); Audio::get()->stopAllSounds();
Audio::get()->stopMusic(); Audio::get()->stopMusic();
} }
@@ -78,22 +78,30 @@ void Logo::checkInput() {
GlobalInputs::check(); GlobalInputs::check();
} }
// Gestiona el logo de JAILGAMES // Maneja la reproducción del sonido del logo
void Logo::updateJAILGAMES() { void Logo::handleSound() {
if (counter_ == 30) { static bool sound_triggered = false;
Audio::get()->playSound("logo.wav");
}
if (counter_ > 30) { if (!sound_triggered && elapsed_time_ms_ >= SOUND_TRIGGER_TIME_MS) {
for (int i = 0; i < (int)jail_sprite_.size(); ++i) { Audio::get()->playSound("logo.wav");
sound_triggered = true;
}
}
// Gestiona el logo de JAILGAMES
void Logo::updateJAILGAMES(float delta_time) {
if (elapsed_time_ms_ > SOUND_TRIGGER_TIME_MS) {
const float PIXELS_TO_MOVE = LOGO_SPEED_PX_PER_MS * delta_time;
for (size_t i = 0; i < jail_sprite_.size(); ++i) {
if (jail_sprite_[i]->getX() != dest_.x) { if (jail_sprite_[i]->getX() != dest_.x) {
if (i % 2 == 0) { if (i % 2 == 0) {
jail_sprite_[i]->incX(-SPEED); jail_sprite_[i]->incX(-PIXELS_TO_MOVE);
if (jail_sprite_[i]->getX() < dest_.x) { if (jail_sprite_[i]->getX() < dest_.x) {
jail_sprite_[i]->setX(dest_.x); jail_sprite_[i]->setX(dest_.x);
} }
} else { } else {
jail_sprite_[i]->incX(SPEED); jail_sprite_[i]->incX(PIXELS_TO_MOVE);
if (jail_sprite_[i]->getX() > dest_.x) { if (jail_sprite_[i]->getX() > dest_.x) {
jail_sprite_[i]->setX(dest_.x); jail_sprite_[i]->setX(dest_.x);
} }
@@ -103,43 +111,41 @@ void Logo::updateJAILGAMES() {
} }
// Comprueba si ha terminado el logo // Comprueba si ha terminado el logo
if (counter_ == END_LOGO_COUNTER_MARK + POST_LOGO_DURATION) { if (elapsed_time_ms_ >= END_LOGO_TIME_MS + POST_LOGO_DURATION_MS) {
Section::name = Section::Name::INTRO; Section::name = Section::Name::INTRO;
} }
} }
// Gestiona el color de las texturas // Gestiona el color de las texturas
void Logo::updateTextureColors() { void Logo::updateTextureColors(float delta_time) {
constexpr int INC = 4;
// Manejo de 'sinceTexture' // Manejo de 'sinceTexture'
for (int i = 0; i <= 7; ++i) { for (int i = 0; i <= MAX_SINCE_COLOR_INDEX; ++i) {
if (counter_ == SHOW_SINCE_SPRITE_COUNTER_MARK + INC * i) { const float target_time = SHOW_SINCE_SPRITE_TIME_MS + COLOR_CHANGE_INTERVAL_MS * i;
if (elapsed_time_ms_ >= target_time && elapsed_time_ms_ - delta_time < target_time) {
since_texture_->setColor(color_[i].r, color_[i].g, color_[i].b); since_texture_->setColor(color_[i].r, color_[i].g, color_[i].b);
} }
} }
// Manejo de 'jailTexture' y 'sinceTexture' en el fade // Manejo de 'jailTexture' y 'sinceTexture' en el fade
for (int i = 0; i <= 6; ++i) { for (int i = 0; i <= MAX_FADE_COLOR_INDEX; ++i) {
if (counter_ == INIT_FADE_COUNTER_MARK + INC * i) { const float target_time = INIT_FADE_TIME_MS + COLOR_CHANGE_INTERVAL_MS * i;
jail_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b); if (elapsed_time_ms_ >= target_time && elapsed_time_ms_ - delta_time < target_time) {
since_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b); jail_texture_->setColor(color_[MAX_FADE_COLOR_INDEX - i].r, color_[MAX_FADE_COLOR_INDEX - i].g, color_[MAX_FADE_COLOR_INDEX - i].b);
since_texture_->setColor(color_[MAX_FADE_COLOR_INDEX - i].r, color_[MAX_FADE_COLOR_INDEX - i].g, color_[MAX_FADE_COLOR_INDEX - i].b);
} }
} }
} }
// Actualiza las variables // Actualiza las variables
void Logo::update() { void Logo::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { elapsed_time_ms_ += delta_time; // Acumula el tiempo transcurrido
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen Screen::get()->update(); // Actualiza el objeto screen
Audio::update(); // Actualiza el objeto audio
updateJAILGAMES(); // Actualiza el logo de JAILGAMES handleSound(); // Maneja la reproducción del sonido
updateTextureColors(); // Actualiza los colores de las texturas updateTextureColors(delta_time); // Actualiza los colores de las texturas
++counter_; // Gestiona el contador updateJAILGAMES(delta_time); // Actualiza el logo de JAILGAMES
}
Audio::update();
} }
// Dibuja en pantalla // Dibuja en pantalla
@@ -154,11 +160,23 @@ void Logo::render() {
SCREEN->render(); SCREEN->render();
} }
// Calcula el tiempo transcurrido desde el último frame
float Logo::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle para el logo del juego // Bucle para el logo del juego
void Logo::run() { void Logo::run() {
last_time_ = SDL_GetTicks();
while (Section::name == Section::Name::LOGO) { while (Section::name == Section::Name::LOGO) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
@@ -171,7 +189,7 @@ void Logo::renderJAILGAMES() {
sprite->render(); sprite->render();
} }
if (counter_ >= SHOW_SINCE_SPRITE_COUNTER_MARK) { if (elapsed_time_ms_ >= SHOW_SINCE_SPRITE_TIME_MS) {
since_sprite_->render(); since_sprite_->render();
} }
} }

View File

@@ -10,12 +10,21 @@
class Texture; class Texture;
// --- Clase Logo: dibuja el logo de JAILGAMES con efectos visuales --- // --- Clase Logo: pantalla de presentación de JAILGAMES con efectos retro ---
// Esta clase gestiona un estado del programa. Se encarga de dibujar por pantalla el //
// logo de "JAILGAMES" utilizando un sencillo efecto consistente en generar un sprite por // Esta clase gestiona el estado inicial del programa, mostrando el logo corporativo
// cada línea del bitmap que forma la palabra "JAILGAMES". Posteriormente realiza una // de JAILGAMES con efectos visuales inspirados en el ZX Spectrum.
// modulación de color sobre la textura para simular un fade to black al estilo //
// ZX Spectrum. // Funcionalidades principales:
// • Animación de convergencia: cada línea del logo entra desde los laterales
// • Efectos de color: transiciones automáticas usando la paleta ZX Spectrum
// • Audio sincronizado: reproduce sonido del logo en momento específico
// • Transición temporal: duración controlada con paso automático al siguiente estado
// • Sistema delta-time: animaciones suaves independientes del framerate
//
// La clase utiliza un sistema de tiempo basado en milisegundos para garantizar
// consistencia visual en diferentes velocidades de procesamiento.
class Logo { class Logo {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
@@ -26,12 +35,35 @@ class Logo {
void run(); void run();
private: private:
// --- Constantes --- // --- Constantes de tiempo (en milisegundos) ---
static constexpr int SHOW_SINCE_SPRITE_COUNTER_MARK = 70; // Tiempo del contador en el que empieza a verse el sprite de "SINCE 1998" static constexpr float SOUND_TRIGGER_TIME_MS = 500.0f; // Tiempo para activar el sonido del logo
static constexpr int INIT_FADE_COUNTER_MARK = 300; // Tiempo del contador cuando inicia el fade a negro static constexpr float SHOW_SINCE_SPRITE_TIME_MS = 1167.0f; // Tiempo para mostrar el sprite "SINCE 1998"
static constexpr int END_LOGO_COUNTER_MARK = 400; // Tiempo del contador para terminar el logo static constexpr float INIT_FADE_TIME_MS = 5000.0f; // Tiempo de inicio del fade a negro
static constexpr int POST_LOGO_DURATION = 20; // Tiempo que dura el logo con el fade al máximo static constexpr float END_LOGO_TIME_MS = 6668.0f; // Tiempo de finalización del logo
static constexpr int SPEED = 8; // Velocidad de desplazamiento de cada línea static constexpr float POST_LOGO_DURATION_MS = 333.0f; // Duración adicional después del fade
static constexpr float LOGO_SPEED_PX_PER_MS = 8.0f / 16.67f; // Velocidad de desplazamiento (píxeles por ms)
static constexpr float COLOR_CHANGE_INTERVAL_MS = 66.7f; // Intervalo entre cambios de color (~4 frames a 60fps)
// --- Constantes de layout ---
static constexpr int SINCE_SPRITE_Y_OFFSET = 83; // Posición Y base del sprite "Since 1998"
static constexpr int LOGO_SPACING = 5; // Espaciado entre elementos del logo
static constexpr int LINE_OFFSET_FACTOR = 3; // Factor de desplazamiento inicial por línea
static constexpr int SPRITE_LINE_HEIGHT = 1; // Altura de cada línea sprite
// --- Constantes de colores ---
static constexpr int MAX_SINCE_COLOR_INDEX = 7; // Índice máximo para colores del sprite "Since"
static constexpr int MAX_FADE_COLOR_INDEX = 6; // Índice máximo para colores del fade
// --- Paleta ZX Spectrum para efectos de logo ---
static constexpr Color SPECTRUM_BLACK = Color(0x00, 0x00, 0x00); // Negro
static constexpr Color SPECTRUM_BLUE = Color(0x00, 0x00, 0xd8); // Azul
static constexpr Color SPECTRUM_RED = Color(0xd8, 0x00, 0x00); // Rojo
static constexpr Color SPECTRUM_MAGENTA = Color(0xd8, 0x00, 0xd8); // Magenta
static constexpr Color SPECTRUM_GREEN = Color(0x00, 0xd8, 0x00); // Verde
static constexpr Color SPECTRUM_CYAN = Color(0x00, 0xd8, 0xd8); // Cian
static constexpr Color SPECTRUM_YELLOW = Color(0xd8, 0xd8, 0x00); // Amarillo
static constexpr Color SPECTRUM_WHITE = Color(0xFF, 0xFF, 0xFF); // Blanco brillante
static constexpr Color RESET_COLOR = Color(255, 255, 255); // Color de reset
// --- Objetos y punteros --- // --- Objetos y punteros ---
std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998" std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998"
@@ -41,16 +73,18 @@ class Logo {
// --- Variables --- // --- Variables ---
std::vector<Color> color_; // Vector con los colores para el fade std::vector<Color> color_; // Vector con los colores para el fade
int counter_ = 0; // Contador float elapsed_time_ms_ = 0.0f; // Tiempo transcurrido en milisegundos
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
SDL_FPoint dest_; // Posición donde dibujar el logo SDL_FPoint dest_; // Posición donde dibujar el logo
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables void update(float delta_time); // Actualiza las variables
void render(); // Dibuja en pantalla void render(); // Dibuja en pantalla
static void checkEvents(); // Comprueba el manejador de eventos static void checkEvents(); // Comprueba el manejador de eventos
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
void updateJAILGAMES(); // Gestiona el logo de JAILGAMES void updateJAILGAMES(float delta_time); // Gestiona el logo de JAILGAMES
void renderJAILGAMES(); // Renderiza el logo de JAILGAMES void renderJAILGAMES(); // Renderiza el logo de JAILGAMES
void updateTextureColors(); // Gestiona el color de las texturas void updateTextureColors(float delta_time); // Gestiona el color de las texturas
void handleSound(); // Maneja la reproducción del sonido del logo
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
}; };

View File

@@ -37,7 +37,13 @@ class Texture;
// Constructor // Constructor
Title::Title() Title::Title()
: text_(Resource::get()->getText("smb2_grad")), fade_(std::make_unique<Fade>()), tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::RANDOM)), 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_(TitleState::LOGO_ANIMATING), num_controllers_(Input::get()->getNumGamepads()) { : text_(Resource::get()->getText("smb2_grad")),
fade_(std::make_unique<Fade>()),
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::RANDOM)),
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()) {
// Configura objetos // Configura objetos
tiled_bg_->setColor(param.title.bg_color); tiled_bg_->setColor(param.title.bg_color);
game_logo_->enable(); game_logo_->enable();
@@ -54,16 +60,16 @@ Title::Title()
Section::attract_mode = IS_TITLE_TO_DEMO ? Section::AttractMode::TITLE_TO_LOGO : Section::AttractMode::TITLE_TO_DEMO; Section::attract_mode = IS_TITLE_TO_DEMO ? Section::AttractMode::TITLE_TO_LOGO : Section::AttractMode::TITLE_TO_DEMO;
// Define los anclajes de los elementos // Define los anclajes de los elementos
anchor_.mini_logo = (param.game.height / 5 * 4) + BLOCK; anchor_.mini_logo = (param.game.height / MINI_LOGO_Y_DIVISOR * MINI_LOGO_Y_FACTOR) + BLOCK;
mini_logo_sprite_->setY(anchor_.mini_logo); mini_logo_sprite_->setY(anchor_.mini_logo);
anchor_.copyright_text = anchor_.mini_logo + mini_logo_sprite_->getHeight() + 3; anchor_.copyright_text = anchor_.mini_logo + mini_logo_sprite_->getHeight() + COPYRIGHT_TEXT_SPACING;
} }
// Destructor // Destructor
Title::~Title() { Title::~Title() {
Audio::get()->stopAllSounds(); Audio::get()->stopAllSounds();
if (Section::name == Section::Name::LOGO) { if (Section::name == Section::Name::LOGO) {
Audio::get()->fadeOutMusic(300); Audio::get()->fadeOutMusic(MUSIC_FADE_OUT_SHORT_MS);
} }
// Desregistra los jugadores de Options // Desregistra los jugadores de Options
@@ -72,20 +78,27 @@ Title::~Title() {
} }
// Actualiza las variables del objeto // Actualiza las variables del objeto
void Title::update() { void Title::update(float deltaTime) {
if (SDL_GetTicks() - ticks_ > param.game.speed) {
ticks_ = SDL_GetTicks();
Screen::get()->update(); Screen::get()->update();
updateFade(); updateFade();
updateState(); updateState(deltaTime);
updateStartPrompt(); updateStartPrompt();
updatePlayers();
for (auto& player : players_) {
player->update(deltaTime);
} }
Audio::update(); Audio::update();
} }
// Calcula el tiempo transcurrido desde el último frame
float Title::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Dibuja el objeto en pantalla // Dibuja el objeto en pantalla
void Title::render() { void Title::render() {
static auto* const SCREEN = Screen::get(); static auto* const SCREEN = Screen::get();
@@ -130,7 +143,7 @@ void Title::handleDebugColorKeys(SDL_Keycode key) {
adjustColorComponent(key, color_); adjustColorComponent(key, color_);
counter_ = 0; counter_time_ = 0.0f;
tiled_bg_->setColor(color_); tiled_bg_->setColor(color_);
printColorValue(color_); printColorValue(color_);
} }
@@ -264,7 +277,7 @@ void Title::handleStartButtonPress(const Options::Gamepad* controller) {
} }
auto Title::canProcessStartButton() const -> bool { auto Title::canProcessStartButton() const -> bool {
return (state_ != TitleState::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP); return (state_ != State::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP);
} }
void Title::processPlayer1Start() { void Title::processPlayer1Start() {
@@ -283,22 +296,26 @@ void Title::processPlayer2Start() {
void Title::activatePlayerAndSetState(Player::Id player_id) { void Title::activatePlayerAndSetState(Player::Id player_id) {
getPlayer(player_id)->setPlayingState(Player::State::TITLE_ANIMATION); getPlayer(player_id)->setPlayingState(Player::State::TITLE_ANIMATION);
setState(TitleState::START_HAS_BEEN_PRESSED); setState(State::START_HAS_BEEN_PRESSED);
counter_ = 0; counter_time_ = 0.0f;
} }
// Bucle para el titulo del juego // Bucle para el titulo del juego
void Title::run() { void Title::run() {
last_time_ = SDL_GetTicks();
while (Section::name == Section::Name::TITLE) { while (Section::name == Section::Name::TITLE) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
} }
// Reinicia el contador interno // Reinicia el contador interno
void Title::resetCounter() { counter_ = 0; } void Title::resetCounter() { counter_time_ = 0.0f; }
// Intercambia la asignación de mandos a los jugadores // Intercambia la asignación de mandos a los jugadores
void Title::swapControllers() { void Title::swapControllers() {
@@ -360,22 +377,22 @@ void Title::updateFade() {
} }
// Actualiza el estado // Actualiza el estado
void Title::updateState() { void Title::updateState(float deltaTime) {
game_logo_->update(deltaTime);
tiled_bg_->update(deltaTime);
// Establece la lógica según el estado // Establece la lógica según el estado
switch (state_) { switch (state_) {
case TitleState::LOGO_ANIMATING: { case State::LOGO_ANIMATING: {
game_logo_->update();
if (game_logo_->hasFinished()) { if (game_logo_->hasFinished()) {
setState(TitleState::LOGO_FINISHED); setState(State::LOGO_FINISHED);
} }
break; break;
} }
case TitleState::LOGO_FINISHED: { case State::LOGO_FINISHED: {
++counter_; // Incrementa el contador counter_time_ += deltaTime;
game_logo_->update(); // Actualiza el logo con el título del juego
tiled_bg_->update(); // Actualiza el mosaico de fondo
if (counter_ == param.title.title_duration) { if (counter_time_ >= param.title.title_duration) {
// El menu ha hecho time out // El menu ha hecho time out
fade_->setPostDuration(0); fade_->setPostDuration(0);
fade_->activate(); fade_->activate();
@@ -383,12 +400,10 @@ void Title::updateState() {
} }
break; break;
} }
case TitleState::START_HAS_BEEN_PRESSED: { case State::START_HAS_BEEN_PRESSED: {
++counter_; // Incrementa el contador counter_time_ += deltaTime;
game_logo_->update(); // Actualiza el logo con el título del juego
tiled_bg_->update(); // Actualiza el mosaico de fondo
if (counter_ == 100) { if (counter_time_ >= START_PRESSED_DELAY_MS) {
fade_->activate(); fade_->activate();
} }
break; break;
@@ -400,22 +415,16 @@ void Title::updateState() {
} }
void Title::updateStartPrompt() { void Title::updateStartPrompt() {
constexpr Uint32 LOGO_BLINK_PERIOD = 833; // milisegundos
constexpr Uint32 LOGO_BLINK_ON_TIME = 583; // 833 - 250
constexpr Uint32 START_BLINK_PERIOD = 167;
constexpr Uint32 START_BLINK_ON_TIME = 83; // 167 - 83
Uint32 time_ms = SDL_GetTicks(); Uint32 time_ms = SDL_GetTicks();
bool condition_met = false; bool condition_met = false;
switch (state_) { switch (state_) {
case TitleState::LOGO_FINISHED: case State::LOGO_FINISHED:
condition_met = (time_ms % LOGO_BLINK_PERIOD) >= (LOGO_BLINK_PERIOD - LOGO_BLINK_ON_TIME); condition_met = (time_ms % LOGO_BLINK_PERIOD_MS) >= (LOGO_BLINK_PERIOD_MS - LOGO_BLINK_ON_TIME_MS);
break; break;
case TitleState::START_HAS_BEEN_PRESSED: case State::START_HAS_BEEN_PRESSED:
condition_met = (time_ms % START_BLINK_PERIOD) >= (START_BLINK_PERIOD - START_BLINK_ON_TIME); condition_met = (time_ms % START_BLINK_PERIOD_MS) >= (START_BLINK_PERIOD_MS - START_BLINK_ON_TIME_MS);
break; break;
default: default:
@@ -439,7 +448,7 @@ void Title::renderStartPrompt() {
} }
void Title::renderCopyright() { void Title::renderCopyright() {
if (state_ != TitleState::LOGO_ANIMATING) { if (state_ != State::LOGO_ANIMATING) {
// Mini logo // Mini logo
mini_logo_sprite_->render(); mini_logo_sprite_->render();
@@ -456,20 +465,20 @@ void Title::renderCopyright() {
} }
// Cambia el estado // Cambia el estado
void Title::setState(TitleState state) { void Title::setState(State state) {
if (state_ == state) { if (state_ == state) {
return; return;
} }
state_ = state; state_ = state;
switch (state_) { switch (state_) {
case TitleState::LOGO_ANIMATING: case State::LOGO_ANIMATING:
break; break;
case TitleState::LOGO_FINISHED: case State::LOGO_FINISHED:
Audio::get()->playMusic("title.ogg"); Audio::get()->playMusic("title.ogg");
break; break;
case TitleState::START_HAS_BEEN_PRESSED: case State::START_HAS_BEEN_PRESSED:
Audio::get()->fadeOutMusic(1500); Audio::get()->fadeOutMusic(MUSIC_FADE_OUT_LONG_MS);
break; break;
} }
} }
@@ -538,13 +547,6 @@ void Title::initPlayers() {
} }
} }
// Actualza los jugadores
void Title::updatePlayers() {
for (auto& player : players_) {
player->update();
}
}
// Renderiza los jugadores // Renderiza los jugadores
void Title::renderPlayers() { void Title::renderPlayers() {
for (auto const& player : players_) { for (auto const& player : players_) {

View File

@@ -19,11 +19,22 @@ namespace Options {
struct Gamepad; struct Gamepad;
} // namespace Options } // namespace Options
// --- Constantes --- // --- Clase Title: pantalla de título y menú principal del juego ---
constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner"; // Texto de copyright //
constexpr bool ALLOW_TITLE_ANIMATION_SKIP = false; // Permite saltar la animación del título // Esta clase gestiona la pantalla de título del juego, incluyendo el menú principal
// y la transición entre diferentes modos de juego.
// --- Clase Title: gestiona el estado de título/menú principal del juego --- //
// Funcionalidades principales:
// • Logo animado: muestra y anima el logotipo principal del juego
// • Selección de jugadores: permite iniciar partidas de 1 o 2 jugadores
// • Modo attract: cicla automáticamente entre título y demo
// • 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 milisegundos para garantizar
// comportamiento consistente independientemente del framerate.
class Title { class Title {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
@@ -34,8 +45,28 @@ class Title {
void run(); void run();
private: private:
// --- Constantes de tiempo (en milisegundos) ---
static constexpr float START_PRESSED_DELAY_MS = 1666.67f; // Tiempo antes de fade tras pulsar start (100 frames a 60fps)
static constexpr int MUSIC_FADE_OUT_LONG_MS = 1500; // Fade out largo de música
static constexpr int MUSIC_FADE_OUT_SHORT_MS = 300; // Fade out corto de música
// --- Constantes de parpadeo ---
static constexpr Uint32 LOGO_BLINK_PERIOD_MS = 833; // Período de parpadeo del logo
static constexpr Uint32 LOGO_BLINK_ON_TIME_MS = 583; // Tiempo encendido del logo (833-250)
static constexpr Uint32 START_BLINK_PERIOD_MS = 167; // Período de parpadeo del start
static constexpr Uint32 START_BLINK_ON_TIME_MS = 83; // Tiempo encendido del start (167-83)
// --- Constantes de layout ---
static constexpr int MINI_LOGO_Y_DIVISOR = 5; // Divisor para posición Y del mini logo
static constexpr int MINI_LOGO_Y_FACTOR = 4; // Factor para posición Y del mini logo
static constexpr int COPYRIGHT_TEXT_SPACING = 3; // Espaciado del texto de copyright
// --- Constantes de texto y configuración ---
static constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner"; // Texto de copyright
static constexpr bool ALLOW_TITLE_ANIMATION_SKIP = false; // Permite saltar la animación del título
// --- Enums --- // --- Enums ---
enum class TitleState { enum class State {
LOGO_ANIMATING, // El logo está animándose LOGO_ANIMATING, // El logo está animándose
LOGO_FINISHED, // El logo ha terminado de animarse LOGO_FINISHED, // El logo ha terminado de animarse
START_HAS_BEEN_PRESSED, // Se ha pulsado el botón de start START_HAS_BEEN_PRESSED, // Se ha pulsado el botón de start
@@ -59,18 +90,19 @@ class Title {
Anchor anchor_; // Anclas para definir la posición de los elementos del título Anchor anchor_; // Anclas para definir la posición de los elementos del título
Section::Name next_section_; // Siguiente sección a cargar Section::Name next_section_; // Siguiente sección a cargar
Section::Options selection_ = Section::Options::TITLE_TIME_OUT; // Opción elegida en el título Section::Options selection_ = Section::Options::TITLE_TIME_OUT; // Opción elegida en el título
TitleState state_; // Estado actual de la sección State state_; // Estado actual de la sección
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
int counter_ = 0; // Temporizador para la pantalla de título float counter_time_ = 0.0f; // Temporizador para la pantalla de título (en milisegundos)
int num_controllers_; // Número de mandos conectados int num_controllers_; // Número de mandos conectados
bool should_render_start_prompt_ = false; // Indica si se muestra el texto de PRESS START BUTTON TO PLAY bool should_render_start_prompt_ = false; // Indica si se muestra el texto de PRESS START BUTTON TO PLAY
bool player1_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 1 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 bool player2_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 2
// --- Ciclo de vida del título --- // --- Ciclo de vida del título ---
void update(); // Actualiza las variables del objeto void update(float deltaTime); // Actualiza las variables del objeto
void updateState(); // Actualiza el estado actual del título float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
void setState(TitleState state); // Cambia el estado del título void updateState(float deltaTime); // Actualiza el estado actual del título
void setState(State state); // Cambia el estado del título
void resetCounter(); // Reinicia el contador interno void resetCounter(); // Reinicia el contador interno
// --- Entrada de usuario --- // --- Entrada de usuario ---

View File

@@ -2,7 +2,7 @@
#include "moving_sprite.h" // Para MovingSprite #include "moving_sprite.h" // Para MovingSprite
// Actualiza la posición y comprueba si ha llegado a su destino // Actualiza la posición y comprueba si ha llegado a su destino (frame-based)
void SmartSprite::update() { void SmartSprite::update() {
if (enabled_) { if (enabled_) {
MovingSprite::update(); MovingSprite::update();
@@ -11,6 +11,15 @@ void SmartSprite::update() {
} }
} }
// Actualiza la posición y comprueba si ha llegado a su destino (time-based)
void SmartSprite::update(float deltaTime) {
if (enabled_) {
MovingSprite::update(deltaTime);
checkMove();
checkFinished();
}
}
// Dibuja el sprite // Dibuja el sprite
void SmartSprite::render() { void SmartSprite::render() {
if (enabled_) { if (enabled_) {

View File

@@ -16,7 +16,8 @@ class SmartSprite : public AnimatedSprite {
~SmartSprite() override = default; ~SmartSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
void update() override; // Actualiza la posición y comprueba si ha llegado a su destino void update() override; // Actualiza la posición y comprueba si ha llegado a su destino (frame-based)
void update(float deltaTime) override; // Actualiza la posición y comprueba si ha llegado a su destino (time-based)
void render() override; // Dibuja el sprite void render() override; // Dibuja el sprite
// --- Getters --- // --- Getters ---

View File

@@ -17,7 +17,7 @@ Tabe::Tabe()
: sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("tabe.png"), Resource::get()->getAnimation("tabe.ani"))), : sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("tabe.png"), Resource::get()->getAnimation("tabe.ani"))),
timer_(Timer(param.tabe.min_spawn_time, param.tabe.max_spawn_time)) {} timer_(Timer(param.tabe.min_spawn_time, param.tabe.max_spawn_time)) {}
// Actualiza la lógica // Actualiza la lógica (frame-based)
void Tabe::update() { void Tabe::update() {
if (enabled_ && !timer_.is_paused) { if (enabled_ && !timer_.is_paused) {
sprite_->update(); sprite_->update();
@@ -31,6 +31,20 @@ void Tabe::update() {
} }
} }
// Actualiza la lógica (time-based)
void Tabe::update(float deltaTime) {
if (enabled_ && !timer_.is_paused) {
sprite_->update(deltaTime);
move(deltaTime);
updateState(deltaTime);
}
timer_.update();
if (timer_.shouldSpawn()) {
enable();
}
}
// Dibuja el objeto // Dibuja el objeto
void Tabe::render() { void Tabe::render() {
if (enabled_) { if (enabled_) {
@@ -38,7 +52,7 @@ void Tabe::render() {
} }
} }
// Mueve el objeto // Mueve el objeto (frame-based)
void Tabe::move() { void Tabe::move() {
const int X = static_cast<int>(x_); const int X = static_cast<int>(x_);
speed_ += accel_; speed_ += accel_;
@@ -103,6 +117,75 @@ void Tabe::move() {
shiftSprite(); shiftSprite();
} }
// Mueve el objeto (time-based)
void Tabe::move(float deltaTime) {
// Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
float frameFactor = deltaTime / (1000.0f / 60.0f);
const int X = static_cast<int>(x_);
speed_ += accel_ * frameFactor;
x_ += speed_ * frameFactor;
fly_distance_ -= std::abs(X - static_cast<int>(x_));
// Comprueba si sale por los bordes
const float MIN_X = param.game.game_area.rect.x - WIDTH;
const float MAX_X = param.game.game_area.rect.x + param.game.game_area.rect.w;
switch (destiny_) {
case Direction::TO_THE_LEFT: {
if (x_ < MIN_X) {
disable();
}
if (x_ > param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH && direction_ == Direction::TO_THE_RIGHT) {
setRandomFlyPath(Direction::TO_THE_LEFT, 80);
x_ = param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH;
}
break;
}
case Direction::TO_THE_RIGHT: {
if (x_ > MAX_X) {
disable();
}
if (x_ < param.game.game_area.rect.x && direction_ == Direction::TO_THE_LEFT) {
setRandomFlyPath(Direction::TO_THE_RIGHT, 80);
x_ = param.game.game_area.rect.x;
}
break;
}
default:
break;
}
if (fly_distance_ <= 0) {
if (waiting_counter_ > 0) {
accel_ = speed_ = 0.0F;
waiting_counter_ -= frameFactor;
if (waiting_counter_ < 0) waiting_counter_ = 0;
} else {
constexpr int CHOICES = 4;
const std::array<Direction, CHOICES> LEFT = {
Direction::TO_THE_LEFT,
Direction::TO_THE_LEFT,
Direction::TO_THE_LEFT,
Direction::TO_THE_RIGHT};
const std::array<Direction, CHOICES> RIGHT = {
Direction::TO_THE_LEFT,
Direction::TO_THE_RIGHT,
Direction::TO_THE_RIGHT,
Direction::TO_THE_RIGHT};
const Direction DIRECTION = destiny_ == Direction::TO_THE_LEFT
? LEFT[rand() % CHOICES]
: RIGHT[rand() % CHOICES];
setRandomFlyPath(DIRECTION, 20 + (rand() % 40));
}
}
shiftSprite();
}
// Habilita el objeto // Habilita el objeto
void Tabe::enable() { void Tabe::enable() {
if (!enabled_) { if (!enabled_) {
@@ -175,11 +258,23 @@ void Tabe::setState(State state) {
} }
} }
// Actualiza el estado // Actualiza el estado (frame-based)
void Tabe::updateState() { void Tabe::updateState() {
if (state_ == State::HIT) { if (state_ == State::HIT) {
--hit_counter_; --hit_counter_;
if (hit_counter_ == 0) { if (hit_counter_ <= 0) {
setState(State::FLY);
}
}
}
// Actualiza el estado (time-based)
void Tabe::updateState(float deltaTime) {
if (state_ == State::HIT) {
// Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
float frameFactor = deltaTime / (1000.0f / 60.0f);
hit_counter_ -= frameFactor;
if (hit_counter_ <= 0) {
setState(State::FLY); setState(State::FLY);
} }
} }
@@ -211,3 +306,13 @@ void Tabe::disable() {
void Tabe::pauseTimer(bool value) { void Tabe::pauseTimer(bool value) {
timer_.setPaused(value); timer_.setPaused(value);
} }
// Deshabilita el spawning permanentemente
void Tabe::disableSpawning() {
timer_.setSpawnDisabled(true);
}
// Habilita el spawning nuevamente
void Tabe::enableSpawning() {
timer_.setSpawnDisabled(false);
}

View File

@@ -26,12 +26,15 @@ class Tabe {
~Tabe() = default; ~Tabe() = default;
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la lógica void update(); // Actualiza la lógica (frame-based)
void update(float deltaTime); // Actualiza la lógica (time-based)
void render(); // Dibuja el objeto void render(); // Dibuja el objeto
void enable(); // Habilita el objeto void enable(); // Habilita el objeto
void setState(State state); // Establece el estado void setState(State state); // Establece el estado
auto tryToGetBonus() -> bool; // Intenta obtener el bonus auto tryToGetBonus() -> bool; // Intenta obtener el bonus
void pauseTimer(bool value); // Detiene/activa el timer void pauseTimer(bool value); // Detiene/activa el timer
void disableSpawning(); // Deshabilita el spawning permanentemente
void enableSpawning(); // Habilita el spawning nuevamente
// --- Getters --- // --- Getters ---
auto getCollider() -> SDL_FRect& { return sprite_->getRect(); } // Obtiene el área de colisión auto getCollider() -> SDL_FRect& { return sprite_->getRect(); } // Obtiene el área de colisión
@@ -54,7 +57,8 @@ class Tabe {
Uint32 current_time; // Tiempo actual Uint32 current_time; // Tiempo actual
Uint32 delta_time; // Diferencia de tiempo desde la última actualización Uint32 delta_time; // Diferencia de tiempo desde la última actualización
Uint32 last_time; // Tiempo de la última actualización Uint32 last_time; // Tiempo de la última actualización
bool is_paused{false}; // Indica si el temporizador está pausado bool is_paused{false}; // Indica si el temporizador está pausado (por pausa de juego)
bool spawn_disabled{false}; // Indica si el spawning está deshabilitado permanentemente
// Constructor - los parámetros min_time y max_time están en mintos // Constructor - los parámetros min_time y max_time están en mintos
Timer(float min_time, float max_time) Timer(float min_time, float max_time)
@@ -75,8 +79,8 @@ class Tabe {
void update() { void update() {
current_time = SDL_GetTicks(); current_time = SDL_GetTicks();
// Solo actualizar si no está pausado // Solo actualizar si no está pausado (ni por juego ni por spawn deshabilitado)
if (!is_paused) { if (!is_paused && !spawn_disabled) {
delta_time = current_time - last_time; delta_time = current_time - last_time;
if (time_until_next_spawn > delta_time) { if (time_until_next_spawn > delta_time) {
@@ -101,9 +105,20 @@ class Tabe {
} }
} }
// Pausa o reanuda el spawning
void setSpawnDisabled(bool disabled) {
if (spawn_disabled != disabled) {
spawn_disabled = disabled;
// Al reactivar, actualizar last_time para evitar saltos
if (!disabled) {
last_time = SDL_GetTicks();
}
}
}
// Indica si el temporizador ha finalizado // Indica si el temporizador ha finalizado
[[nodiscard]] auto shouldSpawn() const -> bool { [[nodiscard]] auto shouldSpawn() const -> bool {
return time_until_next_spawn == 0 && !is_paused; return time_until_next_spawn == 0 && !is_paused && !spawn_disabled;
} }
}; };
@@ -116,21 +131,23 @@ class Tabe {
float speed_ = 0.0F; // Velocidad de movimiento float speed_ = 0.0F; // Velocidad de movimiento
float accel_ = 0.0F; // Aceleración float accel_ = 0.0F; // Aceleración
int fly_distance_ = 0; // Distancia de vuelo int fly_distance_ = 0; // Distancia de vuelo
int waiting_counter_ = 0; // Tiempo que pasa quieto float waiting_counter_ = 0; // Tiempo que pasa quieto
bool enabled_ = false; // Indica si el objeto está activo bool enabled_ = false; // Indica si el objeto está activo
Direction direction_ = Direction::TO_THE_LEFT; // Dirección actual Direction direction_ = Direction::TO_THE_LEFT; // Dirección actual
Direction destiny_ = Direction::TO_THE_LEFT; // Destino Direction destiny_ = Direction::TO_THE_LEFT; // Destino
State state_ = State::FLY; // Estado actual State state_ = State::FLY; // Estado actual
int hit_counter_ = 0; // Contador para el estado HIT float hit_counter_ = 0; // Contador para el estado HIT
int number_of_hits_ = 0; // Cantidad de disparos recibidos int number_of_hits_ = 0; // Cantidad de disparos recibidos
bool has_bonus_ = true; // Indica si aún tiene el bonus para soltar bool has_bonus_ = true; // Indica si aún tiene el bonus para soltar
Timer timer_; // Temporizador para gestionar la aparición Timer timer_; // Temporizador para gestionar la aparición
// --- Métodos internos --- // --- Métodos internos ---
void move(); // Mueve el objeto void move(); // Mueve el objeto (frame-based)
void move(float deltaTime); // Mueve el objeto (time-based)
void shiftSprite() { sprite_->setPos(x_, y_); } // Actualiza la posición del sprite void shiftSprite() { sprite_->setPos(x_, y_); } // Actualiza la posición del sprite
void setRandomFlyPath(Direction direction, int length); // Establece un vuelo aleatorio void setRandomFlyPath(Direction direction, int length); // Establece un vuelo aleatorio
void updateState(); // Actualiza el estado void updateState(); // Actualiza el estado (frame-based)
void updateState(float deltaTime); // Actualiza el estado (time-based)
void updateTimer(); // Actualiza el temporizador void updateTimer(); // Actualiza el temporizador
void disable(); // Deshabilita el objeto void disable(); // Deshabilita el objeto
}; };

View File

@@ -9,11 +9,11 @@
#include <string_view> // Para string_view #include <string_view> // Para string_view
#include "color.h" // Para Color #include "color.h" // Para Color
#include "resource_helper.h" // Para ResourceHelper
#include "screen.h" // Para Screen #include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite #include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture #include "texture.h" // Para Texture
#include "utils.h" // Para getFileName, printWithDots #include "utils.h" // Para getFileName, printWithDots
#include "resource_helper.h" // Para ResourceHelper
// Constructor // Constructor
Text::Text(const std::shared_ptr<Texture> &texture, const std::string &text_file) { Text::Text(const std::shared_ptr<Texture> &texture, const std::string &text_file) {
@@ -200,7 +200,7 @@ void Text::writeColored(int x, int y, const std::string &text, Color color, int
} }
// Escribe el texto con colores usando un sprite específico // Escribe el texto con colores usando un sprite específico
void Text::writeColoredWithSprite(Sprite* sprite, int x, int y, const std::string &text, Color color, int kerning, int length) { void Text::writeColoredWithSprite(Sprite *sprite, int x, int y, const std::string &text, Color color, int kerning, int length) {
int shift = 0; int shift = 0;
const std::string_view VISIBLE_TEXT = (length == -1) ? std::string_view(text) : std::string_view(text).substr(0, length); const std::string_view VISIBLE_TEXT = (length == -1) ? std::string_view(text) : std::string_view(text).substr(0, length);
@@ -391,7 +391,7 @@ auto Text::loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>
file.open(file_path); file.open(file_path);
} }
std::istream& input_stream = using_resource_data ? stream : static_cast<std::istream&>(file); std::istream &input_stream = using_resource_data ? stream : static_cast<std::istream &>(file);
if ((using_resource_data && stream.good()) || (!using_resource_data && file.is_open() && file.good())) { if ((using_resource_data && stream.good()) || (!using_resource_data && file.is_open() && file.good())) {
std::string buffer; std::string buffer;

View File

@@ -84,7 +84,7 @@ class Text {
static auto loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>; // Llena una estructura Text::File desde un fichero static auto loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>; // Llena una estructura Text::File desde un fichero
// --- Métodos privados --- // --- Métodos privados ---
void writeColoredWithSprite(Sprite* sprite, int x, int y, const std::string &text, Color color, int kerning = 1, int length = -1); // Escribe con un sprite específico void writeColoredWithSprite(Sprite *sprite, int x, int y, const std::string &text, Color color, int kerning = 1, int length = -1); // Escribe con un sprite específico
void writeStrokeWithAlpha(int x, int y, const std::string &text, int kerning, Color stroke_color, Uint8 shadow_distance, int length = -1); // Escribe stroke con alpha correcto void writeStrokeWithAlpha(int x, int y, const std::string &text, int kerning, Color stroke_color, Uint8 shadow_distance, int length = -1); // Escribe stroke con alpha correcto
private: private:

View File

@@ -14,9 +14,9 @@
#include "color.h" // Para getFileName, Color, printWithDots #include "color.h" // Para getFileName, Color, printWithDots
#include "external/gif.h" // Para Gif #include "external/gif.h" // Para Gif
#include "resource_helper.h" // Para ResourceHelper
#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha #include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
#include "utils.h" #include "utils.h"
#include "resource_helper.h" // Para ResourceHelper
// Constructor // Constructor
Texture::Texture(SDL_Renderer *renderer, std::string path) Texture::Texture(SDL_Renderer *renderer, std::string path)
@@ -396,7 +396,7 @@ auto Texture::readPalFile(const std::string &file_path) -> Palette {
} }
} }
std::istream& input_stream = using_resource_data ? stream : static_cast<std::istream&>(file); std::istream &input_stream = using_resource_data ? stream : static_cast<std::istream &>(file);
std::string line; std::string line;
int line_number = 0; int line_number = 0;

View File

@@ -21,7 +21,9 @@ struct Surface {
// Constructor // Constructor
Surface(Uint16 width, Uint16 height, std::shared_ptr<Uint8[]> pixels) // NOLINT(modernize-avoid-c-arrays) Surface(Uint16 width, Uint16 height, std::shared_ptr<Uint8[]> pixels) // NOLINT(modernize-avoid-c-arrays)
: data(std::move(pixels)), w(width), h(height) {} : data(std::move(pixels)),
w(width),
h(height) {}
}; };
// Clase Texture: gestiona texturas, paletas y renderizado // Clase Texture: gestiona texturas, paletas y renderizado

View File

@@ -3,7 +3,7 @@
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_DestroyTexture, SDL_FRect, SDL_GetRenderTarget, SDL_RenderTexture, SDL_PixelFormat, SDL_TextureAccess #include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_DestroyTexture, SDL_FRect, SDL_GetRenderTarget, SDL_RenderTexture, SDL_PixelFormat, SDL_TextureAccess
#include <algorithm> #include <algorithm>
#include <cmath> // Para sin #include <cmath> // Para sin, pow
#include <cstdlib> // Para rand #include <cstdlib> // Para rand
#include <memory> // Para allocator, unique_ptr, make_unique #include <memory> // Para allocator, unique_ptr, make_unique
#include <numbers> // Para pi #include <numbers> // Para pi
@@ -81,7 +81,7 @@ void TiledBG::render() {
SDL_RenderTexture(renderer_, canvas_, &window_, &pos_); SDL_RenderTexture(renderer_, canvas_, &window_, &pos_);
} }
// Actualiza la lógica de la clase // Actualiza la lógica de la clase (frame-based)
void TiledBG::update() { void TiledBG::update() {
updateDesp(); updateDesp();
updateStop(); updateStop();
@@ -107,7 +107,33 @@ void TiledBG::update() {
} }
} }
// Detiene el desplazamiento de forma ordenada // Actualiza la lógica de la clase (time-based)
void TiledBG::update(float delta_time) {
updateDesp(delta_time);
updateStop(delta_time);
switch (mode_) {
case TiledBGMode::DIAGONAL: {
// El tileado de fondo se desplaza en diagonal
window_.x = static_cast<int>(desp_) % TILE_WIDTH;
window_.y = static_cast<int>(desp_) % TILE_HEIGHT;
break;
}
case TiledBGMode::CIRCLE: {
// El tileado de fondo se desplaza en circulo
const int INDEX = static_cast<int>(desp_) % 360;
window_.x = 128 + (static_cast<int>(sin_[(INDEX + 270) % 360] * 128));
window_.y = 128 + (static_cast<int>(sin_[(360 - INDEX) % 360] * 96));
break;
}
default:
break;
}
}
// Detiene el desplazamiento de forma ordenada (frame-based)
void TiledBG::updateStop() { void TiledBG::updateStop() {
if (stopping_) { if (stopping_) {
const int UMBRAL = 20 * speed_; // Ajusta este valor según la precisión deseada const int UMBRAL = 20 * speed_; // Ajusta este valor según la precisión deseada
@@ -127,3 +153,26 @@ void TiledBG::updateStop() {
} }
} }
} }
// Detiene el desplazamiento de forma ordenada (time-based)
void TiledBG::updateStop(float delta_time) {
if (stopping_) {
const int UMBRAL = 20 * speed_; // Ajusta este valor según la precisión deseada
// Desacelerar si estamos cerca de completar el ciclo (ventana a punto de regresar a 0)
if (window_.x >= TILE_WIDTH - UMBRAL) {
// Convertir 1.05F por frame a por milisegundo: (1.05^(60*delta_time/1000))
float deceleration_factor = std::pow(1.05F, 60.0F * delta_time / 1000.0F);
speed_ /= deceleration_factor;
// Asegura que no baje demasiado
speed_ = std::max(speed_, 0.1F);
}
// Si estamos en 0, detener
if (window_.x == 0) {
speed_ = 0.0F;
stopping_ = false; // Desactivamos el estado de "stopping"
}
}
}

View File

@@ -25,7 +25,8 @@ class TiledBG {
// --- Métodos principales --- // --- Métodos principales ---
void render(); // Pinta la clase en pantalla void render(); // Pinta la clase en pantalla
void update(); // Actualiza la lógica de la clase void update(); // Actualiza la lógica de la clase (compatibilidad)
void update(float delta_time); // Actualiza la lógica de la clase
// --- Configuración --- // --- Configuración ---
void setSpeed(float speed) { speed_ = speed; } // Establece la velocidad void setSpeed(float speed) { speed_ = speed; } // Establece la velocidad
@@ -55,6 +56,8 @@ class TiledBG {
// --- Métodos internos --- // --- Métodos internos ---
void fillTexture(); // Rellena la textura con el contenido void fillTexture(); // Rellena la textura con el contenido
void updateDesp() { desp_ += speed_; } // Actualiza el desplazamiento void updateDesp() { desp_ += speed_; } // Actualiza el desplazamiento (frame-based)
void updateStop(); // Detiene el desplazamiento de forma ordenada void updateDesp(float delta_time) { desp_ += speed_ * delta_time / (1000.0f / 60.0f); } // Actualiza el desplazamiento (time-based)
void updateStop(); // Detiene el desplazamiento de forma ordenada (frame-based)
void updateStop(float delta_time); // Detiene el desplazamiento de forma ordenada (time-based)
}; };

View File

@@ -23,7 +23,9 @@ class MenuOption {
// --- Constructor y destructor --- // --- Constructor y destructor ---
MenuOption(std::string caption, ServiceMenu::SettingsGroup group, bool hidden = false) MenuOption(std::string caption, ServiceMenu::SettingsGroup group, bool hidden = false)
: caption_(std::move(caption)), group_(group), hidden_(hidden) {} : caption_(std::move(caption)),
group_(group),
hidden_(hidden) {}
virtual ~MenuOption() = default; virtual ~MenuOption() = default;
// --- Getters --- // --- Getters ---
@@ -52,7 +54,8 @@ class MenuOption {
class BoolOption : public MenuOption { class BoolOption : public MenuOption {
public: public:
BoolOption(const std::string &cap, ServiceMenu::SettingsGroup grp, bool *var) BoolOption(const std::string &cap, ServiceMenu::SettingsGroup grp, bool *var)
: MenuOption(cap, grp), linked_variable_(var) {} : MenuOption(cap, grp),
linked_variable_(var) {}
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; } [[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
[[nodiscard]] auto getValueAsString() const -> std::string override { [[nodiscard]] auto getValueAsString() const -> std::string override {
@@ -74,7 +77,11 @@ class BoolOption : public MenuOption {
class IntOption : public MenuOption { class IntOption : public MenuOption {
public: public:
IntOption(const std::string &cap, ServiceMenu::SettingsGroup grp, int *var, int min, int max, int step) IntOption(const std::string &cap, ServiceMenu::SettingsGroup grp, int *var, int min, int max, int step)
: MenuOption(cap, grp), linked_variable_(var), min_value_(min), max_value_(max), step_value_(step) {} : MenuOption(cap, grp),
linked_variable_(var),
min_value_(min),
max_value_(max),
step_value_(step) {}
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; } [[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
[[nodiscard]] auto getValueAsString() const -> std::string override { return std::to_string(*linked_variable_); } [[nodiscard]] auto getValueAsString() const -> std::string override { return std::to_string(*linked_variable_); }
@@ -150,7 +157,8 @@ class ListOption : public MenuOption {
class FolderOption : public MenuOption { class FolderOption : public MenuOption {
public: public:
FolderOption(const std::string &cap, ServiceMenu::SettingsGroup grp, ServiceMenu::SettingsGroup target) FolderOption(const std::string &cap, ServiceMenu::SettingsGroup grp, ServiceMenu::SettingsGroup target)
: MenuOption(cap, grp), target_group_(target) {} : MenuOption(cap, grp),
target_group_(target) {}
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; } [[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; }
[[nodiscard]] auto getTargetGroup() const -> ServiceMenu::SettingsGroup override { return target_group_; } [[nodiscard]] auto getTargetGroup() const -> ServiceMenu::SettingsGroup override { return target_group_; }
@@ -162,7 +170,8 @@ class FolderOption : public MenuOption {
class ActionOption : public MenuOption { class ActionOption : public MenuOption {
public: public:
ActionOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::function<void()> action, bool hidden = false) ActionOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::function<void()> action, bool hidden = false)
: MenuOption(cap, grp, hidden), action_(std::move(action)) {} : MenuOption(cap, grp, hidden),
action_(std::move(action)) {}
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; } [[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; }
void executeAction() override { void executeAction() override {
@@ -183,7 +192,11 @@ class ActionListOption : public MenuOption {
using ActionExecutor = std::function<void()>; using ActionExecutor = std::function<void()>;
ActionListOption(const std::string &caption, ServiceMenu::SettingsGroup group, std::vector<std::string> options, ValueGetter getter, ValueSetter setter, ActionExecutor action_executor, bool hidden = false) ActionListOption(const std::string &caption, ServiceMenu::SettingsGroup group, std::vector<std::string> options, ValueGetter getter, ValueSetter setter, ActionExecutor action_executor, bool hidden = false)
: MenuOption(caption, group, hidden), options_(std::move(options)), value_getter_(std::move(getter)), value_setter_(std::move(setter)), action_executor_(std::move(action_executor)) { : MenuOption(caption, group, hidden),
options_(std::move(options)),
value_getter_(std::move(getter)),
value_setter_(std::move(setter)),
action_executor_(std::move(action_executor)) {
updateCurrentIndex(); updateCurrentIndex();
} }

View File

@@ -44,7 +44,8 @@ void MenuRenderer::ShowHideAnimation::stop() {
} }
MenuRenderer::MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr<Text> element_text, std::shared_ptr<Text> title_text) MenuRenderer::MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr<Text> element_text, std::shared_ptr<Text> title_text)
: element_text_(std::move(element_text)), title_text_(std::move(title_text)) { : element_text_(std::move(element_text)),
title_text_(std::move(title_text)) {
initializeMaxSizes(); initializeMaxSizes();
setPosition(param.game.game_area.center_x, param.game.game_area.center_y, PositionMode::CENTERED); setPosition(param.game.game_area.center_x, param.game.game_area.center_y, PositionMode::CENTERED);
} }

View File

@@ -69,7 +69,9 @@ class Notifier {
// Constructor // Constructor
explicit Notification() explicit Notification()
: texture(nullptr), sprite(nullptr), rect{0, 0, 0, 0} {} : texture(nullptr),
sprite(nullptr),
rect{0, 0, 0, 0} {}
}; };
// --- Objetos y punteros --- // --- Objetos y punteros ---

View File

@@ -49,16 +49,16 @@ void ServiceMenu::toggle() {
return; return;
} }
playBackSound();
if (!enabled_) { // Si está cerrado, abrir if (!enabled_) { // Si está cerrado, abrir
reset(); reset();
Options::gamepad_manager.assignAndLinkGamepads(); Options::gamepad_manager.assignAndLinkGamepads();
renderer_->show(this); renderer_->show(this);
setEnabledInternal(true); setEnabledInternal(true);
playSelectSound();
} else { // Si está abierto, cerrar } else { // Si está abierto, cerrar
renderer_->hide(); renderer_->hide();
setEnabledInternal(false); setEnabledInternal(false);
playBackSound();
} }
} }
@@ -518,7 +518,7 @@ void ServiceMenu::adjustListValues() {
void ServiceMenu::playAdjustSound() { Audio::get()->playSound("service_menu_adjust.wav", Audio::Group::INTERFACE); } void ServiceMenu::playAdjustSound() { Audio::get()->playSound("service_menu_adjust.wav", Audio::Group::INTERFACE); }
void ServiceMenu::playMoveSound() { Audio::get()->playSound("service_menu_move.wav", Audio::Group::INTERFACE); } void ServiceMenu::playMoveSound() { Audio::get()->playSound("service_menu_move.wav", Audio::Group::INTERFACE); }
void ServiceMenu::playSelectSound() { Audio::get()->playSound("service_menu_select.wav", Audio::Group::INTERFACE); } void ServiceMenu::playSelectSound() { Audio::get()->playSound("service_menu_select.wav", Audio::Group::INTERFACE); }
void ServiceMenu::playBackSound() { Audio::get()->playSound("service_menu_select.wav", Audio::Group::INTERFACE); } void ServiceMenu::playBackSound() { Audio::get()->playSound("service_menu_back.wav", Audio::Group::INTERFACE); }
// Devuelve el nombre del grupo como string para el título // Devuelve el nombre del grupo como string para el título
auto ServiceMenu::settingsGroupToString(SettingsGroup group) -> std::string { auto ServiceMenu::settingsGroupToString(SettingsGroup group) -> std::string {

View File

@@ -7,7 +7,9 @@
// Constructor: inicializa el renderizador, el texto y el color del mensaje // Constructor: inicializa el renderizador, el texto y el color del mensaje
UIMessage::UIMessage(std::shared_ptr<Text> text_renderer, std::string message_text, const Color &color) UIMessage::UIMessage(std::shared_ptr<Text> text_renderer, std::string message_text, const Color &color)
: text_renderer_(std::move(text_renderer)), text_(std::move(message_text)), color_(color) {} : text_renderer_(std::move(text_renderer)),
text_(std::move(message_text)),
color_(color) {}
// Muestra el mensaje en la posición base_x, base_y con animación de entrada desde arriba // Muestra el mensaje en la posición base_x, base_y con animación de entrada desde arriba
void UIMessage::show() { void UIMessage::show() {

View File

@@ -22,9 +22,14 @@ struct Overrides {
struct Circle { struct Circle {
int x, y, r; // Coordenadas y radio int x, y, r; // Coordenadas y radio
Circle() : x(0), y(0), r(0) {} Circle()
: x(0),
y(0),
r(0) {}
Circle(int x_coord, int y_coord, int radius) Circle(int x_coord, int y_coord, int radius)
: x(x_coord), y(y_coord), r(radius) {} : x(x_coord),
y(y_coord),
r(radius) {}
}; };
struct DemoKeys { struct DemoKeys {
@@ -36,7 +41,12 @@ struct DemoKeys {
Uint8 fire_right; Uint8 fire_right;
explicit DemoKeys(Uint8 l = 0, Uint8 r = 0, Uint8 ni = 0, Uint8 f = 0, Uint8 fl = 0, Uint8 fr = 0) explicit DemoKeys(Uint8 l = 0, Uint8 r = 0, Uint8 ni = 0, Uint8 f = 0, Uint8 fl = 0, Uint8 fr = 0)
: left(l), right(r), no_input(ni), fire(f), fire_left(fl), fire_right(fr) {} : left(l),
right(r),
no_input(ni),
fire(f),
fire_left(fl),
fire_right(fr) {}
}; };
// --- Tipos --- // --- Tipos ---
@@ -49,9 +59,16 @@ struct Demo {
DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo
std::vector<DemoData> data; // Vector con diferentes sets de datos con los movimientos para la demo std::vector<DemoData> data; // Vector con diferentes sets de datos con los movimientos para la demo
Demo() : enabled(false), recording(false), counter(0) {} Demo()
: enabled(false),
recording(false),
counter(0) {}
Demo(bool e, bool r, int c, const DemoKeys &k, const std::vector<DemoData> &d) Demo(bool e, bool r, int c, const DemoKeys &k, const std::vector<DemoData> &d)
: enabled(e), recording(r), counter(c), keys(k), data(d) {} : enabled(e),
recording(r),
counter(c),
keys(k),
data(d) {}
}; };
struct Zone { struct Zone {

View File

@@ -3,15 +3,14 @@
#include "text.h" // Para Text #include "text.h" // Para Text
// Actualiza el objeto // Actualiza el objeto
void Writer::update() { void Writer::update(float delta_time) {
if (enabled_) { if (enabled_) {
if (!completed_) { if (!completed_) {
// No completado // No completado
if (writing_counter_ > 0) { writing_timer_ += delta_time;
writing_counter_--; if (writing_timer_ >= speed_ms_) {
} else {
index_++; index_++;
writing_counter_ = speed_; writing_timer_ = 0.0f;
} }
if (index_ == length_) { if (index_ == length_) {
@@ -19,10 +18,8 @@ void Writer::update() {
} }
} else { } else {
// Completado // Completado
finished_ = enabled_counter_ <= 0; enabled_timer_ += delta_time;
if (!finished_) { finished_ = enabled_timer_ >= enabled_timer_target_;
enabled_counter_--;
}
} }
} }
} }
@@ -57,8 +54,10 @@ void Writer::setCaption(const std::string &text) {
// Establece el valor de la variable // Establece el valor de la variable
void Writer::setSpeed(int value) { void Writer::setSpeed(int value) {
speed_ = value; // Convierte frames a milisegundos (frames * 16.67ms)
writing_counter_ = value; constexpr float FRAME_TIME_MS = 1000.0f / 60.0f;
speed_ms_ = static_cast<float>(value) * FRAME_TIME_MS;
writing_timer_ = 0.0f;
} }
// Establece el valor de la variable // Establece el valor de la variable
@@ -71,9 +70,10 @@ auto Writer::isEnabled() const -> bool {
return enabled_; return enabled_;
} }
// Establece el valor de la variable // Establece el temporizador para deshabilitar el objeto (en milisegundos)
void Writer::setFinishedCounter(int time) { void Writer::setFinishedTimerMs(float time_ms) {
enabled_counter_ = time; enabled_timer_target_ = time_ms;
enabled_timer_ = 0.0f;
} }
// Centra la cadena de texto a un punto X // Centra la cadena de texto a un punto X

View File

@@ -15,7 +15,7 @@ class Writer {
~Writer() = default; ~Writer() = default;
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza el objeto void update(float delta_time); // Actualiza el objeto
void render() const; // Dibuja el objeto en pantalla void render() const; // Dibuja el objeto en pantalla
// --- Setters --- // --- Setters ---
@@ -25,7 +25,7 @@ class Writer {
void setCaption(const std::string &text); // Establece el texto a escribir void setCaption(const std::string &text); // Establece el texto a escribir
void setSpeed(int value); // Establece la velocidad de escritura void setSpeed(int value); // Establece la velocidad de escritura
void setEnabled(bool value); // Habilita o deshabilita el objeto void setEnabled(bool value); // Habilita o deshabilita el objeto
void setFinishedCounter(int time); // Establece el temporizador para deshabilitar el objeto void setFinishedTimerMs(float time_ms); // Establece el temporizador para deshabilitar el objeto (en ms)
void center(int x); // Centra la cadena de texto a un punto X void center(int x); // Centra la cadena de texto a un punto X
@@ -42,11 +42,12 @@ class Writer {
int pos_x_ = 0; // Posición en el eje X donde empezar a escribir el texto int pos_x_ = 0; // Posición en el eje X donde empezar a escribir el texto
int pos_y_ = 0; // Posición en el eje Y donde empezar a escribir el texto int pos_y_ = 0; // Posición en el eje Y donde empezar a escribir el texto
int kerning_ = 0; // Kerning del texto, es decir, espaciado entre caracteres int kerning_ = 0; // Kerning del texto, es decir, espaciado entre caracteres
int speed_ = 0; // Velocidad de escritura float speed_ms_ = 0.0f; // Velocidad de escritura en milisegundos
int writing_counter_ = 0; // Temporizador de escritura para cada caracter float writing_timer_ = 0.0f; // Temporizador de escritura para cada caracter
int index_ = 0; // Posición del texto que se está escribiendo int index_ = 0; // Posición del texto que se está escribiendo
int length_ = 0; // Longitud de la cadena a escribir int length_ = 0; // Longitud de la cadena a escribir
int enabled_counter_ = 0; // Temporizador para deshabilitar el objeto float enabled_timer_ = 0.0f; // Temporizador para deshabilitar el objeto
float enabled_timer_target_ = 0.0f; // Tiempo objetivo para deshabilitar el objeto
bool completed_ = false; // Indica si se ha escrito todo el texto bool completed_ = false; // Indica si se ha escrito todo el texto
bool enabled_ = false; // Indica si el objeto está habilitado bool enabled_ = false; // Indica si el objeto está habilitado
bool finished_ = false; // Indica si ya ha terminado bool finished_ = false; // Indica si ya ha terminado