15 Commits

Author SHA1 Message Date
b3f6e2fcf0 Metal post-processing pipeline implementation
- Add custom Metal render target creation
- Implement post-processing without GPU-CPU-GPU copies
- Create Metal texture extraction system
- Add Metal CRT shader pipeline integration
- Modify screen rendering to use Metal when available
- Enable shaders by default for testing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-16 07:42:03 +02:00
7f00942517 treballant en metal 2025-09-10 20:44:10 +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
77 changed files with 1620 additions and 903 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

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

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,9 +9,9 @@
#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 "utils.h" // Para printWithDots #include "texture.h" // Para Texture
#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
auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer { auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer {

View File

@@ -50,7 +50,8 @@ 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 ---

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 {
@@ -34,7 +34,7 @@ class Asset {
void add(const std::string &file_path, Type type, bool required = true, bool absolute = false); void add(const std::string &file_path, Type type, bool required = true, bool absolute = false);
void loadFromFile(const std::string &config_file_path, const std::string &prefix = "", const std::string &system_folder = ""); // Con soporte para variables void loadFromFile(const std::string &config_file_path, const std::string &prefix = "", const std::string &system_folder = ""); // Con soporte para variables
[[nodiscard]] auto get(const std::string &filename) const -> std::string; // Mantener nombre original [[nodiscard]] auto get(const std::string &filename) const -> std::string; // Mantener nombre original
[[nodiscard]] auto loadData(const std::string &filename) const -> std::vector<uint8_t>; // Carga datos del archivo [[nodiscard]] auto loadData(const std::string &filename) const -> std::vector<uint8_t>; // Carga datos del archivo
[[nodiscard]] auto check() const -> bool; [[nodiscard]] auto check() const -> bool;
[[nodiscard]] auto getListByType(Type type) const -> std::vector<std::string>; [[nodiscard]] auto getListByType(Type type) const -> std::vector<std::string>;
[[nodiscard]] auto exists(const std::string &filename) const -> bool; // Nueva función para verificar existencia [[nodiscard]] auto exists(const std::string &filename) const -> bool; // Nueva función para verificar existencia
@@ -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,17 +1,18 @@
#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;
void AssetIntegrated::initWithResourcePack(const std::string &executable_path, void AssetIntegrated::initWithResourcePack(const std::string &executable_path,
const std::string &resource_pack_path) { const std::string &resource_pack_path) {
// Inicializar Asset normal // Inicializar Asset normal
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;
@@ -32,7 +33,7 @@ std::vector<uint8_t> AssetIntegrated::loadFile(const std::string &filename) {
// Si la ruta contiene "data/", extraer la parte relativa // Si la ruta contiene "data/", extraer la parte relativa
size_t dataPos = filename.find("data/"); size_t dataPos = filename.find("data/");
if (dataPos != std::string::npos) { if (dataPos != std::string::npos) {
relativePath = filename.substr(dataPos + 5); // +5 para saltar "data/" relativePath = filename.substr(dataPos + 5); // +5 para saltar "data/"
} }
auto data = loader.loadResource(relativePath); auto data = loader.loadResource(relativePath);
@@ -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,28 +1,29 @@
#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");
// Carga un archivo usando ResourceLoader como primera opción // Carga un archivo usando ResourceLoader como primera opción
std::vector<uint8_t> loadFile(const std::string &filename); std::vector<uint8_t> loadFile(const std::string &filename);
// Verifica si un archivo existe (pack o filesystem) // Verifica si un archivo existe (pack o filesystem)
bool fileExists(const std::string &filename) const; bool fileExists(const std::string &filename) const;
// 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
bool shouldUseResourcePack(const std::string &filepath) const; bool shouldUseResourcePack(const std::string &filepath) const;
}; };

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

@@ -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;
} }
@@ -233,8 +231,14 @@ void Balloon::applyGravity() {
} }
void Balloon::playBouncingSound() { void Balloon::playBouncingSound() {
if (bouncing_sound_enabled_) { if (sound_.enabled && sound_.bouncing_enabled) {
playSound(bouncing_sound_); Audio::get()->playSound(sound_.bouncing_file);
}
}
void Balloon::playPoppingSound() {
if (sound_.enabled && sound_.poping_enabled) {
Audio::get()->playSound(sound_.popping_file);
} }
} }
@@ -368,23 +372,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,18 +58,32 @@ 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 ---
@@ -102,9 +122,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 +134,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
@@ -203,47 +241,43 @@ class Balloon {
std::unique_ptr<AnimatedSprite> sprite_; // Sprite del objeto globo std::unique_ptr<AnimatedSprite> sprite_; // Sprite del objeto globo
// --- Variables de estado y físicas --- // --- Variables de estado y físicas ---
float x_; // Posición X float x_; // Posición X
float y_; // Posición Y float y_; // Posición Y
float w_; // Ancho float w_; // Ancho
float h_; // Alto float h_; // Alto
float vx_; // Velocidad X float vx_; // Velocidad X
float vy_; // Velocidad Y float vy_; // Velocidad Y
float gravity_; // Aceleración en Y float gravity_; // Aceleración en Y
float default_vy_; // Velocidad inicial al rebotar float default_vy_; // Velocidad inicial al rebotar
float max_vy_; // Máxima velocidad en Y float max_vy_; // Máxima velocidad en Y
bool being_created_; // Si el globo se está creando bool being_created_; // Si el globo se está creando
bool enabled_ = true; // Si el globo está activo bool enabled_ = true; // Si el globo está activo
bool invulnerable_; // Si el globo es invulnerable bool invulnerable_; // Si el globo es invulnerable
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 Uint16 creation_counter_; // Temporizador de creación
Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación Uint16 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
Uint8 menace_; // Amenaza que genera el globo Uint8 menace_; // Amenaza que genera el globo
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
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 BounceEffect bounce_effect_; // Efecto de rebote
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
// --- Posicionamiento y transformación --- // --- Posicionamiento y transformación ---
void shiftColliders(); // Alinea el círculo de colisión con el sprite void shiftColliders(); // Alinea el círculo de colisión con el sprite
void shiftSprite(); // Alinea el sprite en pantalla void shiftSprite(); // Alinea el sprite en pantalla
// --- 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

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() {
@@ -105,7 +107,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 +128,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);
} }
} }
@@ -152,13 +178,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 +198,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 +230,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 +380,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

@@ -37,14 +37,13 @@ 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

View File

@@ -116,71 +116,71 @@ 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;
size_t index; size_t index;
if (n < colors.size()) { if (n < colors.size()) {
index = n; // Avanza: 0,1,2,3 index = n; // Avanza: 0,1,2,3
} else { } else {
index = 2 * (colors.size() - 1) - n; // Retrocede: 2,1 index = 2 * (colors.size() - 1) - n; // Retrocede: 2,1
}
return colors[index];
} }
auto generateMirroredCycle(Color base, ColorCycleStyle style) -> Cycle { return colors[index];
Cycle result{};
HSV base_hsv = Color::rgbToHsv(base);
for (size_t i = 0; i < CYCLE_SIZE; ++i) {
float t = static_cast<float>(i) / (CYCLE_SIZE - 1); // 0 → 1
float hue_shift = 0.0F;
float sat_shift = 0.0F;
float val_shift = 0.0F;
switch (style) {
case ColorCycleStyle::SUBTLE_PULSE:
// Solo brillo suave
val_shift = 0.07F * sinf(t * M_PI);
break;
case ColorCycleStyle::HUE_WAVE:
// Oscilación leve de tono
hue_shift = 15.0F * (t - 0.5F) * 2.0F;
val_shift = 0.05F * sinf(t * M_PI);
break;
case ColorCycleStyle::VIBRANT:
// Cambios fuertes en tono y brillo
hue_shift = 35.0F * sinf(t * M_PI);
val_shift = 0.2F * sinf(t * M_PI);
sat_shift = -0.2F * sinf(t * M_PI);
break;
case ColorCycleStyle::DARKEN_GLOW:
// Se oscurece al centro
val_shift = -0.15F * sinf(t * M_PI);
break;
case ColorCycleStyle::LIGHT_FLASH:
// Se ilumina al centro
val_shift = 0.25F * sinf(t * M_PI);
break;
}
HSV adjusted = {
.h = fmodf(base_hsv.h + hue_shift + 360.0F, 360.0F),
.s = fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)),
.v = fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))};
Color c = Color::hsvToRgb(adjusted);
result[i] = c;
result[(2 * CYCLE_SIZE) - 1 - i] = c; // espejo
}
return result;
}
} }
auto generateMirroredCycle(Color base, ColorCycleStyle style) -> Cycle {
Cycle result{};
HSV base_hsv = Color::rgbToHsv(base);
for (size_t i = 0; i < CYCLE_SIZE; ++i) {
float t = static_cast<float>(i) / (CYCLE_SIZE - 1); // 0 → 1
float hue_shift = 0.0F;
float sat_shift = 0.0F;
float val_shift = 0.0F;
switch (style) {
case ColorCycleStyle::SUBTLE_PULSE:
// Solo brillo suave
val_shift = 0.07F * sinf(t * M_PI);
break;
case ColorCycleStyle::HUE_WAVE:
// Oscilación leve de tono
hue_shift = 15.0F * (t - 0.5F) * 2.0F;
val_shift = 0.05F * sinf(t * M_PI);
break;
case ColorCycleStyle::VIBRANT:
// Cambios fuertes en tono y brillo
hue_shift = 35.0F * sinf(t * M_PI);
val_shift = 0.2F * sinf(t * M_PI);
sat_shift = -0.2F * sinf(t * M_PI);
break;
case ColorCycleStyle::DARKEN_GLOW:
// Se oscurece al centro
val_shift = -0.15F * sinf(t * M_PI);
break;
case ColorCycleStyle::LIGHT_FLASH:
// Se ilumina al centro
val_shift = 0.25F * sinf(t * M_PI);
break;
}
HSV adjusted = {
.h = fmodf(base_hsv.h + hue_shift + 360.0F, 360.0F),
.s = fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)),
.v = fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))};
Color c = Color::hsvToRgb(adjusted);
result[i] = c;
result[(2 * CYCLE_SIZE) - 1 - i] = c; // espejo
}
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

@@ -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 = {{
@@ -218,7 +219,7 @@ constexpr SDL_ScaleMode VIDEO_SCALE_MODE = SDL_ScaleMode::SDL_SCALEMODE_NEAREST;
constexpr bool VIDEO_FULLSCREEN = false; constexpr bool VIDEO_FULLSCREEN = false;
constexpr bool VIDEO_VSYNC = true; constexpr bool VIDEO_VSYNC = true;
constexpr bool VIDEO_INTEGER_SCALE = true; constexpr bool VIDEO_INTEGER_SCALE = true;
constexpr bool VIDEO_SHADERS = false; constexpr bool VIDEO_SHADERS = true;
// Music // Music
constexpr bool MUSIC_ENABLED = true; constexpr bool MUSIC_ENABLED = true;

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
@@ -42,7 +42,7 @@ Director::Director(int argc, std::span<char *> argv) {
Section::name = Section::Name::GAME; Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P; Section::options = Section::Options::GAME_PLAY_1P;
#elif _DEBUG #elif _DEBUG
Section::name = Section::Name::GAME; Section::name = Section::Name::HI_SCORE_TABLE;
Section::options = Section::Options::GAME_PLAY_1P; Section::options = Section::Options::GAME_PLAY_1P;
#else // NORMAL GAME #else // NORMAL GAME
Section::name = Section::Name::LOGO; Section::name = Section::Name::LOGO;
@@ -77,7 +77,7 @@ Director::~Director() {
// Inicializa todo // Inicializa todo
void Director::init() { void Director::init() {
// Configuración inicial de parametros // Configuración inicial de parametros
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
#ifdef MACOS_BUNDLE #ifdef MACOS_BUNDLE
ResourceHelper::initializeResourceSystem(executable_path_ + "/../Resources/resources.pack"); ResourceHelper::initializeResourceSystem(executable_path_ + "/../Resources/resources.pack");

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

@@ -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 ---

View File

@@ -4,6 +4,20 @@
#include <string> // Para basic_string, string #include <string> // Para basic_string, string
namespace shader { namespace shader {
bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader = ""); bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &shaderSource, const std::string &fragmentShader = "");
void render(); void render();
void cleanup();
bool isUsingOpenGL();
#ifdef __APPLE__
namespace metal {
bool initMetal(SDL_Window* window, SDL_Texture* backBuffer, const std::string& shaderFilename);
SDL_Texture* createMetalRenderTarget(SDL_Renderer* renderer, int width, int height);
void updateMetalTexture(SDL_Texture* backBuffer);
void renderMetal();
void renderWithPostProcessing(SDL_Renderer* renderer, SDL_Texture* sourceTexture);
void cleanupMetal();
}
#endif
} // namespace shader } // namespace shader

387
source/external/jail_shader_metal.mm vendored Normal file
View File

@@ -0,0 +1,387 @@
#include "jail_shader.h"
#ifdef __APPLE__
#include <SDL3/SDL.h>
#include <SDL3/SDL_metal.h>
#include <Metal/Metal.h>
#include <QuartzCore/CAMetalLayer.h>
#include <CoreFoundation/CoreFoundation.h>
#include <stdexcept>
#include <vector>
#include "../asset.h"
namespace shader {
namespace metal {
// Metal objects
id<MTLDevice> device = nullptr;
id<MTLRenderPipelineState> pipelineState = nullptr;
id<MTLBuffer> vertexBuffer = nullptr;
id<MTLTexture> backBufferTexture = nullptr;
id<MTLTexture> gameCanvasTexture = nullptr; // Our custom render target texture
id<MTLSamplerState> sampler = nullptr;
// SDL objects (references from main shader module)
SDL_Window* win = nullptr;
SDL_Renderer* renderer = nullptr;
SDL_Texture* backBuffer = nullptr;
// Vertex data for fullscreen quad
struct Vertex {
float position[4]; // x, y, z, w
float texcoord[2]; // u, v
};
const Vertex quadVertices[] = {
// Position (x, y, z, w) // TexCoord (u, v) - Standard OpenGL-style coordinates
{{-1.0f, -1.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, // Bottom-left
{{ 1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, // Bottom-right
{{-1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}}, // Top-left
{{ 1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 0.0f}}, // Top-right
};
std::string loadMetalShader(const std::string& filename) {
// Try to load the .metal file from the same location as GLSL files
auto data = Asset::get()->loadData(filename);
if (!data.empty()) {
return std::string(data.begin(), data.end());
}
return "";
}
SDL_Texture* createMetalRenderTarget(SDL_Renderer* renderer, int width, int height) {
if (!renderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "createMetalRenderTarget: No renderer provided");
return nullptr;
}
// Crear textura Metal como render target
SDL_Texture* metalTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
if (!metalTexture) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal render target texture: %s", SDL_GetError());
return nullptr;
}
// Configurar filtrado nearest neighbor para look pixelado
SDL_SetTextureScaleMode(metalTexture, SDL_SCALEMODE_NEAREST);
// Try to extract and store the Metal texture directly
SDL_PropertiesID props = SDL_GetTextureProperties(metalTexture);
if (props != 0) {
const char* propertyNames[] = {
"SDL.texture.metal.texture",
"SDL.renderer.metal.texture",
"metal.texture",
"texture.metal",
"MTLTexture",
"texture"
};
for (const char* propName : propertyNames) {
gameCanvasTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
if (gameCanvasTexture) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Successfully extracted Metal texture via property '%s' (size: %lux%lu)",
propName, [gameCanvasTexture width], [gameCanvasTexture height]);
break;
}
}
}
if (!gameCanvasTexture) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not extract Metal texture from SDL texture - shaders may not work");
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Created Metal render target texture (%dx%d)", width, height);
return metalTexture;
}
bool initMetal(SDL_Window* window, SDL_Texture* backBufferTexture, const std::string& shaderFilename) {
// Store references
win = window;
backBuffer = backBufferTexture;
renderer = SDL_GetRenderer(window);
if (!renderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL renderer");
return false;
}
// Get Metal layer from SDL renderer and extract device from it
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*)SDL_GetRenderMetalLayer(renderer);
if (!metalLayer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal layer from SDL renderer");
return false;
}
device = metalLayer.device;
if (!device) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal device from layer");
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Got Metal device from SDL layer: %s", [[device name] UTF8String]);
// Note: We no longer need our own texture - we'll use the backBuffer directly
// Load and compile shaders
std::string metalShaderSource = loadMetalShader(shaderFilename);
if (metalShaderSource.empty()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load Metal shader: %s", shaderFilename.c_str());
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded Metal shader %s (length: %zu)",
shaderFilename.c_str(), metalShaderSource.length());
NSString* shaderNSString = [NSString stringWithUTF8String:metalShaderSource.c_str()];
NSError* error = nil;
id<MTLLibrary> library = [device newLibraryWithSource:shaderNSString options:nil error:&error];
if (!library || error) {
if (error) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile Metal shader: %s",
[[error localizedDescription] UTF8String]);
}
return false;
}
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"vertex_main"];
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"fragment_main"];
if (!vertexFunction || !fragmentFunction) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load Metal shader functions");
return false;
}
// Create render pipeline
MTLRenderPipelineDescriptor* pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineDescriptor.vertexFunction = vertexFunction;
pipelineDescriptor.fragmentFunction = fragmentFunction;
pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
// Set up vertex descriptor
MTLVertexDescriptor* vertexDescriptor = [[MTLVertexDescriptor alloc] init];
vertexDescriptor.attributes[0].format = MTLVertexFormatFloat4;
vertexDescriptor.attributes[0].offset = 0;
vertexDescriptor.attributes[0].bufferIndex = 0;
vertexDescriptor.attributes[1].format = MTLVertexFormatFloat2;
vertexDescriptor.attributes[1].offset = 4 * sizeof(float);
vertexDescriptor.attributes[1].bufferIndex = 0;
vertexDescriptor.layouts[0].stride = sizeof(Vertex);
vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
pipelineDescriptor.vertexDescriptor = vertexDescriptor;
pipelineState = [device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error];
if (!pipelineState || error) {
if (error) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal render pipeline: %s",
[[error localizedDescription] UTF8String]);
}
return false;
}
// Create vertex buffer
vertexBuffer = [device newBufferWithBytes:quadVertices
length:sizeof(quadVertices)
options:MTLResourceOptionCPUCacheModeDefault];
// Create sampler state for nearest neighbor filtering (pixelated look)
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterNearest;
samplerDescriptor.sAddressMode = MTLSamplerAddressModeClampToEdge;
samplerDescriptor.tAddressMode = MTLSamplerAddressModeClampToEdge;
sampler = [device newSamplerStateWithDescriptor:samplerDescriptor];
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** Metal shader system initialized successfully");
return true;
}
void updateMetalTexture(SDL_Texture* backBuffer) {
if (!device || !backBuffer) return;
// Only log this occasionally to avoid spam
static int attemptCount = 0;
static bool hasLogged = false;
// Try multiple property names that SDL3 might use
SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer);
if (props != 0) {
const char* propertyNames[] = {
"SDL.texture.metal.texture",
"SDL.renderer.metal.texture",
"metal.texture",
"texture.metal",
"MTLTexture",
"texture"
};
for (const char* propName : propertyNames) {
id<MTLTexture> sdlMetalTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
if (sdlMetalTexture) {
backBufferTexture = sdlMetalTexture;
if (!hasLogged) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Got Metal texture via property '%s' (size: %lux%lu)",
propName, [backBufferTexture width], [backBufferTexture height]);
hasLogged = true;
}
return;
}
}
}
// If we can't get the texture after several attempts, log once and continue
if (!hasLogged && attemptCount++ > 10) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not access SDL Metal texture after %d attempts - shader will be skipped", attemptCount);
hasLogged = true;
}
}
void renderMetal() {
if (!renderer || !device || !pipelineState) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal render failed: missing components");
return;
}
// Correct pipeline: backBuffer → Metal shaders → screen (no double rendering)
// Try to get the Metal texture directly from the SDL backBuffer texture
updateMetalTexture(backBuffer);
if (!backBufferTexture) {
static int fallbackLogCount = 0;
if (fallbackLogCount++ < 3) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Metal texture not available - falling back to normal SDL rendering (attempt %d)", fallbackLogCount);
}
// Fallback: render without shaders using normal SDL path
SDL_SetRenderTarget(renderer, nullptr);
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
SDL_RenderPresent(renderer);
return;
}
// Apply Metal CRT shader directly: backBuffer texture → screen
SDL_SetRenderTarget(renderer, nullptr);
// Get Metal command encoder to render directly to screen
void* encoder_ptr = SDL_GetRenderMetalCommandEncoder(renderer);
if (encoder_ptr) {
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)encoder_ptr;
static int debugCount = 0;
if (debugCount++ < 5) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal render attempt %d: encoder=%p, pipeline=%p, texture=%p",
debugCount, encoder, pipelineState, backBufferTexture);
}
// Apply CRT shader effect directly to backBuffer texture
[encoder setRenderPipelineState:pipelineState];
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
[encoder setFragmentTexture:backBufferTexture atIndex:0];
[encoder setFragmentSamplerState:sampler atIndex:0];
// Draw fullscreen quad with CRT effect directly to screen
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
static int successCount = 0;
if (successCount++ < 5) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Applied CRT shader to backBuffer (attempt %d) - texture size: %lux%lu",
successCount, [backBufferTexture width], [backBufferTexture height]);
}
} else {
// Fallback: render normally without shaders
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
static int fallbackCount = 0;
if (fallbackCount++ < 3) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal command encoder - fallback rendering");
}
}
SDL_RenderPresent(renderer);
}
void renderWithPostProcessing(SDL_Renderer* renderer, SDL_Texture* sourceTexture) {
if (!renderer || !sourceTexture || !device || !pipelineState) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal post-processing failed: missing components");
// Fallback: render normally without shaders
SDL_SetRenderTarget(renderer, nullptr);
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
SDL_RenderPresent(renderer);
return;
}
// Use our stored Metal texture if available
id<MTLTexture> metalTexture = gameCanvasTexture;
if (!metalTexture) {
static int fallbackLogCount = 0;
if (fallbackLogCount++ < 3) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "gameCanvasTexture not available - falling back to normal rendering (attempt %d)", fallbackLogCount);
}
// Fallback: render normally without shaders
SDL_SetRenderTarget(renderer, nullptr);
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
SDL_RenderPresent(renderer);
return;
}
// Apply Metal CRT shader post-processing: sourceTexture → screen
SDL_SetRenderTarget(renderer, nullptr);
// Get Metal command encoder to render directly to screen
void* encoder_ptr = SDL_GetRenderMetalCommandEncoder(renderer);
if (encoder_ptr) {
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)encoder_ptr;
static int debugCount = 0;
if (debugCount++ < 3) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal post-processing attempt %d: encoder=%p, pipeline=%p, texture=%p",
debugCount, encoder, pipelineState, metalTexture);
}
// Apply CRT shader effect to sourceTexture
[encoder setRenderPipelineState:pipelineState];
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
[encoder setFragmentTexture:metalTexture atIndex:0];
[encoder setFragmentSamplerState:sampler atIndex:0];
// Draw fullscreen quad with CRT effect directly to screen
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
static int successCount = 0;
if (successCount++ < 3) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Applied CRT post-processing (attempt %d) - texture size: %lux%lu",
successCount, [metalTexture width], [metalTexture height]);
}
} else {
// Fallback: render normally without shaders
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
static int fallbackCount = 0;
if (fallbackCount++ < 3) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal command encoder for post-processing - fallback rendering");
}
}
SDL_RenderPresent(renderer);
}
void cleanupMetal() {
// Release Metal objects (ARC handles most of this automatically)
pipelineState = nullptr;
backBufferTexture = nullptr;
gameCanvasTexture = nullptr;
vertexBuffer = nullptr;
sampler = nullptr;
device = nullptr;
renderer = nullptr;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal shader system cleaned up");
}
} // namespace metal
} // namespace shader
#endif // __APPLE__

View File

@@ -41,7 +41,7 @@ void Fade::init() {
num_squares_width_ = param.fade.num_squares_width; num_squares_width_ = param.fade.num_squares_width;
num_squares_height_ = param.fade.num_squares_height; num_squares_height_ = param.fade.num_squares_height;
random_squares_duration_ = param.fade.random_squares_duration_ms; // Usar como duración en ms random_squares_duration_ = param.fade.random_squares_duration_ms; // Usar como duración en ms
square_transition_duration_ = random_squares_duration_ / 4; // 25% del tiempo total para la transición individual square_transition_duration_ = random_squares_duration_ / 4; // 25% del tiempo total para la transición individual
random_squares_start_time_ = 0; random_squares_start_time_ = 0;
} }

View File

@@ -11,12 +11,12 @@ class Fade {
public: public:
// --- Enums --- // --- Enums ---
enum class Type : Uint8 { enum class Type : Uint8 {
FULLSCREEN = 0, // Fundido de pantalla completa FULLSCREEN = 0, // Fundido de pantalla completa
CENTER = 1, // Fundido desde el centro CENTER = 1, // Fundido desde el centro
RANDOM_SQUARE = 2, // Fundido con cuadrados aleatorios RANDOM_SQUARE = 2, // Fundido con cuadrados aleatorios
RANDOM_SQUARE2 = 3, // Fundido con cuadrados aleatorios (variante 2) RANDOM_SQUARE2 = 3, // Fundido con cuadrados aleatorios (variante 2)
DIAGONAL = 4, // Fundido diagonal desde esquina superior izquierda DIAGONAL = 4, // Fundido diagonal desde esquina superior izquierda
VENETIAN = 5, // Fundido tipo persiana veneciana VENETIAN = 5, // Fundido tipo persiana veneciana
}; };
enum class Mode : Uint8 { enum class Mode : Uint8 {
@@ -104,10 +104,10 @@ class Fade {
void calculateVenetianProgress(); // Calcula el progreso del efecto veneciano void calculateVenetianProgress(); // Calcula el progreso del efecto veneciano
// --- Dibujo de efectos visuales --- // --- Dibujo de efectos visuales ---
void drawCenterFadeRectangles(); // Dibuja los rectángulos del fundido central void drawCenterFadeRectangles(); // Dibuja los rectángulos del fundido central
void drawRandomSquares(int active_count = -1); // Dibuja los cuadrados aleatorios del fundido void drawRandomSquares(int active_count = -1); // Dibuja los cuadrados aleatorios del fundido
void drawRandomSquares2(); // Dibuja los cuadrados con transición de color (RANDOM_SQUARE2) void drawRandomSquares2(); // Dibuja los cuadrados con transición de color (RANDOM_SQUARE2)
void drawDiagonal(); // Dibuja los cuadrados con patrón diagonal void drawDiagonal(); // Dibuja los cuadrados con patrón diagonal
void activateDiagonal(int diagonal_index, Uint32 current_time); // Activa una diagonal específica void activateDiagonal(int diagonal_index, Uint32 current_time); // Activa una diagonal específica
void drawVenetianBlinds(); // Dibuja las persianas venecianas del fundido void drawVenetianBlinds(); // Dibuja las persianas venecianas del fundido
}; };

View File

@@ -43,7 +43,12 @@ class GameLogo {
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;

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;
@@ -90,7 +92,7 @@ void Item::move() {
// Si toca el borde lateral // Si toca el borde lateral
if (pos_x_ == MIN_X || pos_x_ == MAX_X) { if (pos_x_ == MIN_X || pos_x_ == MAX_X) {
vel_x_ = -vel_x_; // Invierte la velocidad horizontal vel_x_ = -vel_x_; // Invierte la velocidad horizontal
} }
// Si colisiona por arriba, rebota (excepto la máquina de café) // Si colisiona por arriba, rebota (excepto la máquina de café)

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

@@ -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();
@@ -205,37 +207,37 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = { static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = {
{"balloon.color[0]", [validateBalloonColor](const std::string& v) { {"balloon.color[0]", [validateBalloonColor](const std::string& v) {
if (!validateBalloonColor(v)) { if (!validateBalloonColor(v)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'blue' por defecto.", v.c_str()); SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'blue' por defecto.", v.c_str());
param.balloon.color.at(0) = "blue"; param.balloon.color.at(0) = "blue";
} else { } else {
param.balloon.color.at(0) = v; param.balloon.color.at(0) = v;
} }
}}, }},
{"balloon.color[1]", [validateBalloonColor](const std::string& v) { {"balloon.color[1]", [validateBalloonColor](const std::string& v) {
if (!validateBalloonColor(v)) { if (!validateBalloonColor(v)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'orange' por defecto.", v.c_str()); SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'orange' por defecto.", v.c_str());
param.balloon.color.at(1) = "orange"; param.balloon.color.at(1) = "orange";
} else { } else {
param.balloon.color.at(1) = v; param.balloon.color.at(1) = v;
} }
}}, }},
{"balloon.color[2]", [validateBalloonColor](const std::string& v) { {"balloon.color[2]", [validateBalloonColor](const std::string& v) {
if (!validateBalloonColor(v)) { if (!validateBalloonColor(v)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'red' por defecto.", v.c_str()); SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'red' por defecto.", v.c_str());
param.balloon.color.at(2) = "red"; param.balloon.color.at(2) = "red";
} else { } else {
param.balloon.color.at(2) = v; param.balloon.color.at(2) = v;
} }
}}, }},
{"balloon.color[3]", [validateBalloonColor](const std::string& v) { {"balloon.color[3]", [validateBalloonColor](const std::string& v) {
if (!validateBalloonColor(v)) { if (!validateBalloonColor(v)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'green' por defecto.", v.c_str()); SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'green' por defecto.", v.c_str());
param.balloon.color.at(3) = "green"; param.balloon.color.at(3) = "green";
} else { } else {
param.balloon.color.at(3) = v; param.balloon.color.at(3) = v;
} }
}}}; }}};
// Lambda para intentar cada mapa de parámetros // Lambda para intentar cada mapa de parámetros
auto try_map = [&](const auto& param_map) -> bool { auto try_map = [&](const auto& param_map) -> bool {

View File

@@ -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

@@ -33,7 +33,8 @@ struct Path { // Define un recorrido para el sprite
// Constructor // Constructor
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init) Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init)
: spots(spots_init), waiting_counter(waiting_counter_init) {} : spots(spots_init),
waiting_counter(waiting_counter_init) {}
}; };
// --- Funciones --- // --- Funciones ---

View File

@@ -22,7 +22,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));
@@ -585,6 +595,14 @@ void Player::update() {
updateShowingName(); updateShowingName();
} }
void Player::passShowingName() {
if (game_completed_) {
setPlayingState(State::LEAVING_SCREEN);
} else {
setPlayingState(State::CONTINUE);
}
}
// 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 +642,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: {

View File

@@ -170,7 +170,7 @@ 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
@@ -186,6 +186,7 @@ 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; }

View File

@@ -2,57 +2,57 @@
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError, SDL_SetRenderDrawColor, SDL_EventType, SDL_PollEvent, SDL_RenderFillRect, SDL_RenderRect, SDLK_ESCAPE, SDL_Event #include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError, SDL_SetRenderDrawColor, SDL_EventType, SDL_PollEvent, SDL_RenderFillRect, SDL_RenderRect, SDLK_ESCAPE, SDL_Event
#include <algorithm> // Para find_if, max, find #include <algorithm> // Para find_if, max, find
#include <array> // Para array #include <array> // Para array
#include <cstdlib> // Para exit, getenv #include <cstdlib> // Para exit, getenv
#include <filesystem> // Para filesystem::remove, filesystem::exists #include <filesystem> // Para filesystem::remove, filesystem::exists
#include <fstream> // Para ofstream #include <fstream> // Para ofstream
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include <utility> // Para move #include <utility> // Para move
#include "asset.h" // Para Asset #include "asset.h" // Para Asset
#include "color.h" // Para Color #include "color.h" // Para Color
#ifndef NO_AUDIO #ifndef NO_AUDIO
#include "external/jail_audio.h" // Para JA_LoadMusic, JA_LoadSound, JA_DeleteMusic, JA_DeleteSound #include "external/jail_audio.h" // Para JA_LoadMusic, JA_LoadSound, JA_DeleteMusic, JA_DeleteSound
#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 "screen.h" // Para Screen
#include "text.h" // Para Text
#include "resource_helper.h" // Para ResourceHelper #include "resource_helper.h" // Para ResourceHelper
#include "screen.h" // Para Screen
#include "text.h" // Para Text
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
std::string temp_dir; std::string temp_dir;
#ifdef _WIN32 #ifdef _WIN32
temp_dir = std::getenv("TEMP") ? std::getenv("TEMP") : "C:\\temp"; temp_dir = std::getenv("TEMP") ? std::getenv("TEMP") : "C:\\temp";
#else #else
temp_dir = "/tmp"; temp_dir = "/tmp";
#endif #endif
std::string temp_path = temp_dir + "/ccae_audio_" + std::to_string(std::hash<std::string>{}(file_path)); std::string temp_path = temp_dir + "/ccae_audio_" + std::to_string(std::hash<std::string>{}(file_path));
std::ofstream temp_file(temp_path, std::ios::binary); std::ofstream temp_file(temp_path, std::ios::binary);
if (!temp_file) { if (!temp_file) {
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.close();
// Agregar a la lista de archivos temporales para limpieza posterior
temp_files_tracker.push_back(temp_path);
return temp_path;
} }
return file_path; // Usar ruta original si no está en pack temp_file.write(reinterpret_cast<const char *>(resource_data.data()), resource_data.size());
temp_file.close();
// Agregar a la lista de archivos temporales para limpieza posterior
temp_files_tracker.push_back(temp_path);
return temp_path;
} }
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,95 +1,96 @@
#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();
resource_system_initialized = loader.initialize(pack_file, true);
if (resource_system_initialized) {
std::cout << "Resource system initialized with pack: " << pack_file << std::endl;
} else {
std::cout << "Resource system using fallback mode (filesystem only)" << std::endl;
}
return true; // Always return true as fallback is acceptable
}
void shutdownResourceSystem() {
if (resource_system_initialized) {
ResourceLoader::getInstance().shutdown();
resource_system_initialized = false;
}
}
std::vector<uint8_t> loadFile(const std::string& filepath) {
if (resource_system_initialized && shouldUseResourcePack(filepath)) {
auto& loader = ResourceLoader::getInstance(); auto& loader = ResourceLoader::getInstance();
resource_system_initialized = loader.initialize(pack_file, true); std::string pack_path = getPackPath(filepath);
if (resource_system_initialized) { auto data = loader.loadResource(pack_path);
std::cout << "Resource system initialized with pack: " << pack_file << std::endl; if (!data.empty()) {
} else { return data;
std::cout << "Resource system using fallback mode (filesystem only)" << std::endl;
}
return true; // Always return true as fallback is acceptable
}
void shutdownResourceSystem() {
if (resource_system_initialized) {
ResourceLoader::getInstance().shutdown();
resource_system_initialized = false;
} }
} }
std::vector<uint8_t> loadFile(const std::string& filepath) { // Fallback a filesystem
if (resource_system_initialized && shouldUseResourcePack(filepath)) { std::ifstream file(filepath, std::ios::binary | std::ios::ate);
auto& loader = ResourceLoader::getInstance(); if (!file) {
std::string pack_path = getPackPath(filepath); return {};
auto data = loader.loadResource(pack_path);
if (!data.empty()) {
return data;
}
}
// Fallback a filesystem
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file) {
return {};
}
std::streamsize fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> data(fileSize);
if (!file.read(reinterpret_cast<char*>(data.data()), fileSize)) {
return {};
}
return data;
} }
bool shouldUseResourcePack(const std::string& filepath) { std::streamsize fileSize = file.tellg();
// Archivos que NO van al pack: file.seekg(0, std::ios::beg);
// - config/ (ahora está fuera de data/)
// - archivos absolutos del sistema
if (filepath.find("config/") != std::string::npos) { std::vector<uint8_t> data(fileSize);
return false; if (!file.read(reinterpret_cast<char*>(data.data()), fileSize)) {
} return {};
}
// Si contiene "data/" es candidato para el pack return data;
if (filepath.find("data/") != std::string::npos) { }
return true;
}
bool shouldUseResourcePack(const std::string& filepath) {
// Archivos que NO van al pack:
// - config/ (ahora está fuera de data/)
// - archivos absolutos del sistema
if (filepath.find("config/") != std::string::npos) {
return false; return false;
} }
std::string getPackPath(const std::string& asset_path) { // Si contiene "data/" es candidato para el pack
std::string pack_path = asset_path; if (filepath.find("data/") != std::string::npos) {
return true;
// Normalizar separadores de path a '/'
std::replace(pack_path.begin(), pack_path.end(), '\\', '/');
// Remover prefijo "data/" si existe
size_t data_pos = pack_path.find("data/");
if (data_pos != std::string::npos) {
pack_path = pack_path.substr(data_pos + 5); // +5 para saltar "data/"
}
// Remover cualquier prefijo de path absoluto
size_t last_data = pack_path.rfind("data/");
if (last_data != std::string::npos) {
pack_path = pack_path.substr(last_data + 5);
}
return pack_path;
} }
return false;
} }
std::string getPackPath(const std::string& asset_path) {
std::string pack_path = asset_path;
// Normalizar separadores de path a '/'
std::replace(pack_path.begin(), pack_path.end(), '\\', '/');
// Remover prefijo "data/" si existe
size_t data_pos = pack_path.find("data/");
if (data_pos != std::string::npos) {
pack_path = pack_path.substr(data_pos + 5); // +5 para saltar "data/"
}
// Remover cualquier prefijo de path absoluto
size_t last_data = pack_path.rfind("data/");
if (last_data != std::string::npos) {
pack_path = pack_path.substr(last_data + 5);
}
return pack_path;
}
} // namespace ResourceHelper

View File

@@ -1,46 +1,47 @@
#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());
}
// Crear archivo temporal para funciones que esperan path
std::string temp_path = "/tmp/ccae_" + std::to_string(std::hash<std::string>{}(asset_path));
std::ofstream temp_file(temp_path, std::ios::binary);
temp_file.write(reinterpret_cast<const char*>(data.data()), data.size());
temp_file.close();
T* result = loader_func(temp_path.c_str());
std::filesystem::remove(temp_path);
return result;
} }
// Crear archivo temporal para funciones que esperan path
std::string temp_path = "/tmp/ccae_" + std::to_string(std::hash<std::string>{}(asset_path));
std::ofstream temp_file(temp_path, std::ios::binary);
temp_file.write(reinterpret_cast<const char*>(data.data()), data.size());
temp_file.close();
T* result = loader_func(temp_path.c_str());
std::filesystem::remove(temp_path);
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,37 +1,38 @@
#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;
bool fallbackToFiles; bool fallbackToFiles;
ResourceLoader(); ResourceLoader();
public: public:
static ResourceLoader& getInstance(); static ResourceLoader& getInstance();
~ResourceLoader(); ~ResourceLoader();
bool initialize(const std::string& packFile, bool enableFallback = true); bool initialize(const std::string& packFile, bool enableFallback = true);
void shutdown(); void shutdown();
std::vector<uint8_t> loadResource(const std::string& filename); std::vector<uint8_t> loadResource(const std::string& filename);
bool resourceExists(const std::string& filename); bool resourceExists(const std::string& filename);
void setFallbackToFiles(bool enable) { fallbackToFiles = enable; } void setFallbackToFiles(bool enable) { fallbackToFiles = enable; }
bool getFallbackToFiles() const { return fallbackToFiles; } bool getFallbackToFiles() const { return fallbackToFiles; }
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);
}; };
#endif #endif

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();
@@ -186,7 +188,7 @@ std::vector<uint8_t> ResourcePack::getResource(const std::string& filename) {
} }
std::vector<uint8_t> result(data.begin() + entry.offset, std::vector<uint8_t> result(data.begin() + entry.offset,
data.begin() + entry.offset + entry.size); data.begin() + entry.offset + entry.size);
uint32_t checksum = calculateChecksum(result); uint32_t checksum = calculateChecksum(result);
if (checksum != entry.checksum) { if (checksum != entry.checksum) {

View File

@@ -1,46 +1,46 @@
#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;
uint64_t offset; uint64_t offset;
uint64_t size; uint64_t size;
uint32_t checksum; uint32_t checksum;
}; };
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;
uint32_t calculateChecksum(const std::vector<uint8_t>& data); uint32_t calculateChecksum(const std::vector<uint8_t>& data);
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();
bool loadPack(const std::string& packFile); bool loadPack(const std::string& packFile);
bool savePack(const std::string& packFile); bool savePack(const std::string& packFile);
bool addFile(const std::string& filename, const std::string& filepath); bool addFile(const std::string& filename, const std::string& filepath);
bool addDirectory(const std::string& directory); bool addDirectory(const std::string& directory);
std::vector<uint8_t> getResource(const std::string& filename); std::vector<uint8_t> getResource(const std::string& filename);
bool hasResource(const std::string& filename) const; bool hasResource(const std::string& filename) const;
void clear(); void clear();
size_t getResourceCount() const; size_t getResourceCount() const;
std::vector<std::string> getResourceList() const; std::vector<std::string> getResourceList() const;
static const std::string DEFAULT_ENCRYPT_KEY; static const std::string DEFAULT_ENCRYPT_KEY;
}; };
#endif #endif

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) { SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h);
// 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);
}
// Dibuja la letra
if (j < record_name_.at(panel_index).size()) {
text_scoreboard_->writeColored(rect.x, rect.y, record_name_.at(panel_index).substr(j, 1), COLOR);
}
} }
rect.x += 7;
// Dibuja la letra
if (j < record_name_.at(panel_index).size()) {
enter_name_text_->writeColored(rect.x, rect.y, record_name_.at(panel_index).substr(j, 1), text_color2_);
}
rect.x += enter_name_text_->getCharacterSize();
} }
} }
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

@@ -44,8 +44,21 @@ Screen::Screen()
initSDLVideo(); initSDLVideo();
// Crea la textura de destino // Crea la textura de destino
#ifdef __APPLE__
const auto render_name = SDL_GetRendererName(renderer_);
if (render_name && !strncmp(render_name, "metal", 5)) {
// Usar nuestra propia Metal texture como render target
game_canvas_ = shader::metal::createMetalRenderTarget(renderer_, param.game.width, param.game.height);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using custom Metal render target for game_canvas_");
} else {
// Fallback para otros renderers
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
}
#else
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST); SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
#endif
// Crea el objeto de texto // Crea el objeto de texto
createText(); createText();
@@ -99,7 +112,18 @@ void Screen::renderPresent() {
clean(); clean();
if (Options::video.shaders) { if (Options::video.shaders) {
#ifdef __APPLE__
const auto render_name = SDL_GetRendererName(renderer_);
if (render_name && !strncmp(render_name, "metal", 5)) {
// Use Metal post-processing with our custom render target
shader::metal::renderWithPostProcessing(renderer_, game_canvas_);
} else {
// Fallback to standard shader system for non-Metal renderers
shader::render();
}
#else
shader::render(); shader::render();
#endif
} else { } else {
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr); SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
SDL_RenderPresent(renderer_); SDL_RenderPresent(renderer_);
@@ -296,17 +320,29 @@ auto Screen::initSDLVideo() -> bool {
// Obtener información de la pantalla // Obtener información de la pantalla
getDisplayInfo(); getDisplayInfo();
#ifdef __APPLE__
// Configurar hint para Metal
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal")) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Failed to set Metal hint!");
}
// Configurar flags para la creación de la ventana
SDL_WindowFlags window_flags = SDL_WINDOW_METAL;
#else // NOT APPLE
// Configurar hint para OpenGL // Configurar hint para OpenGL
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) { if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Failed to set OpenGL hint!"); "Warning: Failed to set OpenGL hint!");
} }
// Configurar flags para la creación de la ventana
// Crear ventana
SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL; SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL;
#endif
// Configurar flags para la creación de la ventana
if (Options::video.fullscreen) { if (Options::video.fullscreen) {
window_flags |= SDL_WINDOW_FULLSCREEN; window_flags |= SDL_WINDOW_FULLSCREEN;
} }
// Crear ventana
window_ = SDL_CreateWindow( window_ = SDL_CreateWindow(
Options::window.caption.c_str(), Options::window.caption.c_str(),
param.game.width * Options::window.zoom, param.game.width * Options::window.zoom,

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.");
} }

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;
@@ -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));
} }
} }
@@ -367,15 +378,6 @@ void Game::updateGameStateCompleted() {
updatePathSprites(); updatePathSprites();
cleanVectors(); cleanVectors();
// Para la música y elimina todos los globos e items
if (game_completed_counter_ == 0) {
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 // Comienza las celebraciones
// Muestra el mensaje de felicitación y da los puntos a los jugadores // Muestra el mensaje de felicitación y da los puntos a los jugadores
if (game_completed_counter_ == START_CELEBRATIONS) { if (game_completed_counter_ == START_CELEBRATIONS) {
@@ -606,6 +608,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 +736,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
@@ -1371,17 +1373,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 +1407,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,33 +1433,53 @@ 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->setPlayingState(Player::State::SHOWING_NAME);
updateHiScoreName();
} }
player->setInput(Input::Action::START);
player->setPlayingState(Player::State::SHOWING_NAME);
playSound("service_menu_select.wav");
updateHiScoreName();
} }
} }
@@ -1457,20 +1490,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 +1583,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_) {
@@ -1812,6 +1832,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 {
@@ -1873,20 +1906,17 @@ 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 +1926,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

@@ -254,8 +254,8 @@ class Game {
// --- Sistema de globos y enemigos --- // --- Sistema de globos y enemigos ---
void handleBalloonDestruction(std::shared_ptr<Balloon> balloon, const std::shared_ptr<Player> &player); // Procesa destrucción de globo void handleBalloonDestruction(std::shared_ptr<Balloon> balloon, const std::shared_ptr<Player> &player); // Procesa destrucción de globo
void handleTabeHitEffects(); // Gestiona efectos al golpear a Tabe void handleTabeHitEffects(); // Gestiona efectos al golpear a Tabe
void checkAndUpdateBalloonSpeed(); // Ajusta velocidad de globos según progreso void checkAndUpdateBalloonSpeed(); // Ajusta velocidad de globos según progreso
// --- Gestión de fases y progresión --- // --- Gestión de fases y progresión ---
void updateStage(); // Verifica y actualiza cambio de fase void updateStage(); // Verifica y actualiza cambio de fase

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>()),
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)) {
// 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);

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);

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

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_(TitleState::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();

View File

@@ -55,9 +55,9 @@ class Sprite {
[[nodiscard]] auto getTexture() const -> std::shared_ptr<Texture> { return textures_.at(texture_index_); } [[nodiscard]] auto getTexture() const -> std::shared_ptr<Texture> { return textures_.at(texture_index_); }
void setTexture(std::shared_ptr<Texture> texture) { textures_.at(texture_index_) = std::move(texture); } void setTexture(std::shared_ptr<Texture> texture) { textures_.at(texture_index_) = std::move(texture); }
void addTexture(const std::shared_ptr<Texture>& texture) { textures_.push_back(texture); } void addTexture(const std::shared_ptr<Texture>& texture) { textures_.push_back(texture); }
auto setActiveTexture(size_t index) -> bool; // Cambia la textura activa por índice auto setActiveTexture(size_t index) -> bool; // Cambia la textura activa por índice
[[nodiscard]] auto getActiveTexture() const -> size_t { return texture_index_; } // Alias para getActiveTextureIndex [[nodiscard]] auto getActiveTexture() const -> size_t { return texture_index_; } // Alias para getActiveTextureIndex
[[nodiscard]] auto getTextureCount() const -> size_t { return textures_.size(); } // Obtiene el número total de texturas [[nodiscard]] auto getTextureCount() const -> size_t { return textures_.size(); } // Obtiene el número total de texturas
protected: protected:
// --- Métodos internos --- // --- Métodos internos ---

View File

@@ -211,3 +211,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

@@ -32,6 +32,8 @@ class Tabe {
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 +56,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 +78,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 +104,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;
} }
}; };

View File

@@ -8,12 +8,12 @@
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include <string_view> // Para string_view #include <string_view> // Para string_view
#include "color.h" // Para Color #include "color.h" // Para Color
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
#include "utils.h" // Para getFileName, printWithDots
#include "resource_helper.h" // Para ResourceHelper #include "resource_helper.h" // Para ResourceHelper
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
#include "utils.h" // Para getFileName, printWithDots
// 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

@@ -12,11 +12,11 @@
#include <utility> #include <utility>
#include <vector> // Para vector #include <vector> // Para vector
#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 "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
#include "utils.h"
#include "resource_helper.h" // Para ResourceHelper #include "resource_helper.h" // Para ResourceHelper
#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
#include "utils.h"
// 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

@@ -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

@@ -12,7 +12,7 @@
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include <string> // Para basic_string, allocator, string, operator==, operator+, char_traits #include <string> // Para basic_string, allocator, string, operator==, operator+, char_traits
#include "lang.h" // Para getText #include "lang.h" // Para getText
#include "resource_helper.h" // Para ResourceHelper #include "resource_helper.h" // Para ResourceHelper
// Variables // Variables

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 {