pasaeta loca de clang-format (despres m'arrepentiré pero bueno)

This commit is contained in:
2025-07-18 20:01:13 +02:00
parent 734c220fb0
commit dabba41179
112 changed files with 22361 additions and 26474 deletions

11
.clang-format Normal file
View File

@@ -0,0 +1,11 @@
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 0 # Sin límite de longitud de línea
BreakBeforeBraces: Attach # Llaves en la misma línea
AllowShortIfStatementsOnASingleLine: true
AllowShortBlocksOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AlignOperands: false
AlignAfterOpenBracket: DontAlign
BinPackArguments: false
BinPackParameters: false

40
.clang-tidy Normal file
View File

@@ -0,0 +1,40 @@
Checks: >
readability-identifier-naming,
readability-*,
modernize-*,
clang-analyzer-*
WarningsAsErrors: '*'
HeaderFilterRegex: '.*'
FormatStyle: file
CheckOptions:
# Variables locales en snake_case
- { key: readability-identifier-naming.VariableCase, value: lower_case }
# Miembros privados en snake_case con sufijo _
- { key: readability-identifier-naming.PrivateMemberCase, value: lower_case }
- { key: readability-identifier-naming.PrivateMemberSuffix, value: _ }
# Namespaces en CamelCase
- { key: readability-identifier-naming.NamespaceCase, value: CamelCase }
# Constantes y constexpr en UPPER_CASE
- { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE }
- { key: readability-identifier-naming.ConstexprVariableCase, value: UPPER_CASE }
- { key: readability-identifier-naming.LocalConstantCase, value: UPPER_CASE }
# Clases, structs y enums en CamelCase
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
- { key: readability-identifier-naming.StructCase, value: CamelCase }
- { key: readability-identifier-naming.EnumCase, value: CamelCase }
# Valores de enums en UPPER_CASE
- { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE }
# Métodos en camelBack
- { key: readability-identifier-naming.MethodCase, value: camelBack }
# Funciones en camelBack
- { key: readability-identifier-naming.FunctionCase, value: camelBack }

View File

@@ -1,280 +1,240 @@
#include "animated_sprite.h"
#include <SDL3/SDL.h> // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError
#include <stddef.h> // Para size_t
#include <algorithm> // Para min
#include <fstream> // Para basic_istream, basic_ifstream, basic_ios, ifst...
#include <sstream> // Para basic_stringstream
#include <stdexcept> // Para runtime_error
#include <utility> // Para pair
#include <SDL3/SDL.h> // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError
#include <stddef.h> // Para size_t
#include "texture.h" // Para Texture
#include "utils.h" // Para printWithDots
#include <algorithm> // Para min
#include <fstream> // Para basic_istream, basic_ifstream, basic_ios, ifst...
#include <sstream> // Para basic_stringstream
#include <stdexcept> // Para runtime_error
#include <utility> // Para pair
#include "texture.h" // Para Texture
#include "utils.h" // Para printWithDots
// Carga las animaciones en un vector(Animations) desde un fichero
AnimationsFileBuffer loadAnimationsFromFile(const std::string &file_path)
{
std::ifstream file(file_path);
if (!file)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
throw std::runtime_error("Fichero no encontrado: " + file_path);
}
AnimationsFileBuffer loadAnimationsFromFile(const std::string &file_path) {
std::ifstream file(file_path);
if (!file) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
throw std::runtime_error("Fichero no encontrado: " + file_path);
}
printWithDots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
printWithDots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
std::vector<std::string> buffer;
std::string line;
while (std::getline(file, line))
{
if (!line.empty())
buffer.push_back(line);
}
std::vector<std::string> buffer;
std::string line;
while (std::getline(file, line)) {
if (!line.empty())
buffer.push_back(line);
}
return buffer;
return buffer;
}
// Constructor
AnimatedSprite::AnimatedSprite(std::shared_ptr<Texture> texture, const std::string &file_path)
: MovingSprite(texture)
{
// Carga las animaciones
if (!file_path.empty())
{
AnimationsFileBuffer v = loadAnimationsFromFile(file_path);
loadFromAnimationsFileBuffer(v);
}
: MovingSprite(texture) {
// Carga las animaciones
if (!file_path.empty()) {
AnimationsFileBuffer v = loadAnimationsFromFile(file_path);
loadFromAnimationsFileBuffer(v);
}
}
// Constructor
AnimatedSprite::AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer &animations)
: MovingSprite(texture)
{
if (!animations.empty())
{
loadFromAnimationsFileBuffer(animations);
}
: MovingSprite(texture) {
if (!animations.empty()) {
loadFromAnimationsFileBuffer(animations);
}
}
// Obtiene el índice de la animación a partir del nombre
int AnimatedSprite::getIndex(const std::string &name)
{
auto it = animation_indices_.find(name);
if (it != animation_indices_.end())
{
// Si se encuentra la animación en el mapa, devuelve su índice
return it->second;
}
int AnimatedSprite::getIndex(const std::string &name) {
auto it = animation_indices_.find(name);
if (it != animation_indices_.end()) {
// Si se encuentra la animación en el mapa, devuelve su índice
return it->second;
}
// Si no se encuentra, muestra una advertencia y devuelve -1
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "** Warning: could not find \"%s\" animation", name.c_str());
return -1;
// Si no se encuentra, muestra una advertencia y devuelve -1
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "** Warning: could not find \"%s\" animation", name.c_str());
return -1;
}
// Calcula el frame correspondiente a la animación
void AnimatedSprite::animate()
{
if (animations_[current_animation_].speed == 0)
{
return;
}
void AnimatedSprite::animate() {
if (animations_[current_animation_].speed == 0) {
return;
}
// Calcula el frame actual a partir del contador
animations_[current_animation_].current_frame = animations_[current_animation_].counter / animations_[current_animation_].speed;
// Calcula el frame actual a partir del contador
animations_[current_animation_].current_frame = animations_[current_animation_].counter / animations_[current_animation_].speed;
// Si alcanza el final de la animación, reinicia el contador de la animación
// en función de la variable loop y coloca el nuevo frame
if (animations_[current_animation_].current_frame >= animations_[current_animation_].frames.size())
{
if (animations_[current_animation_].loop == -1)
{ // Si no hay loop, deja el último frame
animations_[current_animation_].current_frame = animations_[current_animation_].frames.size();
animations_[current_animation_].completed = true;
}
else
{ // Si hay loop, vuelve al frame indicado
animations_[current_animation_].counter = 0;
animations_[current_animation_].current_frame = animations_[current_animation_].loop;
}
}
// En caso contrario
else
{
// Escoge el frame correspondiente de la animación
setSpriteClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]);
// Si alcanza el final de la animación, reinicia el contador de la animación
// en función de la variable loop y coloca el nuevo frame
if (animations_[current_animation_].current_frame >= animations_[current_animation_].frames.size()) {
if (animations_[current_animation_].loop == -1) { // Si no hay loop, deja el último frame
animations_[current_animation_].current_frame = animations_[current_animation_].frames.size();
animations_[current_animation_].completed = true;
} else { // Si hay loop, vuelve al frame indicado
animations_[current_animation_].counter = 0;
animations_[current_animation_].current_frame = animations_[current_animation_].loop;
}
}
// En caso contrario
else {
// Escoge el frame correspondiente de la animación
setSpriteClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]);
// Incrementa el contador de la animacion
animations_[current_animation_].counter++;
}
// Incrementa el contador de la animacion
animations_[current_animation_].counter++;
}
}
// Comprueba si ha terminado la animación
bool AnimatedSprite::animationIsCompleted()
{
return animations_[current_animation_].completed;
bool AnimatedSprite::animationIsCompleted() {
return animations_[current_animation_].completed;
}
// Establece la animacion actual
void AnimatedSprite::setCurrentAnimation(const std::string &name, bool reset)
{
const auto NEW_ANIMATION = getIndex(name);
if (current_animation_ != NEW_ANIMATION)
{
const auto OLD_ANIMATION = current_animation_;
current_animation_ = NEW_ANIMATION;
if (reset)
{
animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0;
animations_[current_animation_].completed = false;
}
else
{
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size());
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
}
}
void AnimatedSprite::setCurrentAnimation(const std::string &name, bool reset) {
const auto NEW_ANIMATION = getIndex(name);
if (current_animation_ != NEW_ANIMATION) {
const auto OLD_ANIMATION = current_animation_;
current_animation_ = NEW_ANIMATION;
if (reset) {
animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0;
animations_[current_animation_].completed = false;
} else {
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size());
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
}
}
}
// Establece la animacion actual
void AnimatedSprite::setCurrentAnimation(int index, bool reset)
{
const auto NEW_ANIMATION = index;
if (current_animation_ != NEW_ANIMATION)
{
const auto OLD_ANIMATION = current_animation_;
current_animation_ = NEW_ANIMATION;
if (reset)
{
animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0;
animations_[current_animation_].completed = false;
}
else
{
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size());
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
}
}
void AnimatedSprite::setCurrentAnimation(int index, bool reset) {
const auto NEW_ANIMATION = index;
if (current_animation_ != NEW_ANIMATION) {
const auto OLD_ANIMATION = current_animation_;
current_animation_ = NEW_ANIMATION;
if (reset) {
animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0;
animations_[current_animation_].completed = false;
} else {
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size());
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
}
}
}
// Actualiza las variables del objeto
void AnimatedSprite::update()
{
animate();
MovingSprite::update();
void AnimatedSprite::update() {
animate();
MovingSprite::update();
}
// Reinicia la animación
void AnimatedSprite::resetAnimation()
{
animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0;
animations_[current_animation_].completed = false;
void AnimatedSprite::resetAnimation() {
animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0;
animations_[current_animation_].completed = false;
}
// Carga la animación desde un vector de cadenas
void AnimatedSprite::loadFromAnimationsFileBuffer(const AnimationsFileBuffer &source)
{
float frame_width = 1;
float frame_height = 1;
int frames_per_row = 1;
int max_tiles = 1;
void AnimatedSprite::loadFromAnimationsFileBuffer(const AnimationsFileBuffer &source) {
float frame_width = 1;
float frame_height = 1;
int frames_per_row = 1;
int max_tiles = 1;
size_t index = 0;
while (index < source.size())
{
std::string line = source.at(index);
size_t index = 0;
while (index < source.size()) {
std::string line = source.at(index);
// Parsea el fichero para buscar variables y valores
if (line != "[animation]")
{
// Encuentra la posición del carácter '='
size_t pos = line.find("=");
// Parsea el fichero para buscar variables y valores
if (line != "[animation]") {
// Encuentra la posición del carácter '='
size_t pos = line.find("=");
// Procesa las dos subcadenas
if (pos != std::string::npos)
{
std::string key = line.substr(0, pos);
int value = std::stoi(line.substr(pos + 1));
if (key == "frame_width")
frame_width = value;
else if (key == "frame_height")
frame_height = value;
else
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str());
// Procesa las dos subcadenas
if (pos != std::string::npos) {
std::string key = line.substr(0, pos);
int value = std::stoi(line.substr(pos + 1));
if (key == "frame_width")
frame_width = value;
else if (key == "frame_height")
frame_height = value;
else
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str());
frames_per_row = texture_->getWidth() / frame_width;
const int w = texture_->getWidth() / frame_width;
const int h = texture_->getHeight() / frame_height;
max_tiles = w * h;
}
}
frames_per_row = texture_->getWidth() / frame_width;
const int w = texture_->getWidth() / frame_width;
const int h = texture_->getHeight() / frame_height;
max_tiles = w * h;
}
}
// Si la línea contiene el texto [animation] se realiza el proceso de carga de una animación
if (line == "[animation]")
{
Animation animation;
do
{
index++;
line = source.at(index);
size_t pos = line.find("=");
// Si la línea contiene el texto [animation] se realiza el proceso de carga de una animación
if (line == "[animation]") {
Animation animation;
do {
index++;
line = source.at(index);
size_t pos = line.find("=");
if (pos != std::string::npos)
{
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
if (pos != std::string::npos) {
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
if (key == "name")
animation.name = value;
else if (key == "speed")
animation.speed = std::stoi(value);
else if (key == "loop")
animation.loop = std::stoi(value);
else if (key == "frames")
{
// Se introducen los valores separados por comas en un vector
std::stringstream ss(value);
std::string tmp;
SDL_FRect rect = {0, 0, frame_width, frame_height};
while (getline(ss, tmp, ','))
{
// Comprueba que el tile no sea mayor que el máximo índice permitido
const int num_tile = std::stoi(tmp);
if (num_tile <= max_tiles)
{
rect.x = (num_tile % frames_per_row) * frame_width;
rect.y = (num_tile / frames_per_row) * frame_height;
animation.frames.emplace_back(rect);
}
}
}
else
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str());
}
} while (line != "[/animation]");
if (key == "name")
animation.name = value;
else if (key == "speed")
animation.speed = std::stoi(value);
else if (key == "loop")
animation.loop = std::stoi(value);
else if (key == "frames") {
// Se introducen los valores separados por comas en un vector
std::stringstream ss(value);
std::string tmp;
SDL_FRect rect = {0, 0, frame_width, frame_height};
while (getline(ss, tmp, ',')) {
// Comprueba que el tile no sea mayor que el máximo índice permitido
const int num_tile = std::stoi(tmp);
if (num_tile <= max_tiles) {
rect.x = (num_tile % frames_per_row) * frame_width;
rect.y = (num_tile / frames_per_row) * frame_height;
animation.frames.emplace_back(rect);
}
}
} else
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str());
}
} while (line != "[/animation]");
// Añade la animación al vector de animaciones
animations_.emplace_back(animation);
// Añade la animación al vector de animaciones
animations_.emplace_back(animation);
// Rellena el mapa con el nombre y el nuevo índice
animation_indices_[animation.name] = animations_.size() - 1;
}
// Rellena el mapa con el nombre y el nuevo índice
animation_indices_[animation.name] = animations_.size() - 1;
}
// Una vez procesada la línea, aumenta el índice para pasar a la siguiente
index++;
}
// Una vez procesada la línea, aumenta el índice para pasar a la siguiente
index++;
}
// Pone un valor por defecto
setWidth(frame_width);
setHeight(frame_height);
// Pone un valor por defecto
setWidth(frame_width);
setHeight(frame_height);
}
// Establece la velocidad de la animación
void AnimatedSprite::setAnimationSpeed(size_t value)
{
animations_[current_animation_].speed = value;
void AnimatedSprite::setAnimationSpeed(size_t value) {
animations_[current_animation_].speed = value;
}

View File

@@ -1,27 +1,27 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect
#include <stddef.h> // Para size_t
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string, hash
#include <unordered_map> // Para unordered_map
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_FRect
#include <stddef.h> // Para size_t
#include "moving_sprite.h" // Para MovingSprite
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string, hash
#include <unordered_map> // Para unordered_map
#include <vector> // Para vector
#include "moving_sprite.h" // Para MovingSprite
// Declaración adelantada
class Texture;
// Estructura de Animación
struct Animation
{
std::string name; // Nombre de la animación
std::vector<SDL_FRect> frames; // Frames que componen la animación
int speed; // Velocidad de reproducción
int loop; // Frame de vuelta al terminar (-1 para no repetir)
bool completed; // Indica si la animación ha finalizado
size_t current_frame; // Frame actual en reproducción
int counter; // Contador para la animación
struct Animation {
std::string name; // Nombre de la animación
std::vector<SDL_FRect> frames; // Frames que componen la animación
int speed; // Velocidad de reproducción
int loop; // Frame de vuelta al terminar (-1 para no repetir)
bool completed; // Indica si la animación ha finalizado
size_t current_frame; // Frame actual en reproducción
int counter; // Contador para la animación
Animation() : name(std::string()), speed(5), loop(0), completed(false), current_frame(0), counter(0) {}
};
@@ -33,9 +33,8 @@ using AnimationsFileBuffer = std::vector<std::string>;
AnimationsFileBuffer loadAnimationsFromFile(const std::string &file_path);
// Clase AnimatedSprite: Sprite animado que hereda de MovingSprite
class AnimatedSprite : public MovingSprite
{
public:
class AnimatedSprite : public MovingSprite {
public:
// --- Constructores y destructor ---
AnimatedSprite(std::shared_ptr<Texture> texture, const std::string &file_path);
AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer &animations);
@@ -43,28 +42,28 @@ public:
virtual ~AnimatedSprite() override = default;
// --- Métodos principales ---
void update() override; // Actualiza la animación
void update() override; // Actualiza la animación
// --- Control de animaciones ---
void setCurrentAnimation(const std::string &name = "default", bool reset = true); // Establece la animación por nombre
void setCurrentAnimation(int index = 0, bool reset = true); // Establece la animación por índice
void resetAnimation(); // Reinicia la animación actual
void setAnimationSpeed(size_t value); // Establece la velocidad de la animación
size_t getAnimationSpeed() const { return animations_[current_animation_].speed; } // Obtiene la velocidad de la animación actual
void setCurrentAnimation(const std::string &name = "default", bool reset = true); // Establece la animación por nombre
void setCurrentAnimation(int index = 0, bool reset = true); // Establece la animación por índice
void resetAnimation(); // Reinicia la animación actual
void setAnimationSpeed(size_t value); // Establece la velocidad de la animación
size_t getAnimationSpeed() const { return animations_[current_animation_].speed; } // Obtiene la velocidad de la animación actual
// --- Consultas ---
bool animationIsCompleted(); // Comprueba si la animación ha terminado
int getIndex(const std::string &name); // Obtiene el índice de una animación por nombre
bool animationIsCompleted(); // Comprueba si la animación ha terminado
int getIndex(const std::string &name); // Obtiene el índice de una animación por nombre
protected:
protected:
// --- Datos de animación ---
std::vector<Animation> animations_; // Vector de animaciones disponibles
int current_animation_ = 0; // Índice de la animación activa
std::vector<Animation> animations_; // Vector de animaciones disponibles
int current_animation_ = 0; // Índice de la animación activa
// --- Mapa para búsqueda rápida de animaciones por nombre ---
std::unordered_map<std::string, int> animation_indices_;
// --- Métodos internos ---
void animate(); // Calcula el frame actual de la animación
void loadFromAnimationsFileBuffer(const AnimationsFileBuffer &source); // Carga animaciones desde un buffer
void animate(); // Calcula el frame actual de la animación
void loadFromAnimationsFileBuffer(const AnimationsFileBuffer &source); // Carga animaciones desde un buffer
};

View File

@@ -1,11 +1,12 @@
#include "asset.h"
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError
#include <algorithm> // Para find_if, max
#include <fstream> // Para basic_ifstream, ifstream
#include <string> // Para allocator, string, char_traits, operator+
#include "utils.h" // Para getFileName
#include "utils.h" // Para getFileName
// Singleton
Asset *Asset::instance_ = nullptr;
@@ -20,62 +21,48 @@ void Asset::destroy() { delete Asset::instance_; }
Asset *Asset::get() { return Asset::instance_; }
// Añade un elemento a la lista
void Asset::add(const std::string &file, AssetType type, bool required, bool absolute)
{
void Asset::add(const std::string &file, AssetType type, bool required, bool absolute) {
file_list_.emplace_back(absolute ? file : executable_path_ + file, type, required);
longest_name_ = std::max(longest_name_, static_cast<int>(file_list_.back().file.size()));
}
// Devuelve la ruta completa a un fichero a partir de una cadena
std::string Asset::get(const std::string &text) const
{
auto it = std::find_if(file_list_.begin(), file_list_.end(),
[&text](const auto &f)
{
return getFileName(f.file) == text;
});
std::string Asset::get(const std::string &text) const {
auto it = std::find_if(file_list_.begin(), file_list_.end(), [&text](const auto &f) {
return getFileName(f.file) == text;
});
if (it != file_list_.end())
{
if (it != file_list_.end()) {
return it->file;
}
else
{
} else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: file %s not found", text.c_str());
return "";
}
}
// Comprueba que existen todos los elementos
bool Asset::check() const
{
bool Asset::check() const {
bool success = true;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES");
// Comprueba la lista de ficheros clasificándolos por tipo
for (int type = 0; type < static_cast<int>(AssetType::COUNT); ++type)
{
for (int type = 0; type < static_cast<int>(AssetType::COUNT); ++type) {
// Comprueba si hay ficheros de ese tipo
bool any = false;
for (const auto &f : file_list_)
{
if (f.required && f.type == static_cast<AssetType>(type))
{
for (const auto &f : file_list_) {
if (f.required && f.type == static_cast<AssetType>(type)) {
any = true;
}
}
// Si hay ficheros de ese tipo, comprueba si existen
if (any)
{
if (any) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> %s FILES", getTypeName(static_cast<AssetType>(type)).c_str());
for (const auto &f : file_list_)
{
if (f.required && f.type == static_cast<AssetType>(type))
{
for (const auto &f : file_list_) {
if (f.required && f.type == static_cast<AssetType>(type)) {
success &= checkFile(f.file);
}
}
@@ -85,12 +72,9 @@ bool Asset::check() const
}
// Resultado
if (success)
{
if (success) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES COMPLETED.\n");
}
else
{
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES FAILED.\n");
}
@@ -98,14 +82,12 @@ bool Asset::check() const
}
// Comprueba que existe un fichero
bool Asset::checkFile(const std::string &path) const
{
bool Asset::checkFile(const std::string &path) const {
std::ifstream file(path);
bool success = file.good();
file.close();
if (!success)
{
if (!success) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Checking file: %s [ ERROR ]", getFileName(path).c_str());
}
@@ -113,42 +95,37 @@ bool Asset::checkFile(const std::string &path) const
}
// Devuelve el nombre del tipo de recurso
std::string Asset::getTypeName(AssetType type) const
{
switch (type)
{
case AssetType::BITMAP:
return "BITMAP";
case AssetType::MUSIC:
return "MUSIC";
case AssetType::SOUND:
return "SOUND";
case AssetType::FONT:
return "FONT";
case AssetType::LANG:
return "LANG";
case AssetType::DATA:
return "DATA";
case AssetType::DEMODATA:
return "DEMODATA";
case AssetType::ANIMATION:
return "ANIMATION";
case AssetType::PALETTE:
return "PALETTE";
default:
return "ERROR";
std::string Asset::getTypeName(AssetType type) const {
switch (type) {
case AssetType::BITMAP:
return "BITMAP";
case AssetType::MUSIC:
return "MUSIC";
case AssetType::SOUND:
return "SOUND";
case AssetType::FONT:
return "FONT";
case AssetType::LANG:
return "LANG";
case AssetType::DATA:
return "DATA";
case AssetType::DEMODATA:
return "DEMODATA";
case AssetType::ANIMATION:
return "ANIMATION";
case AssetType::PALETTE:
return "PALETTE";
default:
return "ERROR";
}
}
// Devuelve la lista de recursos de un tipo
std::vector<std::string> Asset::getListByType(AssetType type) const
{
std::vector<std::string> Asset::getListByType(AssetType type) const {
std::vector<std::string> list;
for (auto f : file_list_)
{
if (f.type == type)
{
for (auto f : file_list_) {
if (f.type == type) {
list.push_back(f.file);
}
}

View File

@@ -1,66 +1,63 @@
#pragma once
#include <string> // Para string, basic_string
#include <vector> // Para vector
#include <string> // Para string, basic_string
#include <vector> // Para vector
// Tipos de recursos gestionados por Asset
enum class AssetType : int
{
BITMAP,
MUSIC,
SOUND,
FONT,
LANG,
DATA,
DEMODATA,
ANIMATION,
PALETTE,
COUNT,
enum class AssetType : int {
BITMAP,
MUSIC,
SOUND,
FONT,
LANG,
DATA,
DEMODATA,
ANIMATION,
PALETTE,
COUNT,
};
// Clase Asset: gestor de recursos (singleton)
class Asset
{
public:
// --- Métodos de singleton ---
static void init(const std::string &executable_path); // Inicializa el objeto Asset
static void destroy(); // Libera el objeto Asset
static Asset *get(); // Obtiene el puntero al objeto Asset
class Asset {
public:
// --- Métodos de singleton ---
static void init(const std::string &executable_path); // Inicializa el objeto Asset
static void destroy(); // Libera el objeto Asset
static Asset *get(); // Obtiene el puntero al objeto Asset
// --- Métodos para la gestión de recursos ---
void add(const std::string &file, AssetType type, bool required = true, bool absolute = false); // Añade un recurso a la lista
std::string get(const std::string &text) const; // Obtiene la ruta completa de un recurso a partir de su nombre
bool check() const; // Verifica la existencia de todos los recursos requeridos
std::vector<std::string> getListByType(AssetType type) const; // Devuelve una lista de archivos de un tipo concreto
// --- Métodos para la gestión de recursos ---
void add(const std::string &file, AssetType type, bool required = true, bool absolute = false); // Añade un recurso a la lista
std::string get(const std::string &text) const; // Obtiene la ruta completa de un recurso a partir de su nombre
bool check() const; // Verifica la existencia de todos los recursos requeridos
std::vector<std::string> getListByType(AssetType type) const; // Devuelve una lista de archivos de un tipo concreto
private:
// --- Estructura interna para almacenar información de cada recurso ---
struct AssetItem
{
std::string file; // Ruta del fichero desde la raíz del directorio
AssetType type; // Tipo de recurso
bool required; // Indica si el fichero es obligatorio
private:
// --- Estructura interna para almacenar información de cada recurso ---
struct AssetItem {
std::string file; // Ruta del fichero desde la raíz del directorio
AssetType type; // Tipo de recurso
bool required; // Indica si el fichero es obligatorio
AssetItem(const std::string &filePath, AssetType assetType, bool isRequired)
: file(filePath), type(assetType), required(isRequired) {}
};
AssetItem(const std::string &filePath, AssetType assetType, bool isRequired)
: file(filePath), type(assetType), required(isRequired) {}
};
// --- Variables internas ---
int longest_name_ = 0; // Longitud del nombre más largo
std::vector<AssetItem> file_list_; // Lista con todas las rutas de recursos
std::string executable_path_; // Ruta del ejecutable
// --- Variables internas ---
int longest_name_ = 0; // Longitud del nombre más largo
std::vector<AssetItem> file_list_; // Lista con todas las rutas de recursos
std::string executable_path_; // Ruta del ejecutable
// --- Métodos internos ---
bool checkFile(const std::string &path) const; // Verifica si un archivo existe
std::string getTypeName(AssetType type) const; // Devuelve el nombre textual del tipo de recurso
// --- Métodos internos ---
bool checkFile(const std::string &path) const; // Verifica si un archivo existe
std::string getTypeName(AssetType type) const; // Devuelve el nombre textual del tipo de recurso
// --- Patrón Singleton ---
explicit Asset(const std::string &executable_path)
: executable_path_(executable_path) {}
~Asset() = default;
Asset(const Asset &) = delete;
Asset &operator=(const Asset &) = delete;
// --- Patrón Singleton ---
explicit Asset(const std::string &executable_path)
: executable_path_(executable_path) {}
~Asset() = default;
Asset(const Asset &) = delete;
Asset &operator=(const Asset &) = delete;
// --- Singleton ---
static Asset *instance_;
// --- Singleton ---
static Asset *instance_;
};

View File

@@ -1,11 +1,12 @@
#include "audio.h"
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_G...
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_G...
#include <algorithm> // Para clamp
#include "external/jail_audio.h" // Para JA_FadeOutMusic, JA_Init, JA_PauseM...
#include "options.h" // Para AudioOptions, audio, MusicOptions
#include "resource.h" // Para Resource
#include "external/jail_audio.h" // Para JA_FadeOutMusic, JA_Init, JA_PauseM...
#include "options.h" // Para AudioOptions, audio, MusicOptions
#include "resource.h" // Para Resource
// Singleton
Audio *Audio::instance_ = nullptr;
@@ -26,70 +27,56 @@ Audio::Audio() { initSDLAudio(); }
Audio::~Audio() { JA_Quit(); }
// Reproduce la música
void Audio::playMusic(const std::string &name, const int loop)
{
void Audio::playMusic(const std::string &name, const int loop) {
music_.name = name;
music_.loop = loop;
if (music_enabled_ && music_.state != MusicState::PLAYING)
{
if (music_enabled_ && music_.state != MusicState::PLAYING) {
JA_PlayMusic(Resource::get()->getMusic(name), loop);
music_.state = MusicState::PLAYING;
}
}
// Pausa la música
void Audio::pauseMusic()
{
if (music_enabled_ && music_.state == MusicState::PLAYING)
{
void Audio::pauseMusic() {
if (music_enabled_ && music_.state == MusicState::PLAYING) {
JA_PauseMusic();
music_.state = MusicState::PAUSED;
}
}
// Detiene la música
void Audio::stopMusic()
{
if (music_enabled_)
{
void Audio::stopMusic() {
if (music_enabled_) {
JA_StopMusic();
music_.state = MusicState::STOPPED;
}
}
// Reproduce un sonido
void Audio::playSound(const std::string &name, Group group)
{
if (sound_enabled_)
{
void Audio::playSound(const std::string &name, Group group) {
if (sound_enabled_) {
JA_PlaySound(Resource::get()->getSound(name), 0, static_cast<int>(group));
}
}
// Detiene todos los sonidos
void Audio::stopAllSounds()
{
if (sound_enabled_)
{
void Audio::stopAllSounds() {
if (sound_enabled_) {
JA_StopChannel(-1);
}
}
// Realiza un fundido de salida de la música
void Audio::fadeOutMusic(int milliseconds)
{
if (music_enabled_)
{
void Audio::fadeOutMusic(int milliseconds) {
if (music_enabled_) {
JA_FadeOutMusic(milliseconds);
}
}
// Establece el volumen de los sonidos
void Audio::setSoundVolume(int sound_volume, Group group)
{
if (sound_enabled_)
{
void Audio::setSoundVolume(int sound_volume, Group group) {
if (sound_enabled_) {
sound_volume = std::clamp(sound_volume, 0, 100);
const float CONVERTED_VOLUME = (sound_volume / 100.0f) * (Options::audio.volume / 100.0f);
JA_SetSoundVolume(CONVERTED_VOLUME, static_cast<int>(group));
@@ -97,10 +84,8 @@ void Audio::setSoundVolume(int sound_volume, Group group)
}
// Establece el volumen de la música
void Audio::setMusicVolume(int music_volume)
{
if (music_enabled_)
{
void Audio::setMusicVolume(int music_volume) {
if (music_enabled_) {
music_volume = std::clamp(music_volume, 0, 100);
const float CONVERTED_VOLUME = (music_volume / 100.0f) * (Options::audio.volume / 100.0f);
JA_SetMusicVolume(CONVERTED_VOLUME);
@@ -108,14 +93,12 @@ void Audio::setMusicVolume(int music_volume)
}
// Aplica la configuración
void Audio::applySettings()
{
void Audio::applySettings() {
enable(Options::audio.enabled);
}
// Establecer estado general
void Audio::enable(bool value)
{
void Audio::enable(bool value) {
enabled_ = value;
setSoundVolume(enabled_ ? Options::audio.sound.volume : 0);
@@ -123,14 +106,10 @@ void Audio::enable(bool value)
}
// Inicializa SDL Audio
void Audio::initSDLAudio()
{
if (!SDL_Init(SDL_INIT_AUDIO))
{
void Audio::initSDLAudio() {
if (!SDL_Init(SDL_INIT_AUDIO)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_AUDIO could not initialize! SDL Error: %s", SDL_GetError());
}
else
{
} else {
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "\n** SDL_AUDIO: INITIALIZING\n");
JA_Init(48000, SDL_AUDIO_S16LE, 2);

View File

@@ -3,65 +3,61 @@
#include <string>
// Clase Audio: gestor de audio (singleton)
class Audio
{
public:
enum class Group : int
{
class Audio {
public:
enum class Group : int {
ALL = -1,
GAME = 0,
INTERFACE = 1
};
// --- Métodos de singleton ---
static void init(); // Inicializa el objeto Audio
static void destroy(); // Libera el objeto Audio
static Audio *get(); // Obtiene el puntero al objeto Audio
static void init(); // Inicializa el objeto Audio
static void destroy(); // Libera el objeto Audio
static Audio *get(); // Obtiene el puntero al objeto Audio
// --- Control de Música ---
void playMusic(const std::string &name, int loop = -1); // Reproducir música en bucle
void pauseMusic(); // Pausar reproducción de música
void stopMusic(); // Detener completamente la música
void fadeOutMusic(int milliseconds); // Fundido de salida de la música
void playMusic(const std::string &name, int loop = -1); // Reproducir música en bucle
void pauseMusic(); // Pausar reproducción de música
void stopMusic(); // Detener completamente la música
void fadeOutMusic(int milliseconds); // Fundido de salida de la música
// --- Control de Sonidos ---
void playSound(const std::string &name, Group group = Group::GAME); // Reproducir sonido puntual
void stopAllSounds(); // Detener todos los sonidos
void playSound(const std::string &name, Group group = Group::GAME); // Reproducir sonido puntual
void stopAllSounds(); // Detener todos los sonidos
// --- Configuración General ---
void enable(bool value); // Establecer estado general
void toggleEnabled() { enabled_ = !enabled_; } // Alternar estado general
void applySettings(); // Aplica la configuración
void enable(bool value); // Establecer estado general
void toggleEnabled() { enabled_ = !enabled_; } // Alternar estado general
void applySettings(); // Aplica la configuración
// --- Configuración de Sonidos ---
void enableSound() { sound_enabled_ = true; } // Habilitar sonidos
void disableSound() { sound_enabled_ = false; } // Deshabilitar sonidos
void enableSound(bool value) { sound_enabled_ = value; } // Establecer estado de sonidos
void toggleSound() { sound_enabled_ = !sound_enabled_; } // Alternar estado de sonidos
void enableSound() { sound_enabled_ = true; } // Habilitar sonidos
void disableSound() { sound_enabled_ = false; } // Deshabilitar sonidos
void enableSound(bool value) { sound_enabled_ = value; } // Establecer estado de sonidos
void toggleSound() { sound_enabled_ = !sound_enabled_; } // Alternar estado de sonidos
// --- Configuración de Música ---
void enableMusic() { music_enabled_ = true; } // Habilitar música
void disableMusic() { music_enabled_ = false; } // Deshabilitar música
void enableMusic(bool value) { music_enabled_ = value; } // Establecer estado de música
void toggleMusic() { music_enabled_ = !music_enabled_; } // Alternar estado de música
void enableMusic() { music_enabled_ = true; } // Habilitar música
void disableMusic() { music_enabled_ = false; } // Deshabilitar música
void enableMusic(bool value) { music_enabled_ = value; } // Establecer estado de música
void toggleMusic() { music_enabled_ = !music_enabled_; } // Alternar estado de música
// --- Control de Volumen ---
void setSoundVolume(int volume, Group group = Group::ALL); // Ajustar volumen de efectos
void setMusicVolume(int volume); // Ajustar volumen de música
void setSoundVolume(int volume, Group group = Group::ALL); // Ajustar volumen de efectos
void setMusicVolume(int volume); // Ajustar volumen de música
private:
enum class MusicState
{
private:
enum class MusicState {
PLAYING,
PAUSED,
STOPPED,
};
struct Music
{
MusicState state; // Estado actual de la música (reproduciendo, detenido, en pausa)
std::string name; // Última pista de música reproducida
bool loop; // Indica si la última pista de música se debe reproducir en bucle
struct Music {
MusicState state; // Estado actual de la música (reproduciendo, detenido, en pausa)
std::string name; // Última pista de música reproducida
bool loop; // Indica si la última pista de música se debe reproducir en bucle
// Constructor para inicializar la música con valores predeterminados
Music() : state(MusicState::STOPPED), name(""), loop(false) {}
@@ -74,18 +70,18 @@ private:
Music music_;
// --- Variables de Estado ---
bool enabled_ = true; // Estado general del audio
bool sound_enabled_ = true; // Estado de los efectos de sonido
bool music_enabled_ = true; // Estado de la música
bool enabled_ = true; // Estado general del audio
bool sound_enabled_ = true; // Estado de los efectos de sonido
bool music_enabled_ = true; // Estado de la música
// --- Inicializa SDL Audio ---
void initSDLAudio();
// --- Patrón Singleton ---
Audio(); // Constructor privado
~Audio(); // Destructor privado
Audio(const Audio &) = delete; // Evitar copia
Audio &operator=(const Audio &) = delete; // Evitar asignación
Audio(); // Constructor privado
~Audio(); // Destructor privado
Audio(const Audio &) = delete; // Evitar copia
Audio &operator=(const Audio &) = delete; // Evitar asignación
// --- Singleton ---
static Audio *instance_;

View File

@@ -1,17 +1,18 @@
#define _USE_MATH_DEFINES
#include "background.h"
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_FRect, SDL_Creat...
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_FRect, SDL_Creat...
#include <algorithm> // Para clamp, max
#include <cmath> // Para cos, sin, M_PI
#include <string> // Para basic_string
#include "moving_sprite.h" // Para MovingSprite
#include "param.h" // Para Param, ParamBackground, param
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
#include "moving_sprite.h" // Para MovingSprite
#include "param.h" // Para Param, ParamBackground, param
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
// Constructor
Background::Background()
@@ -47,8 +48,7 @@ Background::Background()
const float TOP_CLOUDS_TEXTURE_HEIGHT = top_clouds_texture_->getHeight() / 4;
const float BOTTOM_CLOUDS_TEXTURE_HEIGHT = bottom_clouds_texture_->getHeight() / 4;
for (int i = 0; i < 4; ++i)
{
for (int i = 0; i < 4; ++i) {
top_clouds_rect_[i] = {0, i * TOP_CLOUDS_TEXTURE_HEIGHT, static_cast<float>(top_clouds_texture_->getWidth()), TOP_CLOUDS_TEXTURE_HEIGHT};
bottom_clouds_rect_[i] = {0, i * BOTTOM_CLOUDS_TEXTURE_HEIGHT, static_cast<float>(bottom_clouds_texture_->getWidth()), BOTTOM_CLOUDS_TEXTURE_HEIGHT};
}
@@ -107,15 +107,13 @@ Background::Background()
}
// Destructor
Background::~Background()
{
Background::~Background() {
SDL_DestroyTexture(canvas_);
SDL_DestroyTexture(color_texture_);
}
// Actualiza la lógica del objeto
void Background::update()
{
void Background::update() {
// Actualiza el valor de alpha_
updateAlphaColorTexture();
@@ -140,8 +138,7 @@ void Background::update()
}
// Dibuja el gradiente de fondo
void Background::renderGradient()
{
void Background::renderGradient() {
// Dibuja el gradiente de detras
gradients_texture_->setAlpha(255);
gradient_sprite_->setSpriteClip(gradient_rect_[(gradient_number_ + 1) % 4]);
@@ -154,8 +151,7 @@ void Background::renderGradient()
}
// Dibuja las nubes de arriba
void Background::renderTopClouds()
{
void Background::renderTopClouds() {
// Dibuja el primer conjunto de nubes, las de detras
top_clouds_texture_->setAlpha(255);
top_clouds_sprite_a_->setSpriteClip(top_clouds_rect_[(gradient_number_ + 1) % 4]);
@@ -172,8 +168,7 @@ void Background::renderTopClouds()
}
// Dibuja las nubes de abajo
void Background::renderBottomClouds()
{
void Background::renderBottomClouds() {
// Dibuja el primer conjunto de nubes, las de detras
bottom_clouds_texture_->setAlpha(255);
bottom_clouds_sprite_a_->setSpriteClip(bottom_clouds_rect_[(gradient_number_ + 1) % 4]);
@@ -190,8 +185,7 @@ void Background::renderBottomClouds()
}
// Compone todos los elementos del fondo en la textura
void Background::fillCanvas()
{
void Background::fillCanvas() {
// Cambia el destino del renderizador
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, canvas_);
@@ -220,8 +214,7 @@ void Background::fillCanvas()
}
// Dibuja el objeto
void Background::render()
{
void Background::render() {
// Fondo
SDL_RenderTexture(renderer_, canvas_, &src_rect_, &dst_rect_);
@@ -230,26 +223,22 @@ void Background::render()
}
// Ajusta el valor de la variable
void Background::setCloudsSpeed(float value)
{
void Background::setCloudsSpeed(float value) {
clouds_speed_ = value;
}
// Ajusta el valor de la variable
void Background::setGradientNumber(int value)
{
void Background::setGradientNumber(int value) {
gradient_number_ = value % 4;
}
// Ajusta el valor de la variable
void Background::setTransition(float value)
{
void Background::setTransition(float value) {
transition_ = std::clamp(value, 0.0f, 1.0f);
}
// Establece la posición del objeto
void Background::setPos(SDL_FRect pos)
{
void Background::setPos(SDL_FRect pos) {
dst_rect_ = pos;
// Si cambian las medidas del destino, hay que cambiar las del origen para evitar deformar la imagen
@@ -260,8 +249,7 @@ void Background::setPos(SDL_FRect pos)
}
// Establece el color_ de atenuación
void Background::setColor(Color color)
{
void Background::setColor(Color color) {
attenuate_color_ = color;
// Colorea la textura
@@ -275,8 +263,7 @@ void Background::setColor(Color color)
}
// Establece la transparencia de la atenuación
void Background::setAlpha(int alpha)
{
void Background::setAlpha(int alpha) {
// Evita que se asignen valores fuera de rango
alpha_ = std::clamp(alpha, 0, 255);
@@ -286,22 +273,17 @@ void Background::setAlpha(int alpha)
}
// Actualiza el valor de alpha_
void Background::updateAlphaColorTexture()
{
if (alpha_color_text_ == alpha_color_text_temp_)
{
void Background::updateAlphaColorTexture() {
if (alpha_color_text_ == alpha_color_text_temp_) {
return;
}
else
{
} else {
alpha_color_text_ > alpha_color_text_temp_ ? ++alpha_color_text_temp_ : --alpha_color_text_temp_;
SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_temp_);
}
}
// Actualiza las nubes
void Background::updateClouds()
{
void Background::updateClouds() {
// Aplica la velocidad calculada a las nubes
top_clouds_sprite_a_->setVelX(clouds_speed_);
top_clouds_sprite_b_->setVelX(clouds_speed_);
@@ -315,37 +297,31 @@ void Background::updateClouds()
bottom_clouds_sprite_b_->update();
// Calcula el offset de las nubes
if (top_clouds_sprite_a_->getPosX() < -top_clouds_sprite_a_->getWidth())
{
if (top_clouds_sprite_a_->getPosX() < -top_clouds_sprite_a_->getWidth()) {
top_clouds_sprite_a_->setPosX(top_clouds_sprite_a_->getWidth());
}
if (top_clouds_sprite_b_->getPosX() < -top_clouds_sprite_b_->getWidth())
{
if (top_clouds_sprite_b_->getPosX() < -top_clouds_sprite_b_->getWidth()) {
top_clouds_sprite_b_->setPosX(top_clouds_sprite_b_->getWidth());
}
if (bottom_clouds_sprite_a_->getPosX() < -bottom_clouds_sprite_a_->getWidth())
{
if (bottom_clouds_sprite_a_->getPosX() < -bottom_clouds_sprite_a_->getWidth()) {
bottom_clouds_sprite_a_->setPosX(bottom_clouds_sprite_a_->getWidth());
}
if (bottom_clouds_sprite_b_->getPosX() < -bottom_clouds_sprite_b_->getWidth())
{
if (bottom_clouds_sprite_b_->getPosX() < -bottom_clouds_sprite_b_->getWidth()) {
bottom_clouds_sprite_b_->setPosX(bottom_clouds_sprite_b_->getWidth());
}
}
// Precalcula el vector con el recorrido del sol
void Background::createSunPath()
{
void Background::createSunPath() {
constexpr float CENTER_X = 170;
const float center_y = base_ - 80;
constexpr float RADIUS = 120;
// Generar puntos de la curva desde 90 a 180 grados
for (double theta = M_PI / 2; theta <= M_PI; theta += 0.01)
{
for (double theta = M_PI / 2; theta <= M_PI; theta += 0.01) {
float x = CENTER_X + (RADIUS * cos(theta));
float y = center_y - (RADIUS * sin(theta));
sun_path_.push_back({x, y});
@@ -354,22 +330,19 @@ void Background::createSunPath()
// Agregar puntos en línea recta después de la curva
constexpr int EXTRA_PIXELS = 40;
SDL_FPoint last_point = sun_path_.back();
for (int i = 1; i <= EXTRA_PIXELS; ++i)
{
for (int i = 1; i <= EXTRA_PIXELS; ++i) {
sun_path_.push_back({last_point.x, last_point.y + i});
}
}
// Precalcula el vector con el recorrido de la luna
void Background::createMoonPath()
{
void Background::createMoonPath() {
constexpr float CENTER_X = 100;
const float center_y = base_ - 50;
constexpr float RADIUS = 140;
// Generar puntos de la curva desde 0 a 90 grados
for (double theta = 0; theta <= M_PI / 2; theta += 0.01)
{
for (double theta = 0; theta <= M_PI / 2; theta += 0.01) {
float x = CENTER_X + (RADIUS * cos(theta));
float y = center_y - (RADIUS * sin(theta));
moon_path_.push_back({x, y});
@@ -377,15 +350,13 @@ void Background::createMoonPath()
}
// Establece la posición del sol
void Background::setSunProgression(float progress)
{
void Background::setSunProgression(float progress) {
progress = std::clamp(progress, 0.0f, 1.0f);
sun_index_ = static_cast<size_t>(progress * (sun_path_.size() - 1));
}
// Establece la posición de la luna
void Background::setMoonProgression(float progress)
{
void Background::setMoonProgression(float progress) {
progress = std::clamp(progress, 0.0f, 1.0f);
moon_index_ = static_cast<size_t>(progress * (moon_path_.size() - 1));
}

View File

@@ -1,11 +1,12 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_FPoint, SDL_Texture, SDL_Renderer
#include <stddef.h> // Para size_t
#include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_FPoint, SDL_Texture, SDL_Renderer
#include <stddef.h> // Para size_t
#include "utils.h" // Para Color
#include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector
#include "utils.h" // Para Color
class MovingSprite;
class Sprite;
@@ -25,89 +26,88 @@ class Texture;
- setAlpha(int alpha) -> Ajusta la transparencia de la capa de atenuación
*/
class Background
{
public:
// Constructor y Destructor
Background();
~Background();
class Background {
public:
// Constructor y Destructor
Background();
~Background();
// Actualización y renderizado
void update(); // Actualiza la lógica del objeto
void render(); // Dibuja el objeto
// Actualización y renderizado
void update(); // Actualiza la lógica del objeto
void render(); // Dibuja el objeto
// Configuración de posición
void setPos(SDL_FRect pos); // Establece la posición del objeto
// Configuración de posición
void setPos(SDL_FRect pos); // Establece la posición del objeto
// Configuración de animaciones y efectos
void setCloudsSpeed(float value); // Ajusta la velocidad de desplazamiento de las nubes
void setGradientNumber(int value); // Establece el degradado de fondo a usar
void setTransition(float value); // Ajusta la transición entre texturas de fondo
// Configuración de animaciones y efectos
void setCloudsSpeed(float value); // Ajusta la velocidad de desplazamiento de las nubes
void setGradientNumber(int value); // Establece el degradado de fondo a usar
void setTransition(float value); // Ajusta la transición entre texturas de fondo
// Configuración de efectos visuales
void setColor(Color color); // Establece el color de atenuación
void setAlpha(int alpha); // Ajusta la transparencia del fondo
// Configuración de efectos visuales
void setColor(Color color); // Establece el color de atenuación
void setAlpha(int alpha); // Ajusta la transparencia del fondo
// Configuración del sol y la luna
void setSunProgression(float progress); // Establece la posición del sol
void setMoonProgression(float progress); // Establece la posición de la luna
// Configuración del sol y la luna
void setSunProgression(float progress); // Establece la posición del sol
void setMoonProgression(float progress); // Establece la posición de la luna
private:
// Objetos y punteros
SDL_Renderer *renderer_; // Renderizador de la ventana
private:
// Objetos y punteros
SDL_Renderer *renderer_; // Renderizador de la ventana
// Texturas
std::shared_ptr<Texture> buildings_texture_;
std::shared_ptr<Texture> top_clouds_texture_;
std::shared_ptr<Texture> bottom_clouds_texture_;
std::shared_ptr<Texture> grass_texture_;
std::shared_ptr<Texture> gradients_texture_;
std::shared_ptr<Texture> sun_texture_;
std::shared_ptr<Texture> moon_texture_;
// Texturas
std::shared_ptr<Texture> buildings_texture_;
std::shared_ptr<Texture> top_clouds_texture_;
std::shared_ptr<Texture> bottom_clouds_texture_;
std::shared_ptr<Texture> grass_texture_;
std::shared_ptr<Texture> gradients_texture_;
std::shared_ptr<Texture> sun_texture_;
std::shared_ptr<Texture> moon_texture_;
// Sprites
std::unique_ptr<MovingSprite> top_clouds_sprite_a_;
std::unique_ptr<MovingSprite> top_clouds_sprite_b_;
std::unique_ptr<MovingSprite> bottom_clouds_sprite_a_;
std::unique_ptr<MovingSprite> bottom_clouds_sprite_b_;
std::unique_ptr<Sprite> buildings_sprite_;
std::unique_ptr<Sprite> gradient_sprite_;
std::unique_ptr<Sprite> grass_sprite_;
std::unique_ptr<Sprite> sun_sprite_;
std::unique_ptr<Sprite> moon_sprite_;
// Sprites
std::unique_ptr<MovingSprite> top_clouds_sprite_a_;
std::unique_ptr<MovingSprite> top_clouds_sprite_b_;
std::unique_ptr<MovingSprite> bottom_clouds_sprite_a_;
std::unique_ptr<MovingSprite> bottom_clouds_sprite_b_;
std::unique_ptr<Sprite> buildings_sprite_;
std::unique_ptr<Sprite> gradient_sprite_;
std::unique_ptr<Sprite> grass_sprite_;
std::unique_ptr<Sprite> sun_sprite_;
std::unique_ptr<Sprite> moon_sprite_;
// Buffers de renderizado
SDL_Texture *canvas_; // Textura para componer el fondo
SDL_Texture *color_texture_; // Textura para atenuar el fondo
// Buffers de renderizado
SDL_Texture *canvas_; // Textura para componer el fondo
SDL_Texture *color_texture_; // Textura para atenuar el fondo
// Variables de control
SDL_FRect gradient_rect_[4];
SDL_FRect top_clouds_rect_[4];
SDL_FRect bottom_clouds_rect_[4];
int gradient_number_ = 0;
int alpha_ = 0;
float clouds_speed_ = 0;
float transition_ = 0;
int counter_ = 0;
SDL_FRect rect_;
SDL_FRect src_rect_;
SDL_FRect dst_rect_;
int base_;
Color attenuate_color_;
int alpha_color_text_;
int alpha_color_text_temp_;
std::vector<SDL_FPoint> sun_path_;
std::vector<SDL_FPoint> moon_path_;
size_t sun_index_ = 0;
size_t moon_index_ = 0;
// Variables de control
SDL_FRect gradient_rect_[4];
SDL_FRect top_clouds_rect_[4];
SDL_FRect bottom_clouds_rect_[4];
int gradient_number_ = 0;
int alpha_ = 0;
float clouds_speed_ = 0;
float transition_ = 0;
int counter_ = 0;
SDL_FRect rect_;
SDL_FRect src_rect_;
SDL_FRect dst_rect_;
int base_;
Color attenuate_color_;
int alpha_color_text_;
int alpha_color_text_temp_;
std::vector<SDL_FPoint> sun_path_;
std::vector<SDL_FPoint> moon_path_;
size_t sun_index_ = 0;
size_t moon_index_ = 0;
// Métodos internos
void renderGradient(); // Dibuja el gradiente de fondo
void renderTopClouds(); // Dibuja las nubes superiores
void renderBottomClouds(); // Dibuja las nubes inferiores
void fillCanvas(); // Compone todos los elementos en la textura
void updateAlphaColorTexture(); // Actualiza el alpha de la textura de atenuación
void updateClouds(); // Actualiza el movimiento de las nubes
void createSunPath(); // Precalcula el recorrido del sol
void createMoonPath(); // Precalcula el recorrido de la luna
// Métodos internos
void renderGradient(); // Dibuja el gradiente de fondo
void renderTopClouds(); // Dibuja las nubes superiores
void renderBottomClouds(); // Dibuja las nubes inferiores
void fillCanvas(); // Compone todos los elementos en la textura
void updateAlphaColorTexture(); // Actualiza el alpha de la textura de atenuación
void updateClouds(); // Actualiza el movimiento de las nubes
void createSunPath(); // Precalcula el recorrido del sol
void createMoonPath(); // Precalcula el recorrido de la luna
};

View File

@@ -1,440 +1,386 @@
#include "balloon.h"
#include <algorithm> // Para clamp
#include <array> // Para array
#include <cmath> // Para fabs
#include <algorithm> // Para clamp
#include <array> // Para array
#include <cmath> // Para fabs
#include "animated_sprite.h" // Para AnimatedSprite
#include "audio.h" // Para Audio
#include "param.h" // Para Param, ParamBalloon, param
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
#include "animated_sprite.h" // Para AnimatedSprite
#include "audio.h" // Para Audio
#include "param.h" // Para Param, ParamBalloon, param
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
// Constructor
Balloon::Balloon(float x, float y, BalloonType type, BalloonSize size, float vel_x, float speed, Uint16 creation_timer, SDL_FRect play_area, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation)
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
x_(x),
y_(y),
vx_(vel_x),
being_created_(creation_timer > 0),
invulnerable_(creation_timer > 0),
stopped_(creation_timer > 0),
creation_counter_(creation_timer),
creation_counter_ini_(creation_timer),
type_(type),
size_(size),
speed_(speed),
play_area_(play_area)
{
switch (type_)
{
case BalloonType::BALLOON:
{
vy_ = 0;
max_vy_ = 3.0f;
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
x_(x),
y_(y),
vx_(vel_x),
being_created_(creation_timer > 0),
invulnerable_(creation_timer > 0),
stopped_(creation_timer > 0),
creation_counter_(creation_timer),
creation_counter_ini_(creation_timer),
type_(type),
size_(size),
speed_(speed),
play_area_(play_area) {
switch (type_) {
case BalloonType::BALLOON: {
vy_ = 0;
max_vy_ = 3.0f;
const int index = static_cast<int>(size_);
gravity_ = param.balloon.settings.at(index).grav;
default_vy_ = param.balloon.settings.at(index).vel;
h_ = w_ = BALLOON_SIZE[index];
power_ = BALLOON_POWER[index];
menace_ = BALLOON_MENACE[index];
score_ = BALLOON_SCORE[index];
bouncing_sound_ = BALLOON_BOUNCING_SOUND[index];
popping_sound_ = BALLOON_POPPING_SOUND[index];
const int index = static_cast<int>(size_);
gravity_ = param.balloon.settings.at(index).grav;
default_vy_ = param.balloon.settings.at(index).vel;
h_ = w_ = BALLOON_SIZE[index];
power_ = BALLOON_POWER[index];
menace_ = BALLOON_MENACE[index];
score_ = BALLOON_SCORE[index];
bouncing_sound_ = BALLOON_BOUNCING_SOUND[index];
popping_sound_ = BALLOON_POPPING_SOUND[index];
break;
}
break;
}
case BalloonType::FLOATER:
{
default_vy_ = max_vy_ = vy_ = fabs(vx_ * 2.0f);
gravity_ = 0.00f;
case BalloonType::FLOATER: {
default_vy_ = max_vy_ = vy_ = fabs(vx_ * 2.0f);
gravity_ = 0.00f;
const int index = static_cast<int>(size_);
h_ = w_ = BALLOON_SIZE[index];
power_ = BALLOON_POWER[index];
menace_ = BALLOON_MENACE[index];
score_ = BALLOON_SCORE[index];
bouncing_sound_ = BALLOON_BOUNCING_SOUND[index];
popping_sound_ = BALLOON_POPPING_SOUND[index];
const int index = static_cast<int>(size_);
h_ = w_ = BALLOON_SIZE[index];
power_ = BALLOON_POWER[index];
menace_ = BALLOON_MENACE[index];
score_ = BALLOON_SCORE[index];
bouncing_sound_ = BALLOON_BOUNCING_SOUND[index];
popping_sound_ = BALLOON_POPPING_SOUND[index];
break;
}
break;
}
case BalloonType::POWERBALL:
{
constexpr int index = 3;
h_ = w_ = BALLOON_SIZE[4];
bouncing_sound_ = BALLOON_BOUNCING_SOUND[3];
popping_sound_ = "power_ball_explosion.wav";
power_ = score_ = menace_ = 0;
case BalloonType::POWERBALL: {
constexpr int index = 3;
h_ = w_ = BALLOON_SIZE[4];
bouncing_sound_ = BALLOON_BOUNCING_SOUND[3];
popping_sound_ = "power_ball_explosion.wav";
power_ = score_ = menace_ = 0;
vy_ = 0;
max_vy_ = 3.0f;
gravity_ = param.balloon.settings.at(index).grav;
default_vy_ = param.balloon.settings.at(index).vel;
vy_ = 0;
max_vy_ = 3.0f;
gravity_ = param.balloon.settings.at(index).grav;
default_vy_ = param.balloon.settings.at(index).vel;
sprite_->setRotate(creation_timer <= 0);
sprite_->setRotateAmount(vx_ > 0.0f ? 2.0 : -2.0);
sprite_->setRotate(creation_timer <= 0);
sprite_->setRotateAmount(vx_ > 0.0f ? 2.0 : -2.0);
break;
}
break;
}
default:
break;
}
default:
break;
}
// Configura el sprite
sprite_->setWidth(w_);
sprite_->setHeight(h_);
shiftSprite();
// Configura el sprite
sprite_->setWidth(w_);
sprite_->setHeight(h_);
shiftSprite();
// Alinea el circulo de colisión con el objeto
collider_.r = w_ / 2;
shiftColliders();
// Alinea el circulo de colisión con el objeto
collider_.r = w_ / 2;
shiftColliders();
// Establece la animación a usar
setAnimation();
// Establece la animación a usar
setAnimation();
}
// Centra el globo en la posición X
void Balloon::alignTo(int x)
{
x_ = static_cast<float>(x - (w_ / 2));
const int min_x = play_area_.x;
const int max_x = play_area_.w - w_;
x_ = std::clamp(x_, static_cast<float>(min_x), static_cast<float>(max_x));
void Balloon::alignTo(int x) {
x_ = static_cast<float>(x - (w_ / 2));
const int min_x = play_area_.x;
const int max_x = play_area_.w - w_;
x_ = std::clamp(x_, static_cast<float>(min_x), static_cast<float>(max_x));
}
// Pinta el globo en la pantalla
void Balloon::render()
{
if (type_ == BalloonType::POWERBALL)
{
// Renderiza el fondo azul
{
auto sp = std::make_unique<Sprite>(sprite_->getTexture(), sprite_->getPosition());
sp->setSpriteClip(0, 0, BALLOON_SIZE[4], BALLOON_SIZE[4]);
sp->render();
}
void Balloon::render() {
if (type_ == BalloonType::POWERBALL) {
// Renderiza el fondo azul
{
auto sp = std::make_unique<Sprite>(sprite_->getTexture(), sprite_->getPosition());
sp->setSpriteClip(0, 0, BALLOON_SIZE[4], BALLOON_SIZE[4]);
sp->render();
}
// Renderiza la estrella
if (!invulnerable_)
{
SDL_FPoint p = {24.0f, 24.0f};
sprite_->setRotatingCenter(p);
sprite_->render();
}
// Renderiza la estrella
if (!invulnerable_) {
SDL_FPoint p = {24.0f, 24.0f};
sprite_->setRotatingCenter(p);
sprite_->render();
}
// Añade la máscara del borde y los reflejos
{
auto sp = std::make_unique<Sprite>(sprite_->getTexture(), sprite_->getPosition());
sp->setSpriteClip(BALLOON_SIZE[4] * 2, 0, BALLOON_SIZE[4], BALLOON_SIZE[4]);
sp->render();
}
}
else
{
// Renderizado para el resto de globos
if (isBeingCreated())
{
// Renderizado con transparencia
sprite_->getTexture()->setAlpha(255 - (int)((float)creation_counter_ * (255.0f / (float)creation_counter_ini_)));
sprite_->render();
sprite_->getTexture()->setAlpha(255);
}
else
{
// Renderizado normal
sprite_->render();
}
}
// Añade la máscara del borde y los reflejos
{
auto sp = std::make_unique<Sprite>(sprite_->getTexture(), sprite_->getPosition());
sp->setSpriteClip(BALLOON_SIZE[4] * 2, 0, BALLOON_SIZE[4], BALLOON_SIZE[4]);
sp->render();
}
} else {
// Renderizado para el resto de globos
if (isBeingCreated()) {
// Renderizado con transparencia
sprite_->getTexture()->setAlpha(255 - (int)((float)creation_counter_ * (255.0f / (float)creation_counter_ini_)));
sprite_->render();
sprite_->getTexture()->setAlpha(255);
} else {
// Renderizado normal
sprite_->render();
}
}
}
// Actualiza la posición y estados del globo
void Balloon::move()
{
// Comprueba si se puede mover
if (!isStopped())
{
// Mueve el globo en horizontal
x_ += vx_ * speed_;
void Balloon::move() {
// Comprueba si se puede mover
if (!isStopped()) {
// Mueve el globo en horizontal
x_ += vx_ * speed_;
// Colisión en las partes laterales de la zona de juego
const int clip = 2;
const float min_x = play_area_.x - clip;
const float max_x = play_area_.x + play_area_.w - w_ + clip;
if (x_ < min_x || x_ > max_x)
{
if (bouncing_sound_enabled_)
playSound(bouncing_sound_);
x_ = std::clamp(x_, min_x, max_x);
vx_ = -vx_;
// Activa el efecto de rebote o invierte la rotación
if (type_ == BalloonType::POWERBALL)
{
sprite_->switchRotate();
}
else
{
enableBounce();
}
}
// Colisión en las partes laterales de la zona de juego
const int clip = 2;
const float min_x = play_area_.x - clip;
const float max_x = play_area_.x + play_area_.w - w_ + clip;
if (x_ < min_x || x_ > max_x) {
if (bouncing_sound_enabled_)
playSound(bouncing_sound_);
x_ = std::clamp(x_, min_x, max_x);
vx_ = -vx_;
// Activa el efecto de rebote o invierte la rotación
if (type_ == BalloonType::POWERBALL) {
sprite_->switchRotate();
} else {
enableBounce();
}
}
// Mueve el globo en vertical
y_ += vy_ * speed_;
// Mueve el globo en vertical
y_ += vy_ * speed_;
// Colisión en la parte superior solo si el globo va de subida
if (vy_ < 0)
{
const int min_y = play_area_.y;
if (y_ < min_y)
{
if (bouncing_sound_enabled_)
playSound(bouncing_sound_);
y_ = min_y;
vy_ = -vy_;
enableBounce();
}
}
// Colisión en la parte superior solo si el globo va de subida
if (vy_ < 0) {
const int min_y = play_area_.y;
if (y_ < min_y) {
if (bouncing_sound_enabled_)
playSound(bouncing_sound_);
y_ = min_y;
vy_ = -vy_;
enableBounce();
}
}
// Colisión en la parte inferior de la zona de juego
const int max_y = play_area_.y + play_area_.h - h_;
if (y_ > max_y)
{
if (bouncing_sound_enabled_)
playSound(bouncing_sound_);
y_ = max_y;
vy_ = -default_vy_;
if (type_ != BalloonType::POWERBALL)
{
enableBounce();
}
else
{
setInvulnerable(false);
}
}
// Colisión en la parte inferior de la zona de juego
const int max_y = play_area_.y + play_area_.h - h_;
if (y_ > max_y) {
if (bouncing_sound_enabled_)
playSound(bouncing_sound_);
y_ = max_y;
vy_ = -default_vy_;
if (type_ != BalloonType::POWERBALL) {
enableBounce();
} else {
setInvulnerable(false);
}
}
/*
/*
Para aplicar la gravedad, el diseño original la aplicaba en cada iteración del bucle
Al añadir el modificador de velocidad se reduce la distancia que recorre el objeto y por
tanto recibe mas gravedad. Para solucionarlo se va a aplicar la gravedad cuando se haya
recorrido una distancia igual a la velocidad en Y, que era el cálculo inicial
Para aplicar la gravedad, el diseño original la aplicaba en cada iteración del bucle
Al añadir el modificador de velocidad se reduce la distancia que recorre el objeto y por
tanto recibe mas gravedad. Para solucionarlo se va a aplicar la gravedad cuando se haya
recorrido una distancia igual a la velocidad en Y, que era el cálculo inicial
*/
*/
// Incrementa la variable que calcula la distancia acumulada en Y
travel_y_ += speed_;
// Incrementa la variable que calcula la distancia acumulada en Y
travel_y_ += speed_;
// Si la distancia acumulada en Y es igual a la velocidad, se aplica la gravedad
if (travel_y_ >= 1.0f)
{
// Quita el excedente
travel_y_ -= 1.0f;
// Si la distancia acumulada en Y es igual a la velocidad, se aplica la gravedad
if (travel_y_ >= 1.0f) {
// Quita el excedente
travel_y_ -= 1.0f;
// Aplica la gravedad al objeto sin pasarse de una velocidad máxima
vy_ += gravity_;
}
}
// Aplica la gravedad al objeto sin pasarse de una velocidad máxima
vy_ += gravity_;
}
}
}
// Actualiza al globo a su posicion, animación y controla los contadores
void Balloon::update()
{
move();
updateState();
updateBounce();
shiftSprite();
shiftColliders();
sprite_->update();
++counter_;
void Balloon::update() {
move();
updateState();
updateBounce();
shiftSprite();
shiftColliders();
sprite_->update();
++counter_;
}
// Actualiza los estados del globo
void Balloon::updateState()
{
// Si se está creando
if (isBeingCreated())
{
// Actualiza el valor de las variables
stop();
setInvulnerable(true);
void Balloon::updateState() {
// Si se está creando
if (isBeingCreated()) {
// Actualiza el valor de las variables
stop();
setInvulnerable(true);
if (creation_counter_ > 0)
{
// Desplaza lentamente el globo hacia abajo y hacia un lado
if (creation_counter_ % 10 == 0)
{
y_++;
x_ += vx_;
if (creation_counter_ > 0) {
// Desplaza lentamente el globo hacia abajo y hacia un lado
if (creation_counter_ % 10 == 0) {
y_++;
x_ += vx_;
// Comprueba no se salga por los laterales
const int min_x = play_area_.x;
const int max_x = play_area_.w - w_;
// Comprueba no se salga por los laterales
const int min_x = play_area_.x;
const int max_x = play_area_.w - w_;
if (x_ < min_x || x_ > max_x)
{
// Corrige y cambia el sentido de la velocidad
x_ -= vx_;
vx_ = -vx_;
}
}
--creation_counter_;
}
if (x_ < min_x || x_ > max_x) {
// Corrige y cambia el sentido de la velocidad
x_ -= vx_;
vx_ = -vx_;
}
}
--creation_counter_;
}
else
{
// El contador ha llegado a cero
being_created_ = false;
start();
setInvulnerable(false);
setAnimation();
}
}
else {
// El contador ha llegado a cero
being_created_ = false;
start();
setInvulnerable(false);
setAnimation();
}
}
}
// Establece la animación correspondiente al estado
void Balloon::setAnimation()
{
std::string creating_animation;
std::string normal_animation;
void Balloon::setAnimation() {
std::string creating_animation;
std::string normal_animation;
switch (type_)
{
case BalloonType::POWERBALL:
creating_animation = "powerball";
normal_animation = "powerball";
break;
case BalloonType::FLOATER:
creating_animation = param.balloon.color.at(2);
normal_animation = param.balloon.color.at(3);
break;
default:
creating_animation = param.balloon.color.at(0);
normal_animation = param.balloon.color.at(1);
break;
}
switch (type_) {
case BalloonType::POWERBALL:
creating_animation = "powerball";
normal_animation = "powerball";
break;
case BalloonType::FLOATER:
creating_animation = param.balloon.color.at(2);
normal_animation = param.balloon.color.at(3);
break;
default:
creating_animation = param.balloon.color.at(0);
normal_animation = param.balloon.color.at(1);
break;
}
// Establece el frame de animación
if (use_reversed_colors_)
sprite_->setCurrentAnimation(creating_animation);
else
sprite_->setCurrentAnimation(isBeingCreated() ? creating_animation : normal_animation);
// Establece el frame de animación
if (use_reversed_colors_)
sprite_->setCurrentAnimation(creating_animation);
else
sprite_->setCurrentAnimation(isBeingCreated() ? creating_animation : normal_animation);
}
// Detiene el globo
void Balloon::stop()
{
stopped_ = true;
if (isPowerBall())
{
sprite_->setRotate(!stopped_);
}
void Balloon::stop() {
stopped_ = true;
if (isPowerBall()) {
sprite_->setRotate(!stopped_);
}
}
// Pone el globo en movimiento
void Balloon::start()
{
stopped_ = false;
if (isPowerBall())
{
sprite_->setRotate(!stopped_);
}
void Balloon::start() {
stopped_ = false;
if (isPowerBall()) {
sprite_->setRotate(!stopped_);
}
}
// Alinea el circulo de colisión con la posición del objeto globo
void Balloon::shiftColliders()
{
collider_.x = static_cast<int>(x_) + collider_.r;
collider_.y = static_cast<int>(y_) + collider_.r;
void Balloon::shiftColliders() {
collider_.x = static_cast<int>(x_) + collider_.r;
collider_.y = static_cast<int>(y_) + collider_.r;
}
// Alinea el sprite con la posición del objeto globo
void Balloon::shiftSprite()
{
sprite_->setPosX(x_);
sprite_->setPosY(y_);
void Balloon::shiftSprite() {
sprite_->setPosX(x_);
sprite_->setPosY(y_);
}
// Establece el nivel de zoom del sprite
void Balloon::zoomSprite()
{
sprite_->setZoomW(bouncing_.zoomW);
sprite_->setZoomH(bouncing_.zoomH);
void Balloon::zoomSprite() {
sprite_->setZoomW(bouncing_.zoomW);
sprite_->setZoomH(bouncing_.zoomH);
}
// Activa el efecto
void Balloon::enableBounce()
{
bouncing_.enabled = true;
bouncing_.reset();
zoomSprite();
void Balloon::enableBounce() {
bouncing_.enabled = true;
bouncing_.reset();
zoomSprite();
}
// Detiene el efecto
void Balloon::disableBounce()
{
bouncing_.enabled = false;
bouncing_.reset();
zoomSprite();
void Balloon::disableBounce() {
bouncing_.enabled = false;
bouncing_.reset();
zoomSprite();
}
// Aplica el efecto
void Balloon::updateBounce()
{
if (bouncing_.enabled)
{
const int index = bouncing_.counter / bouncing_.speed;
bouncing_.zoomW = bouncing_.w[index];
bouncing_.zoomH = bouncing_.h[index];
void Balloon::updateBounce() {
if (bouncing_.enabled) {
const int index = bouncing_.counter / bouncing_.speed;
bouncing_.zoomW = bouncing_.w[index];
bouncing_.zoomH = bouncing_.h[index];
zoomSprite();
zoomSprite();
if (++bouncing_.counter / bouncing_.speed >= MAX_BOUNCE)
{
disableBounce();
}
}
if (++bouncing_.counter / bouncing_.speed >= MAX_BOUNCE) {
disableBounce();
}
}
}
// Pone el color alternativo en el globo
void Balloon::useReverseColor()
{
if (!isBeingCreated())
{
use_reversed_colors_ = true;
setAnimation();
}
void Balloon::useReverseColor() {
if (!isBeingCreated()) {
use_reversed_colors_ = true;
setAnimation();
}
}
// Pone el color normal en el globo
void Balloon::useNormalColor()
{
use_reversed_colors_ = false;
setAnimation();
void Balloon::useNormalColor() {
use_reversed_colors_ = false;
setAnimation();
}
// Reproduce sonido
void Balloon::playSound(const std::string &name)
{
if (!sound_enabled_)
return;
void Balloon::playSound(const std::string &name) {
if (!sound_enabled_)
return;
static auto audio = Audio::get();
audio->playSound(name);
static auto audio = Audio::get();
audio->playSound(name);
}
// Explota el globo
void Balloon::pop(bool should_sound)
{
if (should_sound)
{
if (poping_sound_enabled_)
playSound(popping_sound_);
}
void Balloon::pop(bool should_sound) {
if (should_sound) {
if (poping_sound_enabled_)
playSound(popping_sound_);
}
enabled_ = false;
enabled_ = false;
}

View File

@@ -1,17 +1,18 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint8, Uint16, SDL_FRect, Uint32
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para Uint8, Uint16, SDL_FRect, Uint32
#include "animated_sprite.h" // Para AnimatedSprite
#include "utils.h" // Para Circle
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include "animated_sprite.h" // Para AnimatedSprite
#include "utils.h" // Para Circle
class Texture;
// --- Constantes relacionadas con globos ---
constexpr int MAX_BOUNCE = 10; // Cantidad de elementos del vector de deformación
constexpr int MAX_BOUNCE = 10; // Cantidad de elementos del vector de deformación
constexpr int BALLOON_SCORE[] = {50, 100, 200, 400};
constexpr int BALLOON_POWER[] = {1, 3, 7, 15};
@@ -20,19 +21,17 @@ constexpr int BALLOON_SIZE[] = {10, 16, 26, 48, 49};
const std::string BALLOON_BOUNCING_SOUND[] = {"bubble1.wav", "bubble2.wav", "bubble3.wav", "bubble4.wav"};
const std::string BALLOON_POPPING_SOUND[] = {"balloon1.wav", "balloon2.wav", "balloon3.wav", "balloon4.wav"};
enum class BalloonSize : Uint8
{
SIZE1 = 0,
SIZE2 = 1,
SIZE3 = 2,
SIZE4 = 3,
enum class BalloonSize : Uint8 {
SIZE1 = 0,
SIZE2 = 1,
SIZE3 = 2,
SIZE4 = 3,
};
enum class BalloonType : Uint8
{
BALLOON = 0,
FLOATER = 1,
POWERBALL = 2,
enum class BalloonType : Uint8 {
BALLOON = 0,
FLOATER = 1,
POWERBALL = 2,
};
constexpr float BALLOON_VELX_POSITIVE = 0.7f;
@@ -48,133 +47,130 @@ constexpr int POWERBALL_SCREENPOWER_MINIMUM = 10;
constexpr int POWERBALL_COUNTER = 8;
// --- Clase Balloon ---
class Balloon
{
public:
// --- Constructores y destructor ---
Balloon(
float x,
float y,
BalloonType type,
BalloonSize size,
float vel_x,
float speed,
Uint16 creation_timer,
SDL_FRect play_area,
std::shared_ptr<Texture> texture,
const std::vector<std::string> &animation);
~Balloon() = default;
class Balloon {
public:
// --- Constructores y destructor ---
Balloon(
float x,
float y,
BalloonType type,
BalloonSize size,
float vel_x,
float speed,
Uint16 creation_timer,
SDL_FRect play_area,
std::shared_ptr<Texture> texture,
const std::vector<std::string> &animation);
~Balloon() = default;
// --- Métodos principales ---
void alignTo(int x); // Centra el globo en la posición X
void render(); // Pinta el globo en la pantalla
void move(); // Actualiza la posición y estados del globo
void update(); // Actualiza el globo (posición, animación, contadores)
void stop(); // Detiene el globo
void start(); // Pone el globo en movimiento
void pop(bool should_sound = false); // Explota el globo
// --- Métodos principales ---
void alignTo(int x); // Centra el globo en la posición X
void render(); // Pinta el globo en la pantalla
void move(); // Actualiza la posición y estados del globo
void update(); // Actualiza el globo (posición, animación, contadores)
void stop(); // Detiene el globo
void start(); // Pone el globo en movimiento
void pop(bool should_sound = false); // Explota el globo
// --- Métodos de color ---
void useReverseColor(); // Pone el color alternativo en el globo
void useNormalColor(); // Pone el color normal en el globo
// --- Métodos de color ---
void useReverseColor(); // Pone el color alternativo en el globo
void useNormalColor(); // Pone el color normal en el globo
// --- Getters ---
float getPosX() const { return x_; }
float getPosY() const { return y_; }
int getWidth() const { return w_; }
int getHeight() const { return h_; }
BalloonSize getSize() const { return size_; }
BalloonType getType() const { return type_; }
Uint16 getScore() const { return score_; }
Circle &getCollider() { return collider_; }
Uint8 getMenace() const { return isEnabled() ? menace_ : 0; }
Uint8 getPower() const { return power_; }
bool isStopped() const { return stopped_; }
bool isPowerBall() const { return type_ == BalloonType::POWERBALL; }
bool isInvulnerable() const { return invulnerable_; }
bool isBeingCreated() const { return being_created_; }
bool isEnabled() const { return enabled_; }
bool isUsingReversedColor() { return use_reversed_colors_; }
bool canBePopped() const { return !isBeingCreated(); }
// --- Getters ---
float getPosX() const { return x_; }
float getPosY() const { return y_; }
int getWidth() const { return w_; }
int getHeight() const { return h_; }
BalloonSize getSize() const { return size_; }
BalloonType getType() const { return type_; }
Uint16 getScore() const { return score_; }
Circle &getCollider() { return collider_; }
Uint8 getMenace() const { return isEnabled() ? menace_ : 0; }
Uint8 getPower() const { return power_; }
bool isStopped() const { return stopped_; }
bool isPowerBall() const { return type_ == BalloonType::POWERBALL; }
bool isInvulnerable() const { return invulnerable_; }
bool isBeingCreated() const { return being_created_; }
bool isEnabled() const { return enabled_; }
bool isUsingReversedColor() { return use_reversed_colors_; }
bool canBePopped() const { return !isBeingCreated(); }
// --- Setters ---
void setVelY(float vel_y) { vy_ = vel_y; }
void setSpeed(float speed) { speed_ = speed; }
void setInvulnerable(bool value) { invulnerable_ = value; }
void setBouncingSound(bool value) { bouncing_sound_enabled_ = value; }
void setPoppingSound(bool value) { poping_sound_enabled_ = value; }
void setSound(bool value) { sound_enabled_ = value; }
// --- Setters ---
void setVelY(float vel_y) { vy_ = vel_y; }
void setSpeed(float speed) { speed_ = speed; }
void setInvulnerable(bool value) { invulnerable_ = value; }
void setBouncingSound(bool value) { bouncing_sound_enabled_ = value; }
void setPoppingSound(bool value) { poping_sound_enabled_ = value; }
void setSound(bool value) { sound_enabled_ = value; }
private:
// --- Estructura para el efecto de rebote ---
struct Bouncing
{
bool enabled = false; // Si el efecto está activo
Uint8 counter = 0; // Contador para el efecto
Uint8 speed = 2; // Velocidad del efecto
float zoomW = 1.0f; // Zoom en anchura
float zoomH = 1.0f; // Zoom en altura
float despX = 0.0f; // Desplazamiento X antes de pintar
float despY = 0.0f; // Desplazamiento Y antes de pintar
private:
// --- Estructura para el efecto de rebote ---
struct Bouncing {
bool enabled = false; // Si el efecto está activo
Uint8 counter = 0; // Contador para el efecto
Uint8 speed = 2; // Velocidad del efecto
float zoomW = 1.0f; // Zoom en anchura
float zoomH = 1.0f; // Zoom en altura
float despX = 0.0f; // Desplazamiento X antes de pintar
float despY = 0.0f; // Desplazamiento Y antes de pintar
float w[MAX_BOUNCE] = {1.10f, 1.05f, 1.00f, 0.95f, 0.90f, 0.95f, 1.00f, 1.02f, 1.05f, 1.02f}; // Zoom ancho
float h[MAX_BOUNCE] = {0.90f, 0.95f, 1.00f, 1.05f, 1.10f, 1.05f, 1.00f, 0.98f, 0.95f, 0.98f}; // Zoom alto
float w[MAX_BOUNCE] = {1.10f, 1.05f, 1.00f, 0.95f, 0.90f, 0.95f, 1.00f, 1.02f, 1.05f, 1.02f}; // Zoom ancho
float h[MAX_BOUNCE] = {0.90f, 0.95f, 1.00f, 1.05f, 1.10f, 1.05f, 1.00f, 0.98f, 0.95f, 0.98f}; // Zoom alto
Bouncing() = default;
void reset()
{
counter = 0;
zoomW = 1.0f;
zoomH = 1.0f;
despX = 0.0f;
despY = 0.0f;
}
} bouncing_;
Bouncing() = default;
void reset() {
counter = 0;
zoomW = 1.0f;
zoomH = 1.0f;
despX = 0.0f;
despY = 0.0f;
}
} bouncing_;
// --- Objetos y punteros ---
std::unique_ptr<AnimatedSprite> sprite_; // Sprite del objeto globo
// --- Objetos y punteros ---
std::unique_ptr<AnimatedSprite> sprite_; // Sprite del objeto globo
// --- Variables de estado y físicas ---
float x_; // Posición X
float y_; // Posición Y
float w_; // Ancho
float h_; // Alto
float vx_; // Velocidad X
float vy_; // Velocidad Y
float gravity_; // Aceleración en Y
float default_vy_; // Velocidad inicial al rebotar
float max_vy_; // Máxima velocidad en Y
bool being_created_; // Si el globo se está creando
bool enabled_ = true; // Si el globo está activo
bool invulnerable_; // Si el globo es invulnerable
bool stopped_; // Si el globo está parado
bool use_reversed_colors_ = false; // Si se usa el color alternativo
Circle collider_; // Círculo de colisión
Uint16 creation_counter_; // Temporizador de creación
Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación
Uint16 score_; // Puntos al destruir el globo
BalloonType type_; // Tipo de globo
BalloonSize size_; // Tamaño de globo
Uint8 menace_; // Amenaza que genera el globo
Uint32 counter_ = 0; // Contador interno
float travel_y_ = 1.0f; // Distancia a recorrer en Y antes de aplicar gravedad
float speed_; // Velocidad del globo
Uint8 power_; // Poder que alberga el globo
SDL_FRect play_area_; // Zona de movimiento del globo
std::string bouncing_sound_; // Archivo de sonido al rebotar
std::string popping_sound_; // Archivo de sonido al explotar
bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar
bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar
bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido
// --- Variables de estado y físicas ---
float x_; // Posición X
float y_; // Posición Y
float w_; // Ancho
float h_; // Alto
float vx_; // Velocidad X
float vy_; // Velocidad Y
float gravity_; // Aceleración en Y
float default_vy_; // Velocidad inicial al rebotar
float max_vy_; // Máxima velocidad en Y
bool being_created_; // Si el globo se está creando
bool enabled_ = true; // Si el globo está activo
bool invulnerable_; // Si el globo es invulnerable
bool stopped_; // Si el globo está parado
bool use_reversed_colors_ = false; // Si se usa el color alternativo
Circle collider_; // Círculo de colisión
Uint16 creation_counter_; // Temporizador de creación
Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación
Uint16 score_; // Puntos al destruir el globo
BalloonType type_; // Tipo de globo
BalloonSize size_; // Tamaño de globo
Uint8 menace_; // Amenaza que genera el globo
Uint32 counter_ = 0; // Contador interno
float travel_y_ = 1.0f; // Distancia a recorrer en Y antes de aplicar gravedad
float speed_; // Velocidad del globo
Uint8 power_; // Poder que alberga el globo
SDL_FRect play_area_; // Zona de movimiento del globo
std::string bouncing_sound_; // Archivo de sonido al rebotar
std::string popping_sound_; // Archivo de sonido al explotar
bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar
bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar
bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido
// --- Métodos internos ---
void shiftColliders(); // Alinea el círculo de colisión
void shiftSprite(); // Alinea el sprite
void zoomSprite(); // Establece el nivel de zoom del sprite
void enableBounce(); // Activa el efecto de rebote
void disableBounce(); // Detiene el efecto de rebote
void updateBounce(); // Aplica el efecto de rebote
void updateState(); // Actualiza los estados del globo
void setAnimation(); // Establece la animación correspondiente
void playSound(const std::string &name); // Reproduce sonido
// --- Métodos internos ---
void shiftColliders(); // Alinea el círculo de colisión
void shiftSprite(); // Alinea el sprite
void zoomSprite(); // Establece el nivel de zoom del sprite
void enableBounce(); // Activa el efecto de rebote
void disableBounce(); // Detiene el efecto de rebote
void updateBounce(); // Aplica el efecto de rebote
void updateState(); // Actualiza los estados del globo
void setAnimation(); // Establece la animación correspondiente
void playSound(const std::string &name); // Reproduce sonido
};

View File

@@ -1,457 +1,377 @@
#include "balloon_formations.h"
#include "balloon.h" // Para BALLOON_VELX_NEGATIVE, BALLOON_VELX_POSITIVE
#include "param.h" // Para param
#include "utils.h" // Para ParamGame, Param, Zone, BLOCK
#include "balloon.h" // Para BALLOON_VELX_NEGATIVE, BALLOON_VELX_POSITIVE
#include "param.h" // Para param
#include "utils.h" // Para ParamGame, Param, Zone, BLOCK
void BalloonFormations::initBalloonFormations()
{
constexpr int y4 = -BLOCK;
const int x4_0 = param.game.play_area.rect.x;
const int x4_100 = param.game.play_area.rect.w - BALLOON_SIZE[3];
void BalloonFormations::initBalloonFormations() {
constexpr int y4 = -BLOCK;
const int x4_0 = param.game.play_area.rect.x;
const int x4_100 = param.game.play_area.rect.w - BALLOON_SIZE[3];
constexpr int y3 = -BLOCK;
const int x3_0 = param.game.play_area.rect.x;
const int x3_100 = param.game.play_area.rect.w - BALLOON_SIZE[2];
constexpr int y3 = -BLOCK;
const int x3_0 = param.game.play_area.rect.x;
const int x3_100 = param.game.play_area.rect.w - BALLOON_SIZE[2];
constexpr int y2 = -BLOCK;
const int x2_0 = param.game.play_area.rect.x;
const int x2_100 = param.game.play_area.rect.w - BALLOON_SIZE[1];
constexpr int y2 = -BLOCK;
const int x2_0 = param.game.play_area.rect.x;
const int x2_100 = param.game.play_area.rect.w - BALLOON_SIZE[1];
constexpr int y1 = -BLOCK;
const int x1_0 = param.game.play_area.rect.x;
const int x1_50 = param.game.play_area.center_x - (BALLOON_SIZE[0] / 2);
const int x1_100 = param.game.play_area.rect.w - BALLOON_SIZE[0];
constexpr int y1 = -BLOCK;
const int x1_0 = param.game.play_area.rect.x;
const int x1_50 = param.game.play_area.center_x - (BALLOON_SIZE[0] / 2);
const int x1_100 = param.game.play_area.rect.w - BALLOON_SIZE[0];
balloon_formation_.reserve(NUMBER_OF_BALLOON_FORMATIONS);
balloon_formation_.reserve(NUMBER_OF_BALLOON_FORMATIONS);
constexpr int CREATION_TIME = 300;
constexpr int CREATION_TIME = 300;
// #00 - Dos enemigos BALLOON4 uno a cada extremo
{
std::vector<BalloonFormationParams> init_params = {
BalloonFormationParams(x4_0, y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME),
BalloonFormationParams(x4_100, y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME)};
balloon_formation_.emplace_back(2, init_params);
}
// #00 - Dos enemigos BALLOON4 uno a cada extremo
{
std::vector<BalloonFormationParams> init_params = {
BalloonFormationParams(x4_0, y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME),
BalloonFormationParams(x4_100, y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME)};
balloon_formation_.emplace_back(2, init_params);
}
// #01 - Dos enemigos BALLOON4 uno a cada cuarto. Ambos van hacia el centro
{
std::vector<BalloonFormationParams> init_params = {
BalloonFormationParams(param.game.play_area.first_quarter_x - (BALLOON_SIZE[3] / 2), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME),
BalloonFormationParams(param.game.play_area.third_quarter_x - (BALLOON_SIZE[3] / 2), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME)};
balloon_formation_.emplace_back(2, init_params);
}
// #01 - Dos enemigos BALLOON4 uno a cada cuarto. Ambos van hacia el centro
{
std::vector<BalloonFormationParams> init_params = {
BalloonFormationParams(param.game.play_area.first_quarter_x - (BALLOON_SIZE[3] / 2), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME),
BalloonFormationParams(param.game.play_area.third_quarter_x - (BALLOON_SIZE[3] / 2), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME)};
balloon_formation_.emplace_back(2, init_params);
}
// #02 - Cuatro enemigos BALLOON2 uno detrás del otro. A la izquierda y hacia el centro
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 4; ++i)
{
init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(4, init_params);
}
// #02 - Cuatro enemigos BALLOON2 uno detrás del otro. A la izquierda y hacia el centro
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 4; ++i) {
init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(4, init_params);
}
// #03 - Cuatro enemigos BALLOON2 uno detrás del otro. A la derecha y hacia el centro
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 4; ++i)
{
init_params.emplace_back(x2_100 - (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(4, init_params);
}
// #03 - Cuatro enemigos BALLOON2 uno detrás del otro. A la derecha y hacia el centro
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 4; ++i) {
init_params.emplace_back(x2_100 - (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(4, init_params);
}
// #04 - Tres enemigos BALLOON3. 0, 25, 50. Hacia la derecha
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i)
{
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #04 - Tres enemigos BALLOON3. 0, 25, 50. Hacia la derecha
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i) {
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #05 - Tres enemigos BALLOON3. 50, 75, 100. Hacia la izquierda
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i)
{
init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #05 - Tres enemigos BALLOON3. 50, 75, 100. Hacia la izquierda
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i) {
init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #06 - Tres enemigos BALLOON3. 0, 0, 0. Hacia la derecha
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i)
{
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #06 - Tres enemigos BALLOON3. 0, 0, 0. Hacia la derecha
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i) {
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #07 - Tres enemigos BALLOON3. 100, 100, 100. Hacia la izquierda
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i)
{
init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #07 - Tres enemigos BALLOON3. 100, 100, 100. Hacia la izquierda
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i) {
init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #08 - Seis enemigos BALLOON1. 0, 0, 0, 0, 0, 0. Hacia la derecha
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 6; ++i)
{
init_params.emplace_back(x1_0 + (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(6, init_params);
}
// #08 - Seis enemigos BALLOON1. 0, 0, 0, 0, 0, 0. Hacia la derecha
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 6; ++i) {
init_params.emplace_back(x1_0 + (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(6, init_params);
}
// #09 - Seis enemigos BALLOON1. 100, 100, 100, 100, 100, 100. Hacia la izquierda
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 6; ++i)
{
init_params.emplace_back(x1_100 - (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(6, init_params);
}
// #09 - Seis enemigos BALLOON1. 100, 100, 100, 100, 100, 100. Hacia la izquierda
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 6; ++i) {
init_params.emplace_back(x1_100 - (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(6, init_params);
}
// #10 - Tres enemigos BALLOON4 seguidos desde la izquierda. Hacia la derecha
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i)
{
init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME - (15 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #10 - Tres enemigos BALLOON4 seguidos desde la izquierda. Hacia la derecha
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i) {
init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME - (15 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #11 - Tres enemigos BALLOON4 seguidos desde la derecha. Hacia la izquierda
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i)
{
init_params.emplace_back(x4_100 - (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME - (15 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #11 - Tres enemigos BALLOON4 seguidos desde la derecha. Hacia la izquierda
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 3; ++i) {
init_params.emplace_back(x4_100 - (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME - (15 * i));
}
balloon_formation_.emplace_back(3, init_params);
}
// #12 - Seis enemigos BALLOON2 uno detrás del otro. A la izquierda y hacia el centro
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 6; ++i)
{
init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(6, init_params);
}
// #12 - Seis enemigos BALLOON2 uno detrás del otro. A la izquierda y hacia el centro
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 6; ++i) {
init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(6, init_params);
}
// #13 - Seis enemigos BALLOON2 uno detrás del otro. A la derecha y hacia el centro
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 6; ++i)
{
init_params.emplace_back(x2_100 - (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(6, init_params);
}
// #13 - Seis enemigos BALLOON2 uno detrás del otro. A la derecha y hacia el centro
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 6; ++i) {
init_params.emplace_back(x2_100 - (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(6, init_params);
}
// #14 - Cinco enemigos BALLOON3. Hacia la derecha. Separados
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 5; ++i)
{
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(5, init_params);
}
// #14 - Cinco enemigos BALLOON3. Hacia la derecha. Separados
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 5; ++i) {
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(5, init_params);
}
// #15 - Cinco enemigos BALLOON3. Hacia la izquierda. Separados
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 5; ++i)
{
init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(5, init_params);
}
// #15 - Cinco enemigos BALLOON3. Hacia la izquierda. Separados
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 5; ++i) {
init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(5, init_params);
}
// #16 - Cinco enemigos BALLOON3. Hacia la derecha. Juntos
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 5; ++i)
{
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(5, init_params);
}
// #16 - Cinco enemigos BALLOON3. Hacia la derecha. Juntos
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 5; ++i) {
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(5, init_params);
}
// #17 - Cinco enemigos BALLOON3. Hacia la izquierda. Juntos
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 5; ++i)
{
init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(5, init_params);
}
// #17 - Cinco enemigos BALLOON3. Hacia la izquierda. Juntos
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 5; ++i) {
init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(5, init_params);
}
// #18 - Doce enemigos BALLOON1. Hacia la derecha. Juntos
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 12; ++i)
{
init_params.emplace_back(x1_0 + (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(12, init_params);
}
// #18 - Doce enemigos BALLOON1. Hacia la derecha. Juntos
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 12; ++i) {
init_params.emplace_back(x1_0 + (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(12, init_params);
}
// #19 - Doce enemigos BALLOON1. Hacia la izquierda. Juntos
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 12; ++i)
{
init_params.emplace_back(x1_100 - (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(12, init_params);
}
// #19 - Doce enemigos BALLOON1. Hacia la izquierda. Juntos
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < 12; ++i) {
init_params.emplace_back(x1_100 - (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i));
}
balloon_formation_.emplace_back(12, init_params);
}
// #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 4 / 2;
for (int i = 0; i < 4; ++i)
{
if (i < half)
{
init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i));
}
else
{
init_params.emplace_back(x4_100 - ((i - half) * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i));
}
}
balloon_formation_.emplace_back(4, init_params);
}
// #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 4 / 2;
for (int i = 0; i < 4; ++i) {
if (i < half) {
init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i));
} else {
init_params.emplace_back(x4_100 - ((i - half) * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i));
}
}
balloon_formation_.emplace_back(4, init_params);
}
// #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 4 / 2;
for (int i = 0; i < 4; ++i)
{
if (i < half)
{
init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i));
}
else
{
init_params.emplace_back(x4_100 - ((i - half) * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i));
}
}
balloon_formation_.emplace_back(4, init_params);
}
// #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 4 / 2;
for (int i = 0; i < 4; ++i) {
if (i < half) {
init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i));
} else {
init_params.emplace_back(x4_100 - ((i - half) * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i));
}
}
balloon_formation_.emplace_back(4, init_params);
}
// #21 - Diez enemigos BALLOON2 uno detrás del otro. Izquierda/derecha. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 10 / 2;
for (int i = 0; i < 10; ++i)
{
if (i < half)
{
init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (3 * i));
}
else
{
init_params.emplace_back(x2_100 - ((i - half) * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (3 * (i - half)));
}
}
balloon_formation_.emplace_back(10, init_params);
}
// #21 - Diez enemigos BALLOON2 uno detrás del otro. Izquierda/derecha. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 10 / 2;
for (int i = 0; i < 10; ++i) {
if (i < half) {
init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (3 * i));
} else {
init_params.emplace_back(x2_100 - ((i - half) * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (3 * (i - half)));
}
}
balloon_formation_.emplace_back(10, init_params);
}
// #22 - Diez enemigos BALLOON3. Hacia la derecha/izquierda. Separados. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 10 / 2;
for (int i = 0; i < 10; ++i)
{
if (i < half)
{
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
else
{
init_params.emplace_back(x3_100 - ((i - half) * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * (i - half)));
}
}
balloon_formation_.emplace_back(10, init_params);
}
// #22 - Diez enemigos BALLOON3. Hacia la derecha/izquierda. Separados. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 10 / 2;
for (int i = 0; i < 10; ++i) {
if (i < half) {
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
} else {
init_params.emplace_back(x3_100 - ((i - half) * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * (i - half)));
}
}
balloon_formation_.emplace_back(10, init_params);
}
// #23 - Diez enemigos BALLOON3. Hacia la derecha. Juntos. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 10 / 2;
for (int i = 0; i < 10; ++i)
{
if (i < half)
{
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
}
else
{
init_params.emplace_back(x3_100 - ((i - half) * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * (i - half)));
}
}
balloon_formation_.emplace_back(10, init_params);
}
// #23 - Diez enemigos BALLOON3. Hacia la derecha. Juntos. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 10 / 2;
for (int i = 0; i < 10; ++i) {
if (i < half) {
init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i));
} else {
init_params.emplace_back(x3_100 - ((i - half) * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * (i - half)));
}
}
balloon_formation_.emplace_back(10, init_params);
}
// #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 30 / 2;
for (int i = 0; i < 30; ++i)
{
if (i < half)
{
init_params.emplace_back(x1_50, y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME + (5 * i));
}
else
{
init_params.emplace_back(x1_50, y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME + (5 * (i - half)));
}
}
balloon_formation_.emplace_back(30, init_params);
}
// #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 30 / 2;
for (int i = 0; i < 30; ++i) {
if (i < half) {
init_params.emplace_back(x1_50, y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME + (5 * i));
} else {
init_params.emplace_back(x1_50, y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME + (5 * (i - half)));
}
}
balloon_formation_.emplace_back(30, init_params);
}
// #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 30 / 2;
for (int i = 0; i < 30; ++i)
{
if (i < half)
{
init_params.emplace_back(x1_50 + 20, y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (5 * i));
}
else
{
init_params.emplace_back(x1_50 - 20, y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (5 * (i - half)));
}
}
balloon_formation_.emplace_back(30, init_params);
}
// #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simétricos
{
std::vector<BalloonFormationParams> init_params;
const int half = 30 / 2;
for (int i = 0; i < 30; ++i) {
if (i < half) {
init_params.emplace_back(x1_50 + 20, y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (5 * i));
} else {
init_params.emplace_back(x1_50 - 20, y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (5 * (i - half)));
}
}
balloon_formation_.emplace_back(30, init_params);
}
// Reservar espacio para el vector
balloon_formation_.resize(100);
// Reservar espacio para el vector
balloon_formation_.resize(100);
// Crea las mismas formaciones pero con hexágonos a partir de la posición 50 del vector
for (int k = 0; k < 50; k++)
{
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < balloon_formation_.at(k).number_of_balloons; i++)
{
init_params.emplace_back(
balloon_formation_.at(k).init.at(i).x,
balloon_formation_.at(k).init.at(i).y,
balloon_formation_.at(k).init.at(i).vel_x,
BalloonType::FLOATER,
balloon_formation_.at(k).init.at(i).size,
balloon_formation_.at(k).init.at(i).creation_counter);
}
balloon_formation_.at(k + 50) = BalloonFormationUnit(balloon_formation_.at(k).number_of_balloons, init_params);
}
// Crea las mismas formaciones pero con hexágonos a partir de la posición 50 del vector
for (int k = 0; k < 50; k++) {
std::vector<BalloonFormationParams> init_params;
for (int i = 0; i < balloon_formation_.at(k).number_of_balloons; i++) {
init_params.emplace_back(
balloon_formation_.at(k).init.at(i).x,
balloon_formation_.at(k).init.at(i).y,
balloon_formation_.at(k).init.at(i).vel_x,
BalloonType::FLOATER,
balloon_formation_.at(k).init.at(i).size,
balloon_formation_.at(k).init.at(i).creation_counter);
}
balloon_formation_.at(k + 50) = BalloonFormationUnit(balloon_formation_.at(k).number_of_balloons, init_params);
}
// TEST
std::vector<BalloonFormationParams> test_params = {
{10, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE1, 200},
{50, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE2, 200},
{90, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE3, 200},
{140, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE4, 200}};
// TEST
std::vector<BalloonFormationParams> test_params = {
{10, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE1, 200},
{50, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE2, 200},
{90, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE3, 200},
{140, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE4, 200}};
balloon_formation_.at(99) = BalloonFormationUnit(4, test_params);
balloon_formation_.at(99) = BalloonFormationUnit(4, test_params);
}
// Inicializa los conjuntos de formaciones
void BalloonFormations::initBalloonFormationPools()
{
// Reserva espacio para cada pool de formaciones
balloon_formation_pool_.resize(NUMBER_OF_SETS_PER_POOL);
void BalloonFormations::initBalloonFormationPools() {
// Reserva espacio para cada pool de formaciones
balloon_formation_pool_.resize(NUMBER_OF_SETS_PER_POOL);
// Set #0
balloon_formation_pool_.at(0) = {
&balloon_formation_.at(0), &balloon_formation_.at(1), &balloon_formation_.at(2),
&balloon_formation_.at(3), &balloon_formation_.at(4), &balloon_formation_.at(5),
&balloon_formation_.at(6), &balloon_formation_.at(7), &balloon_formation_.at(8),
&balloon_formation_.at(9)};
// Set #0
balloon_formation_pool_.at(0) = {
&balloon_formation_.at(0), &balloon_formation_.at(1), &balloon_formation_.at(2), &balloon_formation_.at(3), &balloon_formation_.at(4), &balloon_formation_.at(5), &balloon_formation_.at(6), &balloon_formation_.at(7), &balloon_formation_.at(8), &balloon_formation_.at(9)};
// Set #1
balloon_formation_pool_.at(1) = {
&balloon_formation_.at(10), &balloon_formation_.at(11), &balloon_formation_.at(12),
&balloon_formation_.at(13), &balloon_formation_.at(14), &balloon_formation_.at(15),
&balloon_formation_.at(16), &balloon_formation_.at(17), &balloon_formation_.at(18),
&balloon_formation_.at(19)};
// Set #1
balloon_formation_pool_.at(1) = {
&balloon_formation_.at(10), &balloon_formation_.at(11), &balloon_formation_.at(12), &balloon_formation_.at(13), &balloon_formation_.at(14), &balloon_formation_.at(15), &balloon_formation_.at(16), &balloon_formation_.at(17), &balloon_formation_.at(18), &balloon_formation_.at(19)};
// Set #2
balloon_formation_pool_.at(2) = {
&balloon_formation_.at(0), &balloon_formation_.at(1), &balloon_formation_.at(2),
&balloon_formation_.at(3), &balloon_formation_.at(4), &balloon_formation_.at(55),
&balloon_formation_.at(56), &balloon_formation_.at(57), &balloon_formation_.at(58),
&balloon_formation_.at(59)};
// Set #2
balloon_formation_pool_.at(2) = {
&balloon_formation_.at(0), &balloon_formation_.at(1), &balloon_formation_.at(2), &balloon_formation_.at(3), &balloon_formation_.at(4), &balloon_formation_.at(55), &balloon_formation_.at(56), &balloon_formation_.at(57), &balloon_formation_.at(58), &balloon_formation_.at(59)};
// Set #3
balloon_formation_pool_.at(3) = {
&balloon_formation_.at(50), &balloon_formation_.at(51), &balloon_formation_.at(52),
&balloon_formation_.at(53), &balloon_formation_.at(54), &balloon_formation_.at(5),
&balloon_formation_.at(6), &balloon_formation_.at(7), &balloon_formation_.at(8),
&balloon_formation_.at(9)};
// Set #3
balloon_formation_pool_.at(3) = {
&balloon_formation_.at(50), &balloon_formation_.at(51), &balloon_formation_.at(52), &balloon_formation_.at(53), &balloon_formation_.at(54), &balloon_formation_.at(5), &balloon_formation_.at(6), &balloon_formation_.at(7), &balloon_formation_.at(8), &balloon_formation_.at(9)};
// Set #4
balloon_formation_pool_.at(4) = {
&balloon_formation_.at(60), &balloon_formation_.at(61), &balloon_formation_.at(62),
&balloon_formation_.at(63), &balloon_formation_.at(64), &balloon_formation_.at(65),
&balloon_formation_.at(66), &balloon_formation_.at(67), &balloon_formation_.at(68),
&balloon_formation_.at(69)};
// Set #4
balloon_formation_pool_.at(4) = {
&balloon_formation_.at(60), &balloon_formation_.at(61), &balloon_formation_.at(62), &balloon_formation_.at(63), &balloon_formation_.at(64), &balloon_formation_.at(65), &balloon_formation_.at(66), &balloon_formation_.at(67), &balloon_formation_.at(68), &balloon_formation_.at(69)};
// Set #5
balloon_formation_pool_.at(5) = {
&balloon_formation_.at(10), &balloon_formation_.at(61), &balloon_formation_.at(12),
&balloon_formation_.at(63), &balloon_formation_.at(14), &balloon_formation_.at(65),
&balloon_formation_.at(16), &balloon_formation_.at(67), &balloon_formation_.at(18),
&balloon_formation_.at(69)};
// Set #5
balloon_formation_pool_.at(5) = {
&balloon_formation_.at(10), &balloon_formation_.at(61), &balloon_formation_.at(12), &balloon_formation_.at(63), &balloon_formation_.at(14), &balloon_formation_.at(65), &balloon_formation_.at(16), &balloon_formation_.at(67), &balloon_formation_.at(18), &balloon_formation_.at(69)};
// Set #6
balloon_formation_pool_.at(6) = {
&balloon_formation_.at(60), &balloon_formation_.at(11), &balloon_formation_.at(62),
&balloon_formation_.at(13), &balloon_formation_.at(64), &balloon_formation_.at(15),
&balloon_formation_.at(66), &balloon_formation_.at(17), &balloon_formation_.at(68),
&balloon_formation_.at(19)};
// Set #6
balloon_formation_pool_.at(6) = {
&balloon_formation_.at(60), &balloon_formation_.at(11), &balloon_formation_.at(62), &balloon_formation_.at(13), &balloon_formation_.at(64), &balloon_formation_.at(15), &balloon_formation_.at(66), &balloon_formation_.at(17), &balloon_formation_.at(68), &balloon_formation_.at(19)};
// Set #7
balloon_formation_pool_.at(7) = {
&balloon_formation_.at(20), &balloon_formation_.at(21), &balloon_formation_.at(22),
&balloon_formation_.at(23), &balloon_formation_.at(24), &balloon_formation_.at(65),
&balloon_formation_.at(66), &balloon_formation_.at(67), &balloon_formation_.at(68),
&balloon_formation_.at(69)};
// Set #7
balloon_formation_pool_.at(7) = {
&balloon_formation_.at(20), &balloon_formation_.at(21), &balloon_formation_.at(22), &balloon_formation_.at(23), &balloon_formation_.at(24), &balloon_formation_.at(65), &balloon_formation_.at(66), &balloon_formation_.at(67), &balloon_formation_.at(68), &balloon_formation_.at(69)};
// Set #8
balloon_formation_pool_.at(8) = {
&balloon_formation_.at(70), &balloon_formation_.at(71), &balloon_formation_.at(72),
&balloon_formation_.at(73), &balloon_formation_.at(74), &balloon_formation_.at(15),
&balloon_formation_.at(16), &balloon_formation_.at(17), &balloon_formation_.at(18),
&balloon_formation_.at(19)};
// Set #8
balloon_formation_pool_.at(8) = {
&balloon_formation_.at(70), &balloon_formation_.at(71), &balloon_formation_.at(72), &balloon_formation_.at(73), &balloon_formation_.at(74), &balloon_formation_.at(15), &balloon_formation_.at(16), &balloon_formation_.at(17), &balloon_formation_.at(18), &balloon_formation_.at(19)};
// Set #9
balloon_formation_pool_.at(9) = {
&balloon_formation_.at(20), &balloon_formation_.at(21), &balloon_formation_.at(22),
&balloon_formation_.at(23), &balloon_formation_.at(24), &balloon_formation_.at(70),
&balloon_formation_.at(71), &balloon_formation_.at(72), &balloon_formation_.at(73),
&balloon_formation_.at(74)};
// Set #9
balloon_formation_pool_.at(9) = {
&balloon_formation_.at(20), &balloon_formation_.at(21), &balloon_formation_.at(22), &balloon_formation_.at(23), &balloon_formation_.at(24), &balloon_formation_.at(70), &balloon_formation_.at(71), &balloon_formation_.at(72), &balloon_formation_.at(73), &balloon_formation_.at(74)};
}

View File

@@ -2,7 +2,7 @@
#include <vector>
#include "balloon.h" // Para BALLOON_VELX_NEGATIVE, BALLOON_VELX_POSITIVE
#include "balloon.h" // Para BALLOON_VELX_NEGATIVE, BALLOON_VELX_POSITIVE
// --- Constantes de configuración ---
constexpr int NUMBER_OF_BALLOON_FORMATIONS = 100;
@@ -11,14 +11,13 @@ constexpr int NUMBER_OF_SETS_PER_POOL = 10;
constexpr int NUMBER_OF_STAGES = 10;
// --- Estructuras de datos ---
struct BalloonFormationParams
{
int 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 vel_x = 0.0f; // Velocidad inicial en el eje X
BalloonType type = BalloonType::BALLOON; // Tipo de globo
BalloonSize size = BalloonSize::SIZE1; // Tamaño de globo
int creation_counter = 0; // Temporizador para la creación del globo
struct BalloonFormationParams {
int 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 vel_x = 0.0f; // Velocidad inicial en el eje X
BalloonType type = BalloonType::BALLOON; // Tipo de globo
BalloonSize size = BalloonSize::SIZE1; // Tamaño de globo
int creation_counter = 0; // Temporizador para la creación del globo
// Constructor por defecto
BalloonFormationParams() = default;
@@ -28,10 +27,9 @@ struct BalloonFormationParams
: x(x_val), y(y_val), vel_x(vel_x_val), type(type_val), size(size_val), creation_counter(creation_counter_val) {}
};
struct BalloonFormationUnit
{
int number_of_balloons; // Cantidad de globos que forman la formación
std::vector<BalloonFormationParams> init; // Vector con todas las inicializaciones de los globos de la formación
struct BalloonFormationUnit {
int number_of_balloons; // Cantidad de globos que forman la formación
std::vector<BalloonFormationParams> init; // Vector con todas las inicializaciones de los globos de la formación
// Constructor con parámetros
BalloonFormationUnit(int num_balloons, const std::vector<BalloonFormationParams> &init_params)
@@ -44,12 +42,10 @@ struct BalloonFormationUnit
using BalloonFormationPool = std::vector<const BalloonFormationUnit *>;
// --- Clase BalloonFormations ---
class BalloonFormations
{
public:
class BalloonFormations {
public:
// --- Constructor y destructor ---
BalloonFormations()
{
BalloonFormations() {
initBalloonFormations();
initBalloonFormationPools();
}
@@ -60,10 +56,10 @@ public:
const BalloonFormationUnit &getSet(int pool, int set) { return *balloon_formation_pool_.at(pool).at(set); }
const BalloonFormationUnit &getSet(int set) const { return balloon_formation_.at(set); }
private:
private:
// --- Datos ---
std::vector<BalloonFormationUnit> balloon_formation_; // Vector con todas las formaciones enemigas
std::vector<BalloonFormationPool> balloon_formation_pool_; // Conjuntos de formaciones enemigas
std::vector<BalloonFormationUnit> balloon_formation_; // Vector con todas las formaciones enemigas
std::vector<BalloonFormationPool> balloon_formation_pool_; // Conjuntos de formaciones enemigas
// --- Métodos internos de inicialización ---
void initBalloonFormations();

View File

@@ -1,17 +1,18 @@
#include "balloon_manager.h"
#include <stdlib.h> // Para rand
#include <algorithm> // Para remove_if
#include <numeric> // Para accumulate
#include <stdlib.h> // Para rand
#include "balloon.h" // Para Balloon, BALLOON_SCORE, BALLOON_VELX...
#include "balloon_formations.h" // Para BalloonFormationParams, BalloonForma...
#include "explosions.h" // Para Explosions
#include "param.h" // Para Param, ParamGame, param
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "stage.h" // Para addPower
#include "utils.h" // Para Zone, Color, flash_color
#include <algorithm> // Para remove_if
#include <numeric> // Para accumulate
#include "balloon.h" // Para Balloon, BALLOON_SCORE, BALLOON_VELX...
#include "balloon_formations.h" // Para BalloonFormationParams, BalloonForma...
#include "explosions.h" // Para Explosions
#include "param.h" // Para Param, ParamGame, param
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "stage.h" // Para addPower
#include "utils.h" // Para Zone, Color, flash_color
// Constructor
BalloonManager::BalloonManager()
@@ -19,8 +20,7 @@ BalloonManager::BalloonManager()
balloon_formations_(std::make_unique<BalloonFormations>()) { init(); }
// Inicializa
void BalloonManager::init()
{
void BalloonManager::init() {
// Texturas - Globos
balloon_textures_.emplace_back(Resource::get()->getTexture("balloon1.png"));
balloon_textures_.emplace_back(Resource::get()->getTexture("balloon2.png"));
@@ -55,10 +55,8 @@ void BalloonManager::init()
}
// Actualiza
void BalloonManager::update()
{
for (auto balloon : balloons_)
{
void BalloonManager::update() {
for (auto balloon : balloons_) {
balloon->update();
}
updateBalloonDeployCounter();
@@ -66,32 +64,25 @@ void BalloonManager::update()
}
// Renderiza los objetos
void BalloonManager::render()
{
for (auto &balloon : balloons_)
{
void BalloonManager::render() {
for (auto &balloon : balloons_) {
balloon->render();
}
explosions_->render();
}
// Crea una formación de enemigos
void BalloonManager::deployBalloonFormation(int stage)
{
void BalloonManager::deployBalloonFormation(int stage) {
// Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última
if (balloon_deploy_counter_ == 0)
{
if (balloon_deploy_counter_ == 0) {
// En este punto se decide entre crear una powerball o una formación enemiga
if ((rand() % 100 < 15) && (canPowerBallBeCreated()))
{
if ((rand() % 100 < 15) && (canPowerBallBeCreated())) {
// Crea una powerball
createPowerBall();
// Da un poco de margen para que se creen mas enemigos
balloon_deploy_counter_ = 10;
}
else
{
} else {
// Decrementa el contador de despliegues enemigos de la PowerBall
power_ball_counter_ = (power_ball_counter_ > 0) ? (power_ball_counter_ - 1) : 0;
@@ -99,8 +90,7 @@ void BalloonManager::deployBalloonFormation(int stage)
auto formation = rand() % 10;
// Evita repetir la ultima formación enemiga desplegada
if (formation == last_balloon_deploy_)
{
if (formation == last_balloon_deploy_) {
++formation %= 10;
}
@@ -108,8 +98,7 @@ void BalloonManager::deployBalloonFormation(int stage)
const auto set = balloon_formations_->getSet(stage, formation);
const auto num_enemies = set.number_of_balloons;
for (int i = 0; i < num_enemies; ++i)
{
for (int i = 0; i < num_enemies; ++i) {
auto p = set.init[i];
createBalloon(
p.x,
@@ -127,42 +116,34 @@ void BalloonManager::deployBalloonFormation(int stage)
}
// Crea una formación de enemigos específica
void BalloonManager::deploySet(int set_number)
{
void BalloonManager::deploySet(int set_number) {
const auto set = balloon_formations_->getSet(set_number);
const auto num_enemies = set.number_of_balloons;
for (int i = 0; i < num_enemies; ++i)
{
for (int i = 0; i < num_enemies; ++i) {
auto p = set.init[i];
createBalloon(p.x, p.y, p.type, p.size, p.vel_x, balloon_speed_, p.creation_counter);
}
}
// Crea una formación de enemigos específica
void BalloonManager::deploySet(int set_number, int y)
{
void BalloonManager::deploySet(int set_number, int y) {
const auto set = balloon_formations_->getSet(set_number);
const auto num_enemies = set.number_of_balloons;
for (int i = 0; i < num_enemies; ++i)
{
for (int i = 0; i < num_enemies; ++i) {
auto p = set.init[i];
createBalloon(p.x, y, p.type, p.size, p.vel_x, balloon_speed_, p.creation_counter);
}
}
// Vacia del vector de globos los globos que ya no sirven
void BalloonManager::freeBalloons()
{
auto it = std::remove_if(balloons_.begin(), balloons_.end(), [](const auto &balloon)
{ return !balloon->isEnabled(); });
void BalloonManager::freeBalloons() {
auto it = std::remove_if(balloons_.begin(), balloons_.end(), [](const auto &balloon) { return !balloon->isEnabled(); });
balloons_.erase(it, balloons_.end());
}
// Actualiza la variable enemyDeployCounter
void BalloonManager::updateBalloonDeployCounter()
{
if (balloon_deploy_counter_ > 0)
{
void BalloonManager::updateBalloonDeployCounter() {
if (balloon_deploy_counter_ > 0) {
--balloon_deploy_counter_;
}
}
@@ -171,17 +152,13 @@ void BalloonManager::updateBalloonDeployCounter()
bool BalloonManager::canPowerBallBeCreated() { return (!power_ball_enabled_) && (calculateScreenPower() > POWERBALL_SCREENPOWER_MINIMUM) && (power_ball_counter_ == 0); }
// Calcula el poder actual de los globos en pantalla
int BalloonManager::calculateScreenPower()
{
return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon)
{ return sum + (balloon->isEnabled() ? balloon->getPower() : 0); });
int BalloonManager::calculateScreenPower() {
return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getPower() : 0); });
}
// Crea un globo nuevo en el vector de globos
std::shared_ptr<Balloon> BalloonManager::createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer)
{
if (can_deploy_balloons_)
{
std::shared_ptr<Balloon> BalloonManager::createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer) {
if (can_deploy_balloons_) {
const int INDEX = static_cast<int>(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)));
balloons_.back()->setSound(sound_enabled_);
@@ -194,10 +171,8 @@ std::shared_ptr<Balloon> BalloonManager::createBalloon(float x, int y, BalloonTy
}
// Crea un globo a partir de otro globo
void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction)
{
if (can_deploy_balloons_)
{
void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction) {
if (can_deploy_balloons_) {
// Calcula parametros
const float VX = direction == "LEFT" ? BALLOON_VELX_NEGATIVE : BALLOON_VELX_POSITIVE;
const auto SIZE = static_cast<BalloonSize>(static_cast<int>(balloon->getSize()) - 1);
@@ -214,22 +189,18 @@ void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon,
b->setVelY(b->getType() == BalloonType::BALLOON ? -2.50f : BALLOON_VELX_NEGATIVE * 2.0f);
// Herencia de estados
if (balloon->isStopped())
{
if (balloon->isStopped()) {
b->stop();
}
if (balloon->isUsingReversedColor())
{
if (balloon->isUsingReversedColor()) {
b->useReverseColor();
}
}
}
// Crea una PowerBall
void BalloonManager::createPowerBall()
{
if (can_deploy_balloons_)
{
void BalloonManager::createPowerBall() {
if (can_deploy_balloons_) {
constexpr int VALUES = 6;
constexpr float POS_Y = -BALLOON_SIZE[4];
constexpr int CREATION_TIME = 0;
@@ -251,33 +222,26 @@ void BalloonManager::createPowerBall()
}
// Establece la velocidad de los globos
void BalloonManager::setBalloonSpeed(float speed)
{
void BalloonManager::setBalloonSpeed(float speed) {
balloon_speed_ = speed;
for (auto &balloon : balloons_)
{
for (auto &balloon : balloons_) {
balloon->setSpeed(speed);
}
}
// Explosiona un globo. Lo destruye y crea otros dos si es el caso
int BalloonManager::popBalloon(std::shared_ptr<Balloon> balloon)
{
int BalloonManager::popBalloon(std::shared_ptr<Balloon> balloon) {
Stage::addPower(1);
int score = 0;
if (balloon->getType() == BalloonType::POWERBALL)
{
if (balloon->getType() == BalloonType::POWERBALL) {
balloon->pop(true);
score = destroyAllBalloons();
power_ball_enabled_ = false;
balloon_deploy_counter_ = 20;
}
else
{
} else {
score = balloon->getScore();
if (balloon->getSize() != BalloonSize::SIZE1)
{
if (balloon->getSize() != BalloonSize::SIZE1) {
createChildBalloon(balloon, "LEFT");
createChildBalloon(balloon, "RIGHT");
}
@@ -291,28 +255,26 @@ int BalloonManager::popBalloon(std::shared_ptr<Balloon> balloon)
}
// Explosiona un globo. Lo destruye = no crea otros globos
int BalloonManager::destroyBalloon(std::shared_ptr<Balloon> &balloon)
{
int BalloonManager::destroyBalloon(std::shared_ptr<Balloon> &balloon) {
int score = 0;
// Calcula la puntuación y el poder que generaria el globo en caso de romperlo a él y a sus hijos
switch (balloon->getSize())
{
case BalloonSize::SIZE4:
score = BALLOON_SCORE[3] + (2 * BALLOON_SCORE[2]) + (4 * BALLOON_SCORE[1]) + (8 * BALLOON_SCORE[0]);
break;
case BalloonSize::SIZE3:
score = BALLOON_SCORE[2] + (2 * BALLOON_SCORE[1]) + (4 * BALLOON_SCORE[0]);
break;
case BalloonSize::SIZE2:
score = BALLOON_SCORE[1] + (2 * BALLOON_SCORE[0]);
break;
case BalloonSize::SIZE1:
score = BALLOON_SCORE[0];
break;
default:
score = 0;
break;
switch (balloon->getSize()) {
case BalloonSize::SIZE4:
score = BALLOON_SCORE[3] + (2 * BALLOON_SCORE[2]) + (4 * BALLOON_SCORE[1]) + (8 * BALLOON_SCORE[0]);
break;
case BalloonSize::SIZE3:
score = BALLOON_SCORE[2] + (2 * BALLOON_SCORE[1]) + (4 * BALLOON_SCORE[0]);
break;
case BalloonSize::SIZE2:
score = BALLOON_SCORE[1] + (2 * BALLOON_SCORE[0]);
break;
case BalloonSize::SIZE1:
score = BALLOON_SCORE[0];
break;
default:
score = 0;
break;
}
// Aumenta el poder de la fase
@@ -326,11 +288,9 @@ int BalloonManager::destroyBalloon(std::shared_ptr<Balloon> &balloon)
}
// Destruye todos los globos
int BalloonManager::destroyAllBalloons()
{
int BalloonManager::destroyAllBalloons() {
int score = 0;
for (auto &balloon : balloons_)
{
for (auto &balloon : balloons_) {
score += destroyBalloon(balloon);
}
@@ -342,59 +302,46 @@ int BalloonManager::destroyAllBalloons()
}
// Detiene todos los globos
void BalloonManager::stopAllBalloons()
{
for (auto &balloon : balloons_)
{
void BalloonManager::stopAllBalloons() {
for (auto &balloon : balloons_) {
balloon->stop();
}
}
// Pone en marcha todos los globos
void BalloonManager::startAllBalloons()
{
for (auto &balloon : balloons_)
{
if (!balloon->isBeingCreated())
{
void BalloonManager::startAllBalloons() {
for (auto &balloon : balloons_) {
if (!balloon->isBeingCreated()) {
balloon->start();
}
}
}
// Cambia el color de todos los globos
void BalloonManager::reverseColorsToAllBalloons()
{
for (auto &balloon : balloons_)
{
if (balloon->isStopped())
{
void BalloonManager::reverseColorsToAllBalloons() {
for (auto &balloon : balloons_) {
if (balloon->isStopped()) {
balloon->useReverseColor();
}
}
}
// Cambia el color de todos los globos
void BalloonManager::normalColorsToAllBalloons()
{
for (auto &balloon : balloons_)
{
void BalloonManager::normalColorsToAllBalloons() {
for (auto &balloon : balloons_) {
balloon->useNormalColor();
}
}
// Crea dos globos gordos
void BalloonManager::createTwoBigBalloons()
{
void BalloonManager::createTwoBigBalloons() {
deploySet(1);
}
// Crea una disposición de globos aleatoria
void BalloonManager::createRandomBalloons()
{
void BalloonManager::createRandomBalloons() {
const int num_balloons = 2 + rand() % 4;
for (int i = 0; i < num_balloons; ++i)
{
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_SIZE[3];
const int y = param.game.game_area.rect.y + (rand() % 50);
const BalloonSize size = static_cast<BalloonSize>(rand() % 4);
@@ -405,37 +352,29 @@ void BalloonManager::createRandomBalloons()
}
// Obtiene el nivel de ameza actual generado por los globos
int BalloonManager::getMenace()
{
return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon)
{ return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); });
int BalloonManager::getMenace() {
return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); });
}
// Establece el sonido de los globos
void BalloonManager::setSounds(bool value)
{
void BalloonManager::setSounds(bool value) {
sound_enabled_ = value;
for (auto &balloon : balloons_)
{
for (auto &balloon : balloons_) {
balloon->setSound(value);
}
}
// Activa o desactiva los sonidos de rebote los globos
void BalloonManager::setBouncingSounds(bool value)
{
void BalloonManager::setBouncingSounds(bool value) {
bouncing_sound_enabled_ = value;
for (auto &balloon : balloons_)
{
for (auto &balloon : balloons_) {
balloon->setBouncingSound(value);
}
}
// Activa o desactiva los sonidos de los globos al explotar
void BalloonManager::setPoppingSounds(bool value)
{
void BalloonManager::setPoppingSounds(bool value) {
poping_sound_enabled_ = value;
for (auto &balloon : balloons_)
{
for (auto &balloon : balloons_) {
balloon->setPoppingSound(value);
}
}

View File

@@ -1,92 +1,92 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_FRect
#include "balloon.h" // Para BALLOON_SPEED, Balloon
#include "balloon_formations.h" // Para BalloonFormations
#include "explosions.h" // Para Explosions
#include "param.h" // Para Param, ParamGame, param
#include "utils.h" // Para Zone
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "balloon.h" // Para BALLOON_SPEED, Balloon
#include "balloon_formations.h" // Para BalloonFormations
#include "explosions.h" // Para Explosions
#include "param.h" // Para Param, ParamGame, param
#include "utils.h" // Para Zone
class Texture;
using Balloons = std::vector<std::shared_ptr<Balloon>>;
// Clase BalloonManager
class BalloonManager
{
public:
class BalloonManager {
public:
// Constructor y Destructor
BalloonManager();
~BalloonManager() = default;
// Actualización y Renderizado
void update(); // Actualiza el estado de los globos
void render(); // Renderiza los globos en pantalla
void update(); // Actualiza el estado de los globos
void render(); // Renderiza los globos en pantalla
// Gestión de globos
void freeBalloons(); // Libera globos que ya no sirven
void freeBalloons(); // Libera globos que ya no sirven
// Creación de formaciones enemigas
void deployBalloonFormation(int stage); // Crea una formación de enemigos aleatoria
void deploySet(int set); // Crea una formación específica
void deploySet(int set, int y); // Crea una formación específica con coordenadas
void deployBalloonFormation(int stage); // Crea una formación de enemigos aleatoria
void deploySet(int set); // Crea una formación específica
void deploySet(int set, int y); // Crea una formación específica con coordenadas
// Creación de globos
std::shared_ptr<Balloon> createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer); // Crea un nuevo globo
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 createTwoBigBalloons(); // Crea dos globos grandes
void createRandomBalloons(); // Crea una disposición aleatoria de globos
std::shared_ptr<Balloon> createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer); // Crea un nuevo globo
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 createTwoBigBalloons(); // Crea dos globos grandes
void createRandomBalloons(); // Crea una disposición aleatoria de globos
// Control de velocidad y despliegue
void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos
void setDefaultBalloonSpeed(float speed) { default_balloon_speed_ = speed; }; // Establece la velocidad base
void resetBalloonSpeed() { setBalloonSpeed(default_balloon_speed_); }; // Restablece la velocidad de los globos
void updateBalloonDeployCounter(); // Actualiza el contador de despliegue
bool canPowerBallBeCreated(); // Indica si se puede crear una PowerBall
int calculateScreenPower(); // Calcula el poder de los globos en pantalla
void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos
void setDefaultBalloonSpeed(float speed) { default_balloon_speed_ = speed; }; // Establece la velocidad base
void resetBalloonSpeed() { setBalloonSpeed(default_balloon_speed_); }; // Restablece la velocidad de los globos
void updateBalloonDeployCounter(); // Actualiza el contador de despliegue
bool canPowerBallBeCreated(); // Indica si se puede crear una PowerBall
int calculateScreenPower(); // Calcula el poder de los globos en pantalla
// Manipulación de globos existentes
int popBalloon(std::shared_ptr<Balloon> balloon); // Explosiona un globo, creando otros si aplica
int destroyBalloon(std::shared_ptr<Balloon> &balloon); // Explosiona un globo sin crear otros
int destroyAllBalloons(); // Destruye todos los globos
void stopAllBalloons(); // Detiene el movimiento de los globos
void startAllBalloons(); // Reactiva el movimiento de los globos
int popBalloon(std::shared_ptr<Balloon> balloon); // Explosiona un globo, creando otros si aplica
int destroyBalloon(std::shared_ptr<Balloon> &balloon); // Explosiona un globo sin crear otros
int destroyAllBalloons(); // Destruye todos los globos
void stopAllBalloons(); // Detiene el movimiento de los globos
void startAllBalloons(); // Reactiva el movimiento de los globos
// Cambios de apariencia
void reverseColorsToAllBalloons(); // Invierte los colores de los globos
void normalColorsToAllBalloons(); // Restaura los colores originales
void reverseColorsToAllBalloons(); // Invierte los colores de los globos
void normalColorsToAllBalloons(); // Restaura los colores originales
// Configuración de sonido
void setSounds(bool value); // Activa o desactiva los sonidos de los globos
void setBouncingSounds(bool value); // Activa o desactiva los sonidos de rebote los globos
void setPoppingSounds(bool value); // Activa o desactiva los sonidos de los globos al explotar
void setSounds(bool value); // Activa o desactiva los sonidos de los globos
void setBouncingSounds(bool value); // Activa o desactiva los sonidos de rebote los globos
void setPoppingSounds(bool value); // Activa o desactiva los sonidos de los globos al explotar
// Configuración de juego
void setPlayArea(SDL_FRect play_area) { play_area_ = play_area; }; // Define el área de juego
void setCreationTimeEnabled(bool value) { creation_time_enabled_ = value; }; // Activa o desactiva el tiempo de creación de globos
void enableBalloonDeployment(bool value) { can_deploy_balloons_ = value; }; // Activa o desactiva la generación de globos
void setPlayArea(SDL_FRect play_area) { play_area_ = play_area; }; // Define el área de juego
void setCreationTimeEnabled(bool value) { creation_time_enabled_ = value; }; // Activa o desactiva el tiempo de creación de globos
void enableBalloonDeployment(bool value) { can_deploy_balloons_ = value; }; // Activa o desactiva la generación de globos
// Obtención de información
int getMenace(); // Obtiene el nivel de amenaza generado por los globos
int getMenace(); // Obtiene el nivel de amenaza generado por los globos
float getBalloonSpeed() const { return balloon_speed_; }
Balloons &getBalloons() { return balloons_; }
int getNumBalloons() const { return balloons_.size(); }
private:
Balloons balloons_; // Vector con los globos activos
std::unique_ptr<Explosions> explosions_; // Objeto para gestionar explosiones
std::unique_ptr<BalloonFormations> balloon_formations_; // Objeto para manejar formaciones enemigas
private:
Balloons balloons_; // Vector con los globos activos
std::unique_ptr<Explosions> explosions_; // Objeto para gestionar explosiones
std::unique_ptr<BalloonFormations> balloon_formations_; // Objeto para manejar formaciones enemigas
std::vector<std::shared_ptr<Texture>> balloon_textures_; // Texturas de los globos
std::vector<std::shared_ptr<Texture>> explosions_textures_; // Texturas de explosiones
std::vector<std::shared_ptr<Texture>> balloon_textures_; // Texturas de los globos
std::vector<std::shared_ptr<Texture>> explosions_textures_; // Texturas de explosiones
std::vector<std::vector<std::string>> balloon_animations_; // Animaciones de los globos
std::vector<std::vector<std::string>> explosions_animations_; // Animaciones de las explosiones
std::vector<std::vector<std::string>> balloon_animations_; // Animaciones de los globos
std::vector<std::vector<std::string>> explosions_animations_; // Animaciones de las explosiones
// Variables de control de globos
float balloon_speed_ = BALLOON_SPEED[0];
@@ -98,9 +98,9 @@ private:
SDL_FRect play_area_ = param.game.play_area.rect;
bool creation_time_enabled_ = true;
bool can_deploy_balloons_ = true;
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
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
// Metodos privados
void init();

View File

@@ -1,10 +1,10 @@
#include "bullet.h"
#include <memory> // Para allocator, unique_ptr, make_unique
#include <string> // Para char_traits, basic_string, operator+, string
#include <memory> // Para allocator, unique_ptr, make_unique
#include <string> // Para char_traits, basic_string, operator+, string
#include "param.h" // Para Param, ParamGame, param
#include "resource.h" // Para Resource
#include "param.h" // Para Param, ParamGame, param
#include "resource.h" // Para Resource
// Constructor
Bullet::Bullet(float x, float y, BulletType bullet_type, bool powered, int owner)
@@ -12,29 +12,27 @@ Bullet::Bullet(float x, float y, BulletType bullet_type, bool powered, int owner
pos_x_(x),
pos_y_(y),
bullet_type_(bullet_type),
owner_(owner)
{
vel_x_ = (bullet_type_ == BulletType::LEFT) ? VEL_X_LEFT_
: (bullet_type_ == BulletType::RIGHT) ? VEL_X_RIGHT_
: 0;
owner_(owner) {
vel_x_ = (bullet_type_ == BulletType::LEFT) ? VEL_X_LEFT_
: (bullet_type_ == BulletType::RIGHT) ? VEL_X_RIGHT_
: 0;
std::string powered_type = powered ? "powered_" : "normal_";
switch (bullet_type)
{
case BulletType::UP:
sprite_->setCurrentAnimation(powered_type + "up");
break;
switch (bullet_type) {
case BulletType::UP:
sprite_->setCurrentAnimation(powered_type + "up");
break;
case BulletType::LEFT:
sprite_->setCurrentAnimation(powered_type + "left");
break;
case BulletType::LEFT:
sprite_->setCurrentAnimation(powered_type + "left");
break;
case BulletType::RIGHT:
sprite_->setCurrentAnimation(powered_type + "right");
break;
case BulletType::RIGHT:
sprite_->setCurrentAnimation(powered_type + "right");
break;
default:
break;
default:
break;
}
collider_.r = WIDTH / 2;
@@ -42,32 +40,27 @@ Bullet::Bullet(float x, float y, BulletType bullet_type, bool powered, int owner
}
// Implementación de render (llama al render del sprite_)
void Bullet::render()
{
void Bullet::render() {
if (bullet_type_ != BulletType::NONE)
sprite_->render();
}
// Actualiza el estado del objeto
BulletMoveStatus Bullet::update()
{
BulletMoveStatus Bullet::update() {
sprite_->update();
return move();
}
// Implementación del movimiento usando BulletMoveStatus
BulletMoveStatus Bullet::move()
{
BulletMoveStatus Bullet::move() {
pos_x_ += vel_x_;
if (pos_x_ < param.game.play_area.rect.x - WIDTH || pos_x_ > param.game.play_area.rect.w)
{
if (pos_x_ < param.game.play_area.rect.x - WIDTH || pos_x_ > param.game.play_area.rect.w) {
disable();
return BulletMoveStatus::OUT;
}
pos_y_ += VEL_Y_;
if (pos_y_ < param.game.play_area.rect.y - HEIGHT)
{
if (pos_y_ < param.game.play_area.rect.y - HEIGHT) {
disable();
return BulletMoveStatus::OUT;
}
@@ -78,34 +71,28 @@ BulletMoveStatus Bullet::move()
return BulletMoveStatus::OK;
}
bool Bullet::isEnabled() const
{
bool Bullet::isEnabled() const {
return bullet_type_ != BulletType::NONE;
}
void Bullet::disable()
{
void Bullet::disable() {
bullet_type_ = BulletType::NONE;
}
int Bullet::getOwner() const
{
int Bullet::getOwner() const {
return owner_;
}
Circle &Bullet::getCollider()
{
Circle &Bullet::getCollider() {
return collider_;
}
void Bullet::shiftColliders()
{
void Bullet::shiftColliders() {
collider_.x = pos_x_ + collider_.r;
collider_.y = pos_y_ + collider_.r;
}
void Bullet::shiftSprite()
{
void Bullet::shiftSprite() {
sprite_->setX(pos_x_);
sprite_->setY(pos_y_);
}

View File

@@ -1,70 +1,68 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint8
#include <memory> // Para unique_ptr
#include <SDL3/SDL.h> // Para Uint8
#include "animated_sprite.h" // Para AnimatedSprite
#include "utils.h" // Para Circle
#include <memory> // Para unique_ptr
#include "animated_sprite.h" // Para AnimatedSprite
#include "utils.h" // Para Circle
// Tipos de balas
enum class BulletType : Uint8
{
UP,
LEFT,
RIGHT,
NONE
enum class BulletType : Uint8 {
UP,
LEFT,
RIGHT,
NONE
};
// Resultado del movimiento de la bala
enum class BulletMoveStatus : Uint8
{
OK = 0,
OUT = 1
enum class BulletMoveStatus : Uint8 {
OK = 0,
OUT = 1
};
// Clase Bullet
class Bullet
{
public:
// Constantes
static constexpr float WIDTH = 12.0f;
static constexpr float HEIGHT = 12.0f;
class Bullet {
public:
// Constantes
static constexpr float WIDTH = 12.0f;
static constexpr float HEIGHT = 12.0f;
// Constructor y Destructor
Bullet(float x, float y, BulletType bullet_type, bool powered, int owner);
~Bullet() = default;
// Constructor y Destructor
Bullet(float x, float y, BulletType bullet_type, bool powered, int owner);
~Bullet() = default;
// Métodos principales
void render(); // Dibuja la bala en pantalla
BulletMoveStatus update(); // Actualiza el estado del objeto
// Métodos principales
void render(); // Dibuja la bala en pantalla
BulletMoveStatus update(); // Actualiza el estado del objeto
// Estado de la bala
bool isEnabled() const; // Comprueba si está activa
void disable(); // Desactiva la bala
// Estado de la bala
bool isEnabled() const; // Comprueba si está activa
void disable(); // Desactiva la bala
// Getters
int getOwner() const; // Devuelve el identificador del dueño
Circle &getCollider(); // Devuelve el círculo de colisión
// Getters
int getOwner() const; // Devuelve el identificador del dueño
Circle &getCollider(); // Devuelve el círculo de colisión
private:
// Constantes
static constexpr float VEL_Y_ = -3.0f;
static constexpr float VEL_X_LEFT_ = -2.0f;
static constexpr float VEL_X_RIGHT_ = 2.0f;
private:
// Constantes
static constexpr float VEL_Y_ = -3.0f;
static constexpr float VEL_X_LEFT_ = -2.0f;
static constexpr float VEL_X_RIGHT_ = 2.0f;
// Propiedades
std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos
// Propiedades
std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos
float pos_x_; // Posición en el eje X
float pos_y_; // Posición en el eje Y
float vel_x_; // Velocidad en el eje X
float pos_x_; // Posición en el eje X
float pos_y_; // Posición en el eje Y
float vel_x_; // Velocidad en el eje X
BulletType bullet_type_; // Tipo de bala
int owner_; // Identificador del dueño
Circle collider_; // Círculo de colisión
BulletType bullet_type_; // Tipo de bala
int owner_; // Identificador del dueño
Circle collider_; // Círculo de colisión
// Métodos internos
void shiftColliders(); // Ajusta el círculo de colisión
void shiftSprite(); // Ajusta el sprite
BulletMoveStatus move(); // Mueve la bala y devuelve su estado
// Métodos internos
void shiftColliders(); // Ajusta el círculo de colisión
void shiftSprite(); // Ajusta el sprite
BulletMoveStatus move(); // Mueve la bala y devuelve su estado
};

View File

@@ -1,34 +1,30 @@
#include "define_buttons.h"
#include "input.h" // Para Input, InputAction
#include "lang.h" // Para getText
#include "options.h" // Para OptionsController, Options, options
#include "param.h" // Para Param, param, ParamGame, ParamTitle
#include "resource.h" // Para Resource
#include "text.h" // Para Text
#include "input.h" // Para Input, InputAction
#include "lang.h" // Para getText
#include "options.h" // Para OptionsController, Options, options
#include "param.h" // Para Param, param, ParamGame, ParamTitle
#include "resource.h" // Para Resource
#include "text.h" // Para Text
// Constructor
DefineButtons::DefineButtons()
: input_(Input::get()),
text_(Resource::get()->getText("8bithud"))
{
text_(Resource::get()->getText("8bithud")) {
// Inicializa variables
x_ = param.game.width / 2;
y_ = param.title.press_start_position;
clearButtons();
for (int i = 0; i < input_->getNumControllers(); ++i)
{
for (int i = 0; i < input_->getNumControllers(); ++i) {
controller_names_.emplace_back(input_->getControllerName(i));
}
}
// Dibuja el objeto en pantalla
void DefineButtons::render()
{
if (enabled_)
{
void DefineButtons::render() {
if (enabled_) {
text_->writeCentered(x_, y_ - 10, Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(Options::controllers.at(index_controller_).player_id));
text_->writeCentered(x_, y_, controller_names_.at(index_controller_));
text_->writeCentered(x_, y_ + 10, buttons_.at(index_button_).label);
@@ -36,27 +32,22 @@ void DefineButtons::render()
}
// Comprueba el botón que se ha pulsado
void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent &event)
{
void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent &event) {
// Solo pilla botones del mando que toca
if (input_->getJoyIndex(event.which) != static_cast<int>(index_controller_))
{
if (input_->getJoyIndex(event.which) != static_cast<int>(index_controller_)) {
return;
}
const auto button = static_cast<SDL_GamepadButton>(event.button);
if (checkButtonNotInUse(button))
{
if (checkButtonNotInUse(button)) {
buttons_.at(index_button_).button = button;
incIndexButton();
}
}
// Asigna los botones definidos al input_
void DefineButtons::bindButtons()
{
for (const auto &button : buttons_)
{
void DefineButtons::bindButtons() {
for (const auto &button : buttons_) {
input_->bindGameControllerButton(index_controller_, button.input, button.button);
}
@@ -66,29 +57,24 @@ void DefineButtons::bindButtons()
}
// Comprueba los eventos
void DefineButtons::checkEvents(const SDL_Event &event)
{
if (enabled_)
{
switch (event.type)
{
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
doControllerButtonDown(event.gbutton);
break;
case SDL_EVENT_GAMEPAD_BUTTON_UP:
checkEnd();
break;
default:
break;
void DefineButtons::checkEvents(const SDL_Event &event) {
if (enabled_) {
switch (event.type) {
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
doControllerButtonDown(event.gbutton);
break;
case SDL_EVENT_GAMEPAD_BUTTON_UP:
checkEnd();
break;
default:
break;
}
}
}
// Habilita el objeto
bool DefineButtons::enable(int index)
{
if (index < input_->getNumControllers())
{
bool DefineButtons::enable(int index) {
if (index < input_->getNumControllers()) {
enabled_ = true;
finished_ = false;
index_controller_ = index;
@@ -104,37 +90,28 @@ bool DefineButtons::enable(int index)
bool DefineButtons::isEnabled() const { return enabled_; }
// Incrementa el indice de los botones
void DefineButtons::incIndexButton()
{
if (index_button_ < buttons_.size() - 1)
{
void DefineButtons::incIndexButton() {
if (index_button_ < buttons_.size() - 1) {
++index_button_;
}
else
{
} else {
finished_ = true;
}
}
// Guarda los cambios en las opciones
void DefineButtons::saveBindingsToOptions()
{
void DefineButtons::saveBindingsToOptions() {
// Modifica las opciones para colocar los valores asignados
auto &controller = Options::controllers.at(index_controller_);
controller.name = input_->getControllerName(index_controller_);
for (size_t j = 0; j < controller.inputs.size(); ++j)
{
for (size_t j = 0; j < controller.inputs.size(); ++j) {
controller.buttons.at(j) = input_->getControllerBinding(index_controller_, controller.inputs.at(j));
}
}
// Comprueba que un botón no esté ya asignado
bool DefineButtons::checkButtonNotInUse(SDL_GamepadButton button)
{
for (const auto &b : buttons_)
{
if (b.button == button)
{
bool DefineButtons::checkButtonNotInUse(SDL_GamepadButton button) {
for (const auto &b : buttons_) {
if (b.button == button) {
return false;
}
}
@@ -142,8 +119,7 @@ bool DefineButtons::checkButtonNotInUse(SDL_GamepadButton button)
}
// Limpia la asignación de botones
void DefineButtons::clearButtons()
{
void DefineButtons::clearButtons() {
buttons_.clear();
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_LEFT"), InputAction::FIRE_LEFT, SDL_GAMEPAD_BUTTON_INVALID);
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_UP"), InputAction::FIRE_CENTER, SDL_GAMEPAD_BUTTON_INVALID);
@@ -153,11 +129,9 @@ void DefineButtons::clearButtons()
}
// Comprueba si ha finalizado
void DefineButtons::checkEnd()
{
void DefineButtons::checkEnd() {
// Comprueba si ha finalizado
if (finished_)
{
if (finished_) {
// Asigna los botones definidos al input_
bindButtons();

View File

@@ -1,10 +1,11 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_GamepadButton, SDL_Event, SDL_GamepadButtonEvent
#include <stddef.h> // Para size_t
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_GamepadButton, SDL_Event, SDL_GamepadButtonEvent
#include <stddef.h> // Para size_t
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
// Declaraciones adelantadas
class Input;
@@ -12,48 +13,46 @@ class Text;
enum class InputAction : int;
// Estructura para definir botones
struct DefineButtonsButton
{
std::string label; // Texto en pantalla
InputAction input; // Acción asociada
SDL_GamepadButton button; // Botón del mando
struct DefineButtonsButton {
std::string label; // Texto en pantalla
InputAction input; // Acción asociada
SDL_GamepadButton button; // Botón del mando
DefineButtonsButton(const std::string &lbl, InputAction inp, SDL_GamepadButton btn)
: label(lbl), input(inp), button(btn) {}
};
// Clase DefineButtons
class DefineButtons
{
public:
class DefineButtons {
public:
DefineButtons();
~DefineButtons() = default;
void render(); // Dibuja el objeto en pantalla
void checkEvents(const SDL_Event &event); // Procesa los eventos
bool enable(int index_controller); // Habilita la redefinición de botones
bool isEnabled() const; // Comprueba si está habilitado
void render(); // Dibuja el objeto en pantalla
void checkEvents(const SDL_Event &event); // Procesa los eventos
bool enable(int index_controller); // Habilita la redefinición de botones
bool isEnabled() const; // Comprueba si está habilitado
private:
private:
// Objetos
Input *input_ = nullptr; // Gestión de entrada
std::shared_ptr<Text> text_; // Renderizado de texto
Input *input_ = nullptr; // Gestión de entrada
std::shared_ptr<Text> text_; // Renderizado de texto
// Variables
bool enabled_ = false; // Indica si está activo
int x_ = 0, y_ = 0; // Coordenadas de texto
std::vector<DefineButtonsButton> buttons_; // Definiciones de botones
size_t index_controller_ = 0; // Índice del controlador asignado
size_t index_button_ = 0; // Índice del botón en proceso
std::vector<std::string> controller_names_; // Nombres de los mandos
bool enabled_ = false; // Indica si está activo
int x_ = 0, y_ = 0; // Coordenadas de texto
std::vector<DefineButtonsButton> buttons_; // Definiciones de botones
size_t index_controller_ = 0; // Índice del controlador asignado
size_t index_button_ = 0; // Índice del botón en proceso
std::vector<std::string> controller_names_; // Nombres de los mandos
bool finished_ = false;
// Métodos internos
void incIndexButton(); // Incrementa el índice de botones
void doControllerButtonDown(const SDL_GamepadButtonEvent &event); // Procesa pulsaciones
void bindButtons(); // Asigna botones al sistema de entrada
void saveBindingsToOptions(); // Guarda configuraciones
bool checkButtonNotInUse(SDL_GamepadButton button); // Verifica uso de botones
void clearButtons(); // Limpia asignaciones actuales
void checkEnd(); // Comprueba si ha finalizado
void incIndexButton(); // Incrementa el índice de botones
void doControllerButtonDown(const SDL_GamepadButtonEvent &event); // Procesa pulsaciones
void bindButtons(); // Asigna botones al sistema de entrada
void saveBindingsToOptions(); // Guarda configuraciones
bool checkButtonNotInUse(SDL_GamepadButton button); // Verifica uso de botones
void clearButtons(); // Limpia asignaciones actuales
void checkEnd(); // Comprueba si ha finalizado
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,55 +1,53 @@
#pragma once
#include <string> // Para manejar cadenas de texto
#include <string> // Para manejar cadenas de texto
namespace Lang
{
enum class Code : int;
namespace Lang {
enum class Code : int;
}
class Director
{
public:
// --- Constructor y destructor ---
Director(int argc, const char *argv[]);
~Director();
class Director {
public:
// --- Constructor y destructor ---
Director(int argc, const char *argv[]);
~Director();
// --- Bucle principal ---
int run();
// --- Bucle principal ---
int run();
private:
// --- Variables internas ---
std::string executable_path_; // Ruta del ejecutable
std::string system_folder_; // Carpeta del sistema para almacenar datos
private:
// --- Variables internas ---
std::string executable_path_; // Ruta del ejecutable
std::string system_folder_; // Carpeta del sistema para almacenar datos
// --- Inicialización y cierre del sistema ---
void init(); // Inicializa la aplicación
void close(); // Cierra y libera recursos
// --- Inicialización y cierre del sistema ---
void init(); // Inicializa la aplicación
void close(); // Cierra y libera recursos
// --- Configuración inicial ---
void loadParams(); // Carga los parámetros del programa
void loadScoreFile(); // Carga el fichero de puntuaciones
void createSystemFolder(const std::string &folder); // Crea la carpeta del sistema
// --- Configuración inicial ---
void loadParams(); // Carga los parámetros del programa
void loadScoreFile(); // Carga el fichero de puntuaciones
void createSystemFolder(const std::string &folder); // Crea la carpeta del sistema
// --- Gestión de entrada y archivos ---
void bindInputs(); // Asigna botones y teclas al sistema de entrada
void setFileList(); // Crea el índice de archivos disponibles
void checkProgramArguments(int argc, const char *argv[]); // Verifica los parámetros del programa
// --- Gestión de entrada y archivos ---
void bindInputs(); // Asigna botones y teclas al sistema de entrada
void setFileList(); // Crea el índice de archivos disponibles
void checkProgramArguments(int argc, const char *argv[]); // Verifica los parámetros del programa
// --- Secciones del programa ---
void runLogo(); // Ejecuta la pantalla con el logo
void runIntro(); // Ejecuta la introducción del juego
void runTitle(); // Ejecuta la pantalla de título
void runGame(); // Inicia el juego
void runInstructions(); // Muestra las instrucciones
void runCredits(); // Muestra los créditos del juego
void runHiScoreTable(); // Muestra la tabla de puntuaciones
void runDemoGame(); // Ejecuta el modo demo
void reset(); // Reinicia objetos y vuelve a la sección inicial
// --- Secciones del programa ---
void runLogo(); // Ejecuta la pantalla con el logo
void runIntro(); // Ejecuta la introducción del juego
void runTitle(); // Ejecuta la pantalla de título
void runGame(); // Inicia el juego
void runInstructions(); // Muestra las instrucciones
void runCredits(); // Muestra los créditos del juego
void runHiScoreTable(); // Muestra la tabla de puntuaciones
void runDemoGame(); // Ejecuta el modo demo
void reset(); // Reinicia objetos y vuelve a la sección inicial
// --- Gestión de archivos de idioma ---
std::string getLangFile(Lang::Code code); // Obtiene un fichero de idioma según el código
// --- Gestión de archivos de idioma ---
std::string getLangFile(Lang::Code code); // Obtiene un fichero de idioma según el código
// --- Apagado del sistema ---
void shutdownSystem(bool should_shutdown); // Apaga el sistema
// --- Apagado del sistema ---
void shutdownSystem(bool should_shutdown); // Apaga el sistema
};

View File

@@ -1,10 +1,11 @@
#include "enter_name.h"
#include <stddef.h> // Para size_t
#include <stdlib.h> // Para rand
#include <string_view> // Para basic_string_view, string_view
#include <stddef.h> // Para size_t
#include <stdlib.h> // Para rand
#include "utils.h" // Para trim
#include <string_view> // Para basic_string_view, string_view
#include "utils.h" // Para trim
// Constructor
EnterName::EnterName()
@@ -12,18 +13,15 @@ EnterName::EnterName()
character_index_{0} {}
// Inicializa el objeto
void EnterName::init(const std::string &name)
{
void EnterName::init(const std::string &name) {
// No se pasa ningún nombre
if (name.empty())
{
if (name.empty()) {
name_ = "A";
position_ = 0;
position_overflow_ = false;
}
// Se pasa un nombre
else
{
else {
name_ = name;
position_ = name_.length();
position_overflow_ = position_ >= NAME_SIZE ? true : false;
@@ -34,28 +32,22 @@ void EnterName::init(const std::string &name)
}
// Incrementa la posición
void EnterName::incPosition()
{
if (position_overflow_)
{
void EnterName::incPosition() {
if (position_overflow_) {
// Si ya estamos en overflow, no incrementamos más.
return;
}
++position_;
if (position_ >= NAME_SIZE)
{
position_ = NAME_SIZE; // Mantenemos en el índice máximo válido.
position_overflow_ = true; // Activamos el flag de overflow.
}
else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGHT
if (position_ >= NAME_SIZE) {
position_ = NAME_SIZE; // Mantenemos en el índice máximo válido.
position_overflow_ = true; // Activamos el flag de overflow.
} else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGHT
{
// Copiamos el índice del carácter anterior si es posible.
character_index_[position_] = character_index_[position_ - 1];
}
else
{
} else {
// Si position_ es 0, inicializamos el carácter actual.
character_index_[position_] = 0;
}
@@ -64,36 +56,27 @@ void EnterName::incPosition()
}
// Decrementa la posición
void EnterName::decPosition()
{
if (position_overflow_)
{
void EnterName::decPosition() {
if (position_overflow_) {
// Si estaba en overflow, lo desactivamos y mantenemos position_ en el máximo.
position_overflow_ = false;
position_ = NAME_SIZE - 1;
}
else
{
if (position_ > 0)
{
} else {
if (position_ > 0) {
--position_;
// Limpiamos el carácter siguiente si el índice es válido.
if (position_ + 1 < NAME_SIZE)
{
if (position_ + 1 < NAME_SIZE) {
character_index_[position_ + 1] = 0;
}
}
else
{
} else {
// Si position_ es 0, aseguramos que no vaya a ser negativo y limpiamos el carácter actual.
position_ = 0;
// character_index_[position_] = 0;
}
// Si position_ es menor que NAME_LENGHT, aseguramos que el overflow esté desactivado.
if (position_ < NAME_SIZE)
{
if (position_ < NAME_SIZE) {
position_overflow_ = false;
}
}
@@ -102,71 +85,57 @@ void EnterName::decPosition()
}
// Incrementa el índice
void EnterName::incIndex()
{
if (position_overflow_)
{
void EnterName::incIndex() {
if (position_overflow_) {
return;
}
++character_index_[position_];
if (character_index_[position_] >= static_cast<int>(character_list_.size()))
{
if (character_index_[position_] >= static_cast<int>(character_list_.size())) {
character_index_[position_] = 0;
}
updateNameFromCharacterIndex();
}
// Decrementa el índice
void EnterName::decIndex()
{
if (position_overflow_)
{
void EnterName::decIndex() {
if (position_overflow_) {
return;
}
--character_index_[position_];
if (character_index_[position_] < 0)
{
if (character_index_[position_] < 0) {
character_index_[position_] = character_list_.size() - 1;
}
updateNameFromCharacterIndex();
}
// Actualiza el nombre a partir de la lista de índices
void EnterName::updateNameFromCharacterIndex()
{
void EnterName::updateNameFromCharacterIndex() {
name_.clear();
for (size_t i = 0; i < NAME_SIZE; ++i)
{
for (size_t i = 0; i < NAME_SIZE; ++i) {
name_.push_back(character_list_[character_index_[i]]);
}
name_ = trim(name_);
}
// Actualiza la variable
void EnterName::initCharacterIndex(const std::string &name)
{
void EnterName::initCharacterIndex(const std::string &name) {
// Rellena de espacios
for (size_t i = 0; i < NAME_SIZE; ++i)
{
for (size_t i = 0; i < NAME_SIZE; ++i) {
character_index_[i] = 0;
}
// Coloca los índices en función de los caracteres que forman el nombre
for (size_t i = 0; i < name.substr(0, NAME_SIZE).size(); ++i)
{
for (size_t i = 0; i < name.substr(0, NAME_SIZE).size(); ++i) {
character_index_[i] = findIndex(name.at(i));
}
}
// Encuentra el indice de un caracter en "character_list_"
int EnterName::findIndex(char character) const
{
for (size_t i = 0; i < character_list_.size(); ++i)
{
if (character == character_list_.at(i))
{
int EnterName::findIndex(char character) const {
for (size_t i = 0; i < character_list_.size(); ++i) {
if (character == character_list_.at(i)) {
return i;
}
}
@@ -174,18 +143,15 @@ int EnterName::findIndex(char character) const
}
// Devuelve un nombre al azar
std::string EnterName::getRandomName()
{
std::string EnterName::getRandomName() {
static constexpr std::array<std::string_view, 8> NAMES = {
"BAL1", "TABE", "DOC", "MON", "SAM1", "JORDI", "JDES", "PEPE"};
return std::string(NAMES[rand() % NAMES.size()]);
}
// Obtiene el nombre final introducido
std::string EnterName::getFinalName()
{
std::string EnterName::getFinalName() {
auto name = trim(name_.substr(0, position_));
if (name.empty())
{
if (name.empty()) {
name = getRandomName();
}
name_ = name;

View File

@@ -1,43 +1,43 @@
#pragma once
#include <stddef.h> // Para size_t
#include <array> // Para array
#include <string> // Para string, basic_string
#include <stddef.h> // Para size_t
#include "utils.h" // Para trim
#include <array> // Para array
#include <string> // Para string, basic_string
#include "utils.h" // Para trim
// Tamaño máximo del nombre
constexpr size_t NAME_SIZE = 5;
// Clase EnterName
class EnterName
{
public:
class EnterName {
public:
EnterName();
~EnterName() = default;
void init(const std::string &name = ""); // Inicializa con un nombre opcional
void init(const std::string &name = ""); // Inicializa con un nombre opcional
void incPosition(); // Incrementa la posición del carácter actual
void decPosition(); // Decrementa la posición del carácter actual
void incIndex(); // Incrementa el índice del carácter en la lista
void decIndex(); // Decrementa el índice del carácter en la lista
void incPosition(); // Incrementa la posición del carácter actual
void decPosition(); // Decrementa la posición del carácter actual
void incIndex(); // Incrementa el índice del carácter en la lista
void decIndex(); // Decrementa el índice del carácter en la lista
std::string getFinalName(); // Obtiene el nombre final introducido
std::string getCurrentName() const { return trim(name_); } // Obtiene el nombre actual en proceso
std::string getFinalName(); // Obtiene el nombre final introducido
std::string getCurrentName() const { return trim(name_); } // Obtiene el nombre actual en proceso
int getPosition() const { return position_; } // Posición actual del carácter editado
bool getPositionOverflow() const { return position_overflow_; } // Indica si la posición excede el límite
int getPosition() const { return position_; } // Posición actual del carácter editado
bool getPositionOverflow() const { return position_overflow_; } // Indica si la posición excede el límite
private:
std::string character_list_; // Lista de caracteres permitidos
std::string name_; // Nombre en proceso
size_t position_ = 0; // Índice del carácter que se edita
bool position_overflow_ = false; // Flag para exceder límite
std::array<int, NAME_SIZE> character_index_; // Índices a "character_list_"
private:
std::string character_list_; // Lista de caracteres permitidos
std::string name_; // Nombre en proceso
size_t position_ = 0; // Índice del carácter que se edita
bool position_overflow_ = false; // Flag para exceder límite
std::array<int, NAME_SIZE> character_index_; // Índices a "character_list_"
void updateNameFromCharacterIndex(); // Actualiza "name_" según "character_index_"
void initCharacterIndex(const std::string &name); // Inicializa índices desde el nombre
int findIndex(char character) const; // Busca el índice de un carácter en "character_list_"
static std::string getRandomName(); // Devuelve un nombre al azar
void updateNameFromCharacterIndex(); // Actualiza "name_" según "character_index_"
void initCharacterIndex(const std::string &name); // Inicializa índices desde el nombre
int findIndex(char character) const; // Busca el índice de un carácter en "character_list_"
static std::string getRandomName(); // Devuelve un nombre al azar
};

View File

@@ -1,14 +1,12 @@
#include "explosions.h"
#include "animated_sprite.h" // Para AnimatedSprite
#include "animated_sprite.h" // Para AnimatedSprite
class Texture; // lines 4-4
class Texture; // lines 4-4
// Actualiza la lógica de la clase
void Explosions::update()
{
for (auto &explosion : explosions_)
{
void Explosions::update() {
for (auto &explosion : explosions_) {
explosion->update();
}
@@ -17,37 +15,29 @@ void Explosions::update()
}
// Dibuja el objeto en pantalla
void Explosions::render()
{
for (auto &explosion : explosions_)
{
void Explosions::render() {
for (auto &explosion : explosions_) {
explosion->render();
}
}
// Añade texturas al objeto
void Explosions::addTexture(int size, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation)
{
void Explosions::addTexture(int size, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation) {
textures_.emplace_back(ExplosionTexture(size, texture, animation));
}
// Añade una explosión
void Explosions::add(int x, int y, int size)
{
void Explosions::add(int x, int y, int size) {
const auto INDEX = getIndexBySize(size);
explosions_.emplace_back(std::make_unique<AnimatedSprite>(textures_[INDEX].texture, textures_[INDEX].animation));
explosions_.back()->setPos(x, y);
}
// Vacia el vector de elementos finalizados
void Explosions::freeExplosions()
{
if (explosions_.empty() == false)
{
for (int i = explosions_.size() - 1; i >= 0; --i)
{
if (explosions_[i]->animationIsCompleted())
{
void Explosions::freeExplosions() {
if (explosions_.empty() == false) {
for (int i = explosions_.size() - 1; i >= 0; --i) {
if (explosions_[i]->animationIsCompleted()) {
explosions_.erase(explosions_.begin() + i);
}
}
@@ -55,12 +45,9 @@ void Explosions::freeExplosions()
}
// Busca una textura a partir del tamaño
int Explosions::getIndexBySize(int size)
{
for (int i = 0; i < (int)textures_.size(); ++i)
{
if (size == textures_[i].size)
{
int Explosions::getIndexBySize(int size) {
for (int i = 0; i < (int)textures_.size(); ++i) {
if (size == textures_[i].size) {
return i;
}
}

View File

@@ -1,28 +1,26 @@
#pragma once
#include <memory> // Para unique_ptr, shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include <memory> // Para unique_ptr, shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "animated_sprite.h" // Para AnimatedSprite
#include "animated_sprite.h" // Para AnimatedSprite
class Texture;
// Estructura para almacenar la información de una textura de explosión
struct ExplosionTexture
{
int size; // Tamaño de la explosión
std::shared_ptr<Texture> texture; // Textura para la explosión
std::vector<std::string> animation; // Animación para la textura
struct ExplosionTexture {
int size; // Tamaño de la explosión
std::shared_ptr<Texture> texture; // Textura para la explosión
std::vector<std::string> animation; // Animación para la textura
ExplosionTexture(int sz, std::shared_ptr<Texture> tex, const std::vector<std::string> &anim)
: size(sz), texture(tex), animation(anim) {}
};
// Clase Explosions
class Explosions
{
public:
class Explosions {
public:
// Constructor y destructor
Explosions() = default;
~Explosions() = default;
@@ -39,7 +37,7 @@ public:
// Añade una explosión
void add(int x, int y, int size);
private:
private:
// Vector con las texturas a utilizar
std::vector<ExplosionTexture> textures_;

View File

@@ -1,249 +1,214 @@
#include "gif.h"
#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, SDL_LogInfo
#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, SDL_LogInfo
#include <cstring> // Para memcpy, size_t
#include <stdexcept> // Para runtime_error
#include <string> // Para char_traits, operator==, basic_string, string
namespace GIF
{
inline void readBytes(const uint8_t *&buffer, void *dst, size_t size)
{
std::memcpy(dst, buffer, size);
buffer += size;
namespace GIF {
inline void readBytes(const uint8_t *&buffer, void *dst, size_t size) {
std::memcpy(dst, buffer, size);
buffer += size;
}
void Gif::decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out) {
if (code_length < 2 || code_length > 12) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid LZW code length: %d", code_length);
throw std::runtime_error("Invalid LZW code length");
}
void Gif::decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out)
{
if (code_length < 2 || code_length > 12)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid LZW code length: %d", code_length);
throw std::runtime_error("Invalid LZW code length");
int i, bit;
int prev = -1;
std::vector<DictionaryEntry> dictionary;
int dictionary_ind;
unsigned int mask = 0x01;
int reset_code_length = code_length;
int clear_code = 1 << code_length;
int stop_code = clear_code + 1;
int match_len = 0;
dictionary.resize(1 << (code_length + 1));
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
dictionary[dictionary_ind].prev = -1;
dictionary[dictionary_ind].len = 1;
}
dictionary_ind += 2;
while (input_length > 0) {
int code = 0;
for (i = 0; i < (code_length + 1); i++) {
if (input_length <= 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unexpected end of input in decompress");
throw std::runtime_error("Unexpected end of input in decompress");
}
bit = ((*input & mask) != 0) ? 1 : 0;
mask <<= 1;
if (mask == 0x100) {
mask = 0x01;
input++;
input_length--;
}
code |= (bit << i);
}
int i, bit;
int prev = -1;
std::vector<DictionaryEntry> dictionary;
int dictionary_ind;
unsigned int mask = 0x01;
int reset_code_length = code_length;
int clear_code = 1 << code_length;
int stop_code = clear_code + 1;
int match_len = 0;
dictionary.resize(1 << (code_length + 1));
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++)
{
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
dictionary[dictionary_ind].prev = -1;
dictionary[dictionary_ind].len = 1;
if (code == clear_code) {
code_length = reset_code_length;
dictionary.resize(1 << (code_length + 1));
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
dictionary[dictionary_ind].prev = -1;
dictionary[dictionary_ind].len = 1;
}
dictionary_ind += 2;
prev = -1;
continue;
} else if (code == stop_code) {
break;
}
dictionary_ind += 2;
while (input_length > 0)
{
int code = 0;
for (i = 0; i < (code_length + 1); i++)
{
if (input_length <= 0)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unexpected end of input in decompress");
throw std::runtime_error("Unexpected end of input in decompress");
}
bit = ((*input & mask) != 0) ? 1 : 0;
mask <<= 1;
if (mask == 0x100)
{
mask = 0x01;
input++;
input_length--;
}
code |= (bit << i);
if (prev > -1 && code_length < 12) {
if (code > dictionary_ind) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "LZW error: code (%d) exceeds dictionary_ind (%d)", code, dictionary_ind);
throw std::runtime_error("LZW error: code exceeds dictionary_ind.");
}
if (code == clear_code)
{
code_length = reset_code_length;
int ptr;
if (code == dictionary_ind) {
ptr = prev;
while (dictionary[ptr].prev != -1)
ptr = dictionary[ptr].prev;
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
} else {
ptr = code;
while (dictionary[ptr].prev != -1)
ptr = dictionary[ptr].prev;
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
}
dictionary[dictionary_ind].prev = prev;
dictionary[dictionary_ind].len = dictionary[prev].len + 1;
dictionary_ind++;
if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11)) {
code_length++;
dictionary.resize(1 << (code_length + 1));
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++)
{
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
dictionary[dictionary_ind].prev = -1;
dictionary[dictionary_ind].len = 1;
}
dictionary_ind += 2;
prev = -1;
continue;
}
else if (code == stop_code)
{
break;
}
if (prev > -1 && code_length < 12)
{
if (code > dictionary_ind)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "LZW error: code (%d) exceeds dictionary_ind (%d)", code, dictionary_ind);
throw std::runtime_error("LZW error: code exceeds dictionary_ind.");
}
int ptr;
if (code == dictionary_ind)
{
ptr = prev;
while (dictionary[ptr].prev != -1)
ptr = dictionary[ptr].prev;
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
}
else
{
ptr = code;
while (dictionary[ptr].prev != -1)
ptr = dictionary[ptr].prev;
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
}
dictionary[dictionary_ind].prev = prev;
dictionary[dictionary_ind].len = dictionary[prev].len + 1;
dictionary_ind++;
if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11))
{
code_length++;
dictionary.resize(1 << (code_length + 1));
}
}
prev = code;
if (code < 0 || static_cast<size_t>(code) >= dictionary.size())
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid LZW code %d, dictionary size %lu", code, static_cast<unsigned long>(dictionary.size()));
throw std::runtime_error("LZW error: invalid code encountered");
}
int curCode = code;
match_len = dictionary[curCode].len;
while (curCode != -1)
{
out[dictionary[curCode].len - 1] = dictionary[curCode].byte;
if (dictionary[curCode].prev == curCode)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Internal error; self-reference detected.");
throw std::runtime_error("Internal error in decompress: self-reference");
}
curCode = dictionary[curCode].prev;
}
out += match_len;
}
}
std::vector<uint8_t> Gif::readSubBlocks(const uint8_t *&buffer)
{
std::vector<uint8_t> data;
uint8_t block_size = *buffer;
prev = code;
if (code < 0 || static_cast<size_t>(code) >= dictionary.size()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid LZW code %d, dictionary size %lu", code, static_cast<unsigned long>(dictionary.size()));
throw std::runtime_error("LZW error: invalid code encountered");
}
int curCode = code;
match_len = dictionary[curCode].len;
while (curCode != -1) {
out[dictionary[curCode].len - 1] = dictionary[curCode].byte;
if (dictionary[curCode].prev == curCode) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Internal error; self-reference detected.");
throw std::runtime_error("Internal error in decompress: self-reference");
}
curCode = dictionary[curCode].prev;
}
out += match_len;
}
}
std::vector<uint8_t> Gif::readSubBlocks(const uint8_t *&buffer) {
std::vector<uint8_t> data;
uint8_t block_size = *buffer;
buffer++;
while (block_size != 0) {
data.insert(data.end(), buffer, buffer + block_size);
buffer += block_size;
block_size = *buffer;
buffer++;
while (block_size != 0)
{
data.insert(data.end(), buffer, buffer + block_size);
buffer += block_size;
block_size = *buffer;
buffer++;
}
return data;
}
std::vector<uint8_t> Gif::processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits) {
ImageDescriptor image_descriptor;
readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor));
uint8_t lzw_code_size;
readBytes(buffer, &lzw_code_size, sizeof(uint8_t));
std::vector<uint8_t> compressed_data = readSubBlocks(buffer);
int uncompressed_data_length = image_descriptor.image_width * image_descriptor.image_height;
std::vector<uint8_t> uncompressed_data(uncompressed_data_length);
decompress(lzw_code_size, compressed_data.data(), static_cast<int>(compressed_data.size()), uncompressed_data.data());
return uncompressed_data;
}
std::vector<uint32_t> Gif::loadPalette(const uint8_t *buffer) {
uint8_t header[6];
std::memcpy(header, buffer, 6);
buffer += 6;
ScreenDescriptor screen_descriptor;
std::memcpy(&screen_descriptor, buffer, sizeof(ScreenDescriptor));
buffer += sizeof(ScreenDescriptor);
std::vector<uint32_t> global_color_table;
if (screen_descriptor.fields & 0x80) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size);
for (int i = 0; i < global_color_table_size; ++i) {
uint8_t r = buffer[0];
uint8_t g = buffer[1];
uint8_t b = buffer[2];
global_color_table[i] = (r << 16) | (g << 8) | b;
buffer += 3;
}
return data;
}
return global_color_table;
}
std::vector<uint8_t> Gif::processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h) {
uint8_t header[6];
std::memcpy(header, buffer, 6);
buffer += 6;
std::string headerStr(reinterpret_cast<char *>(header), 6);
if (headerStr != "GIF87a" && headerStr != "GIF89a") {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Formato de archivo GIF inválido: %s", headerStr.c_str());
throw std::runtime_error("Formato de archivo GIF inválido.");
}
std::vector<uint8_t> Gif::processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits)
{
ImageDescriptor image_descriptor;
readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor));
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Procesando GIF con cabecera: %s", headerStr.c_str());
uint8_t lzw_code_size;
readBytes(buffer, &lzw_code_size, sizeof(uint8_t));
ScreenDescriptor screen_descriptor;
readBytes(buffer, &screen_descriptor, sizeof(ScreenDescriptor));
std::vector<uint8_t> compressed_data = readSubBlocks(buffer);
int uncompressed_data_length = image_descriptor.image_width * image_descriptor.image_height;
std::vector<uint8_t> uncompressed_data(uncompressed_data_length);
w = screen_descriptor.width;
h = screen_descriptor.height;
decompress(lzw_code_size, compressed_data.data(), static_cast<int>(compressed_data.size()), uncompressed_data.data());
return uncompressed_data;
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Resolución del GIF: %dx%d", w, h);
int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
std::vector<RGB> global_color_table;
if (screen_descriptor.fields & 0x80) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size);
std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size);
buffer += 3 * global_color_table_size;
}
std::vector<uint32_t> Gif::loadPalette(const uint8_t *buffer)
{
uint8_t header[6];
std::memcpy(header, buffer, 6);
buffer += 6;
ScreenDescriptor screen_descriptor;
std::memcpy(&screen_descriptor, buffer, sizeof(ScreenDescriptor));
buffer += sizeof(ScreenDescriptor);
std::vector<uint32_t> global_color_table;
if (screen_descriptor.fields & 0x80)
{
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size);
for (int i = 0; i < global_color_table_size; ++i)
{
uint8_t r = buffer[0];
uint8_t g = buffer[1];
uint8_t b = buffer[2];
global_color_table[i] = (r << 16) | (g << 8) | b;
buffer += 3;
}
}
return global_color_table;
}
std::vector<uint8_t> Gif::processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h)
{
uint8_t header[6];
std::memcpy(header, buffer, 6);
buffer += 6;
std::string headerStr(reinterpret_cast<char *>(header), 6);
if (headerStr != "GIF87a" && headerStr != "GIF89a")
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Formato de archivo GIF inválido: %s", headerStr.c_str());
throw std::runtime_error("Formato de archivo GIF inválido.");
}
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Procesando GIF con cabecera: %s", headerStr.c_str());
ScreenDescriptor screen_descriptor;
readBytes(buffer, &screen_descriptor, sizeof(ScreenDescriptor));
w = screen_descriptor.width;
h = screen_descriptor.height;
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Resolución del GIF: %dx%d", w, h);
int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
std::vector<RGB> global_color_table;
if (screen_descriptor.fields & 0x80)
{
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size);
std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size);
buffer += 3 * global_color_table_size;
}
uint8_t block_type = *buffer++;
while (block_type != TRAILER)
{
if (block_type == EXTENSION_INTRODUCER)
{
uint8_t extension_label = *buffer++;
switch (extension_label)
{
case GRAPHIC_CONTROL:
{
uint8_t block_type = *buffer++;
while (block_type != TRAILER) {
if (block_type == EXTENSION_INTRODUCER) {
uint8_t extension_label = *buffer++;
switch (extension_label) {
case GRAPHIC_CONTROL: {
uint8_t blockSize = *buffer++;
buffer += blockSize;
uint8_t subBlockSize = *buffer++;
while (subBlockSize != 0)
{
while (subBlockSize != 0) {
buffer += subBlockSize;
subBlockSize = *buffer++;
}
@@ -251,51 +216,42 @@ namespace GIF
}
case APPLICATION_EXTENSION:
case COMMENT_EXTENSION:
case PLAINTEXT_EXTENSION:
{
case PLAINTEXT_EXTENSION: {
uint8_t blockSize = *buffer++;
buffer += blockSize;
uint8_t subBlockSize = *buffer++;
while (subBlockSize != 0)
{
while (subBlockSize != 0) {
buffer += subBlockSize;
subBlockSize = *buffer++;
}
break;
}
default:
{
default: {
uint8_t blockSize = *buffer++;
buffer += blockSize;
uint8_t subBlockSize = *buffer++;
while (subBlockSize != 0)
{
while (subBlockSize != 0) {
buffer += subBlockSize;
subBlockSize = *buffer++;
}
break;
}
}
}
else if (block_type == IMAGE_DESCRIPTOR)
{
return processImageDescriptor(buffer, global_color_table, color_resolution_bits);
}
else
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized block type: 0x%X", block_type);
return std::vector<uint8_t>{};
}
block_type = *buffer++;
} else if (block_type == IMAGE_DESCRIPTOR) {
return processImageDescriptor(buffer, global_color_table, color_resolution_bits);
} else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized block type: 0x%X", block_type);
return std::vector<uint8_t>{};
}
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "GIF procesado correctamente.");
return std::vector<uint8_t>{};
block_type = *buffer++;
}
std::vector<uint8_t> Gif::loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h)
{
return processGifStream(buffer, w, h);
}
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "GIF procesado correctamente.");
return std::vector<uint8_t>{};
}
} // namespace GIF
std::vector<uint8_t> Gif::loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h) {
return processGifStream(buffer, w, h);
}
} // namespace GIF

152
source/external/gif.h vendored
View File

@@ -1,102 +1,92 @@
#pragma once
#include <cstdint> // Para uint8_t, uint16_t, uint32_t
#include <vector> // Para vector
#include <cstdint> // Para uint8_t, uint16_t, uint32_t
#include <vector> // Para vector
namespace GIF
{
namespace GIF {
// Constantes definidas con constexpr, en lugar de macros
constexpr uint8_t EXTENSION_INTRODUCER = 0x21;
constexpr uint8_t IMAGE_DESCRIPTOR = 0x2C;
constexpr uint8_t TRAILER = 0x3B;
constexpr uint8_t GRAPHIC_CONTROL = 0xF9;
constexpr uint8_t APPLICATION_EXTENSION = 0xFF;
constexpr uint8_t COMMENT_EXTENSION = 0xFE;
constexpr uint8_t PLAINTEXT_EXTENSION = 0x01;
// Constantes definidas con constexpr, en lugar de macros
constexpr uint8_t EXTENSION_INTRODUCER = 0x21;
constexpr uint8_t IMAGE_DESCRIPTOR = 0x2C;
constexpr uint8_t TRAILER = 0x3B;
constexpr uint8_t GRAPHIC_CONTROL = 0xF9;
constexpr uint8_t APPLICATION_EXTENSION = 0xFF;
constexpr uint8_t COMMENT_EXTENSION = 0xFE;
constexpr uint8_t PLAINTEXT_EXTENSION = 0x01;
#pragma pack(push, 1)
struct ScreenDescriptor
{
uint16_t width;
uint16_t height;
uint8_t fields;
uint8_t background_color_index;
uint8_t pixel_aspect_ratio;
};
struct ScreenDescriptor {
uint16_t width;
uint16_t height;
uint8_t fields;
uint8_t background_color_index;
uint8_t pixel_aspect_ratio;
};
struct RGB
{
uint8_t r, g, b;
};
struct RGB {
uint8_t r, g, b;
};
struct ImageDescriptor
{
uint16_t image_left_position;
uint16_t image_top_position;
uint16_t image_width;
uint16_t image_height;
uint8_t fields;
};
struct ImageDescriptor {
uint16_t image_left_position;
uint16_t image_top_position;
uint16_t image_width;
uint16_t image_height;
uint8_t fields;
};
#pragma pack(pop)
struct DictionaryEntry
{
uint8_t byte;
int prev;
int len;
};
struct DictionaryEntry {
uint8_t byte;
int prev;
int len;
};
struct Extension
{
uint8_t extension_code;
uint8_t block_size;
};
struct Extension {
uint8_t extension_code;
uint8_t block_size;
};
struct GraphicControlExtension
{
uint8_t fields;
uint16_t delay_time;
uint8_t transparent_color_index;
};
struct GraphicControlExtension {
uint8_t fields;
uint16_t delay_time;
uint8_t transparent_color_index;
};
struct ApplicationExtension
{
uint8_t application_id[8];
uint8_t version[3];
};
struct ApplicationExtension {
uint8_t application_id[8];
uint8_t version[3];
};
struct PlaintextExtension
{
uint16_t left, top, width, height;
uint8_t cell_width, cell_height;
uint8_t foreground_color, background_color;
};
struct PlaintextExtension {
uint16_t left, top, width, height;
uint8_t cell_width, cell_height;
uint8_t foreground_color, background_color;
};
class Gif
{
public:
// Descompone (uncompress) el bloque comprimido usando LZW.
// Este método puede lanzar std::runtime_error en caso de error.
void decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out);
class Gif {
public:
// Descompone (uncompress) el bloque comprimido usando LZW.
// Este método puede lanzar std::runtime_error en caso de error.
void decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out);
// Carga la paleta (global color table) a partir de un buffer,
// retornándola en un vector de uint32_t (cada color se compone de R, G, B).
std::vector<uint32_t> loadPalette(const uint8_t *buffer);
// Carga la paleta (global color table) a partir de un buffer,
// retornándola en un vector de uint32_t (cada color se compone de R, G, B).
std::vector<uint32_t> loadPalette(const uint8_t *buffer);
// Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y
// asigna el ancho y alto mediante referencias.
std::vector<uint8_t> loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h);
// Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y
// asigna el ancho y alto mediante referencias.
std::vector<uint8_t> loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h);
private:
// Lee los sub-bloques de datos y los acumula en un std::vector<uint8_t>.
std::vector<uint8_t> readSubBlocks(const uint8_t *&buffer);
private:
// Lee los sub-bloques de datos y los acumula en un std::vector<uint8_t>.
std::vector<uint8_t> readSubBlocks(const uint8_t *&buffer);
// Procesa el Image Descriptor y retorna el vector de datos sin comprimir.
std::vector<uint8_t> processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits);
// Procesa el Image Descriptor y retorna el vector de datos sin comprimir.
std::vector<uint8_t> processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits);
// Procesa el stream completo del GIF y devuelve los datos sin comprimir.
std::vector<uint8_t> processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h);
};
// Procesa el stream completo del GIF y devuelve los datos sin comprimir.
std::vector<uint8_t> processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h);
};
} // namespace GIF
} // namespace GIF

View File

@@ -1,26 +1,24 @@
#ifndef JA_USESDLMIXER
#include "jail_audio.h"
#include <SDL3/SDL.h> // Para SDL_AudioFormat, SDL_BindAudioStream, SDL_Se...
#include <stdint.h> // Para uint32_t, uint8_t
#include <stdio.h> // Para NULL, fseek, printf, fclose, fopen, fread
#include <stdlib.h> // Para free, malloc
#include <string.h> // Para strcpy, strlen
#include <SDL3/SDL.h> // Para SDL_AudioFormat, SDL_BindAudioStream, SDL_Se...
#include <stdint.h> // Para uint32_t, uint8_t
#include <stdio.h> // Para NULL, fseek, printf, fclose, fopen, fread
#include <stdlib.h> // Para free, malloc
#include <string.h> // Para strcpy, strlen
#include "stb_vorbis.h" // Para stb_vorbis_decode_memory
#include "stb_vorbis.h" // Para stb_vorbis_decode_memory
#define JA_MAX_SIMULTANEOUS_CHANNELS 20
#define JA_MAX_GROUPS 2
struct JA_Sound_t
{
struct JA_Sound_t {
SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000};
Uint32 length{0};
Uint8 *buffer{NULL};
};
struct JA_Channel_t
{
struct JA_Channel_t {
JA_Sound_t *sound{nullptr};
int pos{0};
int times{0};
@@ -29,8 +27,7 @@ struct JA_Channel_t
JA_Channel_state state{JA_CHANNEL_FREE};
};
struct JA_Music_t
{
struct JA_Music_t {
SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000};
Uint32 length{0};
Uint8 *buffer{nullptr};
@@ -58,59 +55,43 @@ int fade_start_time;
int fade_duration;
int fade_initial_volume;
Uint32 JA_UpdateCallback(void *userdata, SDL_TimerID timerID, Uint32 interval)
{
if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING)
{
if (fading)
{
Uint32 JA_UpdateCallback(void *userdata, SDL_TimerID timerID, Uint32 interval) {
if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING) {
if (fading) {
int time = SDL_GetTicks();
if (time > (fade_start_time + fade_duration))
{
if (time > (fade_start_time + fade_duration)) {
fading = false;
JA_StopMusic();
return 30;
}
else
{
} else {
const int time_passed = time - fade_start_time;
const float percent = (float)time_passed / (float)fade_duration;
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume * (1.0 - percent));
}
}
if (current_music->times != 0)
{
if ((Uint32)SDL_GetAudioStreamAvailable(current_music->stream) < (current_music->length / 2))
{
if (current_music->times != 0) {
if ((Uint32)SDL_GetAudioStreamAvailable(current_music->stream) < (current_music->length / 2)) {
SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length);
}
if (current_music->times > 0)
current_music->times--;
}
else
{
} else {
if (SDL_GetAudioStreamAvailable(current_music->stream) == 0)
JA_StopMusic();
}
}
if (JA_soundEnabled)
{
if (JA_soundEnabled) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i)
if (channels[i].state == JA_CHANNEL_PLAYING)
{
if (channels[i].times != 0)
{
if ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length / 2))
{
if (channels[i].state == JA_CHANNEL_PLAYING) {
if (channels[i].times != 0) {
if ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length / 2)) {
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length);
if (channels[i].times > 0)
channels[i].times--;
}
}
else
{
} else {
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0)
JA_StopChannel(i);
}
@@ -120,8 +101,7 @@ Uint32 JA_UpdateCallback(void *userdata, SDL_TimerID timerID, Uint32 interval)
return 30;
}
void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels)
{
void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels) {
#ifdef DEBUG
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
#endif
@@ -140,8 +120,7 @@ void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channel
JA_timerID = SDL_AddTimer(30, JA_UpdateCallback, nullptr);
}
void JA_Quit()
{
void JA_Quit() {
if (JA_timerID)
SDL_RemoveTimer(JA_timerID);
@@ -150,8 +129,7 @@ void JA_Quit()
sdlAudioDevice = 0;
}
JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length)
{
JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length) {
JA_Music_t *music = new JA_Music_t();
int chan, samplerate;
@@ -170,8 +148,7 @@ JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length)
return music;
}
JA_Music_t *JA_LoadMusic(const char *filename)
{
JA_Music_t *JA_LoadMusic(const char *filename) {
// [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid.
FILE *f = fopen(filename, "rb");
fseek(f, 0, SEEK_END);
@@ -191,8 +168,7 @@ JA_Music_t *JA_LoadMusic(const char *filename)
return music;
}
void JA_PlayMusic(JA_Music_t *music, const int loop)
{
void JA_PlayMusic(JA_Music_t *music, const int loop) {
if (!JA_musicEnabled)
return;
@@ -212,15 +188,13 @@ void JA_PlayMusic(JA_Music_t *music, const int loop)
// SDL_ResumeAudioStreamDevice(current_music->stream);
}
char *JA_GetMusicFilename(JA_Music_t *music)
{
char *JA_GetMusicFilename(JA_Music_t *music) {
if (!music)
music = current_music;
return music->filename;
}
void JA_PauseMusic()
{
void JA_PauseMusic() {
if (!JA_musicEnabled)
return;
if (!current_music || current_music->state == JA_MUSIC_INVALID)
@@ -231,8 +205,7 @@ void JA_PauseMusic()
SDL_UnbindAudioStream(current_music->stream);
}
void JA_ResumeMusic()
{
void JA_ResumeMusic() {
if (!JA_musicEnabled)
return;
if (!current_music || current_music->state == JA_MUSIC_INVALID)
@@ -243,8 +216,7 @@ void JA_ResumeMusic()
SDL_BindAudioStream(sdlAudioDevice, current_music->stream);
}
void JA_StopMusic()
{
void JA_StopMusic() {
if (!JA_musicEnabled)
return;
if (!current_music || current_music->state == JA_MUSIC_INVALID)
@@ -259,8 +231,7 @@ void JA_StopMusic()
current_music->filename = nullptr;
}
void JA_FadeOutMusic(const int milliseconds)
{
void JA_FadeOutMusic(const int milliseconds) {
if (!JA_musicEnabled)
return;
if (current_music == NULL || current_music->state == JA_MUSIC_INVALID)
@@ -272,8 +243,7 @@ void JA_FadeOutMusic(const int milliseconds)
fade_initial_volume = JA_musicVolume;
}
JA_Music_state JA_GetMusicState()
{
JA_Music_state JA_GetMusicState() {
if (!JA_musicEnabled)
return JA_MUSIC_DISABLED;
if (!current_music)
@@ -282,8 +252,7 @@ JA_Music_state JA_GetMusicState()
return current_music->state;
}
void JA_DeleteMusic(JA_Music_t *music)
{
void JA_DeleteMusic(JA_Music_t *music) {
if (current_music == music)
current_music = nullptr;
SDL_free(music->buffer);
@@ -292,68 +261,59 @@ void JA_DeleteMusic(JA_Music_t *music)
delete music;
}
float JA_SetMusicVolume(float volume)
{
float JA_SetMusicVolume(float volume) {
JA_musicVolume = SDL_clamp(volume, 0.0f, 1.0f);
if (current_music)
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
return JA_musicVolume;
}
void JA_SetMusicPosition(float value)
{
void JA_SetMusicPosition(float value) {
if (!current_music)
return;
current_music->pos = value * current_music->spec.freq;
}
float JA_GetMusicPosition()
{
float JA_GetMusicPosition() {
if (!current_music)
return 0;
return float(current_music->pos) / float(current_music->spec.freq);
}
void JA_EnableMusic(const bool value)
{
void JA_EnableMusic(const bool value) {
if (!value && current_music && (current_music->state == JA_MUSIC_PLAYING))
JA_StopMusic();
JA_musicEnabled = value;
}
JA_Sound_t *JA_NewSound(Uint8 *buffer, Uint32 length)
{
JA_Sound_t *JA_NewSound(Uint8 *buffer, Uint32 length) {
JA_Sound_t *sound = new JA_Sound_t();
sound->buffer = buffer;
sound->length = length;
return sound;
}
JA_Sound_t *JA_LoadSound(uint8_t *buffer, uint32_t size)
{
JA_Sound_t *JA_LoadSound(uint8_t *buffer, uint32_t size) {
JA_Sound_t *sound = new JA_Sound_t();
SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), 1, &sound->spec, &sound->buffer, &sound->length);
return sound;
}
JA_Sound_t *JA_LoadSound(const char *filename)
{
JA_Sound_t *JA_LoadSound(const char *filename) {
JA_Sound_t *sound = new JA_Sound_t();
SDL_LoadWAV(filename, &sound->spec, &sound->buffer, &sound->length);
return sound;
}
int JA_PlaySound(JA_Sound_t *sound, const int loop, const int group)
{
int JA_PlaySound(JA_Sound_t *sound, const int loop, const int group) {
if (!JA_soundEnabled)
return -1;
int channel = 0;
while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE)
{
while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) {
channel++;
}
if (channel == JA_MAX_SIMULTANEOUS_CHANNELS)
@@ -372,8 +332,7 @@ int JA_PlaySound(JA_Sound_t *sound, const int loop, const int group)
return channel;
}
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop, const int group)
{
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop, const int group) {
if (!JA_soundEnabled)
return -1;
@@ -393,10 +352,8 @@ int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop,
return channel;
}
void JA_DeleteSound(JA_Sound_t *sound)
{
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
{
void JA_DeleteSound(JA_Sound_t *sound) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].sound == sound)
JA_StopChannel(i);
}
@@ -404,25 +361,19 @@ void JA_DeleteSound(JA_Sound_t *sound)
delete sound;
}
void JA_PauseChannel(const int channel)
{
void JA_PauseChannel(const int channel) {
if (!JA_soundEnabled)
return;
if (channel == -1)
{
if (channel == -1) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
if (channels[i].state == JA_CHANNEL_PLAYING)
{
if (channels[i].state == JA_CHANNEL_PLAYING) {
channels[i].state = JA_CHANNEL_PAUSED;
// SDL_PauseAudioStreamDevice(channels[i].stream);
SDL_UnbindAudioStream(channels[i].stream);
}
}
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
{
if (channels[channel].state == JA_CHANNEL_PLAYING)
{
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
if (channels[channel].state == JA_CHANNEL_PLAYING) {
channels[channel].state = JA_CHANNEL_PAUSED;
// SDL_PauseAudioStreamDevice(channels[channel].stream);
SDL_UnbindAudioStream(channels[channel].stream);
@@ -430,25 +381,19 @@ void JA_PauseChannel(const int channel)
}
}
void JA_ResumeChannel(const int channel)
{
void JA_ResumeChannel(const int channel) {
if (!JA_soundEnabled)
return;
if (channel == -1)
{
if (channel == -1) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
if (channels[i].state == JA_CHANNEL_PAUSED)
{
if (channels[i].state == JA_CHANNEL_PAUSED) {
channels[i].state = JA_CHANNEL_PLAYING;
// SDL_ResumeAudioStreamDevice(channels[i].stream);
SDL_BindAudioStream(sdlAudioDevice, channels[i].stream);
}
}
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
{
if (channels[channel].state == JA_CHANNEL_PAUSED)
{
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
if (channels[channel].state == JA_CHANNEL_PAUSED) {
channels[channel].state = JA_CHANNEL_PLAYING;
// SDL_ResumeAudioStreamDevice(channels[channel].stream);
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
@@ -456,15 +401,12 @@ void JA_ResumeChannel(const int channel)
}
}
void JA_StopChannel(const int channel)
{
void JA_StopChannel(const int channel) {
if (!JA_soundEnabled)
return;
if (channel == -1)
{
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
{
if (channel == -1) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].state != JA_CHANNEL_FREE)
SDL_DestroyAudioStream(channels[i].stream);
channels[i].stream = nullptr;
@@ -472,9 +414,7 @@ void JA_StopChannel(const int channel)
channels[i].pos = 0;
channels[i].sound = NULL;
}
}
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
{
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
if (channels[channel].state != JA_CHANNEL_FREE)
SDL_DestroyAudioStream(channels[channel].stream);
channels[channel].stream = nullptr;
@@ -484,8 +424,7 @@ void JA_StopChannel(const int channel)
}
}
JA_Channel_state JA_GetChannelState(const int channel)
{
JA_Channel_state JA_GetChannelState(const int channel) {
if (!JA_soundEnabled)
return JA_SOUND_DISABLED;
@@ -495,11 +434,9 @@ JA_Channel_state JA_GetChannelState(const int channel)
return channels[channel].state;
}
float JA_SetSoundVolume(float volume, const int group)
{
float JA_SetSoundVolume(float volume, const int group) {
const float v = SDL_clamp(volume, 0.0f, 1.0f);
for (int i = 0; i < JA_MAX_GROUPS; ++i)
{
for (int i = 0; i < JA_MAX_GROUPS; ++i) {
if (group == -1 || group == i)
JA_soundVolume[i] = v;
}
@@ -512,18 +449,15 @@ float JA_SetSoundVolume(float volume, const int group)
return v;
}
void JA_EnableSound(const bool value)
{
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
{
void JA_EnableSound(const bool value) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].state == JA_CHANNEL_PLAYING)
JA_StopChannel(i);
}
JA_soundEnabled = value;
}
float JA_SetVolume(float volume)
{
float JA_SetVolume(float volume) {
JA_SetSoundVolume(JA_SetMusicVolume(volume) / 2.0f);
return JA_musicVolume;

View File

@@ -1,16 +1,14 @@
#pragma once
#include <SDL3/SDL.h>
enum JA_Channel_state
{
enum JA_Channel_state {
JA_CHANNEL_INVALID,
JA_CHANNEL_FREE,
JA_CHANNEL_PLAYING,
JA_CHANNEL_PAUSED,
JA_SOUND_DISABLED
};
enum JA_Music_state
{
enum JA_Music_state {
JA_MUSIC_INVALID,
JA_MUSIC_PLAYING,
JA_MUSIC_PAUSED,

View File

@@ -1,461 +1,416 @@
#include "jail_shader.h"
#include <SDL3/SDL.h> // Para SDL_GL_GetProcAddress, SDL_LogError
#include <stdint.h> // Para uintptr_t
#include <SDL3/SDL.h> // Para SDL_GL_GetProcAddress, SDL_LogError
#include <stdint.h> // Para uintptr_t
#include <cstring> // Para strncmp
#include <stdexcept> // Para runtime_error
#include <vector> // Para vector
#ifdef __APPLE__
#include <OpenGL/OpenGL.h> // Para OpenGL en macOS
#include <OpenGL/OpenGL.h> // Para OpenGL en macOS
#include "CoreFoundation/CoreFoundation.h" // Para Core Foundation en macOS
#include "CoreFoundation/CoreFoundation.h" // Para Core Foundation en macOS
#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
#include <OpenGL/gl3.h> // Para OpenGL 3 en macOS
#else // NO ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
#include <OpenGL/gl.h> // Para OpenGL (compatibilidad) en macOS
#endif // ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
#else // SI NO ES __APPLE__
#include <SDL3/SDL_opengl.h> // Para GLuint, GLint, glTexCoord2f, glVertex2f
#endif // __APPLE__
#include <OpenGL/gl3.h> // Para OpenGL 3 en macOS
#else // NO ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
#include <OpenGL/gl.h> // Para OpenGL (compatibilidad) en macOS
#endif // ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
#else // SI NO ES __APPLE__
#include <SDL3/SDL_opengl.h> // Para GLuint, GLint, glTexCoord2f, glVertex2f
#endif // __APPLE__
namespace shader
{
// Constantes
const GLuint INVALID_SHADER_ID = 0;
const GLuint INVALID_PROGRAM_ID = 0;
const GLuint DEFAULT_TEXTURE_ID = 1;
namespace shader {
// Constantes
const GLuint INVALID_SHADER_ID = 0;
const GLuint INVALID_PROGRAM_ID = 0;
const GLuint DEFAULT_TEXTURE_ID = 1;
// Variables globales
SDL_Window *win = nullptr;
SDL_Renderer *renderer = nullptr;
GLuint programId = 0;
SDL_Texture *backBuffer = nullptr;
SDL_Point win_size = {320 * 4, 256 * 4};
SDL_FPoint tex_size = {320, 256};
bool usingOpenGL = false;
// Variables globales
SDL_Window *win = nullptr;
SDL_Renderer *renderer = nullptr;
GLuint programId = 0;
SDL_Texture *backBuffer = nullptr;
SDL_Point win_size = {320 * 4, 256 * 4};
SDL_FPoint tex_size = {320, 256};
bool usingOpenGL = false;
#ifndef __APPLE__
// Declaración de funciones de extensión de OpenGL (evitando GLEW)
PFNGLCREATESHADERPROC glCreateShader;
PFNGLSHADERSOURCEPROC glShaderSource;
PFNGLCOMPILESHADERPROC glCompileShader;
PFNGLGETSHADERIVPROC glGetShaderiv;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
PFNGLDELETESHADERPROC glDeleteShader;
PFNGLATTACHSHADERPROC glAttachShader;
PFNGLCREATEPROGRAMPROC glCreateProgram;
PFNGLLINKPROGRAMPROC glLinkProgram;
PFNGLVALIDATEPROGRAMPROC glValidateProgram;
PFNGLGETPROGRAMIVPROC glGetProgramiv;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
PFNGLUSEPROGRAMPROC glUseProgram;
PFNGLDELETEPROGRAMPROC glDeleteProgram;
// Declaración de funciones de extensión de OpenGL (evitando GLEW)
PFNGLCREATESHADERPROC glCreateShader;
PFNGLSHADERSOURCEPROC glShaderSource;
PFNGLCOMPILESHADERPROC glCompileShader;
PFNGLGETSHADERIVPROC glGetShaderiv;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
PFNGLDELETESHADERPROC glDeleteShader;
PFNGLATTACHSHADERPROC glAttachShader;
PFNGLCREATEPROGRAMPROC glCreateProgram;
PFNGLLINKPROGRAMPROC glLinkProgram;
PFNGLVALIDATEPROGRAMPROC glValidateProgram;
PFNGLGETPROGRAMIVPROC glGetProgramiv;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
PFNGLUSEPROGRAMPROC glUseProgram;
PFNGLDELETEPROGRAMPROC glDeleteProgram;
bool initGLExtensions()
{
glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader");
glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource");
glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader");
glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv");
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog");
glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader");
glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader");
glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram");
glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram");
glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram");
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv");
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog");
glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram");
glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram");
bool initGLExtensions() {
glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader");
glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource");
glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader");
glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv");
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog");
glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader");
glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader");
glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram");
glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram");
glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram");
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv");
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog");
glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram");
glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram");
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog &&
glUseProgram && glDeleteProgram;
}
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog &&
glUseProgram && glDeleteProgram;
}
#endif
// Función para verificar errores de OpenGL
void checkGLError(const char *operation)
{
GLenum error = glGetError();
if (error != GL_NO_ERROR)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error OpenGL en %s: 0x%x", operation, error);
}
// Función para verificar errores de OpenGL
void checkGLError(const char *operation) {
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error OpenGL en %s: 0x%x",
operation,
error);
}
}
// Función para compilar un shader a partir de un std::string
GLuint compileShader(const std::string &source, GLuint shader_type) {
if (source.empty()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR FATAL: El código fuente del shader está vacío.");
throw std::runtime_error("ERROR FATAL: El código fuente del shader está vacío.");
}
// Función para compilar un shader a partir de un std::string
GLuint compileShader(const std::string &source, GLuint shader_type)
{
if (source.empty())
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR FATAL: El código fuente del shader está vacío.");
throw std::runtime_error("ERROR FATAL: El código fuente del shader está vacío.");
// Crear identificador del shader
GLuint shader_id = glCreateShader(shader_type);
if (shader_id == INVALID_SHADER_ID) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear el shader.");
checkGLError("glCreateShader");
return INVALID_SHADER_ID;
}
// Agregar una directiva según el tipo de shader
std::string directive = (shader_type == GL_VERTEX_SHADER)
? "#define VERTEX\n"
: "#define FRAGMENT\n";
const char *sources[2] = {directive.c_str(), source.c_str()};
// Especificar el código fuente del shader
glShaderSource(shader_id, 2, sources, nullptr);
checkGLError("glShaderSource");
// Compilar el shader
glCompileShader(shader_id);
checkGLError("glCompileShader");
// Verificar si la compilación fue exitosa
GLint compiled_ok = GL_FALSE;
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &compiled_ok);
if (compiled_ok != GL_TRUE) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error en la compilación del shader (%d)!", shader_id);
GLint log_length;
glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
std::vector<GLchar> log(log_length);
glGetShaderInfoLog(shader_id, log_length, &log_length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Registro de compilación del shader: %s", log.data());
}
glDeleteShader(shader_id);
return INVALID_SHADER_ID;
}
return shader_id;
}
// Crear identificador del shader
GLuint shader_id = glCreateShader(shader_type);
if (shader_id == INVALID_SHADER_ID)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear el shader.");
checkGLError("glCreateShader");
return INVALID_SHADER_ID;
}
// Función para compilar un programa de shaders (vertex y fragment) a partir de std::string
GLuint compileProgram(const std::string &vertex_shader_source, const std::string &fragment_shader_source) {
GLuint program_id = glCreateProgram();
if (program_id == INVALID_PROGRAM_ID) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear el programa de shaders.");
checkGLError("glCreateProgram");
return INVALID_PROGRAM_ID;
}
// Agregar una directiva según el tipo de shader
std::string directive = (shader_type == GL_VERTEX_SHADER)
? "#define VERTEX\n"
: "#define FRAGMENT\n";
// Si el fragment shader está vacío, reutilizamos el código del vertex shader
GLuint vertex_shader_id = compileShader(vertex_shader_source, GL_VERTEX_SHADER);
GLuint fragment_shader_id = compileShader(fragment_shader_source.empty() ? vertex_shader_source : fragment_shader_source, GL_FRAGMENT_SHADER);
const char *sources[2] = {directive.c_str(), source.c_str()};
if (vertex_shader_id != INVALID_SHADER_ID && fragment_shader_id != INVALID_SHADER_ID) {
// Asociar los shaders al programa
glAttachShader(program_id, vertex_shader_id);
checkGLError("glAttachShader vertex");
glAttachShader(program_id, fragment_shader_id);
checkGLError("glAttachShader fragment");
// Especificar el código fuente del shader
glShaderSource(shader_id, 2, sources, nullptr);
checkGLError("glShaderSource");
glLinkProgram(program_id);
checkGLError("glLinkProgram");
// Compilar el shader
glCompileShader(shader_id);
checkGLError("glCompileShader");
// Verificar si la compilación fue exitosa
GLint compiled_ok = GL_FALSE;
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &compiled_ok);
if (compiled_ok != GL_TRUE)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error en la compilación del shader (%d)!", shader_id);
// Verificar el estado del enlace
GLint isLinked = GL_FALSE;
glGetProgramiv(program_id, GL_LINK_STATUS, &isLinked);
if (isLinked == GL_FALSE) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al enlazar el programa de shaders.");
GLint log_length;
glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0)
{
std::vector<GLchar> log(log_length);
glGetShaderInfoLog(shader_id, log_length, &log_length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Registro de compilación del shader: %s", log.data());
glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
std::vector<char> log(log_length);
glGetProgramInfoLog(program_id, log_length, &log_length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Registro de enlace del programa: %s", log.data());
}
glDeleteShader(shader_id);
return INVALID_SHADER_ID;
}
return shader_id;
}
// Función para compilar un programa de shaders (vertex y fragment) a partir de std::string
GLuint compileProgram(const std::string &vertex_shader_source, const std::string &fragment_shader_source)
{
GLuint program_id = glCreateProgram();
if (program_id == INVALID_PROGRAM_ID)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear el programa de shaders.");
checkGLError("glCreateProgram");
return INVALID_PROGRAM_ID;
}
// Si el fragment shader está vacío, reutilizamos el código del vertex shader
GLuint vertex_shader_id = compileShader(vertex_shader_source, GL_VERTEX_SHADER);
GLuint fragment_shader_id = compileShader(fragment_shader_source.empty() ? vertex_shader_source : fragment_shader_source, GL_FRAGMENT_SHADER);
if (vertex_shader_id != INVALID_SHADER_ID && fragment_shader_id != INVALID_SHADER_ID)
{
// Asociar los shaders al programa
glAttachShader(program_id, vertex_shader_id);
checkGLError("glAttachShader vertex");
glAttachShader(program_id, fragment_shader_id);
checkGLError("glAttachShader fragment");
glLinkProgram(program_id);
checkGLError("glLinkProgram");
// Verificar el estado del enlace
GLint isLinked = GL_FALSE;
glGetProgramiv(program_id, GL_LINK_STATUS, &isLinked);
if (isLinked == GL_FALSE)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al enlazar el programa de shaders.");
GLint log_length;
glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0)
{
std::vector<char> log(log_length);
glGetProgramInfoLog(program_id, log_length, &log_length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Registro de enlace del programa: %s", log.data());
}
glDeleteProgram(program_id);
program_id = INVALID_PROGRAM_ID;
}
else
{
glValidateProgram(program_id);
checkGLError("glValidateProgram");
// Log de información del programa (solo si hay información)
GLint log_length;
glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 1) // > 1 porque algunos drivers devuelven 1 para cadena vacía
{
std::vector<char> log(log_length);
glGetProgramInfoLog(program_id, log_length, &log_length, log.data());
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Registro de información del programa:\n%s", log.data());
}
}
}
else
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudieron compilar los shaders.");
glDeleteProgram(program_id);
program_id = INVALID_PROGRAM_ID;
}
} else {
glValidateProgram(program_id);
checkGLError("glValidateProgram");
// Limpiar los shaders (ya no son necesarios después del enlace)
if (vertex_shader_id != INVALID_SHADER_ID)
{
glDeleteShader(vertex_shader_id);
// Log de información del programa (solo si hay información)
GLint log_length;
glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 1) // > 1 porque algunos drivers devuelven 1 para cadena vacía
{
std::vector<char> log(log_length);
glGetProgramInfoLog(program_id, log_length, &log_length, log.data());
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Registro de información del programa:\n%s", log.data());
}
}
if (fragment_shader_id != INVALID_SHADER_ID)
{
glDeleteShader(fragment_shader_id);
}
return program_id;
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudieron compilar los shaders.");
glDeleteProgram(program_id);
program_id = INVALID_PROGRAM_ID;
}
// Función para obtener el ID de textura OpenGL desde SDL3
GLuint getTextureID(SDL_Texture *texture)
{
if (!texture)
return DEFAULT_TEXTURE_ID;
// Intentar obtener el ID de textura OpenGL desde las propiedades de SDL3
SDL_PropertiesID props = SDL_GetTextureProperties(texture);
GLuint textureId = 0;
// Intentar diferentes nombres de propiedades según la versión de SDL3
textureId = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "SDL.texture.opengl.texture", nullptr);
// Si la primera no funciona, intentar con el nombre alternativo
if (textureId == 0)
{
textureId = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "texture.opengl.texture", nullptr);
}
// Si aún no funciona, intentar obtener como número
if (textureId == 0)
{
textureId = (GLuint)SDL_GetNumberProperty(props, "SDL.texture.opengl.texture", DEFAULT_TEXTURE_ID);
}
// Si ninguna funciona, usar el método manual de bindeo de textura
if (textureId == 0)
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"No se pudo obtener el ID de textura OpenGL, usando ID por defecto (%d)", DEFAULT_TEXTURE_ID);
textureId = DEFAULT_TEXTURE_ID;
}
return textureId;
// Limpiar los shaders (ya no son necesarios después del enlace)
if (vertex_shader_id != INVALID_SHADER_ID) {
glDeleteShader(vertex_shader_id);
}
if (fragment_shader_id != INVALID_SHADER_ID) {
glDeleteShader(fragment_shader_id);
}
bool init(SDL_Window *window, SDL_Texture *back_buffer_texture, const std::string &vertex_shader, const std::string &fragment_shader)
{
shader::win = window;
shader::renderer = SDL_GetRenderer(window);
shader::backBuffer = back_buffer_texture;
return program_id;
}
if (!shader::renderer)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el renderer de la ventana.");
return false;
}
// Función para obtener el ID de textura OpenGL desde SDL3
GLuint getTextureID(SDL_Texture *texture) {
if (!texture)
return DEFAULT_TEXTURE_ID;
SDL_GetWindowSize(window, &win_size.x, &win_size.y);
SDL_GetTextureSize(back_buffer_texture, &tex_size.x, &tex_size.y);
// Intentar obtener el ID de textura OpenGL desde las propiedades de SDL3
SDL_PropertiesID props = SDL_GetTextureProperties(texture);
GLuint textureId = 0;
const auto render_name = SDL_GetRendererName(renderer);
if (!render_name)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el nombre del renderer.");
return false;
}
// Intentar diferentes nombres de propiedades según la versión de SDL3
textureId = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "SDL.texture.opengl.texture", nullptr);
// Verificar que el renderer sea OpenGL
if (!strncmp(render_name, "opengl", 6))
{
// Si la primera no funciona, intentar con el nombre alternativo
if (textureId == 0) {
textureId = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "texture.opengl.texture", nullptr);
}
// Si aún no funciona, intentar obtener como número
if (textureId == 0) {
textureId = (GLuint)SDL_GetNumberProperty(props, "SDL.texture.opengl.texture", DEFAULT_TEXTURE_ID);
}
// Si ninguna funciona, usar el método manual de bindeo de textura
if (textureId == 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"No se pudo obtener el ID de textura OpenGL, usando ID por defecto (%d)",
DEFAULT_TEXTURE_ID);
textureId = DEFAULT_TEXTURE_ID;
}
return textureId;
}
bool init(SDL_Window *window, SDL_Texture *back_buffer_texture, const std::string &vertex_shader, const std::string &fragment_shader) {
shader::win = window;
shader::renderer = SDL_GetRenderer(window);
shader::backBuffer = back_buffer_texture;
if (!shader::renderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el renderer de la ventana.");
return false;
}
SDL_GetWindowSize(window, &win_size.x, &win_size.y);
SDL_GetTextureSize(back_buffer_texture, &tex_size.x, &tex_size.y);
const auto render_name = SDL_GetRendererName(renderer);
if (!render_name) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el nombre del renderer.");
return false;
}
// Verificar que el renderer sea OpenGL
if (!strncmp(render_name, "opengl", 6)) {
#ifndef __APPLE__
if (!initGLExtensions())
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se han podido inicializar las extensiones de OpenGL.");
usingOpenGL = false;
return false;
}
#endif
// Compilar el programa de shaders utilizando std::string
programId = compileProgram(vertex_shader, fragment_shader);
if (programId == INVALID_PROGRAM_ID)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se pudo compilar el programa de shaders.");
usingOpenGL = false;
return false;
}
}
else
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: El driver del renderer no es OpenGL (%s).", render_name);
if (!initGLExtensions()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se han podido inicializar las extensiones de OpenGL.");
usingOpenGL = false;
return false;
}
usingOpenGL = true;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Shader system initialized successfully.");
return true;
#endif
// Compilar el programa de shaders utilizando std::string
programId = compileProgram(vertex_shader, fragment_shader);
if (programId == INVALID_PROGRAM_ID) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se pudo compilar el programa de shaders.");
usingOpenGL = false;
return false;
}
} else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: El driver del renderer no es OpenGL (%s).", render_name);
usingOpenGL = false;
return false;
}
void render()
{
// Establece el color de fondo
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_SetRenderTarget(renderer, nullptr);
SDL_RenderClear(renderer);
usingOpenGL = true;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Shader system initialized successfully.");
return true;
}
if (usingOpenGL && programId != INVALID_PROGRAM_ID)
{
// Guardar estados de OpenGL
GLint oldProgramId;
glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId);
void render() {
// Establece el color de fondo
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_SetRenderTarget(renderer, nullptr);
SDL_RenderClear(renderer);
GLint oldViewport[4];
glGetIntegerv(GL_VIEWPORT, oldViewport);
if (usingOpenGL && programId != INVALID_PROGRAM_ID) {
// Guardar estados de OpenGL
GLint oldProgramId;
glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId);
GLboolean wasTextureEnabled = glIsEnabled(GL_TEXTURE_2D);
GLint oldTextureId;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTextureId);
GLint oldViewport[4];
glGetIntegerv(GL_VIEWPORT, oldViewport);
// Obtener y bindear la textura
GLuint textureId = getTextureID(backBuffer);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);
checkGLError("glBindTexture");
GLboolean wasTextureEnabled = glIsEnabled(GL_TEXTURE_2D);
GLint oldTextureId;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTextureId);
// Usar nuestro programa de shaders
glUseProgram(programId);
checkGLError("glUseProgram");
// Obtener y bindear la textura
GLuint textureId = getTextureID(backBuffer);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);
checkGLError("glBindTexture");
// Recupera el tamaño lógico configurado con SDL_RenderSetLogicalSize
int logicalW, logicalH;
SDL_RendererLogicalPresentation mode;
SDL_GetRenderLogicalPresentation(renderer, &logicalW, &logicalH, &mode);
if (logicalW == 0 || logicalH == 0)
{
logicalW = win_size.x;
logicalH = win_size.y;
// Usar nuestro programa de shaders
glUseProgram(programId);
checkGLError("glUseProgram");
// Recupera el tamaño lógico configurado con SDL_RenderSetLogicalSize
int logicalW, logicalH;
SDL_RendererLogicalPresentation mode;
SDL_GetRenderLogicalPresentation(renderer, &logicalW, &logicalH, &mode);
if (logicalW == 0 || logicalH == 0) {
logicalW = win_size.x;
logicalH = win_size.y;
}
// Cálculo del viewport
int viewportX = 0, viewportY = 0, viewportW = win_size.x, viewportH = win_size.y;
const bool USE_INTEGER_SCALE = mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE;
if (USE_INTEGER_SCALE) {
// Calcula el factor de escalado entero máximo que se puede aplicar
int scaleX = win_size.x / logicalW;
int scaleY = win_size.y / logicalH;
int scale = (scaleX < scaleY ? scaleX : scaleY);
if (scale < 1) {
scale = 1;
}
// Cálculo del viewport
int viewportX = 0, viewportY = 0, viewportW = win_size.x, viewportH = win_size.y;
const bool USE_INTEGER_SCALE = mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE;
if (USE_INTEGER_SCALE)
{
// Calcula el factor de escalado entero máximo que se puede aplicar
int scaleX = win_size.x / logicalW;
int scaleY = win_size.y / logicalH;
int scale = (scaleX < scaleY ? scaleX : scaleY);
if (scale < 1)
{
scale = 1;
}
viewportW = logicalW * scale;
viewportH = logicalH * scale;
viewportW = logicalW * scale;
viewportH = logicalH * scale;
viewportX = (win_size.x - viewportW) / 2;
viewportY = (win_size.y - viewportH) / 2;
} else {
// Letterboxing: preserva la relación de aspecto usando una escala flotante
float windowAspect = static_cast<float>(win_size.x) / win_size.y;
float logicalAspect = static_cast<float>(logicalW) / logicalH;
if (windowAspect > logicalAspect) {
viewportW = static_cast<int>(logicalAspect * win_size.y);
viewportX = (win_size.x - viewportW) / 2;
} else {
viewportH = static_cast<int>(win_size.x / logicalAspect);
viewportY = (win_size.y - viewportH) / 2;
}
else
{
// Letterboxing: preserva la relación de aspecto usando una escala flotante
float windowAspect = static_cast<float>(win_size.x) / win_size.y;
float logicalAspect = static_cast<float>(logicalW) / logicalH;
if (windowAspect > logicalAspect)
{
viewportW = static_cast<int>(logicalAspect * win_size.y);
viewportX = (win_size.x - viewportW) / 2;
}
else
{
viewportH = static_cast<int>(win_size.x / logicalAspect);
viewportY = (win_size.y - viewportH) / 2;
}
}
glViewport(viewportX, viewportY, viewportW, viewportH);
checkGLError("glViewport");
// Configurar la proyección ortográfica usando el espacio lógico
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Queremos que el origen esté en la esquina superior izquierda del espacio lógico.
glOrtho(0, static_cast<GLdouble>(logicalW), static_cast<GLdouble>(logicalH), 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Dibuja el quad con las coordenadas ajustadas.
// Se asignan las coordenadas de textura "normales" para que no quede espejado horizontalmente,
// y se mantiene el flip vertical para que la imagen no aparezca volteada.
glBegin(GL_TRIANGLE_STRIP);
// Vértice superior izquierdo
glTexCoord2f(0.0f, 1.0f);
glVertex2f(0.0f, 0.0f);
// Vértice superior derecho
glTexCoord2f(1.0f, 1.0f);
glVertex2f(static_cast<GLfloat>(logicalW), 0.0f);
// Vértice inferior izquierdo
glTexCoord2f(0.0f, 0.0f);
glVertex2f(0.0f, static_cast<GLfloat>(logicalH));
// Vértice inferior derecho
glTexCoord2f(1.0f, 0.0f);
glVertex2f(static_cast<GLfloat>(logicalW), static_cast<GLfloat>(logicalH));
glEnd();
checkGLError("render quad");
SDL_GL_SwapWindow(win);
// Restaurar estados de OpenGL
glUseProgram(oldProgramId);
glBindTexture(GL_TEXTURE_2D, oldTextureId);
if (!wasTextureEnabled)
{
glDisable(GL_TEXTURE_2D);
}
glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
}
else
{
// Fallback a renderizado normal de SDL
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
SDL_RenderPresent(renderer);
glViewport(viewportX, viewportY, viewportW, viewportH);
checkGLError("glViewport");
// Configurar la proyección ortográfica usando el espacio lógico
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Queremos que el origen esté en la esquina superior izquierda del espacio lógico.
glOrtho(0, static_cast<GLdouble>(logicalW), static_cast<GLdouble>(logicalH), 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Dibuja el quad con las coordenadas ajustadas.
// Se asignan las coordenadas de textura "normales" para que no quede espejado horizontalmente,
// y se mantiene el flip vertical para que la imagen no aparezca volteada.
glBegin(GL_TRIANGLE_STRIP);
// Vértice superior izquierdo
glTexCoord2f(0.0f, 1.0f);
glVertex2f(0.0f, 0.0f);
// Vértice superior derecho
glTexCoord2f(1.0f, 1.0f);
glVertex2f(static_cast<GLfloat>(logicalW), 0.0f);
// Vértice inferior izquierdo
glTexCoord2f(0.0f, 0.0f);
glVertex2f(0.0f, static_cast<GLfloat>(logicalH));
// Vértice inferior derecho
glTexCoord2f(1.0f, 0.0f);
glVertex2f(static_cast<GLfloat>(logicalW), static_cast<GLfloat>(logicalH));
glEnd();
checkGLError("render quad");
SDL_GL_SwapWindow(win);
// Restaurar estados de OpenGL
glUseProgram(oldProgramId);
glBindTexture(GL_TEXTURE_2D, oldTextureId);
if (!wasTextureEnabled) {
glDisable(GL_TEXTURE_2D);
}
}
void cleanup()
{
if (programId != INVALID_PROGRAM_ID)
{
glDeleteProgram(programId);
programId = INVALID_PROGRAM_ID;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Programa de shaders liberado.");
}
// Reinicializar variables
win = nullptr;
renderer = nullptr;
backBuffer = nullptr;
usingOpenGL = false;
}
bool isUsingOpenGL()
{
return usingOpenGL;
}
GLuint getProgramId()
{
return programId;
glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
} else {
// Fallback a renderizado normal de SDL
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
SDL_RenderPresent(renderer);
}
}
void cleanup() {
if (programId != INVALID_PROGRAM_ID) {
glDeleteProgram(programId);
programId = INVALID_PROGRAM_ID;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Programa de shaders liberado.");
}
// Reinicializar variables
win = nullptr;
renderer = nullptr;
backBuffer = nullptr;
usingOpenGL = false;
}
bool isUsingOpenGL() {
return usingOpenGL;
}
GLuint getProgramId() {
return programId;
}
} // namespace shader

View File

@@ -1,10 +1,10 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_Texture, SDL_Window
#include <string> // Para basic_string, string
#include <SDL3/SDL.h> // Para SDL_Texture, SDL_Window
namespace shader
{
bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader = "");
void render();
}
#include <string> // Para basic_string, string
namespace shader {
bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader = "");
void render();
} // namespace shader

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,374 +1,325 @@
#include "fade.h"
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_FRect, SDL_GetRenderT...
#include <stdlib.h> // Para rand, size_t
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_FRect, SDL_GetRenderT...
#include <stdlib.h> // Para rand, size_t
#include <algorithm> // Para min, max
#include "param.h" // Para Param, param, ParamGame, ParamFade
#include "screen.h" // Para Screen
#include "utils.h" // Para Color
#include "param.h" // Para Param, param, ParamGame, ParamFade
#include "screen.h" // Para Screen
#include "utils.h" // Para Color
// Constructor
Fade::Fade()
: renderer_(Screen::get()->getRenderer())
{
// Crea la textura donde dibujar el fade
backbuffer_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
: renderer_(Screen::get()->getRenderer()) {
// Crea la textura donde dibujar el fade
backbuffer_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
// Inicializa las variables
init();
// Inicializa las variables
init();
}
// Destructor
Fade::~Fade()
{
SDL_DestroyTexture(backbuffer_);
Fade::~Fade() {
SDL_DestroyTexture(backbuffer_);
}
// Inicializa las variables
void Fade::init()
{
type_ = FadeType::CENTER;
mode_ = FadeMode::OUT;
counter_ = 0;
r_ = 0;
g_ = 0;
b_ = 0;
a_ = 0;
post_duration_ = 0;
post_counter_ = 0;
pre_duration_ = 0;
pre_counter_ = 0;
num_squares_width_ = param.fade.num_squares_width;
num_squares_height_ = param.fade.num_squares_height;
fade_random_squares_delay_ = param.fade.random_squares_delay;
fade_random_squares_mult_ = param.fade.random_squares_mult;
void Fade::init() {
type_ = FadeType::CENTER;
mode_ = FadeMode::OUT;
counter_ = 0;
r_ = 0;
g_ = 0;
b_ = 0;
a_ = 0;
post_duration_ = 0;
post_counter_ = 0;
pre_duration_ = 0;
pre_counter_ = 0;
num_squares_width_ = param.fade.num_squares_width;
num_squares_height_ = param.fade.num_squares_height;
fade_random_squares_delay_ = param.fade.random_squares_delay;
fade_random_squares_mult_ = param.fade.random_squares_mult;
}
// Resetea algunas variables para volver a hacer el fade sin perder ciertos parametros
void Fade::reset()
{
state_ = FadeState::NOT_ENABLED;
counter_ = 0;
void Fade::reset() {
state_ = FadeState::NOT_ENABLED;
counter_ = 0;
}
// Pinta una transición en pantalla
void Fade::render()
{
if (state_ != FadeState::NOT_ENABLED)
{
SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr);
}
void Fade::render() {
if (state_ != FadeState::NOT_ENABLED) {
SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr);
}
}
// Actualiza las variables internas
void Fade::update()
{
if (state_ == FadeState::PRE)
{
// Actualiza el contador
if (pre_counter_ == pre_duration_)
{
state_ = FadeState::FADING;
}
else
{
pre_counter_++;
}
}
void Fade::update() {
if (state_ == FadeState::PRE) {
// Actualiza el contador
if (pre_counter_ == pre_duration_) {
state_ = FadeState::FADING;
} else {
pre_counter_++;
}
}
if (state_ == FadeState::FADING)
{
switch (type_)
{
case FadeType::FULLSCREEN:
{
// Modifica la transparencia
a_ = mode_ == FadeMode::OUT ? std::min(counter_ * 4, 255) : 255 - std::min(counter_ * 4, 255);
if (state_ == FadeState::FADING) {
switch (type_) {
case FadeType::FULLSCREEN: {
// Modifica la transparencia
a_ = mode_ == FadeMode::OUT ? std::min(counter_ * 4, 255) : 255 - std::min(counter_ * 4, 255);
SDL_SetTextureAlphaMod(backbuffer_, a_);
SDL_SetTextureAlphaMod(backbuffer_, a_);
// Comprueba si ha terminado
if (counter_ >= 255 / 4)
{
state_ = FadeState::POST;
}
// Comprueba si ha terminado
if (counter_ >= 255 / 4) {
state_ = FadeState::POST;
}
break;
}
break;
}
case FadeType::CENTER:
{
// Dibuja sobre el backbuffer_
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
case FadeType::CENTER: {
// Dibuja sobre el backbuffer_
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_);
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_);
for (int i = 0; i < counter_; i++)
{
rect1_.h = rect2_.h = i * 4;
rect2_.y = param.game.height - (i * 4);
for (int i = 0; i < counter_; i++) {
rect1_.h = rect2_.h = i * 4;
rect2_.y = param.game.height - (i * 4);
SDL_RenderFillRect(renderer_, &rect1_);
SDL_RenderFillRect(renderer_, &rect2_);
SDL_RenderFillRect(renderer_, &rect1_);
SDL_RenderFillRect(renderer_, &rect2_);
value_ = calculateValue(0, counter_, i);
}
value_ = calculateValue(0, counter_, i);
}
// Deja el renderizador como estaba
SDL_SetRenderTarget(renderer_, temp);
// Deja el renderizador como estaba
SDL_SetRenderTarget(renderer_, temp);
// Comprueba si ha terminado
if ((counter_ * 4) > param.game.height)
{
state_ = FadeState::POST;
a_ = 255;
}
break;
}
// Comprueba si ha terminado
if ((counter_ * 4) > param.game.height) {
state_ = FadeState::POST;
a_ = 255;
}
break;
}
case FadeType::RANDOM_SQUARE:
{
if (counter_ % fade_random_squares_delay_ == 0)
{
// Cambia el renderizador al backbuffer_ y modifica sus opciones
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
SDL_BlendMode blend_mode;
SDL_GetRenderDrawBlendMode(renderer_, &blend_mode);
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_);
case FadeType::RANDOM_SQUARE: {
if (counter_ % fade_random_squares_delay_ == 0) {
// Cambia el renderizador al backbuffer_ y modifica sus opciones
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
SDL_BlendMode blend_mode;
SDL_GetRenderDrawBlendMode(renderer_, &blend_mode);
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_);
// Dibuja el cuadrado correspondiente
const int INDEX = std::min(counter_ / fade_random_squares_delay_, (num_squares_width_ * num_squares_height_) - 1);
for (int i = 0; i < fade_random_squares_mult_; ++i)
{
const int INDEX2 = std::min(INDEX * fade_random_squares_mult_ + i, (int)square_.size() - 1);
SDL_RenderFillRect(renderer_, &square_[INDEX2]);
}
// Dibuja el cuadrado correspondiente
const int INDEX = std::min(counter_ / fade_random_squares_delay_, (num_squares_width_ * num_squares_height_) - 1);
for (int i = 0; i < fade_random_squares_mult_; ++i) {
const int INDEX2 = std::min(INDEX * fade_random_squares_mult_ + i, (int)square_.size() - 1);
SDL_RenderFillRect(renderer_, &square_[INDEX2]);
}
// Deja el renderizador como estaba
SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
SDL_SetRenderTarget(renderer_, temp);
}
// Deja el renderizador como estaba
SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
SDL_SetRenderTarget(renderer_, temp);
}
value_ = calculateValue(0, static_cast<int>(num_squares_width_ * num_squares_height_), static_cast<int>(counter_ * fade_random_squares_mult_ / fade_random_squares_delay_));
value_ = calculateValue(0, static_cast<int>(num_squares_width_ * num_squares_height_), static_cast<int>(counter_ * fade_random_squares_mult_ / fade_random_squares_delay_));
// Comprueba si ha terminado
if (counter_ * fade_random_squares_mult_ / fade_random_squares_delay_ >= num_squares_width_ * num_squares_height_)
{
state_ = FadeState::POST;
}
// Comprueba si ha terminado
if (counter_ * fade_random_squares_mult_ / fade_random_squares_delay_ >= num_squares_width_ * num_squares_height_) {
state_ = FadeState::POST;
}
break;
}
break;
}
case FadeType::VENETIAN:
{
// Counter debe ir de 0 a 150 <-- comprobar si esto es aún cierto
if (square_.back().h < param.fade.venetian_size)
{
// Dibuja sobre el backbuffer_
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
SDL_BlendMode blend_mode;
SDL_GetRenderDrawBlendMode(renderer_, &blend_mode);
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_);
case FadeType::VENETIAN: {
// Counter debe ir de 0 a 150 <-- comprobar si esto es aún cierto
if (square_.back().h < param.fade.venetian_size) {
// Dibuja sobre el backbuffer_
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
SDL_BlendMode blend_mode;
SDL_GetRenderDrawBlendMode(renderer_, &blend_mode);
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_);
// Dibuja el cuadrado correspondiente
for (const auto rect : square_)
{
SDL_RenderFillRect(renderer_, &rect);
}
// Dibuja el cuadrado correspondiente
for (const auto rect : square_) {
SDL_RenderFillRect(renderer_, &rect);
}
// Deja el renderizador como estaba
SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
SDL_SetRenderTarget(renderer_, temp);
// Deja el renderizador como estaba
SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
SDL_SetRenderTarget(renderer_, temp);
// Modifica el tamaño de los rectangulos
const auto h = counter_ / 2;
for (size_t i = 0; i < square_.size(); ++i)
{
// A partir del segundo rectangulo se pinta en función del anterior
square_.at(i).h = i == 0 ? h : std::max(static_cast<int>(square_.at(i - 1).h) - 2, 0);
}
// Modifica el tamaño de los rectangulos
const auto h = counter_ / 2;
for (size_t i = 0; i < square_.size(); ++i) {
// A partir del segundo rectangulo se pinta en función del anterior
square_.at(i).h = i == 0 ? h : std::max(static_cast<int>(square_.at(i - 1).h) - 2, 0);
}
int completed = 0;
for (const auto &square : square_)
{
if (square.h >= param.fade.venetian_size)
{
++completed;
}
}
value_ = calculateValue(0, square_.size() - 1, completed);
}
else
{
state_ = FadeState::POST;
}
int completed = 0;
for (const auto &square : square_) {
if (square.h >= param.fade.venetian_size) {
++completed;
}
}
value_ = calculateValue(0, square_.size() - 1, completed);
} else {
state_ = FadeState::POST;
}
break;
}
default:
break;
}
counter_++;
}
break;
}
default:
break;
}
counter_++;
}
if (state_ == FadeState::POST)
{
// Actualiza el contador
if (post_counter_ == post_duration_)
{
state_ = FadeState::FINISHED;
}
else
{
post_counter_++;
}
if (state_ == FadeState::POST) {
// Actualiza el contador
if (post_counter_ == post_duration_) {
state_ = FadeState::FINISHED;
} else {
post_counter_++;
}
// Deja el backbuffer_ todo del mismo color
cleanBackbuffer(r_, g_, b_, a_);
}
// Deja el backbuffer_ todo del mismo color
cleanBackbuffer(r_, g_, b_, a_);
}
}
// Activa el fade
void Fade::activate()
{
// Si ya está habilitado, no hay que volverlo a activar
if (state_ != FadeState::NOT_ENABLED)
{
return;
}
void Fade::activate() {
// Si ya está habilitado, no hay que volverlo a activar
if (state_ != FadeState::NOT_ENABLED) {
return;
}
state_ = FadeState::PRE;
counter_ = 0;
post_counter_ = 0;
pre_counter_ = 0;
state_ = FadeState::PRE;
counter_ = 0;
post_counter_ = 0;
pre_counter_ = 0;
switch (type_)
{
case FadeType::FULLSCREEN:
{
// Pinta el backbuffer_ de color sólido
cleanBackbuffer(r_, g_, b_, 255);
break;
}
switch (type_) {
case FadeType::FULLSCREEN: {
// Pinta el backbuffer_ de color sólido
cleanBackbuffer(r_, g_, b_, 255);
break;
}
case FadeType::CENTER:
{
rect1_ = {0, 0, static_cast<float>(param.game.width), 0};
rect2_ = {0, 0, static_cast<float>(param.game.width), 0};
a_ = 64;
break;
}
case FadeType::CENTER: {
rect1_ = {0, 0, static_cast<float>(param.game.width), 0};
rect2_ = {0, 0, static_cast<float>(param.game.width), 0};
a_ = 64;
break;
}
case FadeType::RANDOM_SQUARE:
{
rect1_ = {0, 0, static_cast<float>(param.game.width / num_squares_width_), static_cast<float>(param.game.height / num_squares_height_)};
square_.clear();
case FadeType::RANDOM_SQUARE: {
rect1_ = {0, 0, static_cast<float>(param.game.width / num_squares_width_), static_cast<float>(param.game.height / num_squares_height_)};
square_.clear();
// Añade los cuadrados al vector
for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i)
{
rect1_.x = (i % num_squares_width_) * rect1_.w;
rect1_.y = (i / num_squares_width_) * rect1_.h;
square_.push_back(rect1_);
}
// Añade los cuadrados al vector
for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) {
rect1_.x = (i % num_squares_width_) * rect1_.w;
rect1_.y = (i / num_squares_width_) * rect1_.h;
square_.push_back(rect1_);
}
// Desordena el vector de cuadrados
auto num = num_squares_width_ * num_squares_height_;
while (num > 1)
{
auto num_arreu = rand() % num;
SDL_FRect temp = square_[num_arreu];
square_[num_arreu] = square_[num - 1];
square_[num - 1] = temp;
num--;
}
// Desordena el vector de cuadrados
auto num = num_squares_width_ * num_squares_height_;
while (num > 1) {
auto num_arreu = rand() % num;
SDL_FRect temp = square_[num_arreu];
square_[num_arreu] = square_[num - 1];
square_[num - 1] = temp;
num--;
}
// Limpia la textura
a_ = mode_ == FadeMode::OUT ? 0 : 255;
cleanBackbuffer(r_, g_, b_, a_);
// Limpia la textura
a_ = mode_ == FadeMode::OUT ? 0 : 255;
cleanBackbuffer(r_, g_, b_, a_);
// Deja el color listo para usar
a_ = mode_ == FadeMode::OUT ? 255 : 0;
// Deja el color listo para usar
a_ = mode_ == FadeMode::OUT ? 255 : 0;
break;
}
break;
}
case FadeType::VENETIAN:
{
// Limpia la textura
a_ = mode_ == FadeMode::OUT ? 0 : 255;
cleanBackbuffer(r_, g_, b_, a_);
case FadeType::VENETIAN: {
// Limpia la textura
a_ = mode_ == FadeMode::OUT ? 0 : 255;
cleanBackbuffer(r_, g_, b_, a_);
// Deja el color listo para usar
a_ = mode_ == FadeMode::OUT ? 255 : 0;
// Deja el color listo para usar
a_ = mode_ == FadeMode::OUT ? 255 : 0;
// Añade los cuadrados al vector
square_.clear();
rect1_ = {0, 0, static_cast<float>(param.game.width), 0};
const int MAX = param.game.height / param.fade.venetian_size;
// Añade los cuadrados al vector
square_.clear();
rect1_ = {0, 0, static_cast<float>(param.game.width), 0};
const int MAX = param.game.height / param.fade.venetian_size;
for (int i = 0; i < MAX; ++i)
{
rect1_.y = i * param.fade.venetian_size;
square_.push_back(rect1_);
}
for (int i = 0; i < MAX; ++i) {
rect1_.y = i * param.fade.venetian_size;
square_.push_back(rect1_);
}
break;
}
}
break;
}
}
}
// Establece el color del fade
void Fade::setColor(Uint8 r, Uint8 g, Uint8 b)
{
r_ = r;
g_ = g;
b_ = b;
void Fade::setColor(Uint8 r, Uint8 g, Uint8 b) {
r_ = r;
g_ = g;
b_ = b;
}
// Establece el color del fade
void Fade::setColor(Color color)
{
r_ = color.r;
g_ = color.g;
b_ = color.b;
void Fade::setColor(Color color) {
r_ = color.r;
g_ = color.g;
b_ = color.b;
}
// Limpia el backbuffer
void Fade::cleanBackbuffer(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
// Dibujamos sobre el backbuffer_
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
void Fade::cleanBackbuffer(Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
// Dibujamos sobre el backbuffer_
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
// Pintamos la textura con el color del fade
SDL_SetRenderDrawColor(renderer_, r, g, b, a);
SDL_RenderClear(renderer_);
// Pintamos la textura con el color del fade
SDL_SetRenderDrawColor(renderer_, r, g, b, a);
SDL_RenderClear(renderer_);
// Vuelve a dejar el renderizador como estaba
SDL_SetRenderTarget(renderer_, temp);
// Vuelve a dejar el renderizador como estaba
SDL_SetRenderTarget(renderer_, temp);
}
// Calcula el valor del estado del fade
int Fade::calculateValue(int min, int max, int current)
{
if (current < min)
{
return 0;
}
int Fade::calculateValue(int min, int max, int current) {
if (current < min) {
return 0;
}
if (current > max)
{
return 100;
}
if (current > max) {
return 100;
}
return static_cast<int>(100.0 * (current - min) / (max - min));
return static_cast<int>(100.0 * (current - min) / (max - min));
}

View File

@@ -1,93 +1,90 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint8, SDL_FRect, SDL_Renderer, SDL_Texture, Uint16
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para Uint8, SDL_FRect, SDL_Renderer, SDL_Texture, Uint16
#include <vector> // Para vector
struct Color;
// Tipos de fundido
enum class FadeType : Uint8
{
FULLSCREEN = 0,
CENTER = 1,
RANDOM_SQUARE = 2,
VENETIAN = 3,
enum class FadeType : Uint8 {
FULLSCREEN = 0,
CENTER = 1,
RANDOM_SQUARE = 2,
VENETIAN = 3,
};
// Modos de fundido
enum class FadeMode : Uint8
{
IN = 0,
OUT = 1,
enum class FadeMode : Uint8 {
IN = 0,
OUT = 1,
};
// Estados del objeto
enum class FadeState : Uint8
{
NOT_ENABLED = 0,
PRE = 1,
FADING = 2,
POST = 3,
FINISHED = 4,
enum class FadeState : Uint8 {
NOT_ENABLED = 0,
PRE = 1,
FADING = 2,
POST = 3,
FINISHED = 4,
};
class Fade
{
public:
// --- Constructores y destructor ---
Fade();
~Fade();
class Fade {
public:
// --- Constructores y destructor ---
Fade();
~Fade();
// --- Métodos principales ---
void reset(); // Resetea variables para reutilizar el fade
void render(); // Dibuja la transición en pantalla
void update(); // Actualiza el estado interno
void activate(); // Activa el fade
// --- Métodos principales ---
void reset(); // Resetea variables para reutilizar el fade
void render(); // Dibuja la transición en pantalla
void update(); // Actualiza el estado interno
void activate(); // Activa el fade
// --- Configuración ---
void setColor(Uint8 r, Uint8 g, Uint8 b);
void setColor(Color color);
void setType(FadeType type) { type_ = type; }
void setMode(FadeMode mode) { mode_ = mode; }
void setPostDuration(int value) { post_duration_ = value; }
void setPreDuration(int value) { pre_duration_ = value; }
// --- Configuración ---
void setColor(Uint8 r, Uint8 g, Uint8 b);
void setColor(Color color);
void setType(FadeType type) { type_ = type; }
void setMode(FadeMode mode) { mode_ = mode; }
void setPostDuration(int value) { post_duration_ = value; }
void setPreDuration(int value) { pre_duration_ = value; }
// --- Getters ---
int getValue() const { return value_; }
bool isEnabled() const { return state_ != FadeState::NOT_ENABLED; }
bool hasEnded() const { return state_ == FadeState::FINISHED; }
// --- Getters ---
int getValue() const { return value_; }
bool isEnabled() const { return state_ != FadeState::NOT_ENABLED; }
bool hasEnded() const { return state_ == FadeState::FINISHED; }
private:
// --- Objetos y punteros ---
SDL_Renderer *renderer_; // Renderizador de la ventana
SDL_Texture *backbuffer_; // Backbuffer para efectos
private:
// --- Objetos y punteros ---
SDL_Renderer *renderer_; // Renderizador de la ventana
SDL_Texture *backbuffer_; // Backbuffer para efectos
// --- Variables de estado ---
FadeType type_; // Tipo de fade
FadeMode mode_; // Modo de fade
FadeState state_ = FadeState::NOT_ENABLED; // Estado actual
Uint16 counter_; // Contador interno
// --- Variables de estado ---
FadeType type_; // Tipo de fade
FadeMode mode_; // Modo de fade
FadeState state_ = FadeState::NOT_ENABLED; // Estado actual
Uint16 counter_; // Contador interno
// --- Parámetros de color y geometría ---
Uint8 r_, g_, b_, a_; // Color del fade
SDL_FRect rect1_, rect2_; // Rectángulos para efectos
// --- Parámetros de color y geometría ---
Uint8 r_, g_, b_, a_; // Color del fade
SDL_FRect rect1_, rect2_; // Rectángulos para efectos
// --- Parámetros para RANDOM_SQUARE ---
int num_squares_width_; // Cuadrados en horizontal
int num_squares_height_; // Cuadrados en vertical
std::vector<SDL_FRect> square_; // Vector de cuadrados
int fade_random_squares_delay_; // Delay entre cuadrados
int fade_random_squares_mult_; // Cuadrados por paso
// --- Parámetros para RANDOM_SQUARE ---
int num_squares_width_; // Cuadrados en horizontal
int num_squares_height_; // Cuadrados en vertical
std::vector<SDL_FRect> square_; // Vector de cuadrados
int fade_random_squares_delay_; // Delay entre cuadrados
int fade_random_squares_mult_; // Cuadrados por paso
// --- Temporizadores ---
int post_duration_ = 0, post_counter_ = 0;
int pre_duration_ = 0, pre_counter_ = 0;
// --- Temporizadores ---
int post_duration_ = 0, post_counter_ = 0;
int pre_duration_ = 0, pre_counter_ = 0;
// --- Valor de progreso ---
int value_ = 0; // Estado del fade (0-100)
// --- Valor de progreso ---
int value_ = 0; // Estado del fade (0-100)
// --- Métodos internos ---
void init(); // Inicializa variables
void cleanBackbuffer(Uint8 r, Uint8 g, Uint8 b, Uint8 a); // Limpia el backbuffer
int calculateValue(int min, int max, int current); // Calcula el valor del fade
// --- Métodos internos ---
void init(); // Inicializa variables
void cleanBackbuffer(Uint8 r, Uint8 g, Uint8 b, Uint8 a); // Limpia el backbuffer
int calculateValue(int min, int max, int current); // Calcula el valor del fade
};

View File

@@ -1,18 +1,19 @@
#include "game_logo.h"
#include <SDL3/SDL.h> // Para SDL_SetTextureScaleMode, SDL_FlipMode
#include <SDL3/SDL.h> // Para SDL_SetTextureScaleMode, SDL_FlipMode
#include <algorithm> // Para max
#include <string> // Para basic_string
#include "animated_sprite.h" // Para AnimatedSprite
#include "audio.h" // Para Audio
#include "param.h" // Para Param, param, ParamGame, ParamTitle
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "smart_sprite.h" // Para SmartSprite
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
#include "utils.h" // Para Color
#include "animated_sprite.h" // Para AnimatedSprite
#include "audio.h" // Para Audio
#include "param.h" // Para Param, param, ParamGame, ParamTitle
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "smart_sprite.h" // Para SmartSprite
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
#include "utils.h" // Para Color
constexpr int ZOOM_FACTOR = 5;
constexpr int FLASH_DELAY = 3;
@@ -33,8 +34,7 @@ GameLogo::GameLogo(int x, int y)
y_(y) {}
// Inicializa las variables
void GameLogo::init()
{
void GameLogo::init() {
const auto xp = x_ - coffee_texture_->getWidth() / 2;
const auto desp = getInitialVerticalDesp();
@@ -97,14 +97,12 @@ void GameLogo::init()
}
// Pinta la clase en pantalla
void GameLogo::render()
{
void GameLogo::render() {
// Dibuja el logo
coffee_sprite_->render();
crisis_sprite_->render();
if (arcade_edition_status_ != Status::DISABLED)
{
if (arcade_edition_status_ != Status::DISABLED) {
arcade_edition_sprite_->render();
}
@@ -114,145 +112,119 @@ void GameLogo::render()
}
// Actualiza la lógica de la clase
void GameLogo::update()
{
switch (coffee_crisis_status_)
{
case Status::MOVING:
{
coffee_sprite_->update();
crisis_sprite_->update();
void GameLogo::update() {
switch (coffee_crisis_status_) {
case Status::MOVING: {
coffee_sprite_->update();
crisis_sprite_->update();
// Si los objetos han llegado a su destino, cambia el estado
if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished())
{
coffee_crisis_status_ = Status::SHAKING;
// Si los objetos han llegado a su destino, cambia el estado
if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished()) {
coffee_crisis_status_ = Status::SHAKING;
// Reproduce el efecto sonoro
Audio::get()->playSound("title.wav");
Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY);
Screen::get()->shake();
}
break;
}
case Status::SHAKING:
{
// Agita "COFFEE CRISIS"
if (shake_.remaining > 0)
{
if (shake_.counter > 0)
{
shake_.counter--;
// Reproduce el efecto sonoro
Audio::get()->playSound("title.wav");
Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY);
Screen::get()->shake();
}
else
{
shake_.counter = shake_.delay;
const auto desp = shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp;
coffee_sprite_->setPosX(shake_.origin + desp);
crisis_sprite_->setPosX(shake_.origin + desp + 15);
shake_.remaining--;
break;
}
case Status::SHAKING: {
// Agita "COFFEE CRISIS"
if (shake_.remaining > 0) {
if (shake_.counter > 0) {
shake_.counter--;
} else {
shake_.counter = shake_.delay;
const auto desp = shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp;
coffee_sprite_->setPosX(shake_.origin + desp);
crisis_sprite_->setPosX(shake_.origin + desp + 15);
shake_.remaining--;
}
} else {
coffee_sprite_->setPosX(shake_.origin);
crisis_sprite_->setPosX(shake_.origin + 15);
coffee_crisis_status_ = Status::FINISHED;
arcade_edition_status_ = Status::MOVING;
}
}
else
{
coffee_sprite_->setPosX(shake_.origin);
crisis_sprite_->setPosX(shake_.origin + 15);
coffee_crisis_status_ = Status::FINISHED;
arcade_edition_status_ = Status::MOVING;
dust_right_sprite_->update();
dust_left_sprite_->update();
break;
}
dust_right_sprite_->update();
dust_left_sprite_->update();
case Status::FINISHED: {
dust_right_sprite_->update();
dust_left_sprite_->update();
break;
break;
}
default:
break;
}
case Status::FINISHED:
{
dust_right_sprite_->update();
dust_left_sprite_->update();
break;
}
default:
break;
}
switch (arcade_edition_status_)
{
case Status::MOVING:
{
zoom_ -= 0.1f * ZOOM_FACTOR;
arcade_edition_sprite_->setZoom(zoom_);
if (zoom_ <= 1.0f)
{
arcade_edition_status_ = Status::SHAKING;
zoom_ = 1.0f;
switch (arcade_edition_status_) {
case Status::MOVING: {
zoom_ -= 0.1f * ZOOM_FACTOR;
arcade_edition_sprite_->setZoom(zoom_);
shake_.init(1, 2, 8, arcade_edition_sprite_->getX());
Audio::get()->playSound("title.wav");
Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY);
Screen::get()->shake();
}
break;
}
case Status::SHAKING:
{
// Agita "ARCADE EDITION"
if (shake_.remaining > 0)
{
if (shake_.counter > 0)
{
shake_.counter--;
}
else
{
shake_.counter = shake_.delay;
const auto desp = shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp;
arcade_edition_sprite_->setX(shake_.origin + desp);
shake_.remaining--;
if (zoom_ <= 1.0f) {
arcade_edition_status_ = Status::SHAKING;
zoom_ = 1.0f;
arcade_edition_sprite_->setZoom(zoom_);
shake_.init(1, 2, 8, arcade_edition_sprite_->getX());
Audio::get()->playSound("title.wav");
Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY);
Screen::get()->shake();
}
break;
}
else
{
arcade_edition_sprite_->setX(shake_.origin);
arcade_edition_status_ = Status::FINISHED;
}
break;
}
default:
break;
case Status::SHAKING: {
// Agita "ARCADE EDITION"
if (shake_.remaining > 0) {
if (shake_.counter > 0) {
shake_.counter--;
} else {
shake_.counter = shake_.delay;
const auto desp = shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp;
arcade_edition_sprite_->setX(shake_.origin + desp);
shake_.remaining--;
}
} else {
arcade_edition_sprite_->setX(shake_.origin);
arcade_edition_status_ = Status::FINISHED;
}
break;
}
default:
break;
}
if (coffee_crisis_status_ == Status::FINISHED &&
arcade_edition_status_ == Status::FINISHED &&
post_finished_counter_ > 0)
{
post_finished_counter_ > 0) {
--post_finished_counter_;
}
}
// Activa la clase
void GameLogo::enable()
{
void GameLogo::enable() {
init();
coffee_crisis_status_ = Status::MOVING;
}
// Indica si ha terminado la animación
bool GameLogo::hasFinished() const
{
bool GameLogo::hasFinished() const {
return post_finished_counter_ == 0;
}
// Calcula el desplazamiento vertical inicial
int GameLogo::getInitialVerticalDesp()
{
int GameLogo::getInitialVerticalDesp() {
const float OFFSET_UP = y_;
const float OFFSET_DOWN = param.game.height - y_;

View File

@@ -1,54 +1,50 @@
#pragma once
#include <memory> // Para unique_ptr, shared_ptr
#include <memory> // Para unique_ptr, shared_ptr
#include "animated_sprite.h" // Para AnimatedSprite
#include "smart_sprite.h" // Para SmartSprite
#include "sprite.h" // Para Sprite
#include "animated_sprite.h" // Para AnimatedSprite
#include "smart_sprite.h" // Para SmartSprite
#include "sprite.h" // Para Sprite
class Texture;
// Clase GameLogo
class GameLogo
{
public:
class GameLogo {
public:
// --- Constructores y destructor ---
GameLogo(int x, int y);
~GameLogo() = default;
// --- Métodos principales ---
void render(); // Pinta la clase en pantalla
void update(); // Actualiza la lógica de la clase
void enable(); // Activa la clase
void render(); // Pinta la clase en pantalla
void update(); // Actualiza la lógica de la clase
void enable(); // Activa la clase
// --- Getters ---
bool hasFinished() const; // Indica si ha terminado la animación
bool hasFinished() const; // Indica si ha terminado la animación
private:
private:
// --- Tipos internos ---
enum class Status
{
enum class Status {
DISABLED,
MOVING,
SHAKING,
FINISHED,
};
struct Shake
{
int desp = 1; // Pixels de desplazamiento para agitar la pantalla en el eje x
int delay = 2; // Retraso entre cada desplazamiento de la pantalla al agitarse
int lenght = 8; // Cantidad de desplazamientos a realizar
int remaining = lenght; // Cantidad de desplazamientos pendientes a realizar
int counter = delay; // Contador para el retraso
int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento
struct Shake {
int desp = 1; // Pixels de desplazamiento para agitar la pantalla en el eje x
int delay = 2; // Retraso entre cada desplazamiento de la pantalla al agitarse
int lenght = 8; // Cantidad de desplazamientos a realizar
int remaining = lenght; // Cantidad de desplazamientos pendientes a realizar
int counter = delay; // Contador para el retraso
int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento
Shake() = default;
Shake(int d, int de, int l, int o)
: desp(d), delay(de), lenght(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;
delay = de;
lenght = l;
@@ -59,28 +55,28 @@ private:
};
// --- Objetos y punteros ---
std::shared_ptr<Texture> dust_texture_; // Textura con los graficos del polvo
std::shared_ptr<Texture> coffee_texture_; // Textura con los graficos de la palabra "COFFEE"
std::shared_ptr<Texture> crisis_texture_; // Textura con los graficos de la palabra "CRISIS"
std::shared_ptr<Texture> arcade_edition_texture_; // Textura con los graficos de "Arcade Edition"
std::shared_ptr<Texture> dust_texture_; // Textura con los graficos del polvo
std::shared_ptr<Texture> coffee_texture_; // Textura con los graficos de la palabra "COFFEE"
std::shared_ptr<Texture> crisis_texture_; // Textura con los graficos de la palabra "CRISIS"
std::shared_ptr<Texture> arcade_edition_texture_; // Textura con los graficos de "Arcade Edition"
std::unique_ptr<AnimatedSprite> dust_left_sprite_; // Sprite del polvo (izquierda)
std::unique_ptr<AnimatedSprite> dust_right_sprite_; // Sprite del polvo (derecha)
std::unique_ptr<SmartSprite> coffee_sprite_; // Sprite de "COFFEE"
std::unique_ptr<SmartSprite> crisis_sprite_; // Sprite de "CRISIS"
std::unique_ptr<Sprite> arcade_edition_sprite_; // Sprite de "Arcade Edition"
std::unique_ptr<AnimatedSprite> dust_left_sprite_; // Sprite del polvo (izquierda)
std::unique_ptr<AnimatedSprite> dust_right_sprite_; // Sprite del polvo (derecha)
std::unique_ptr<SmartSprite> coffee_sprite_; // Sprite de "COFFEE"
std::unique_ptr<SmartSprite> crisis_sprite_; // Sprite de "CRISIS"
std::unique_ptr<Sprite> arcade_edition_sprite_; // Sprite de "Arcade Edition"
// --- Variables de estado ---
float x_; // Posición X del logo
float y_; // Posición Y del logo
float zoom_ = 1.0f; // Zoom aplicado al texto "ARCADE EDITION"
int post_finished_counter_ = 1; // Contador final tras animaciones
float x_; // Posición X del logo
float y_; // Posición Y del logo
float zoom_ = 1.0f; // Zoom aplicado al texto "ARCADE EDITION"
int post_finished_counter_ = 1; // Contador final tras animaciones
Status coffee_crisis_status_ = Status::DISABLED; // Estado de "COFFEE CRISIS"
Status arcade_edition_status_ = Status::DISABLED; // Estado de "ARCADE EDITION"
Shake shake_; // Efecto de agitación
Status coffee_crisis_status_ = Status::DISABLED; // Estado de "COFFEE CRISIS"
Status arcade_edition_status_ = Status::DISABLED; // Estado de "ARCADE EDITION"
Shake shake_; // Efecto de agitación
// --- Métodos internos ---
void init(); // Inicializa las variables
int getInitialVerticalDesp(); // Calcula el desplazamiento vertical inicial
void init(); // Inicializa las variables
int getInitialVerticalDesp(); // Calcula el desplazamiento vertical inicial
};

View File

@@ -1,19 +1,16 @@
#include "global_events.h"
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory
#include "mouse.h" // Para handleEvent
#include "mouse.h" // Para handleEvent
#include "screen.h"
#include "section.h" // Para Name, Options, name, options
namespace GlobalEvents
{
// Comprueba los eventos que se pueden producir en cualquier sección del juego
void check(const SDL_Event &event)
{
switch (event.type)
{
case SDL_EVENT_QUIT: // Evento de salida de la aplicación
namespace GlobalEvents {
// Comprueba los eventos que se pueden producir en cualquier sección del juego
void check(const SDL_Event &event) {
switch (event.type) {
case SDL_EVENT_QUIT: // Evento de salida de la aplicación
Section::name = Section::Name::QUIT;
Section::options = Section::Options::NONE;
return;
@@ -29,8 +26,8 @@ namespace GlobalEvents
default:
break;
}
Mouse::handleEvent(event);
}
Mouse::handleEvent(event);
}
} // namespace GlobalEvents

View File

@@ -2,8 +2,7 @@
#include <SDL3/SDL.h>
namespace GlobalEvents
{
// Comprueba los eventos que se pueden producir en cualquier sección del juego
void check(const SDL_Event &event);
}
namespace GlobalEvents {
// Comprueba los eventos que se pueden producir en cualquier sección del juego
void check(const SDL_Event &event);
} // namespace GlobalEvents

View File

@@ -1,87 +1,72 @@
#include "global_inputs.h"
#include <string> // Para basic_string, operator+, allocator, cha...
#include <vector> // Para vector
#include <string> // Para basic_string, operator+, allocator, cha...
#include <vector> // Para vector
#include "asset.h" // Para Asset
#include "audio.h" // Para Audio
#include "input.h" // Para Input, INPUT_DO_NOT_ALLOW_REPEAT, Input...
#include "lang.h" // Para getText, Code, getNextLangCode, loadFro...
#include "notifier.h" // Para Notifier
#include "options.h" // Para SettingsOptions, settings, VideoOptions
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name, Options, options, AttractMode
#include "ui/service_menu.h" // Para ServiceMenu
#include "utils.h" // Para boolToOnOff
#include "asset.h" // Para Asset
#include "audio.h" // Para Audio
#include "input.h" // Para Input, INPUT_DO_NOT_ALLOW_REPEAT, Input...
#include "lang.h" // Para getText, Code, getNextLangCode, loadFro...
#include "notifier.h" // Para Notifier
#include "options.h" // Para SettingsOptions, settings, VideoOptions
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name, Options, options, AttractMode
#include "ui/service_menu.h" // Para ServiceMenu
#include "utils.h" // Para boolToOnOff
namespace GlobalInputs
{
// Termina
void quit()
{
const std::string CODE = "QUIT";
if (Notifier::get()->checkCode(CODE))
{
// Si la notificación de salir está activa, cambia de sección
Section::name = Section::Name::QUIT;
Section::options = Section::Options::NONE;
}
else
{
// Si la notificación de salir no está activa, muestra la notificación
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 01"), std::string()}, -1, CODE);
}
namespace GlobalInputs {
// Termina
void quit() {
const std::string CODE = "QUIT";
if (Notifier::get()->checkCode(CODE)) {
// Si la notificación de salir está activa, cambia de sección
Section::name = Section::Name::QUIT;
Section::options = Section::Options::NONE;
} else {
// Si la notificación de salir no está activa, muestra la notificación
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 01"), std::string()}, -1, CODE);
}
}
// Reinicia
void reset()
{
const std::string CODE = "RESET";
if (Notifier::get()->checkCode(CODE))
{
Section::name = Section::Name::RESET;
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 15")});
}
else
{
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 03"), std::string()}, -1, CODE);
}
// Reinicia
void reset() {
const std::string CODE = "RESET";
if (Notifier::get()->checkCode(CODE)) {
Section::name = Section::Name::RESET;
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 15")});
} else {
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 03"), std::string()}, -1, CODE);
}
}
// Activa o desactiva el audio
void toggleAudio()
{
Options::audio.enabled = !Options::audio.enabled;
Audio::get()->enable(Options::audio.enabled);
Notifier::get()->show({"Audio " + boolToOnOff(Options::audio.enabled)});
}
// Activa o desactiva el audio
void toggleAudio() {
Options::audio.enabled = !Options::audio.enabled;
Audio::get()->enable(Options::audio.enabled);
Notifier::get()->show({"Audio " + boolToOnOff(Options::audio.enabled)});
}
// Cambia el modo de escalado entero
void toggleIntegerScale()
{
Screen::get()->toggleIntegerScale();
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 12") + " " + boolToOnOff(Options::video.integer_scale)});
}
// Cambia el modo de escalado entero
void toggleIntegerScale() {
Screen::get()->toggleIntegerScale();
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 12") + " " + boolToOnOff(Options::video.integer_scale)});
}
// Activa / desactiva el vsync
void toggleVSync()
{
Screen::get()->toggleVSync();
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 14") + " " + boolToOnOff(Options::video.v_sync)});
}
// Activa / desactiva el vsync
void toggleVSync() {
Screen::get()->toggleVSync();
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 14") + " " + boolToOnOff(Options::video.v_sync)});
}
// Activa o desactiva los shaders
void toggleShaders()
{
Screen::get()->toggleShaders();
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.shaders)});
}
// Activa o desactiva los shaders
void toggleShaders() {
Screen::get()->toggleShaders();
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.shaders)});
}
// Obtiene una fichero a partir de un lang::Code
std::string getLangFile(Lang::Code code)
{
switch (code)
{
// Obtiene una fichero a partir de un lang::Code
std::string getLangFile(Lang::Code code) {
switch (code) {
case Lang::Code::VALENCIAN:
return Asset::get()->get("ba_BA.json");
break;
@@ -91,14 +76,12 @@ namespace GlobalInputs
default:
return Asset::get()->get("en_UK.json");
break;
}
}
}
// Obtiene una cadena a partir de un lang::Code
std::string getLangName(Lang::Code code)
{
switch (code)
{
// Obtiene una cadena a partir de un lang::Code
std::string getLangName(Lang::Code code) {
switch (code) {
case Lang::Code::VALENCIAN:
return " \"ba_BA\"";
break;
@@ -108,47 +91,39 @@ namespace GlobalInputs
default:
return " \"en_UK\"";
break;
}
}
}
// Cambia el idioma
void changeLang()
{
const std::string CODE = "LANG";
if (Notifier::get()->checkCode(CODE))
{
Options::settings.language = Lang::getNextLangCode(Options::settings.language);
Lang::loadFromFile(getLangFile(static_cast<Lang::Code>(Options::settings.language)));
Section::name = Section::Name::RESET;
Section::options = Section::Options::RELOAD;
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 05") + getLangName(Options::settings.language)});
}
else
{
const auto NEXT = Lang::getNextLangCode(Options::settings.language);
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 04") + getLangName(NEXT), std::string()}, -1, CODE);
}
// Cambia el idioma
void changeLang() {
const std::string CODE = "LANG";
if (Notifier::get()->checkCode(CODE)) {
Options::settings.language = Lang::getNextLangCode(Options::settings.language);
Lang::loadFromFile(getLangFile(static_cast<Lang::Code>(Options::settings.language)));
Section::name = Section::Name::RESET;
Section::options = Section::Options::RELOAD;
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 05") + getLangName(Options::settings.language)});
} else {
const auto NEXT = Lang::getNextLangCode(Options::settings.language);
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 04") + getLangName(NEXT), std::string()}, -1, CODE);
}
}
// Cambia el modo de disparo
void toggleFireMode()
{
Options::settings.autofire = !Options::settings.autofire;
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 08") + " " + boolToOnOff(Options::settings.autofire)});
}
// Cambia el modo de disparo
void toggleFireMode() {
Options::settings.autofire = !Options::settings.autofire;
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 08") + " " + boolToOnOff(Options::settings.autofire)});
}
// Salta una sección del juego
void skipSection()
{
switch (Section::name)
{
// Salta una sección del juego
void skipSection() {
switch (Section::name) {
case Section::Name::INTRO:
Audio::get()->stopMusic();
/* Continua en el case de abajo */
case Section::Name::LOGO:
case Section::Name::HI_SCORE_TABLE:
case Section::Name::INSTRUCTIONS:
{
case Section::Name::INSTRUCTIONS: {
Section::name = Section::Name::TITLE;
Section::options = Section::Options::TITLE_1;
Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
@@ -156,284 +131,243 @@ namespace GlobalInputs
}
default:
break;
}
}
// Activa el menu de servicio
void toggleServiceMenu() {
ServiceMenu::get()->toggle();
}
// Cambia el modo de pantalla completa
void toggleFullscreen() {
Screen::get()->toggleFullscreen();
const std::string MODE = Options::video.fullscreen ? Lang::getText("[NOTIFICATIONS] 11") : Lang::getText("[NOTIFICATIONS] 10");
Notifier::get()->show({MODE});
}
// Reduce el tamaño de la ventana
void decWindowSize() {
if (Screen::get()->decWindowSize()) {
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
}
}
// Aumenta el tamaño de la ventana
void incWindowSize() {
if (Screen::get()->incWindowSize()) {
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
}
}
// Comprueba el boton de servicio
bool checkServiceButton() {
// Teclado
if (Input::get()->checkInput(InputAction::SERVICE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
toggleServiceMenu();
return true;
}
// Mandos
{
for (int i = 0; i < Input::get()->getNumControllers(); ++i) {
if (Input::get()->checkInput(InputAction::SERVICE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) {
toggleServiceMenu();
return true;
}
}
}
return false;
}
// Activa el menu de servicio
void toggleServiceMenu()
{
ServiceMenu::get()->toggle();
}
// Comprueba las entradas del menú de servicio
bool checkServiceInputs() {
if (!ServiceMenu::get()->isEnabled())
return false;
// Cambia el modo de pantalla completa
void toggleFullscreen()
// Teclado
{
Screen::get()->toggleFullscreen();
const std::string MODE = Options::video.fullscreen ? Lang::getText("[NOTIFICATIONS] 11") : Lang::getText("[NOTIFICATIONS] 10");
Notifier::get()->show({MODE});
}
// Reduce el tamaño de la ventana
void decWindowSize()
{
if (Screen::get()->decWindowSize())
{
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
}
}
// Aumenta el tamaño de la ventana
void incWindowSize()
{
if (Screen::get()->incWindowSize())
{
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
}
}
// Comprueba el boton de servicio
bool checkServiceButton()
{
// Teclado
if (Input::get()->checkInput(InputAction::SERVICE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
toggleServiceMenu();
// Arriba
if (Input::get()->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
ServiceMenu::get()->setSelectorUp();
return true;
}
// Mandos
{
for (int i = 0; i < Input::get()->getNumControllers(); ++i)
{
if (Input::get()->checkInput(InputAction::SERVICE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i))
{
toggleServiceMenu();
return true;
}
}
// Abajo
if (Input::get()->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
ServiceMenu::get()->setSelectorDown();
return true;
}
// Derecha
if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
ServiceMenu::get()->adjustOption(true);
return true;
}
// Izquierda
if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
ServiceMenu::get()->adjustOption(false);
return true;
}
// Aceptar
if (Input::get()->checkInput(InputAction::SM_SELECT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
ServiceMenu::get()->selectOption();
return true;
}
// Atras
if (Input::get()->checkInput(InputAction::SM_BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
ServiceMenu::get()->moveBack();
return true;
}
return false;
}
// Comprueba las entradas del menú de servicio
bool checkServiceInputs()
// Mandos
{
if (!ServiceMenu::get()->isEnabled())
return false;
// Teclado
{
for (int i = 0; i < Input::get()->getNumControllers(); ++i) {
// Arriba
if (Input::get()->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
if (Input::get()->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) {
ServiceMenu::get()->setSelectorUp();
return true;
}
// Abajo
if (Input::get()->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
if (Input::get()->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) {
ServiceMenu::get()->setSelectorDown();
return true;
}
// Derecha
if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) {
ServiceMenu::get()->adjustOption(true);
return true;
}
// Izquierda
if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) {
ServiceMenu::get()->adjustOption(false);
return true;
}
// Aceptar
if (Input::get()->checkInput(InputAction::SM_SELECT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
if (Input::get()->checkInput(InputAction::SM_SELECT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) {
ServiceMenu::get()->selectOption();
return true;
}
// Atras
if (Input::get()->checkInput(InputAction::SM_BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
if (Input::get()->checkInput(InputAction::SM_BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) {
ServiceMenu::get()->moveBack();
return true;
}
}
// Mandos
{
for (int i = 0; i < Input::get()->getNumControllers(); ++i)
{
// Arriba
if (Input::get()->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i))
{
ServiceMenu::get()->setSelectorUp();
return true;
}
// Abajo
if (Input::get()->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i))
{
ServiceMenu::get()->setSelectorDown();
return true;
}
// Derecha
if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i))
{
ServiceMenu::get()->adjustOption(true);
return true;
}
// Izquierda
if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i))
{
ServiceMenu::get()->adjustOption(false);
return true;
}
// Aceptar
if (Input::get()->checkInput(InputAction::SM_SELECT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i))
{
ServiceMenu::get()->selectOption();
return true;
}
// Atras
if (Input::get()->checkInput(InputAction::SM_BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i))
{
ServiceMenu::get()->moveBack();
return true;
}
}
}
return false;
}
return false;
}
// Comprueba las entradas fuera del menú de servicio
bool checkInputs()
// Comprueba las entradas fuera del menú de servicio
bool checkInputs() {
// Teclado
{
// Teclado
{
// Comprueba el teclado para cambiar entre pantalla completa y ventana
if (Input::get()->checkInput(InputAction::WINDOW_FULLSCREEN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
Screen::get()->toggleFullscreen();
const std::string MODE = Options::video.fullscreen ? Lang::getText("[NOTIFICATIONS] 11") : Lang::getText("[NOTIFICATIONS] 10");
Notifier::get()->show({MODE});
return true;
}
// Comprueba el teclado para cambiar entre pantalla completa y ventana
if (Input::get()->checkInput(InputAction::WINDOW_FULLSCREEN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
Screen::get()->toggleFullscreen();
const std::string MODE = Options::video.fullscreen ? Lang::getText("[NOTIFICATIONS] 11") : Lang::getText("[NOTIFICATIONS] 10");
Notifier::get()->show({MODE});
return true;
}
// Comprueba el teclado para decrementar el tamaño de la ventana
if (Input::get()->checkInput(InputAction::WINDOW_DEC_SIZE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
if (Screen::get()->decWindowSize())
{
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
}
return true;
// Comprueba el teclado para decrementar el tamaño de la ventana
if (Input::get()->checkInput(InputAction::WINDOW_DEC_SIZE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
if (Screen::get()->decWindowSize()) {
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
}
return true;
}
// Comprueba el teclado para incrementar el tamaño de la ventana
if (Input::get()->checkInput(InputAction::WINDOW_INC_SIZE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
if (Screen::get()->incWindowSize())
{
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
}
return true;
// Comprueba el teclado para incrementar el tamaño de la ventana
if (Input::get()->checkInput(InputAction::WINDOW_INC_SIZE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
if (Screen::get()->incWindowSize()) {
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
}
return true;
}
// Salir
if (Input::get()->checkInput(InputAction::EXIT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
quit();
return true;
}
// Salir
if (Input::get()->checkInput(InputAction::EXIT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
quit();
return true;
}
// Saltar sección
if (Input::get()->checkAnyButton() && !ServiceMenu::get()->isEnabled())
{
skipSection();
return true;
}
// Saltar sección
if (Input::get()->checkAnyButton() && !ServiceMenu::get()->isEnabled()) {
skipSection();
return true;
}
// Reset
if (Input::get()->checkInput(InputAction::RESET, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
reset();
return true;
}
// Reset
if (Input::get()->checkInput(InputAction::RESET, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
reset();
return true;
}
// Audio
if (Input::get()->checkInput(InputAction::TOGGLE_AUDIO, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
toggleAudio();
return true;
}
// Audio
if (Input::get()->checkInput(InputAction::TOGGLE_AUDIO, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
toggleAudio();
return true;
}
// Autofire
if (Input::get()->checkInput(InputAction::TOGGLE_AUTO_FIRE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
toggleFireMode();
return true;
}
// Autofire
if (Input::get()->checkInput(InputAction::TOGGLE_AUTO_FIRE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
toggleFireMode();
return true;
}
// Idioma
if (Input::get()->checkInput(InputAction::CHANGE_LANG, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
changeLang();
return true;
}
// Idioma
if (Input::get()->checkInput(InputAction::CHANGE_LANG, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
changeLang();
return true;
}
// Shaders
if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_SHADERS, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
toggleShaders();
return true;
}
// Shaders
if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_SHADERS, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
toggleShaders();
return true;
}
// Integer Scale
if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_INTEGER_SCALE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
toggleIntegerScale();
return true;
}
// Integer Scale
if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_INTEGER_SCALE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
toggleIntegerScale();
return true;
}
// VSync
if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_VSYNC, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
toggleVSync();
return true;
}
// VSync
if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_VSYNC, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
toggleVSync();
return true;
}
#ifdef DEBUG
// Debug info
if (Input::get()->checkInput(InputAction::SHOW_INFO, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD))
{
Screen::get()->toggleDebugInfo();
return true;
}
#endif
// Debug info
if (Input::get()->checkInput(InputAction::SHOW_INFO, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) {
Screen::get()->toggleDebugInfo();
return true;
}
return false;
}
// Comprueba los inputs que se pueden introducir en cualquier sección del juego
bool check()
{
if (checkServiceButton())
return true;
if (checkServiceInputs())
return true;
if (checkInputs())
return true;
return false;
#endif
}
return false;
}
// Comprueba los inputs que se pueden introducir en cualquier sección del juego
bool check() {
if (checkServiceButton())
return true;
if (checkServiceInputs())
return true;
if (checkInputs())
return true;
return false;
}
} // namespace GlobalInputs

View File

@@ -1,7 +1,6 @@
#pragma once
namespace GlobalInputs
{
// Comprueba los inputs que se pueden introducir en cualquier sección del juego
bool check();
}
namespace GlobalInputs {
// Comprueba los inputs que se pueden introducir en cualquier sección del juego
bool check();
} // namespace GlobalInputs

View File

@@ -1,11 +1,12 @@
#include "input.h"
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_GetGamepa...
#include <stddef.h> // Para size_t
#include <algorithm> // Para find
#include <iterator> // Para distance
#include <unordered_map> // Para unordered_map, _Node_const_iterator, operat...
#include <utility> // Para pair
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_GetGamepa...
#include <stddef.h> // Para size_t
#include <algorithm> // Para find
#include <iterator> // Para distance
#include <unordered_map> // Para unordered_map, _Node_const_iterator, operat...
#include <utility> // Para pair
// Singleton
Input *Input::instance_ = nullptr;
@@ -21,232 +22,188 @@ Input *Input::get() { return Input::instance_; }
// Constructor
Input::Input(const std::string &game_controller_db_path)
: game_controller_db_path_(game_controller_db_path)
{
// Inicializa el subsistema SDL_INIT_GAMEPAD
initSDLGamePad();
: game_controller_db_path_(game_controller_db_path) {
// Inicializa el subsistema SDL_INIT_GAMEPAD
initSDLGamePad();
// Inicializa los vectores
key_bindings_.resize(static_cast<int>(InputAction::SIZE), KeyBindings());
controller_bindings_.resize(num_gamepads_, std::vector<ControllerBindings>(static_cast<int>(InputAction::SIZE), ControllerBindings()));
// Inicializa los vectores
key_bindings_.resize(static_cast<int>(InputAction::SIZE), KeyBindings());
controller_bindings_.resize(num_gamepads_, std::vector<ControllerBindings>(static_cast<int>(InputAction::SIZE), ControllerBindings()));
// Listado de los inputs para jugar que utilizan botones, ni palancas ni crucetas
button_inputs_ = {InputAction::FIRE_LEFT, InputAction::FIRE_CENTER, InputAction::FIRE_RIGHT, InputAction::START};
// Listado de los inputs para jugar que utilizan botones, ni palancas ni crucetas
button_inputs_ = {InputAction::FIRE_LEFT, InputAction::FIRE_CENTER, InputAction::FIRE_RIGHT, InputAction::START};
}
// Asigna inputs a teclas
void Input::bindKey(InputAction input, SDL_Scancode code)
{
key_bindings_.at(static_cast<int>(input)).scancode = code;
void Input::bindKey(InputAction input, SDL_Scancode code) {
key_bindings_.at(static_cast<int>(input)).scancode = code;
}
// Asigna inputs a botones del mando
void Input::bindGameControllerButton(int controller_index, InputAction input, SDL_GamepadButton button)
{
if (controller_index < num_gamepads_)
{
controller_bindings_.at(controller_index).at(static_cast<int>(input)).button = button;
}
void Input::bindGameControllerButton(int controller_index, InputAction input, SDL_GamepadButton button) {
if (controller_index < num_gamepads_) {
controller_bindings_.at(controller_index).at(static_cast<int>(input)).button = button;
}
}
// Asigna inputs a botones del mando
void Input::bindGameControllerButton(int controller_index, InputAction input_target, InputAction input_source)
{
if (controller_index < num_gamepads_)
{
controller_bindings_.at(controller_index).at(static_cast<int>(input_target)).button = controller_bindings_.at(controller_index).at(static_cast<int>(input_source)).button;
}
void Input::bindGameControllerButton(int controller_index, InputAction input_target, InputAction input_source) {
if (controller_index < num_gamepads_) {
controller_bindings_.at(controller_index).at(static_cast<int>(input_target)).button = controller_bindings_.at(controller_index).at(static_cast<int>(input_source)).button;
}
}
// Comprueba si un input esta activo
bool Input::checkInput(InputAction input, bool repeat, InputDevice device, int controller_index)
{
bool success_keyboard = false;
bool success_controller = false;
const int input_index = static_cast<int>(input);
bool Input::checkInput(InputAction input, bool repeat, InputDevice device, int controller_index) {
bool success_keyboard = false;
bool success_controller = false;
const int input_index = static_cast<int>(input);
if (device == InputDevice::KEYBOARD || device == InputDevice::ANY)
{
if (repeat)
{ // El usuario quiere saber si está pulsada (estado mantenido)
success_keyboard = key_bindings_[input_index].is_held;
}
else
{ // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma)
success_keyboard = key_bindings_[input_index].just_pressed;
}
}
if (device == InputDevice::KEYBOARD || device == InputDevice::ANY) {
if (repeat) { // El usuario quiere saber si está pulsada (estado mantenido)
success_keyboard = key_bindings_[input_index].is_held;
} else { // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma)
success_keyboard = key_bindings_[input_index].just_pressed;
}
}
if (gameControllerFound() && controller_index >= 0 && controller_index < num_gamepads_)
{
if ((device == InputDevice::CONTROLLER) || (device == InputDevice::ANY))
{
success_controller = checkAxisInput(input, controller_index, repeat);
if (gameControllerFound() && controller_index >= 0 && controller_index < num_gamepads_) {
if ((device == InputDevice::CONTROLLER) || (device == InputDevice::ANY)) {
success_controller = checkAxisInput(input, controller_index, repeat);
if (!success_controller)
{
if (repeat)
{ // El usuario quiere saber si está pulsada (estado mantenido)
success_controller = controller_bindings_.at(controller_index).at(input_index).is_held;
}
else
{ // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma)
success_controller = controller_bindings_.at(controller_index).at(input_index).just_pressed;
}
}
}
}
if (!success_controller) {
if (repeat) { // El usuario quiere saber si está pulsada (estado mantenido)
success_controller = controller_bindings_.at(controller_index).at(input_index).is_held;
} else { // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma)
success_controller = controller_bindings_.at(controller_index).at(input_index).just_pressed;
}
}
}
}
return (success_keyboard || success_controller);
return (success_keyboard || success_controller);
}
// Comprueba si hay almenos un input activo
bool Input::checkAnyInput(InputDevice device, int controller_index)
{
// Obtenemos el número total de acciones posibles para iterar sobre ellas.
const int num_actions = static_cast<int>(InputAction::SIZE);
bool Input::checkAnyInput(InputDevice device, int controller_index) {
// Obtenemos el número total de acciones posibles para iterar sobre ellas.
const int num_actions = static_cast<int>(InputAction::SIZE);
// --- Comprobación del Teclado ---
if (device == InputDevice::KEYBOARD || device == InputDevice::ANY)
{
for (int i = 0; i < num_actions; ++i)
{
// Simplemente leemos el estado pre-calculado por Input::update().
// Ya no se llama a SDL_GetKeyboardState ni se modifica el estado '.active'.
if (key_bindings_.at(i).just_pressed)
{
return true; // Se encontró una acción recién pulsada.
}
}
}
// --- Comprobación del Teclado ---
if (device == InputDevice::KEYBOARD || device == InputDevice::ANY) {
for (int i = 0; i < num_actions; ++i) {
// Simplemente leemos el estado pre-calculado por Input::update().
// Ya no se llama a SDL_GetKeyboardState ni se modifica el estado '.active'.
if (key_bindings_.at(i).just_pressed) {
return true; // Se encontró una acción recién pulsada.
}
}
}
// --- Comprobación del Mando ---
// Comprobamos si hay mandos y si el índice solicitado es válido.
if (gameControllerFound() && controller_index >= 0 && controller_index < num_gamepads_)
{
if (device == InputDevice::CONTROLLER || device == InputDevice::ANY)
{
// Bucle CORREGIDO: Iteramos sobre todas las acciones, no sobre el número de mandos.
for (int i = 0; i < num_actions; ++i)
{
// Leemos el estado pre-calculado para el mando y la acción específicos.
if (controller_bindings_.at(controller_index).at(i).just_pressed)
{
return true; // Se encontró una acción recién pulsada en el mando.
}
}
}
}
// --- Comprobación del Mando ---
// Comprobamos si hay mandos y si el índice solicitado es válido.
if (gameControllerFound() && controller_index >= 0 && controller_index < num_gamepads_) {
if (device == InputDevice::CONTROLLER || device == InputDevice::ANY) {
// Bucle CORREGIDO: Iteramos sobre todas las acciones, no sobre el número de mandos.
for (int i = 0; i < num_actions; ++i) {
// Leemos el estado pre-calculado para el mando y la acción específicos.
if (controller_bindings_.at(controller_index).at(i).just_pressed) {
return true; // Se encontró una acción recién pulsada en el mando.
}
}
}
}
// Si llegamos hasta aquí, no se detectó ninguna nueva pulsación.
return false;
// Si llegamos hasta aquí, no se detectó ninguna nueva pulsación.
return false;
}
// Comprueba si hay algún botón pulsado. Devuelve 0 en caso de no encontrar nada o el indice del dispositivo + 1. Se hace así para poder gastar el valor devuelto como un valor "booleano"
int Input::checkAnyButton(bool repeat)
{
// Solo comprueba los botones definidos previamente
for (auto bi : button_inputs_)
{
// Comprueba el teclado
if (checkInput(bi, repeat, InputDevice::KEYBOARD))
{
return 1;
}
int Input::checkAnyButton(bool repeat) {
// Solo comprueba los botones definidos previamente
for (auto bi : button_inputs_) {
// Comprueba el teclado
if (checkInput(bi, repeat, InputDevice::KEYBOARD)) {
return 1;
}
// Comprueba los mandos
for (int i = 0; i < num_gamepads_; ++i)
{
if (checkInput(bi, repeat, InputDevice::CONTROLLER, i))
{
return i + 1;
}
}
}
// Comprueba los mandos
for (int i = 0; i < num_gamepads_; ++i) {
if (checkInput(bi, repeat, InputDevice::CONTROLLER, i)) {
return i + 1;
}
}
}
return 0;
return 0;
}
// Busca si hay mandos conectados
bool Input::discoverGameControllers()
{
bool found = false;
bool Input::discoverGameControllers() {
bool found = false;
if (SDL_AddGamepadMappingsFromFile(game_controller_db_path_.c_str()) < 0)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: could not load %s file: %s", game_controller_db_path_.c_str(), SDL_GetError());
}
if (SDL_AddGamepadMappingsFromFile(game_controller_db_path_.c_str()) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: could not load %s file: %s", game_controller_db_path_.c_str(), SDL_GetError());
}
// En SDL3, SDL_GetJoysticks devuelve un array de IDs, no un contador
SDL_JoystickID *joystick_ids = SDL_GetJoysticks(&num_joysticks_);
num_gamepads_ = 0;
// En SDL3, SDL_GetJoysticks devuelve un array de IDs, no un contador
SDL_JoystickID *joystick_ids = SDL_GetJoysticks(&num_joysticks_);
num_gamepads_ = 0;
// Cuenta el número de mandos
joysticks_.clear();
for (int i = 0; i < num_joysticks_; ++i)
{
// Usar el ID del joystick, no el índice
auto joy = SDL_OpenJoystick(joystick_ids[i]);
joysticks_.push_back(joy);
// Cuenta el número de mandos
joysticks_.clear();
for (int i = 0; i < num_joysticks_; ++i) {
// Usar el ID del joystick, no el índice
auto joy = SDL_OpenJoystick(joystick_ids[i]);
joysticks_.push_back(joy);
// En SDL3, SDL_IsGamepad toma un SDL_JoystickID, no un índice
if (SDL_IsGamepad(joystick_ids[i]))
{
num_gamepads_++;
}
}
// En SDL3, SDL_IsGamepad toma un SDL_JoystickID, no un índice
if (SDL_IsGamepad(joystick_ids[i])) {
num_gamepads_++;
}
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, ">> LOOKING FOR GAME CONTROLLERS");
if (num_joysticks_ != num_gamepads_)
{
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Joysticks found: %d", num_joysticks_);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Gamepads found : %d", num_gamepads_);
}
else
{
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Gamepads found: %d", num_gamepads_);
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, ">> LOOKING FOR GAME CONTROLLERS");
if (num_joysticks_ != num_gamepads_) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Joysticks found: %d", num_joysticks_);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Gamepads found : %d", num_gamepads_);
} else {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Gamepads found: %d", num_gamepads_);
}
if (num_gamepads_ > 0)
{
found = true;
if (num_gamepads_ > 0) {
found = true;
// Recorrer los joysticks y abrir solo los que son gamepads
for (int i = 0; i < num_joysticks_; i++)
{
if (SDL_IsGamepad(joystick_ids[i]))
{
// Abre el mando usando el ID del joystick
auto pad = SDL_OpenGamepad(joystick_ids[i]);
if (pad != nullptr)
{
connected_controllers_.push_back(pad);
// Recorrer los joysticks y abrir solo los que son gamepads
for (int i = 0; i < num_joysticks_; i++) {
if (SDL_IsGamepad(joystick_ids[i])) {
// Abre el mando usando el ID del joystick
auto pad = SDL_OpenGamepad(joystick_ids[i]);
if (pad != nullptr) {
connected_controllers_.push_back(pad);
// Obtener el nombre usando el ID del joystick
const char *name_cstr = SDL_GetGamepadNameForID(joystick_ids[i]);
std::string name = name_cstr ? name_cstr : "Unknown Gamepad";
// Obtener el nombre usando el ID del joystick
const char *name_cstr = SDL_GetGamepadNameForID(joystick_ids[i]);
std::string name = name_cstr ? name_cstr : "Unknown Gamepad";
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "#%d: %s", i, name.c_str());
controller_names_.push_back(name);
}
else
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open gamepad %d: %s", joystick_ids[i], SDL_GetError());
}
}
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "#%d: %s", i, name.c_str());
controller_names_.push_back(name);
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open gamepad %d: %s", joystick_ids[i], SDL_GetError());
}
}
}
SDL_SetGamepadEventsEnabled(true);
}
SDL_SetGamepadEventsEnabled(true);
}
// Liberar el array de IDs
if (joystick_ids)
{
SDL_free(joystick_ids);
}
// Liberar el array de IDs
if (joystick_ids) {
SDL_free(joystick_ids);
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> FINISHED LOOKING FOR GAME CONTROLLERS");
return found;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> FINISHED LOOKING FOR GAME CONTROLLERS");
return found;
}
// Comprueba si hay algun mando conectado
@@ -259,202 +216,168 @@ std::string Input::getControllerName(int controller_index) const { return num_ga
int Input::getNumControllers() const { return num_gamepads_; }
// Obtiene el indice del controlador a partir de un event.id
int Input::getJoyIndex(SDL_JoystickID id) const
{
for (int i = 0; i < num_joysticks_; ++i)
{
if (SDL_GetJoystickID(joysticks_[i]) == id)
{
return i;
}
}
return -1;
int Input::getJoyIndex(SDL_JoystickID id) const {
for (int i = 0; i < num_joysticks_; ++i) {
if (SDL_GetJoystickID(joysticks_[i]) == id) {
return i;
}
}
return -1;
}
// Muestra por consola los controles asignados
void Input::printBindings(InputDevice device, int controller_index) const
{
if (device == InputDevice::ANY || device == InputDevice::KEYBOARD)
{
return;
}
void Input::printBindings(InputDevice device, int controller_index) const {
if (device == InputDevice::ANY || device == InputDevice::KEYBOARD) {
return;
}
if (device == InputDevice::CONTROLLER)
{
if (controller_index >= num_gamepads_)
{
return;
}
if (device == InputDevice::CONTROLLER) {
if (controller_index >= num_gamepads_) {
return;
}
// Muestra el nombre del mando
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n%s", controller_names_.at(controller_index).c_str());
// Muestra el nombre del mando
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n%s", controller_names_.at(controller_index).c_str());
// Muestra los botones asignados
for (auto bi : button_inputs_)
{
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s : %d", to_string(bi).c_str(), controller_bindings_.at(controller_index).at(static_cast<int>(bi)).button);
}
}
// Muestra los botones asignados
for (auto bi : button_inputs_) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s : %d", to_string(bi).c_str(), controller_bindings_.at(controller_index).at(static_cast<int>(bi)).button);
}
}
}
// Obtiene el SDL_GamepadButton asignado a un input
SDL_GamepadButton Input::getControllerBinding(int controller_index, InputAction input) const
{
return controller_bindings_[controller_index][static_cast<int>(input)].button;
SDL_GamepadButton Input::getControllerBinding(int controller_index, InputAction input) const {
return controller_bindings_[controller_index][static_cast<int>(input)].button;
}
// Obtiene el indice a partir del nombre del mando
int Input::getIndexByName(const std::string &name) const
{
auto it = std::find(controller_names_.begin(), controller_names_.end(), name);
return it != controller_names_.end() ? std::distance(controller_names_.begin(), it) : -1;
int Input::getIndexByName(const std::string &name) const {
auto it = std::find(controller_names_.begin(), controller_names_.end(), name);
return it != controller_names_.end() ? std::distance(controller_names_.begin(), it) : -1;
}
// Convierte un InputAction a std::string
std::string Input::to_string(InputAction input) const
{
switch (input)
{
case InputAction::FIRE_LEFT:
return "input_fire_left";
case InputAction::FIRE_CENTER:
return "input_fire_center";
case InputAction::FIRE_RIGHT:
return "input_fire_right";
case InputAction::START:
return "input_start";
case InputAction::SERVICE:
return "input_service";
default:
return "";
}
std::string Input::to_string(InputAction input) const {
switch (input) {
case InputAction::FIRE_LEFT:
return "input_fire_left";
case InputAction::FIRE_CENTER:
return "input_fire_center";
case InputAction::FIRE_RIGHT:
return "input_fire_right";
case InputAction::START:
return "input_start";
case InputAction::SERVICE:
return "input_service";
default:
return "";
}
}
// Convierte un std::string a InputAction
InputAction Input::to_inputs_e(const std::string &name) const
{
static const std::unordered_map<std::string, InputAction> inputMap = {
{"input_fire_left", InputAction::FIRE_LEFT},
{"input_fire_center", InputAction::FIRE_CENTER},
{"input_fire_right", InputAction::FIRE_RIGHT},
{"input_start", InputAction::START},
{"input_service", InputAction::SERVICE}};
InputAction Input::to_inputs_e(const std::string &name) const {
static const std::unordered_map<std::string, InputAction> inputMap = {
{"input_fire_left", InputAction::FIRE_LEFT},
{"input_fire_center", InputAction::FIRE_CENTER},
{"input_fire_right", InputAction::FIRE_RIGHT},
{"input_start", InputAction::START},
{"input_service", InputAction::SERVICE}};
auto it = inputMap.find(name);
return it != inputMap.end() ? it->second : InputAction::NONE;
auto it = inputMap.find(name);
return it != inputMap.end() ? it->second : InputAction::NONE;
}
// Comprueba el eje del mando
bool Input::checkAxisInput(InputAction input, int controller_index, bool repeat)
{
// Umbral para considerar el eje como activo
bool axis_active_now = false;
bool Input::checkAxisInput(InputAction input, int controller_index, bool repeat) {
// Umbral para considerar el eje como activo
bool axis_active_now = false;
switch (input)
{
case InputAction::LEFT:
axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTX) < -AXIS_THRESHOLD_;
break;
case InputAction::RIGHT:
axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTX) > AXIS_THRESHOLD_;
break;
case InputAction::UP:
axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTY) < -AXIS_THRESHOLD_;
break;
case InputAction::DOWN:
axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTY) > AXIS_THRESHOLD_;
break;
default:
return false;
}
switch (input) {
case InputAction::LEFT:
axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTX) < -AXIS_THRESHOLD_;
break;
case InputAction::RIGHT:
axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTX) > AXIS_THRESHOLD_;
break;
case InputAction::UP:
axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTY) < -AXIS_THRESHOLD_;
break;
case InputAction::DOWN:
axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTY) > AXIS_THRESHOLD_;
break;
default:
return false;
}
// Referencia al binding correspondiente
auto &binding = controller_bindings_.at(controller_index).at(static_cast<int>(input));
// Referencia al binding correspondiente
auto &binding = controller_bindings_.at(controller_index).at(static_cast<int>(input));
if (repeat)
{
// Si se permite repetir, simplemente devolvemos el estado actual
return axis_active_now;
}
else
{
// Si no se permite repetir, aplicamos la lógica de transición
if (axis_active_now && !binding.axis_active)
{
// Transición de inactivo a activo
binding.axis_active = true;
return true;
}
else if (!axis_active_now && binding.axis_active)
{
// Transición de activo a inactivo
binding.axis_active = false;
}
// Mantener el estado actual
return false;
}
if (repeat) {
// Si se permite repetir, simplemente devolvemos el estado actual
return axis_active_now;
} else {
// Si no se permite repetir, aplicamos la lógica de transición
if (axis_active_now && !binding.axis_active) {
// Transición de inactivo a activo
binding.axis_active = true;
return true;
} else if (!axis_active_now && binding.axis_active) {
// Transición de activo a inactivo
binding.axis_active = false;
}
// Mantener el estado actual
return false;
}
}
void Input::initSDLGamePad()
{
if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1)
{
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD))
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GAMEPAD could not initialize! SDL Error: %s", SDL_GetError());
}
else
{
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "\n** SDL_GAMEPAD: INITIALIZING");
discoverGameControllers();
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "** SDL_GAMEPAD: INITIALIZATION COMPLETE\n");
}
}
void Input::initSDLGamePad() {
if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1) {
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GAMEPAD could not initialize! SDL Error: %s", SDL_GetError());
} else {
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "\n** SDL_GAMEPAD: INITIALIZING");
discoverGameControllers();
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "** SDL_GAMEPAD: INITIALIZATION COMPLETE\n");
}
}
}
void Input::resetInputStates()
{
// Resetear todos los KeyBindings.active a false
for (auto &key : key_bindings_)
{
key.is_held = false;
key.just_pressed = false;
}
// Resetear todos los ControllerBindings.active a false
for (auto &controller_vec : controller_bindings_)
{
for (auto &binding : controller_vec)
{
binding.is_held = false;
binding.just_pressed = false;
}
}
void Input::resetInputStates() {
// Resetear todos los KeyBindings.active a false
for (auto &key : key_bindings_) {
key.is_held = false;
key.just_pressed = false;
}
// Resetear todos los ControllerBindings.active a false
for (auto &controller_vec : controller_bindings_) {
for (auto &binding : controller_vec) {
binding.is_held = false;
binding.just_pressed = false;
}
}
}
void Input::update()
{
// --- TECLADO ---
const bool *key_states = SDL_GetKeyboardState(nullptr);
void Input::update() {
// --- TECLADO ---
const bool *key_states = SDL_GetKeyboardState(nullptr);
for (size_t i = 0; i < key_bindings_.size(); ++i)
{
bool key_is_down_now = key_states[key_bindings_[i].scancode];
for (size_t i = 0; i < key_bindings_.size(); ++i) {
bool key_is_down_now = key_states[key_bindings_[i].scancode];
// El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo
key_bindings_[i].just_pressed = key_is_down_now && !key_bindings_[i].is_held;
key_bindings_[i].is_held = key_is_down_now;
}
// El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo
key_bindings_[i].just_pressed = key_is_down_now && !key_bindings_[i].is_held;
key_bindings_[i].is_held = key_is_down_now;
}
// --- MANDOS ---
for (int c = 0; c < num_gamepads_; ++c)
{
for (size_t i = 0; i < controller_bindings_[c].size(); ++i)
{
bool button_is_down_now = SDL_GetGamepadButton(connected_controllers_.at(c), controller_bindings_.at(c).at(i).button) != 0;
// --- MANDOS ---
for (int c = 0; c < num_gamepads_; ++c) {
for (size_t i = 0; i < controller_bindings_[c].size(); ++i) {
bool button_is_down_now = SDL_GetGamepadButton(connected_controllers_.at(c), controller_bindings_.at(c).at(i).button) != 0;
// El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo
controller_bindings_[c][i].just_pressed = button_is_down_now && !controller_bindings_[c][i].is_held;
controller_bindings_[c][i].is_held = button_is_down_now;
}
}
// El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo
controller_bindings_[c][i].just_pressed = button_is_down_now && !controller_bindings_[c][i].is_held;
controller_bindings_[c][i].is_held = button_is_down_now;
}
}
}

View File

@@ -1,154 +1,150 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_GamepadButton, Uint8, SDL_Gamepad, SDL_Joystick, SDL_JoystickID, SDL_Scancode, Sint16
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_GamepadButton, Uint8, SDL_Gamepad, SDL_Joystick, SDL_JoystickID, SDL_Scancode, Sint16
#include <string> // Para basic_string, string
#include <vector> // Para vector
/*
connectedControllers es un vector donde están todos los mandos encontrados [0 .. n]
checkInput requiere de un índice para comprobar las pulsaciones de un controlador en concreto [0 .. n]
device contiene el tipo de dispositivo a comprobar:
InputDeviceToUse::KEYBOARD solo mirará el teclado
InputDeviceToUse::CONTROLLER solo mirará el controlador especificado (Si no se especifica, el primero)
InputDeviceToUse::ANY mirará tanto el teclado como el PRIMER controlador
InputDeviceToUse::KEYBOARD solo mirará el teclado
InputDeviceToUse::CONTROLLER solo mirará el controlador especificado (Si no se especifica, el primero)
InputDeviceToUse::ANY mirará tanto el teclado como el PRIMER controlador
*/
// Acciones de entrada posibles en el juego
enum class InputAction : int
{
// Inputs de movimiento
UP,
DOWN,
LEFT,
RIGHT,
enum class InputAction : int {
// Inputs de movimiento
UP,
DOWN,
LEFT,
RIGHT,
// Inputs personalizados
FIRE_LEFT,
FIRE_CENTER,
FIRE_RIGHT,
START,
// Inputs personalizados
FIRE_LEFT,
FIRE_CENTER,
FIRE_RIGHT,
START,
// Service Menu
SM_SELECT,
SM_BACK,
// Service Menu
SM_SELECT,
SM_BACK,
// Inputs de control
BACK,
EXIT,
PAUSE,
SERVICE,
WINDOW_FULLSCREEN,
WINDOW_INC_SIZE,
WINDOW_DEC_SIZE,
TOGGLE_VIDEO_SHADERS,
TOGGLE_VIDEO_INTEGER_SCALE,
TOGGLE_VIDEO_VSYNC,
RESET,
TOGGLE_AUDIO,
CHANGE_LANG,
SHOW_INFO,
CONFIG,
SWAP_CONTROLLERS,
TOGGLE_AUTO_FIRE,
// Inputs de control
BACK,
EXIT,
PAUSE,
SERVICE,
WINDOW_FULLSCREEN,
WINDOW_INC_SIZE,
WINDOW_DEC_SIZE,
TOGGLE_VIDEO_SHADERS,
TOGGLE_VIDEO_INTEGER_SCALE,
TOGGLE_VIDEO_VSYNC,
RESET,
TOGGLE_AUDIO,
CHANGE_LANG,
SHOW_INFO,
CONFIG,
SWAP_CONTROLLERS,
TOGGLE_AUTO_FIRE,
// Input obligatorio
NONE,
SIZE,
// Input obligatorio
NONE,
SIZE,
};
constexpr bool INPUT_ALLOW_REPEAT = true;
constexpr bool INPUT_DO_NOT_ALLOW_REPEAT = false;
// Tipos de dispositivos de entrada
enum class InputDevice : int
{
KEYBOARD = 0,
CONTROLLER = 1,
ANY = 2,
enum class InputDevice : int {
KEYBOARD = 0,
CONTROLLER = 1,
ANY = 2,
};
// Clase Input: gestiona la entrada de teclado y mandos (singleton)
class Input
{
public:
// --- Métodos de singleton ---
static void init(const std::string &game_controller_db_path); // Inicializa el singleton
static void destroy(); // Libera el singleton
static Input *get(); // Obtiene la instancia
class Input {
public:
// --- Métodos de singleton ---
static void init(const std::string &game_controller_db_path); // Inicializa el singleton
static void destroy(); // Libera el singleton
static Input *get(); // Obtiene la instancia
// --- Métodos de configuración de controles ---
void bindKey(InputAction input, SDL_Scancode code); // Asigna inputs a teclas
void bindGameControllerButton(int controller_index, InputAction input, SDL_GamepadButton button); // Asigna inputs a botones del mando
void bindGameControllerButton(int controller_index, InputAction inputTarget, InputAction inputSource); // Asigna inputs a otros inputs del mando
// --- Métodos de configuración de controles ---
void bindKey(InputAction input, SDL_Scancode code); // Asigna inputs a teclas
void bindGameControllerButton(int controller_index, InputAction input, SDL_GamepadButton button); // Asigna inputs a botones del mando
void bindGameControllerButton(int controller_index, InputAction inputTarget, InputAction inputSource); // Asigna inputs a otros inputs del mando
// --- Métodos de consulta de entrada ---
void update(); // Comprueba fisicamente los botones y teclas que se han pulsado
bool checkInput(InputAction input, bool repeat = true, InputDevice device = InputDevice::ANY, int controller_index = 0); // Comprueba si un input está activo
bool checkAnyInput(InputDevice device = InputDevice::ANY, int controller_index = 0); // Comprueba si hay al menos un input activo
int checkAnyButton(bool repeat = INPUT_DO_NOT_ALLOW_REPEAT); // Comprueba si hay algún botón pulsado
// --- Métodos de consulta de entrada ---
void update(); // Comprueba fisicamente los botones y teclas que se han pulsado
bool checkInput(InputAction input, bool repeat = true, InputDevice device = InputDevice::ANY, int controller_index = 0); // Comprueba si un input está activo
bool checkAnyInput(InputDevice device = InputDevice::ANY, int controller_index = 0); // Comprueba si hay al menos un input activo
int checkAnyButton(bool repeat = INPUT_DO_NOT_ALLOW_REPEAT); // Comprueba si hay algún botón pulsado
// --- Métodos de gestión de mandos ---
bool discoverGameControllers(); // Busca si hay mandos conectados
bool gameControllerFound(); // Comprueba si hay algún mando conectado
int getNumControllers() const; // Obtiene el número de mandos conectados
std::string getControllerName(int controller_index) const; // Obtiene el nombre de un mando de juego
int getJoyIndex(SDL_JoystickID id) const; // Obtiene el índice del controlador a partir de un event.id
// --- Métodos de gestión de mandos ---
bool discoverGameControllers(); // Busca si hay mandos conectados
bool gameControllerFound(); // Comprueba si hay algún mando conectado
int getNumControllers() const; // Obtiene el número de mandos conectados
std::string getControllerName(int controller_index) const; // Obtiene el nombre de un mando de juego
int getJoyIndex(SDL_JoystickID id) const; // Obtiene el índice del controlador a partir de un event.id
// --- Métodos de consulta y utilidades ---
void printBindings(InputDevice device = InputDevice::KEYBOARD, int controller_index = 0) const; // Muestra por consola los controles asignados
SDL_GamepadButton getControllerBinding(int controller_index, InputAction input) const; // Obtiene el SDL_GamepadButton asignado a un input
std::string to_string(InputAction input) const; // Convierte un InputAction a std::string
InputAction to_inputs_e(const std::string &name) const; // Convierte un std::string a InputAction
int getIndexByName(const std::string &name) const; // Obtiene el índice a partir del nombre del mando
// --- Métodos de consulta y utilidades ---
void printBindings(InputDevice device = InputDevice::KEYBOARD, int controller_index = 0) const; // Muestra por consola los controles asignados
SDL_GamepadButton getControllerBinding(int controller_index, InputAction input) const; // Obtiene el SDL_GamepadButton asignado a un input
std::string to_string(InputAction input) const; // Convierte un InputAction a std::string
InputAction to_inputs_e(const std::string &name) const; // Convierte un std::string a InputAction
int getIndexByName(const std::string &name) const; // Obtiene el índice a partir del nombre del mando
// --- Métodos de reseteo de estado de entrada ---
void resetInputStates(); // Pone todos los KeyBindings.active y ControllerBindings.active a false
// --- Métodos de reseteo de estado de entrada ---
void resetInputStates(); // Pone todos los KeyBindings.active y ControllerBindings.active a false
private:
// --- Singleton ---
static Input *instance_;
private:
// --- Singleton ---
static Input *instance_;
// --- Estructuras internas ---
struct KeyBindings
{
Uint8 scancode; // Scancode asociado
bool is_held; // Está pulsada ahora mismo
bool just_pressed; // Se acaba de pulsar en este fotograma
// --- Estructuras internas ---
struct KeyBindings {
Uint8 scancode; // Scancode asociado
bool is_held; // Está pulsada ahora mismo
bool just_pressed; // Se acaba de pulsar en este fotograma
KeyBindings(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false)
: scancode(scancode), is_held(is_held), just_pressed(just_pressed) {}
};
KeyBindings(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false)
: scancode(scancode), is_held(is_held), just_pressed(just_pressed) {}
};
struct ControllerBindings
{
SDL_GamepadButton button; // GameControllerButton asociado
bool is_held; // Está pulsada ahora mismo
bool just_pressed; // Se acaba de pulsar en este fotograma
bool axis_active; // Estado del eje
struct ControllerBindings {
SDL_GamepadButton button; // GameControllerButton asociado
bool is_held; // Está pulsada ahora mismo
bool just_pressed; // Se acaba de pulsar en este fotograma
bool axis_active; // Estado del eje
ControllerBindings(SDL_GamepadButton btn = 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) {}
};
ControllerBindings(SDL_GamepadButton btn = 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) {}
};
// --- Constantes ---
static constexpr Sint16 AXIS_THRESHOLD_ = 30000;
// --- Constantes ---
static constexpr Sint16 AXIS_THRESHOLD_ = 30000;
// --- Variables internas ---
std::vector<SDL_Gamepad *> connected_controllers_; // Vector con todos los mandos conectados
std::vector<SDL_Joystick *> joysticks_; // Vector con todos los joysticks conectados
std::vector<KeyBindings> key_bindings_; // Vector con las teclas asociadas a los inputs predefinidos
std::vector<std::vector<ControllerBindings>> controller_bindings_; // Vector con los botones asociados a los inputs predefinidos para cada mando
std::vector<std::string> controller_names_; // Vector con los nombres de los mandos
std::vector<InputAction> button_inputs_; // Inputs asignados al jugador y a botones, excluyendo direcciones
int num_joysticks_ = 0; // Número de joysticks conectados
int num_gamepads_ = 0; // Número de mandos conectados
std::string game_controller_db_path_; // Ruta al archivo gamecontrollerdb.txt
// --- Variables internas ---
std::vector<SDL_Gamepad *> connected_controllers_; // Vector con todos los mandos conectados
std::vector<SDL_Joystick *> joysticks_; // Vector con todos los joysticks conectados
std::vector<KeyBindings> key_bindings_; // Vector con las teclas asociadas a los inputs predefinidos
std::vector<std::vector<ControllerBindings>> controller_bindings_; // Vector con los botones asociados a los inputs predefinidos para cada mando
std::vector<std::string> controller_names_; // Vector con los nombres de los mandos
std::vector<InputAction> button_inputs_; // Inputs asignados al jugador y a botones, excluyendo direcciones
int num_joysticks_ = 0; // Número de joysticks conectados
int num_gamepads_ = 0; // Número de mandos conectados
std::string game_controller_db_path_; // Ruta al archivo gamecontrollerdb.txt
// --- Métodos internos ---
void initSDLGamePad(); // Inicializa SDL para la gestión de mandos
bool checkAxisInput(InputAction input, int controller_index, bool repeat); // Comprueba el eje del mando
// --- Métodos internos ---
void initSDLGamePad(); // Inicializa SDL para la gestión de mandos
bool checkAxisInput(InputAction input, int controller_index, bool repeat); // Comprueba el eje del mando
// --- Constructor y destructor ---
explicit Input(const std::string &game_controller_db_path); // Constructor privado
~Input() = default; // Destructor privado
// --- Constructor y destructor ---
explicit Input(const std::string &game_controller_db_path); // Constructor privado
~Input() = default; // Destructor privado
};

View File

@@ -1,249 +1,208 @@
#include "item.h"
#include <stdlib.h> // Para rand
#include <algorithm> // Para clamp
#include <stdlib.h> // Para rand
#include "animated_sprite.h" // Para AnimatedSprite
#include "param.h" // Para Param, ParamGame, param
#include <algorithm> // Para clamp
class Texture; // lines 6-6
#include "animated_sprite.h" // Para AnimatedSprite
#include "param.h" // Para Param, ParamGame, param
class Texture; // lines 6-6
Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation)
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
type_(type),
play_area_(play_area)
{
switch (type)
{
case ItemType::COFFEE_MACHINE:
{
width_ = COFFEE_MACHINE_WIDTH;
height_ = COFFEE_MACHINE_HEIGHT;
pos_x_ = getCoffeeMachineSpawn(x, width_, play_area_.w);
pos_y_ = y;
vel_x_ = ((rand() % 3) - 1) * 0.5f;
vel_y_ = -0.1f;
accel_y_ = 0.1f;
collider_.r = 10;
break;
}
default:
{
width_ = param.game.item_size;
height_ = param.game.item_size;
pos_x_ = x;
pos_y_ = y;
vel_x_ = -1.0f + ((rand() % 5) * 0.5f);
vel_y_ = -4.0f;
accel_y_ = 0.2f;
collider_.r = width_ / 2;
break;
}
}
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
type_(type),
play_area_(play_area) {
switch (type) {
case ItemType::COFFEE_MACHINE: {
width_ = COFFEE_MACHINE_WIDTH;
height_ = COFFEE_MACHINE_HEIGHT;
pos_x_ = getCoffeeMachineSpawn(x, width_, play_area_.w);
pos_y_ = y;
vel_x_ = ((rand() % 3) - 1) * 0.5f;
vel_y_ = -0.1f;
accel_y_ = 0.1f;
collider_.r = 10;
break;
}
default: {
width_ = param.game.item_size;
height_ = param.game.item_size;
pos_x_ = x;
pos_y_ = y;
vel_x_ = -1.0f + ((rand() % 5) * 0.5f);
vel_y_ = -4.0f;
accel_y_ = 0.2f;
collider_.r = width_ / 2;
break;
}
}
// Actualiza el sprite
shiftSprite();
shiftColliders();
// Actualiza el sprite
shiftSprite();
shiftColliders();
}
void Item::alignTo(int x)
{
const float min_x = param.game.play_area.rect.x + 1;
const float max_x = play_area_.w - width_ - 1;
void Item::alignTo(int x) {
const float min_x = param.game.play_area.rect.x + 1;
const float max_x = play_area_.w - width_ - 1;
pos_x_ = x - (width_ / 2);
pos_x_ = x - (width_ / 2);
// Ajusta para que no quede fuera de la zona de juego
pos_x_ = std::clamp(pos_x_, min_x, max_x);
// Ajusta para que no quede fuera de la zona de juego
pos_x_ = std::clamp(pos_x_, min_x, max_x);
// Actualiza el sprite
shiftSprite();
shiftColliders();
// Actualiza el sprite
shiftSprite();
shiftColliders();
}
void Item::render()
{
if (enabled_)
{
if (time_to_live_ > 200)
{
sprite_->render();
}
else if (time_to_live_ % 20 > 10)
{
sprite_->render();
}
}
void Item::render() {
if (enabled_) {
if (time_to_live_ > 200) {
sprite_->render();
} else if (time_to_live_ % 20 > 10) {
sprite_->render();
}
}
}
void Item::move()
{
floor_collision_ = false;
void Item::move() {
floor_collision_ = false;
// Calcula la nueva posición
pos_x_ += vel_x_;
pos_y_ += vel_y_;
// Calcula la nueva posición
pos_x_ += vel_x_;
pos_y_ += vel_y_;
// Aplica las aceleraciones a la velocidad
vel_x_ += accel_x_;
vel_y_ += accel_y_;
// Aplica las aceleraciones a la velocidad
vel_x_ += accel_x_;
vel_y_ += accel_y_;
// Comprueba los laterales de la zona de juego
const float MIN_X = param.game.play_area.rect.x;
const float MAX_X = play_area_.w - width_;
pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);
// Comprueba los laterales de la zona de juego
const float MIN_X = param.game.play_area.rect.x;
const float MAX_X = play_area_.w - width_;
pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);
// Si toca el borde lateral, invierte la velocidad horizontal
if (pos_x_ == MIN_X || pos_x_ == MAX_X)
{
vel_x_ = -vel_x_;
}
// Si toca el borde lateral, invierte la velocidad horizontal
if (pos_x_ == MIN_X || pos_x_ == MAX_X) {
vel_x_ = -vel_x_;
}
// Si colisiona por arriba, rebota (excepto la máquina de café)
if ((pos_y_ < param.game.play_area.rect.y) && !(type_ == ItemType::COFFEE_MACHINE))
{
// Corrige
pos_y_ = param.game.play_area.rect.y;
// Si colisiona por arriba, rebota (excepto la máquina de café)
if ((pos_y_ < param.game.play_area.rect.y) && !(type_ == ItemType::COFFEE_MACHINE)) {
// Corrige
pos_y_ = param.game.play_area.rect.y;
// Invierte la velocidad
vel_y_ = -vel_y_;
}
// Invierte la velocidad
vel_y_ = -vel_y_;
}
// Si colisiona con la parte inferior
if (pos_y_ > play_area_.h - height_)
{
// Corrige la posición
pos_y_ = play_area_.h - height_;
// Si colisiona con la parte inferior
if (pos_y_ > play_area_.h - height_) {
// Corrige la posición
pos_y_ = play_area_.h - height_;
switch (type_)
{
case ItemType::COFFEE_MACHINE:
// La máquina de café es mas pesada y tiene una fisica diferente, ademas hace ruido
floor_collision_ = true;
if (vel_y_ < 1.0f)
{
// Si la velocidad vertical es baja, detiene el objeto
vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0;
}
else
{
// Si la velocidad vertical es alta, el objeto rebota y pierde velocidad
vel_y_ *= -0.20f;
vel_x_ *= 0.75f;
}
break;
default:
// Si no es una máquina de café
if (vel_y_ < 1.0f)
{
// Si la velocidad vertical es baja, detiene el objeto
vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0;
}
else
{
// Si la velocidad vertical es alta, el objeto rebota y pierde velocidad
vel_y_ *= -0.5f;
vel_x_ *= 0.75f;
}
break;
}
}
switch (type_) {
case ItemType::COFFEE_MACHINE:
// La máquina de café es mas pesada y tiene una fisica diferente, ademas hace ruido
floor_collision_ = true;
if (vel_y_ < 1.0f) {
// Si la velocidad vertical es baja, detiene el objeto
vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0;
} else {
// Si la velocidad vertical es alta, el objeto rebota y pierde velocidad
vel_y_ *= -0.20f;
vel_x_ *= 0.75f;
}
break;
default:
// Si no es una máquina de café
if (vel_y_ < 1.0f) {
// Si la velocidad vertical es baja, detiene el objeto
vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0;
} else {
// Si la velocidad vertical es alta, el objeto rebota y pierde velocidad
vel_y_ *= -0.5f;
vel_x_ *= 0.75f;
}
break;
}
}
// Actualiza la posición del sprite
shiftSprite();
shiftColliders();
// Actualiza la posición del sprite
shiftSprite();
shiftColliders();
}
void Item::disable() { enabled_ = false; }
void Item::update()
{
move();
sprite_->update();
updateTimeToLive();
void Item::update() {
move();
sprite_->update();
updateTimeToLive();
}
void Item::updateTimeToLive()
{
if (time_to_live_ > 0)
{
time_to_live_--;
}
else
{
disable();
}
void Item::updateTimeToLive() {
if (time_to_live_ > 0) {
time_to_live_--;
} else {
disable();
}
}
void Item::shiftColliders()
{
collider_.x = static_cast<int>(pos_x_ + (width_ / 2));
collider_.y = static_cast<int>(pos_y_ + (height_ / 2));
void Item::shiftColliders() {
collider_.x = static_cast<int>(pos_x_ + (width_ / 2));
collider_.y = static_cast<int>(pos_y_ + (height_ / 2));
}
void Item::shiftSprite()
{
sprite_->setPosX(pos_x_);
sprite_->setPosY(pos_y_);
void Item::shiftSprite() {
sprite_->setPosX(pos_x_);
sprite_->setPosY(pos_y_);
}
// Calcula la zona de aparición de la máquina de café
int Item::getCoffeeMachineSpawn(int player_x, int item_width, int area_width, int margin)
{
// Distancia mínima del jugador (ajusta según necesites)
const int MIN_DISTANCE_FROM_PLAYER = area_width / 2;
int Item::getCoffeeMachineSpawn(int player_x, int item_width, int area_width, int margin) {
// Distancia mínima del jugador (ajusta según necesites)
const int MIN_DISTANCE_FROM_PLAYER = area_width / 2;
const int LEFT_BOUND = margin;
const int RIGHT_BOUND = area_width - item_width - margin;
const int LEFT_BOUND = margin;
const int RIGHT_BOUND = area_width - item_width - margin;
// Calcular zona de exclusión alrededor del jugador
int exclude_left = player_x - MIN_DISTANCE_FROM_PLAYER;
int exclude_right = player_x + MIN_DISTANCE_FROM_PLAYER;
// Calcular zona de exclusión alrededor del jugador
int exclude_left = player_x - MIN_DISTANCE_FROM_PLAYER;
int exclude_right = player_x + MIN_DISTANCE_FROM_PLAYER;
// Verificar si hay espacio suficiente a la izquierda
bool can_spawn_left = (exclude_left > LEFT_BOUND) && (exclude_left - LEFT_BOUND > item_width);
// Verificar si hay espacio suficiente a la izquierda
bool can_spawn_left = (exclude_left > LEFT_BOUND) && (exclude_left - LEFT_BOUND > item_width);
// Verificar si hay espacio suficiente a la derecha
bool can_spawn_right = (exclude_right < RIGHT_BOUND) && (RIGHT_BOUND - exclude_right > item_width);
// Verificar si hay espacio suficiente a la derecha
bool can_spawn_right = (exclude_right < RIGHT_BOUND) && (RIGHT_BOUND - exclude_right > item_width);
if (can_spawn_left && can_spawn_right)
{
// Ambos lados disponibles, elegir aleatoriamente
if (rand() % 2 == 0)
{
// Lado izquierdo
return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND;
}
else
{
// Lado derecho
return rand() % (RIGHT_BOUND - exclude_right) + exclude_right;
}
}
else if (can_spawn_left)
{
// Solo lado izquierdo disponible
return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND;
}
else if (can_spawn_right)
{
// Solo lado derecho disponible
return rand() % (RIGHT_BOUND - exclude_right) + exclude_right;
}
else
{
// No hay espacio suficiente lejos del jugador
// Por ahora, intentar spawn en el extremo más lejano posible
int distance_to_left = abs(player_x - LEFT_BOUND);
int distance_to_right = abs(RIGHT_BOUND - player_x);
if (can_spawn_left && can_spawn_right) {
// Ambos lados disponibles, elegir aleatoriamente
if (rand() % 2 == 0) {
// Lado izquierdo
return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND;
} else {
// Lado derecho
return rand() % (RIGHT_BOUND - exclude_right) + exclude_right;
}
} else if (can_spawn_left) {
// Solo lado izquierdo disponible
return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND;
} else if (can_spawn_right) {
// Solo lado derecho disponible
return rand() % (RIGHT_BOUND - exclude_right) + exclude_right;
} else {
// No hay espacio suficiente lejos del jugador
// Por ahora, intentar spawn en el extremo más lejano posible
int distance_to_left = abs(player_x - LEFT_BOUND);
int distance_to_right = abs(RIGHT_BOUND - player_x);
if (distance_to_left > distance_to_right)
{
return LEFT_BOUND;
}
else
{
return RIGHT_BOUND - item_width;
}
}
if (distance_to_left > distance_to_right) {
return LEFT_BOUND;
} else {
return RIGHT_BOUND - item_width;
}
}
}

View File

@@ -1,34 +1,33 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect, Uint16
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_FRect, Uint16
#include "animated_sprite.h" // Para AnimatedSprite
#include "utils.h" // Para Circle
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "animated_sprite.h" // Para AnimatedSprite
#include "utils.h" // Para Circle
class Texture;
// Tipos de objetos disponibles en el juego.
// Define los diferentes tipos de objetos que pueden existir en el juego.
enum class ItemType : int
{
DISK = 1, // Disco
GAVINA = 2, // Gavina
PACMAR = 3, // Pacman
CLOCK = 4, // Reloj
COFFEE = 5, // Café
DEBIAN = 6, // Debian
COFFEE_MACHINE = 7, // Máquina de café
NONE = 8, // Ninguno
enum class ItemType : int {
DISK = 1, // Disco
GAVINA = 2, // Gavina
PACMAR = 3, // Pacman
CLOCK = 4, // Reloj
COFFEE = 5, // Café
DEBIAN = 6, // Debian
COFFEE_MACHINE = 7, // Máquina de café
NONE = 8, // Ninguno
};
// Clase Item.
// Representa un objeto en el juego, con sus propiedades y métodos para gestionar su comportamiento.
class Item
{
public:
class Item {
public:
// Constantes
static constexpr int COFFEE_MACHINE_WIDTH = 30;
static constexpr int COFFEE_MACHINE_HEIGHT = 39;
@@ -64,25 +63,25 @@ public:
bool isOnFloor() const { return floor_collision_; }
Circle &getCollider() { return collider_; }
private:
private:
// Objetos y punteros
std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos del objeto
std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos del objeto
// Variables de estado y físicas
float pos_x_; // Posición X del objeto
float pos_y_; // Posición Y del objeto
int width_; // Ancho del objeto
int height_; // Alto del objeto
float vel_x_; // Velocidad en el eje X
float vel_y_; // Velocidad en el eje Y
float accel_x_ = 0.0f; // Aceleración en el eje X
float accel_y_; // Aceleración en el eje Y
bool floor_collision_ = false; // Indica si el objeto colisiona con el suelo
ItemType type_; // Tipo de objeto
bool enabled_ = true; // Indica si el objeto está habilitado
Circle collider_; // Círculo de colisión del objeto
SDL_FRect play_area_; // Rectángulo con la zona de juego
Uint16 time_to_live_ = 600; // Tiempo que el objeto está presente
float pos_x_; // Posición X del objeto
float pos_y_; // Posición Y del objeto
int width_; // Ancho del objeto
int height_; // Alto del objeto
float vel_x_; // Velocidad en el eje X
float vel_y_; // Velocidad en el eje Y
float accel_x_ = 0.0f; // Aceleración en el eje X
float accel_y_; // Aceleración en el eje Y
bool floor_collision_ = false; // Indica si el objeto colisiona con el suelo
ItemType type_; // Tipo de objeto
bool enabled_ = true; // Indica si el objeto está habilitado
Circle collider_; // Círculo de colisión del objeto
SDL_FRect play_area_; // Rectángulo con la zona de juego
Uint16 time_to_live_ = 600; // Tiempo que el objeto está presente
// Alinea el círculo de colisión con la posición del objeto.
// Actualiza las coordenadas X e Y del colisionador.

View File

@@ -1,123 +1,105 @@
#include "lang.h"
#include <stddef.h> // Para size_t
#include <exception> // Para exception
#include <fstream> // Para basic_ifstream, basic_istream, ifstream
#include <unordered_map> // Para unordered_map, _Node_iterator, operator==
#include <utility> // Para pair
#include <vector> // Para vector
#include <stddef.h> // Para size_t
#include "asset.h" // Para Asset
#include "external/json.hpp" // Para basic_json, iteration_proxy_value, oper...
#include "options.h" // Para Difficulty, DifficultyCode, SettingsOpt...
#include <exception> // Para exception
#include <fstream> // Para basic_ifstream, basic_istream, ifstream
#include <unordered_map> // Para unordered_map, _Node_iterator, operator==
#include <utility> // Para pair
#include <vector> // Para vector
#include "asset.h" // Para Asset
#include "external/json.hpp" // Para basic_json, iteration_proxy_value, oper...
#include "options.h" // Para Difficulty, DifficultyCode, SettingsOpt...
using json = nlohmann::json;
namespace Lang
{
std::unordered_map<std::string, std::string> texts;
namespace Lang {
std::unordered_map<std::string, std::string> texts;
// Vector con los idiomas soportados
std::vector<Language> languages = {
{Code::SPANISH, "Castellano", "es_ES.json"},
{Code::VALENCIAN, "Balooncia", "ba_BA.json"},
{Code::ENGLISH, "Ingles", "en_UK.json"}};
// Vector con los idiomas soportados
std::vector<Language> languages = {
{Code::SPANISH, "Castellano", "es_ES.json"},
{Code::VALENCIAN, "Balooncia", "ba_BA.json"},
{Code::ENGLISH, "Ingles", "en_UK.json"}};
// Inicializa los textos del juego en el idioma seleccionado
bool loadFromFile(const std::string &file_path)
{
texts.clear();
// Inicializa los textos del juego en el idioma seleccionado
bool loadFromFile(const std::string &file_path) {
texts.clear();
std::ifstream rfile(file_path);
if (!rfile.is_open())
return false;
std::ifstream rfile(file_path);
if (!rfile.is_open())
return false;
try
{
json j;
rfile >> j;
try {
json j;
rfile >> j;
for (auto &el : j.items())
{
texts[el.key()] = el.value();
}
for (auto &el : j.items()) {
texts[el.key()] = el.value();
}
catch (const std::exception &e)
{
// Puedes loguear el error si quieres
return false;
}
return true;
} catch (const std::exception &e) {
// Puedes loguear el error si quieres
return false;
}
// Obtiene el texto por clave
std::string getText(const std::string &key)
{
auto it = texts.find(key);
if (it != texts.end())
return it->second;
else
return "[missing text: " + key + "]";
}
return true;
}
// Obtiene el código del siguiente idioma disponible
Code getNextLangCode(Code lang)
{
for (size_t i = 0; i < languages.size(); ++i)
{
if (languages[i].code == lang)
{
return languages[(i + 1) % languages.size()].code;
}
// Obtiene el texto por clave
std::string getText(const std::string &key) {
auto it = texts.find(key);
if (it != texts.end())
return it->second;
else
return "[missing text: " + key + "]";
}
// Obtiene el código del siguiente idioma disponible
Code getNextLangCode(Code lang) {
for (size_t i = 0; i < languages.size(); ++i) {
if (languages[i].code == lang) {
return languages[(i + 1) % languages.size()].code;
}
// Si no se encuentra, devuelve el primero por defecto
return languages[0].code;
}
// Si no se encuentra, devuelve el primero por defecto
return languages[0].code;
}
// Obtiene un idioma del vector de idiomas a partir de un código
Language getLanguage(Code code)
{
for (const auto &lang : languages)
{
if (lang.code == code)
return lang;
}
// Si no se encuentra, devuelve el primero por defecto
return languages[0];
// Obtiene un idioma del vector de idiomas a partir de un código
Language getLanguage(Code code) {
for (const auto &lang : languages) {
if (lang.code == code)
return lang;
}
// Si no se encuentra, devuelve el primero por defecto
return languages[0];
}
// Devuelve el código de un idioma a partir de un nombre
Code getCodeFromName(const std::string &name)
{
for (const auto &lang : languages)
{
if (lang.name == name)
return lang.code;
}
// Si no se encuentra, devuelve el primero por defecto
return languages[0].code;
// Devuelve el código de un idioma a partir de un nombre
Code getCodeFromName(const std::string &name) {
for (const auto &lang : languages) {
if (lang.name == name)
return lang.code;
}
// Si no se encuentra, devuelve el primero por defecto
return languages[0].code;
}
// Devuelve el nombre de un idioma a partir de un código
std::string getNameFromCode(Code code)
{
for (const auto &lang : languages)
{
if (lang.code == code)
return lang.name;
}
// Si no se encuentra, devuelve el nombre del primer idioma por defecto
return languages[0].name;
// Devuelve el nombre de un idioma a partir de un código
std::string getNameFromCode(Code code) {
for (const auto &lang : languages) {
if (lang.code == code)
return lang.name;
}
// Si no se encuentra, devuelve el nombre del primer idioma por defecto
return languages[0].name;
}
// Actualiza los nombres de los idiomas
void updateLanguageNames()
{
for (auto &lang : languages)
{
switch (lang.code)
{
// Actualiza los nombres de los idiomas
void updateLanguageNames() {
for (auto &lang : languages) {
switch (lang.code) {
case Code::SPANISH:
lang.name = Lang::getText("[SERVICE_MENU] LANG_ES");
break;
@@ -130,17 +112,14 @@ namespace Lang
default:
lang.name = "Unknown";
break;
}
}
}
}
// Actualiza los nombres de las dificultades
void updateDifficultyNames()
{
for (auto &difficulty : Options::difficulties)
{
switch (difficulty.code)
{
// Actualiza los nombres de las dificultades
void updateDifficultyNames() {
for (auto &difficulty : Options::difficulties) {
switch (difficulty.code) {
case Options::DifficultyCode::EASY:
difficulty.name = Lang::getText("[SERVICE_MENU] EASY");
break;
@@ -153,28 +132,25 @@ namespace Lang
default:
difficulty.name = "Unknown";
break;
}
}
}
// Obtiene una fichero a partir de un lang::Code
std::string getLanguageFileName(Lang::Code code)
{
for (const auto &lang : languages)
{
if (lang.code == code)
return Asset::get()->get(lang.file_name);
}
// Si no se encuentra, devuelve el fichero del primer idioma por defecto
return Asset::get()->get(languages[0].file_name);
}
// Establece el idioma
void setLanguage(Code lang)
{
Options::settings.language = lang;
loadFromFile(Asset::get()->get(getLanguage(lang).file_name));
updateLanguageNames();
updateDifficultyNames();
}
}
// Obtiene una fichero a partir de un lang::Code
std::string getLanguageFileName(Lang::Code code) {
for (const auto &lang : languages) {
if (lang.code == code)
return Asset::get()->get(lang.file_name);
}
// Si no se encuentra, devuelve el fichero del primer idioma por defecto
return Asset::get()->get(languages[0].file_name);
}
// Establece el idioma
void setLanguage(Code lang) {
Options::settings.language = lang;
loadFromFile(Asset::get()->get(getLanguage(lang).file_name));
updateLanguageNames();
updateDifficultyNames();
}
} // namespace Lang

View File

@@ -1,52 +1,49 @@
#pragma once
#include <string> // Para string, basic_string
#include <string> // Para string, basic_string
namespace Lang
{
// --- Códigos de idioma soportados ---
enum class Code : int
{
SPANISH = 0,
VALENCIAN = 1,
ENGLISH = 2
};
namespace Lang {
// --- Códigos de idioma soportados ---
enum class Code : int {
SPANISH = 0,
VALENCIAN = 1,
ENGLISH = 2
};
// Estructura que representa un idioma
struct Language
{
Code code; // Código que identifica al idioma
std::string name; // Nombre que identifica el idioma
std::string file_name; // Nombre del fichero con los textos
// Estructura que representa un idioma
struct Language {
Code code; // Código que identifica al idioma
std::string name; // Nombre que identifica el idioma
std::string file_name; // Nombre del fichero con los textos
Language(Code c, const std::string &n, const std::string &fn)
: code(c), name(n), file_name(fn) {}
};
Language(Code c, const std::string &n, const std::string &fn)
: code(c), name(n), file_name(fn) {}
};
// Carga los textos desde el fichero JSON especificado
bool loadFromFile(const std::string &file_path);
// Carga los textos desde el fichero JSON especificado
bool loadFromFile(const std::string &file_path);
// Obtiene el texto por clave
std::string getText(const std::string &key);
// Obtiene el texto por clave
std::string getText(const std::string &key);
// Obtiene el código del siguiente idioma (circular)
Code getNextLangCode(Code current_lang);
// Obtiene el código del siguiente idioma (circular)
Code getNextLangCode(Code current_lang);
// Obtiene el idioma correspondiente al código proporcionado
Language getLanguage(Code code);
// Obtiene el idioma correspondiente al código proporcionado
Language getLanguage(Code code);
// Devuelve el código de un idioma a partir de un nombre
Code getCodeFromName(const std::string &name);
// Devuelve el código de un idioma a partir de un nombre
Code getCodeFromName(const std::string &name);
// Devuelve el nombre de un idioma a partir de un código
std::string getNameFromCode(Code code);
// Devuelve el nombre de un idioma a partir de un código
std::string getNameFromCode(Code code);
// Actualiza los nombres de los idiomas
void updateLanguageNames();
// Actualiza los nombres de los idiomas
void updateLanguageNames();
// Obtiene el nombre del fichero de textos asociado a un código de idioma
std::string getLanguageFileName(Code code);
// Obtiene el nombre del fichero de textos asociado a un código de idioma
std::string getLanguageFileName(Code code);
// Establece el idioma actual
void setLanguage(Code lang);
}
// Establece el idioma actual
void setLanguage(Code lang);
} // namespace Lang

View File

@@ -7,12 +7,11 @@ Actualizando a la versión "Arcade Edition" en 08/05/2024
*/
#include <memory> // Para make_unique, unique_ptr
#include <memory> // Para make_unique, unique_ptr
#include "director.h" // Para Director
#include "director.h" // Para Director
int main(int argc, char *argv[])
{
int main(int argc, char *argv[]) {
// Crea el objeto Director
auto director = std::make_unique<Director>(argc, const_cast<const char **>(argv));

View File

@@ -1,14 +1,14 @@
#include "manage_hiscore_table.h"
#include <SDL3/SDL.h> // Para SDL_ReadIO, SDL_WriteIO, SDL_CloseIO, SDL_GetE...
#include <SDL3/SDL.h> // Para SDL_ReadIO, SDL_WriteIO, SDL_CloseIO, SDL_GetE...
#include <algorithm> // Para find_if, sort
#include <iterator> // Para distance
#include "utils.h" // Para getFileName
#include "utils.h" // Para getFileName
// Resetea la tabla a los valores por defecto
void ManageHiScoreTable::clear()
{
void ManageHiScoreTable::clear() {
// Limpia la tabla
table_.clear();
@@ -28,8 +28,7 @@ void ManageHiScoreTable::clear()
}
// Añade un elemento a la tabla
int ManageHiScoreTable::add(const HiScoreEntry &entry)
{
int ManageHiScoreTable::add(const HiScoreEntry &entry) {
// Añade la entrada a la tabla
table_.push_back(entry);
@@ -37,26 +36,22 @@ int ManageHiScoreTable::add(const HiScoreEntry &entry)
sort();
// Encontrar la posición del nuevo elemento
auto it = std::find_if(table_.begin(), table_.end(), [&](const HiScoreEntry &e)
{ return e.name == entry.name &&
e.score == entry.score &&
e.one_credit_complete == entry.one_credit_complete; });
auto it = std::find_if(table_.begin(), table_.end(), [&](const HiScoreEntry &e) { return e.name == entry.name &&
e.score == entry.score &&
e.one_credit_complete == entry.one_credit_complete; });
int position = -1;
if (it != table_.end())
{
if (it != table_.end()) {
position = std::distance(table_.begin(), it);
}
// Deja solo las 10 primeras entradas
if (table_.size() > 10)
{
if (table_.size() > 10) {
table_.resize(10);
// Si el nuevo elemento quedó fuera del top 10
if (position >= 10)
{
position = -1; // No entró en el top 10
if (position >= 10) {
position = -1; // No entró en el top 10
}
}
@@ -65,8 +60,7 @@ int ManageHiScoreTable::add(const HiScoreEntry &entry)
}
// Ordena la tabla
void ManageHiScoreTable::sort()
{
void ManageHiScoreTable::sort() {
struct
{
bool operator()(const HiScoreEntry &a, const HiScoreEntry &b) const { return a.score > b.score; }
@@ -76,23 +70,20 @@ void ManageHiScoreTable::sort()
}
// Carga la tabla desde un fichero
bool ManageHiScoreTable::loadFromFile(const std::string &file_path)
{
bool ManageHiScoreTable::loadFromFile(const std::string &file_path) {
clear();
auto success = true;
auto file = SDL_IOFromFile(file_path.c_str(), "rb");
if (file)
{
table_.clear(); // Limpia la tabla actual
if (file) {
table_.clear(); // Limpia la tabla actual
// Lee el número de entradas en la tabla
int table_size = 0;
SDL_ReadIO(file, &table_size, sizeof(int));
// Lee los datos de cada entrada
for (int i = 0; i < table_size; ++i)
{
for (int i = 0; i < table_size; ++i) {
HiScoreEntry entry;
// Lee la puntuación
@@ -104,7 +95,7 @@ bool ManageHiScoreTable::loadFromFile(const std::string &file_path)
std::vector<char> name_buffer(name_size + 1);
SDL_ReadIO(file, name_buffer.data(), name_size);
name_buffer[name_size] = '\0'; // Asegurar el fin de la cadena
name_buffer[name_size] = '\0'; // Asegurar el fin de la cadena
entry.name = std::string(name_buffer.data());
// Lee el valor de one_credit_complete
@@ -118,9 +109,7 @@ bool ManageHiScoreTable::loadFromFile(const std::string &file_path)
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(file_path).c_str());
SDL_CloseIO(file);
}
else
{
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to load %s file! %s", getFileName(file_path).c_str(), SDL_GetError());
success = false;
}
@@ -128,20 +117,17 @@ bool ManageHiScoreTable::loadFromFile(const std::string &file_path)
}
// Guarda la tabla en un fichero
bool ManageHiScoreTable::saveToFile(const std::string &file_path)
{
bool ManageHiScoreTable::saveToFile(const std::string &file_path) {
auto success = true;
auto file = SDL_IOFromFile(file_path.c_str(), "w+b");
if (file)
{
if (file) {
// Guarda el número de entradas en la tabla
int table_size = static_cast<int>(table_.size());
SDL_WriteIO(file, &table_size, sizeof(int));
// Guarda los datos de cada entrada
for (int i = 0; i < table_size; ++i)
{
for (int i = 0; i < table_size; ++i) {
const HiScoreEntry &entry = table_.at(i);
// Guarda la puntuación
@@ -159,9 +145,7 @@ bool ManageHiScoreTable::saveToFile(const std::string &file_path)
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(file_path).c_str());
SDL_CloseIO(file);
}
else
{
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to save %s file! %s", getFileName(file_path).c_str(), SDL_GetError());
success = false;
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include <string> // Para std::string
#include <vector> // Para std::vector
#include <string> // Para std::string
#include <vector> // Para std::vector
/*
Esta clase sirve para añadir elementos HiScoreEntry a un vector (tabla), de manera
@@ -12,11 +12,10 @@
*/
// --- Estructura para las entradas de la tabla de records ---
struct HiScoreEntry
{
std::string name; // Nombre
int score; // Puntuación
bool one_credit_complete; // Indica si se ha conseguido 1CC
struct HiScoreEntry {
std::string name; // Nombre
int score; // Puntuación
bool one_credit_complete; // Indica si se ha conseguido 1CC
// Constructor
explicit HiScoreEntry(const std::string &n = "", int s = 0, bool occ = false)
@@ -24,9 +23,8 @@ struct HiScoreEntry
};
// --- Clase ManageHiScoreTable ---
class ManageHiScoreTable
{
public:
class ManageHiScoreTable {
public:
// Constructor
explicit ManageHiScoreTable(std::vector<HiScoreEntry> &table)
: table_(table) {}
@@ -46,7 +44,7 @@ public:
// Guarda la tabla en un fichero
bool saveToFile(const std::string &file_path);
private:
private:
// Referencia a la tabla con los records
std::vector<HiScoreEntry> &table_;

View File

@@ -1,33 +1,27 @@
#include "mouse.h"
#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_HideCursor, SDL_Show...
#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_HideCursor, SDL_Show...
namespace Mouse
{
Uint32 cursor_hide_time = 3000; // Tiempo en milisegundos para ocultar el cursor
Uint32 last_mouse_move_time = 0; // Última vez que el ratón se movió
bool cursor_visible = true; // Estado del cursor
namespace Mouse {
Uint32 cursor_hide_time = 3000; // Tiempo en milisegundos para ocultar el cursor
Uint32 last_mouse_move_time = 0; // Última vez que el ratón se movió
bool cursor_visible = true; // Estado del cursor
void handleEvent(const SDL_Event &event)
{
if (event.type == SDL_EVENT_MOUSE_MOTION)
{
last_mouse_move_time = SDL_GetTicks();
if (!cursor_visible)
{
SDL_ShowCursor();
cursor_visible = true;
}
}
}
void updateCursorVisibility()
{
Uint32 current_time = SDL_GetTicks();
if (cursor_visible && (current_time - last_mouse_move_time > cursor_hide_time))
{
SDL_HideCursor();
cursor_visible = false;
void handleEvent(const SDL_Event &event) {
if (event.type == SDL_EVENT_MOUSE_MOTION) {
last_mouse_move_time = SDL_GetTicks();
if (!cursor_visible) {
SDL_ShowCursor();
cursor_visible = true;
}
}
}
void updateCursorVisibility() {
Uint32 current_time = SDL_GetTicks();
if (cursor_visible && (current_time - last_mouse_move_time > cursor_hide_time)) {
SDL_HideCursor();
cursor_visible = false;
}
}
} // namespace Mouse

View File

@@ -1,15 +1,14 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint32, SDL_Event
#include <SDL3/SDL.h> // Para Uint32, SDL_Event
namespace Mouse
{
// --- Variables de estado del cursor ---
extern Uint32 cursor_hide_time; // Tiempo en milisegundos para ocultar el cursor tras inactividad
extern Uint32 last_mouse_move_time; // Última vez (en ms) que el ratón se movió
extern bool cursor_visible; // Indica si el cursor está visible
namespace Mouse {
// --- Variables de estado del cursor ---
extern Uint32 cursor_hide_time; // Tiempo en milisegundos para ocultar el cursor tras inactividad
extern Uint32 last_mouse_move_time; // Última vez (en ms) que el ratón se movió
extern bool cursor_visible; // Indica si el cursor está visible
// --- Gestión de eventos y visibilidad ---
void handleEvent(const SDL_Event &event); // Procesa eventos de ratón (movimiento, clic, etc.)
void updateCursorVisibility(); // Actualiza la visibilidad del cursor según la inactividad
}
// --- Gestión de eventos y visibilidad ---
void handleEvent(const SDL_Event &event); // Procesa eventos de ratón (movimiento, clic, etc.)
void updateCursorVisibility(); // Actualiza la visibilidad del cursor según la inactividad
} // namespace Mouse

View File

@@ -1,130 +1,119 @@
#include "moving_sprite.h"
#include "texture.h" // Para Texture
#include "texture.h" // Para Texture
// Constructor
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos, Rotate rotate, float zoom_w, float zoom_h, SDL_FlipMode flip)
: Sprite(texture, pos),
x_(pos.x),
y_(pos.y),
rotate_(rotate),
zoom_w_(zoom_w),
zoom_h_(zoom_h),
flip_(flip) {}
: Sprite(texture, pos),
x_(pos.x),
y_(pos.y),
rotate_(rotate),
zoom_w_(zoom_w),
zoom_h_(zoom_h),
flip_(flip) {}
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos)
: Sprite(texture, pos),
x_(pos.x),
y_(pos.y),
rotate_(Rotate()),
zoom_w_(1.0f),
zoom_h_(1.0f),
flip_(SDL_FLIP_NONE) {}
: Sprite(texture, pos),
x_(pos.x),
y_(pos.y),
rotate_(Rotate()),
zoom_w_(1.0f),
zoom_h_(1.0f),
flip_(SDL_FLIP_NONE) {}
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture)
: Sprite(texture),
x_(0.0f),
y_(0.0f),
rotate_(Rotate()),
zoom_w_(1.0f),
zoom_h_(1.0f),
flip_(SDL_FLIP_NONE) { Sprite::clear(); }
: Sprite(texture),
x_(0.0f),
y_(0.0f),
rotate_(Rotate()),
zoom_w_(1.0f),
zoom_h_(1.0f),
flip_(SDL_FLIP_NONE) { Sprite::clear(); }
// Reinicia todas las variables
void MovingSprite::clear()
{
x_ = 0.0f; // Posición en el eje X
y_ = 0.0f; // Posición en el eje Y
void MovingSprite::clear() {
x_ = 0.0f; // Posición en el eje X
y_ = 0.0f; // Posición en el eje Y
vx_ = 0.0f; // Velocidad en el eje X. Cantidad de pixeles a desplazarse
vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse
vx_ = 0.0f; // Velocidad en el eje X. Cantidad de pixeles a desplazarse
vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse
ax_ = 0.0f; // Aceleración en el eje X. Variación de la velocidad
ay_ = 0.0f; // Aceleración en el eje Y. Variación de la velocidad
ax_ = 0.0f; // Aceleración en el eje X. Variación de la velocidad
ay_ = 0.0f; // Aceleración en el eje Y. Variación de la velocidad
rotate_ = Rotate(); // Inicializa la estructura
rotate_ = Rotate(); // Inicializa la estructura
zoom_w_ = 1.0f; // Zoom aplicado a la anchura
zoom_h_ = 1.0f; // Zoom aplicado a la altura
zoom_w_ = 1.0f; // Zoom aplicado a la anchura
zoom_h_ = 1.0f; // Zoom aplicado a la altura
flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite
flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite
Sprite::clear();
Sprite::clear();
}
// Mueve el sprite
void MovingSprite::move()
{
x_ += vx_;
y_ += vy_;
void MovingSprite::move() {
x_ += vx_;
y_ += vy_;
vx_ += ax_;
vy_ += ay_;
vx_ += ax_;
vy_ += ay_;
pos_.x = static_cast<int>(x_);
pos_.y = static_cast<int>(y_);
pos_.x = static_cast<int>(x_);
pos_.y = static_cast<int>(y_);
}
// Actualiza las variables internas del objeto
void MovingSprite::update()
{
move();
rotate();
void MovingSprite::update() {
move();
rotate();
}
// Muestra el sprite por pantalla
void MovingSprite::render() { texture_->render(pos_.x, pos_.y, &sprite_clip_, zoom_w_, zoom_h_, rotate_.angle, &rotate_.center, flip_); }
// Establece la rotacion
void MovingSprite::rotate()
{
if (rotate_.enabled)
{
++rotate_.counter;
if (rotate_.counter % rotate_.speed == 0)
{
updateAngle();
rotate_.counter = 0;
}
}
void MovingSprite::rotate() {
if (rotate_.enabled) {
++rotate_.counter;
if (rotate_.counter % rotate_.speed == 0) {
updateAngle();
rotate_.counter = 0;
}
}
}
// Activa o desactiva el efecto de rotación
void MovingSprite::setRotate(bool enable)
{
rotate_.enabled = enable;
rotate_.counter = 0;
void MovingSprite::setRotate(bool enable) {
rotate_.enabled = enable;
rotate_.counter = 0;
}
// Establece la posición y_ el tamaño del objeto
void MovingSprite::setPos(SDL_FRect rect)
{
x_ = static_cast<float>(rect.x);
y_ = static_cast<float>(rect.y);
void MovingSprite::setPos(SDL_FRect rect) {
x_ = static_cast<float>(rect.x);
y_ = static_cast<float>(rect.y);
pos_ = rect;
pos_ = rect;
}
// Establece el valor de las variables
void MovingSprite::setPos(float x, float y)
{
x_ = x;
y_ = y;
void MovingSprite::setPos(float x, float y) {
x_ = x;
y_ = y;
pos_.x = static_cast<int>(x_);
pos_.y = static_cast<int>(y_);
pos_.x = static_cast<int>(x_);
pos_.y = static_cast<int>(y_);
}
// Establece el valor de la variable
void MovingSprite::setPosX(float value)
{
x_ = value;
pos_.x = static_cast<int>(x_);
void MovingSprite::setPosX(float value) {
x_ = value;
pos_.x = static_cast<int>(x_);
}
// Establece el valor de la variable
void MovingSprite::setPosY(float value)
{
y_ = value;
pos_.y = static_cast<int>(y_);
void MovingSprite::setPosY(float value) {
y_ = value;
pos_.y = static_cast<int>(y_);
}

View File

@@ -1,26 +1,25 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FlipMode, SDL_FPoint, SDL_FRect
#include <SDL3/SDL.h> // Para SDL_FlipMode, SDL_FPoint, SDL_FRect
#include <algorithm> // Para max
#include <memory> // Para shared_ptr
#include "sprite.h" // Para Sprite
#include "sprite.h" // Para Sprite
class Texture;
// Clase MovingSprite. Añade movimiento y efectos de rotación, zoom y flip al sprite
class MovingSprite : public Sprite
{
public:
class MovingSprite : public Sprite {
public:
// --- Estructura para la rotación ---
struct Rotate
{
bool enabled; // Indica si ha de rotar
int counter; // Contador
int speed; // Velocidad de giro
double angle; // Ángulo para dibujarlo
float amount; // Cantidad de grados a girar en cada iteración
SDL_FPoint center; // Centro de rotación
struct Rotate {
bool enabled; // Indica si ha de rotar
int counter; // Contador
int speed; // Velocidad de giro
double angle; // Ángulo para dibujarlo
float amount; // Cantidad de grados a girar en cada iteración
SDL_FPoint center; // Centro de rotación
Rotate() : enabled(false), counter(0), speed(1), angle(0.0), amount(0.0f), center({0.0f, 0.0f}) {}
};
@@ -32,9 +31,9 @@ public:
virtual ~MovingSprite() override = default;
// --- Métodos principales ---
virtual void update(); // Actualiza las variables internas del objeto
void clear() override; // Reinicia todas las variables a cero
void render() override; // Muestra el sprite por pantalla
virtual void update(); // Actualiza las variables internas del objeto
void clear() override; // Reinicia todas las variables a cero
void render() override; // Muestra el sprite por pantalla
// --- Getters de posición y movimiento ---
float getPosX() const { return x_; }
@@ -56,10 +55,10 @@ public:
void setZoomH(float value) { zoom_h_ = value; }
void setAngle(double value) { rotate_.angle = value; }
void setRotatingCenter(SDL_FPoint point) { rotate_.center = point; }
void setRotate(bool enable); // Activa o desactiva el efecto de rotación
void setRotate(bool enable); // Activa o desactiva el efecto de rotación
void setRotateSpeed(int value) { rotate_.speed = std::max(1, value); }
void setRotateAmount(double value) { rotate_.amount = value; }
void switchRotate() { rotate_.amount *= -1; } // Cambia el sentido de la rotación
void switchRotate() { rotate_.amount *= -1; } // Cambia el sentido de la rotación
// --- Flip ---
void setFlip(SDL_FlipMode flip) { flip_ = flip; }
@@ -67,30 +66,30 @@ public:
SDL_FlipMode getFlip() { return flip_; }
// --- Posición y tamaño ---
void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto
void setPos(float x, float y); // Establece la posición del objeto
void setPosX(float value); // Establece la posición X
void setPosY(float value); // Establece la posición Y
void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto
void setPos(float x, float y); // Establece la posición del objeto
void setPosX(float value); // Establece la posición X
void setPosY(float value); // Establece la posición Y
protected:
protected:
// --- Variables de posición y movimiento ---
float x_ = 0.0f; // Posición en el eje X
float y_ = 0.0f; // Posición en el eje Y
float x_ = 0.0f; // Posición en el eje X
float y_ = 0.0f; // Posición en el eje Y
float vx_ = 0.0f; // Velocidad en el eje X. Cantidad de píxeles a desplazarse
float vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de píxeles a desplazarse
float vx_ = 0.0f; // Velocidad en el eje X. Cantidad de píxeles a desplazarse
float vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de píxeles a desplazarse
float ax_ = 0.0f; // Aceleración en el eje X. Variación de la velocidad
float ay_ = 0.0f; // Aceleración en el eje Y. Variación de la velocidad
float ax_ = 0.0f; // Aceleración en el eje X. Variación de la velocidad
float ay_ = 0.0f; // Aceleración en el eje Y. Variación de la velocidad
// --- Efectos visuales ---
Rotate rotate_; // Variables usadas para controlar la rotación del sprite
float zoom_w_; // Zoom aplicado a la anchura
float zoom_h_; // Zoom aplicado a la altura
SDL_FlipMode flip_; // Indica cómo se voltea el sprite
Rotate rotate_; // Variables usadas para controlar la rotación del sprite
float zoom_w_; // Zoom aplicado a la anchura
float zoom_h_; // Zoom aplicado a la altura
SDL_FlipMode flip_; // Indica cómo se voltea el sprite
// --- Métodos internos ---
void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo
void move(); // Mueve el sprite según velocidad y aceleración
void rotate(); // Rota el sprite según los parámetros de rotación
void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo
void move(); // Mueve el sprite según velocidad y aceleración
void rotate(); // Rota el sprite según los parámetros de rotación
};

View File

@@ -1,16 +1,17 @@
#include "notifier.h"
#include <SDL3/SDL.h> // Para SDL_RenderFillRect, SDL_FRect, SDL_RenderClear
#include <SDL3/SDL.h> // Para SDL_RenderFillRect, SDL_FRect, SDL_RenderClear
#include <algorithm> // Para remove_if
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include "audio.h" // Para Audio
#include "param.h" // Para Param, param, ParamNotification, ParamGame
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include "text.h" // Para Text
#include "texture.h" // Para Texture
#include "audio.h" // Para Audio
#include "param.h" // Para Param, param, ParamNotification, ParamGame
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include "text.h" // Para Text
#include "texture.h" // Para Texture
// Singleton
Notifier *Notifier::instance_ = nullptr;
@@ -35,24 +36,18 @@ Notifier::Notifier(std::string icon_file, std::shared_ptr<Text> text)
has_icons_(!icon_file.empty()) {}
// Dibuja las notificaciones por pantalla
void Notifier::render()
{
for (int i = (int)notifications_.size() - 1; i >= 0; --i)
{
void Notifier::render() {
for (int i = (int)notifications_.size() - 1; i >= 0; --i) {
notifications_[i].sprite->render();
}
}
// Actualiza el estado de las notificaiones
void Notifier::update()
{
for (int i = 0; i < (int)notifications_.size(); ++i)
{
void Notifier::update() {
for (int i = 0; i < (int)notifications_.size(); ++i) {
// Si la notificación anterior está "saliendo", no hagas nada
if (i > 0)
{
if (notifications_[i - 1].state == NotificationStatus::RISING)
{
if (i > 0) {
if (notifications_[i - 1].state == NotificationStatus::RISING) {
break;
}
}
@@ -60,12 +55,9 @@ void Notifier::update()
notifications_[i].counter++;
// Hace sonar la notificación en el primer frame
if (notifications_[i].counter == 1)
{
if (param.notification.sound)
{
if (notifications_[i].state == NotificationStatus::RISING)
{
if (notifications_[i].counter == 1) {
if (param.notification.sound) {
if (notifications_[i].state == NotificationStatus::RISING) {
// Reproduce el sonido de la notificación
Audio::get()->playSound("notify.wav", Audio::Group::INTERFACE);
}
@@ -73,55 +65,41 @@ void Notifier::update()
}
// Comprueba los estados
if (notifications_[i].state == NotificationStatus::RISING)
{
if (notifications_[i].state == NotificationStatus::RISING) {
const float step = ((float)notifications_[i].counter / notifications_[i].travel_dist);
const int alpha = 255 * step;
if (param.notification.pos_v == NotifyPosition::TOP)
{
if (param.notification.pos_v == NotifyPosition::TOP) {
notifications_[i].rect.y++;
}
else
{
} else {
notifications_[i].rect.y--;
}
notifications_[i].texture->setAlpha(alpha);
if (notifications_[i].rect.y == notifications_[i].y)
{
if (notifications_[i].rect.y == notifications_[i].y) {
notifications_[i].state = NotificationStatus::STAY;
notifications_[i].texture->setAlpha(255);
notifications_[i].counter = 0;
}
}
else if (notifications_[i].state == NotificationStatus::STAY)
{
if (notifications_[i].counter == wait_time_)
{
else if (notifications_[i].state == NotificationStatus::STAY) {
if (notifications_[i].counter == wait_time_) {
notifications_[i].state = NotificationStatus::VANISHING;
notifications_[i].counter = 0;
}
}
else if (notifications_[i].state == NotificationStatus::VANISHING)
{
} else if (notifications_[i].state == NotificationStatus::VANISHING) {
const float step = (notifications_[i].counter / (float)notifications_[i].travel_dist);
const int alpha = 255 * (1 - step);
if (param.notification.pos_v == NotifyPosition::TOP)
{
if (param.notification.pos_v == NotifyPosition::TOP) {
notifications_[i].rect.y--;
}
else
{
} else {
notifications_[i].rect.y++;
}
notifications_[i].texture->setAlpha(alpha);
if (notifications_[i].rect.y == notifications_[i].y - notifications_[i].travel_dist)
{
if (notifications_[i].rect.y == notifications_[i].y - notifications_[i].travel_dist) {
notifications_[i].state = NotificationStatus::FINISHED;
}
}
@@ -133,40 +111,32 @@ void Notifier::update()
}
// Elimina las notificaciones finalizadas
void Notifier::clearFinishedNotifications()
{
for (int i = (int)notifications_.size() - 1; i >= 0; --i)
{
if (notifications_[i].state == NotificationStatus::FINISHED)
{
void Notifier::clearFinishedNotifications() {
for (int i = (int)notifications_.size() - 1; i >= 0; --i) {
if (notifications_[i].state == NotificationStatus::FINISHED) {
notifications_.erase(notifications_.begin() + i);
}
}
}
void Notifier::show(std::vector<std::string> texts, int icon, const std::string &code)
{
void Notifier::show(std::vector<std::string> texts, int icon, const std::string &code) {
// Si no hay texto, acaba
if (texts.empty())
{
if (texts.empty()) {
return;
}
// Si las notificaciones no se apilan, elimina las anteriores
if (!stack_)
{
if (!stack_) {
clearAllNotifications();
}
// Elimina las cadenas vacías
texts.erase(std::remove_if(texts.begin(), texts.end(), [](const std::string &s)
{ return s.empty(); }),
texts.end());
texts.erase(std::remove_if(texts.begin(), texts.end(), [](const std::string &s) { return s.empty(); }),
texts.end());
// Encuentra la cadena más larga
std::string longest;
for (const auto &text : texts)
{
for (const auto &text : texts) {
if (text.length() > longest.length())
longest = text;
}
@@ -183,23 +153,22 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string
// Posición horizontal
float desp_h = 0;
switch (param.notification.pos_h)
{
case NotifyPosition::LEFT:
desp_h = PADDING_OUT;
break;
switch (param.notification.pos_h) {
case NotifyPosition::LEFT:
desp_h = PADDING_OUT;
break;
case NotifyPosition::MIDDLE:
desp_h = ((param.game.width / 2) - (WIDTH / 2));
break;
case NotifyPosition::MIDDLE:
desp_h = ((param.game.width / 2) - (WIDTH / 2));
break;
case NotifyPosition::RIGHT:
desp_h = param.game.width - WIDTH - PADDING_OUT;
break;
case NotifyPosition::RIGHT:
desp_h = param.game.width - WIDTH - PADDING_OUT;
break;
default:
desp_h = 0;
break;
default:
desp_h = 0;
break;
}
// Posición vertical
@@ -208,8 +177,8 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string
// Offset
const auto TRAVEL_DIST = HEIGHT + PADDING_OUT;
auto OFFSET = notifications_.empty()
? DESP_V
: notifications_.back().y + (param.notification.pos_v == NotifyPosition::TOP ? TRAVEL_DIST : -TRAVEL_DIST);
? DESP_V
: notifications_.back().y + (param.notification.pos_v == NotifyPosition::TOP ? TRAVEL_DIST : -TRAVEL_DIST);
// Crea la notificacion
Notification n;
@@ -234,8 +203,7 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string
// Dibuja el fondo de la notificación
SDL_SetRenderDrawColor(renderer_, bg_color_.r, bg_color_.g, bg_color_.b, 255);
SDL_FRect rect;
if (SHAPE == NotificationShape::ROUNDED)
{
if (SHAPE == NotificationShape::ROUNDED) {
rect = {4, 0, WIDTH - (4 * 2), HEIGHT};
SDL_RenderFillRect(renderer_, &rect);
@@ -249,14 +217,12 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string
SDL_RenderFillRect(renderer_, &rect);
}
else if (SHAPE == NotificationShape::SQUARED)
{
else if (SHAPE == NotificationShape::SQUARED) {
SDL_RenderClear(renderer_);
}
// Dibuja el icono de la notificación
if (has_icons_ && icon >= 0 && texts.size() >= 2)
{
if (has_icons_ && icon >= 0 && texts.size() >= 2) {
auto sp = std::make_unique<Sprite>(icon_texture_, (SDL_FRect){0, 0, ICON_SIZE, ICON_SIZE});
sp->setPosition({PADDING_IN_H, PADDING_IN_V, ICON_SIZE, ICON_SIZE});
sp->setSpriteClip(SDL_FRect{
@@ -270,8 +236,7 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string
// Escribe el texto de la notificación
const Color color{255, 255, 255};
int iterator = 0;
for (const auto &text : texts)
{
for (const auto &text : texts) {
text_->writeColored(PADDING_IN_H + ICON_SPACE, PADDING_IN_V + iterator * (text_->getCharacterSize() + 1), text, color);
++iterator;
}
@@ -290,10 +255,8 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string
}
// Finaliza y elimnina todas las notificaciones activas
void Notifier::clearAllNotifications()
{
for (auto &notification : notifications_)
{
void Notifier::clearAllNotifications() {
for (auto &notification : notifications_) {
notification.state = NotificationStatus::FINISHED;
}
@@ -301,11 +264,9 @@ void Notifier::clearAllNotifications()
}
// Obtiene los códigos de las notificaciones
std::vector<std::string> Notifier::getCodes()
{
std::vector<std::string> Notifier::getCodes() {
std::vector<std::string> codes;
for (const auto &notification : notifications_)
{
for (const auto &notification : notifications_) {
codes.emplace_back(notification.code);
}
return codes;

View File

@@ -1,91 +1,87 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_Renderer
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_Renderer
#include "utils.h" // Para stringInVector, Color
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include "utils.h" // Para stringInVector, Color
class Sprite;
class Text;
class Texture;
// --- Clase Notifier: gestiona las notificaciones en pantalla (singleton) ---
class Notifier
{
public:
class Notifier {
public:
// --- Métodos de singleton ---
static void init(const std::string &icon_file, std::shared_ptr<Text> text); // Inicializa el singleton
static void destroy(); // Libera el singleton
static Notifier *get(); // Obtiene la instancia
static void init(const std::string &icon_file, std::shared_ptr<Text> text); // Inicializa el singleton
static void destroy(); // Libera el singleton
static Notifier *get(); // Obtiene la instancia
// --- Métodos principales ---
void render(); // Dibuja las notificaciones por pantalla
void update(); // Actualiza el estado de las notificaciones
void render(); // Dibuja las notificaciones por pantalla
void update(); // Actualiza el estado de las notificaciones
// --- Gestión de notificaciones ---
void show(std::vector<std::string> texts, int icon = -1, const std::string &code = std::string()); // Muestra una notificación de texto por pantalla
bool isActive() const { return !notifications_.empty(); } // Indica si hay notificaciones activas
std::vector<std::string> getCodes(); // Obtiene los códigos de las notificaciones activas
bool checkCode(const std::string &code) { return stringInVector(getCodes(), code); } // Comprueba si hay alguna notificación con un código concreto
void show(std::vector<std::string> texts, int icon = -1, const std::string &code = std::string()); // Muestra una notificación de texto por pantalla
bool isActive() const { return !notifications_.empty(); } // Indica si hay notificaciones activas
std::vector<std::string> getCodes(); // Obtiene los códigos de las notificaciones activas
bool checkCode(const std::string &code) { return stringInVector(getCodes(), code); } // Comprueba si hay alguna notificación con un código concreto
private:
private:
// --- Singleton ---
static Notifier *instance_;
// --- Tipos internos ---
enum class NotificationStatus
{
enum class NotificationStatus {
RISING,
STAY,
VANISHING,
FINISHED,
};
enum class NotificationShape
{
enum class NotificationShape {
ROUNDED,
SQUARED,
};
// --- Estructura Notification ---
struct Notification
{
std::shared_ptr<Texture> texture; // Textura de la notificación
std::shared_ptr<Sprite> sprite; // Sprite asociado
std::vector<std::string> texts; // Textos a mostrar
int counter; // Contador de tiempo
NotificationStatus state; // Estado de la notificación
NotificationShape shape; // Forma de la notificación
SDL_FRect rect; // Rectángulo de la notificación
int y; // Posición vertical
int travel_dist; // Distancia a recorrer
std::string code; // Código identificador de la notificación
struct Notification {
std::shared_ptr<Texture> texture; // Textura de la notificación
std::shared_ptr<Sprite> sprite; // Sprite asociado
std::vector<std::string> texts; // Textos a mostrar
int counter; // Contador de tiempo
NotificationStatus state; // Estado de la notificación
NotificationShape shape; // Forma de la notificación
SDL_FRect rect; // Rectángulo de la notificación
int y; // Posición vertical
int travel_dist; // Distancia a recorrer
std::string code; // Código identificador de la notificación
// Constructor
explicit Notification()
: texture(nullptr), sprite(nullptr), texts(), counter(0), state(NotificationStatus::RISING),
shape(NotificationShape::SQUARED), rect{0, 0, 0, 0}, y(0), travel_dist(0), code("") {}
: texture(nullptr), sprite(nullptr), texts(), counter(0), state(NotificationStatus::RISING), shape(NotificationShape::SQUARED), rect{0, 0, 0, 0}, y(0), travel_dist(0), code("") {}
};
// --- Objetos y punteros ---
SDL_Renderer *renderer_; // El renderizador de la ventana
std::shared_ptr<Texture> icon_texture_; // Textura para los iconos de las notificaciones
std::shared_ptr<Text> text_; // Objeto para dibujar texto
SDL_Renderer *renderer_; // El renderizador de la ventana
std::shared_ptr<Texture> icon_texture_; // Textura para los iconos de las notificaciones
std::shared_ptr<Text> text_; // Objeto para dibujar texto
// --- Variables de estado ---
Color bg_color_; // Color de fondo de las notificaciones
int wait_time_; // Tiempo que se ve la notificación
std::vector<Notification> notifications_; // Lista de notificaciones activas
bool stack_; // Indica si las notificaciones se apilan
bool has_icons_; // Indica si el notificador tiene textura para iconos
Color bg_color_; // Color de fondo de las notificaciones
int wait_time_; // Tiempo que se ve la notificación
std::vector<Notification> notifications_; // Lista de notificaciones activas
bool stack_; // Indica si las notificaciones se apilan
bool has_icons_; // Indica si el notificador tiene textura para iconos
// --- Métodos internos ---
void clearFinishedNotifications(); // Elimina las notificaciones finalizadas
void clearAllNotifications(); // Finaliza y elimina todas las notificaciones activas
void clearFinishedNotifications(); // Elimina las notificaciones finalizadas
void clearAllNotifications(); // Finaliza y elimina todas las notificaciones activas
// --- Constructor y destructor ---
Notifier(std::string icon_file, std::shared_ptr<Text> text); // Constructor privado
~Notifier() = default; // Destructor privado
Notifier(std::string icon_file, std::shared_ptr<Text> text); // Constructor privado
~Notifier() = default; // Destructor privado
};

View File

@@ -1,423 +1,328 @@
#include "options.h"
#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogInfo, SDL_LogError
#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogInfo, SDL_LogError
#include <algorithm> // Para clamp
#include <fstream> // Para basic_ostream, operator<<, basic_ostream::...
#include <utility> // Para swap
#include <vector> // Para vector
#include <fstream> // Para basic_ostream, operator<<, basic_ostream::...
#include <utility> // Para swap
#include <vector> // Para vector
#include "asset.h"
#include "input.h" // Para InputDeviceToUse
#include "lang.h" // Para Code
#include "utils.h" // Para boolToString, stringToBool, getFileName
#include "input.h" // Para InputDeviceToUse
#include "lang.h" // Para Code
#include "utils.h" // Para boolToString, stringToBool, getFileName
namespace Options
{
// --- Variables globales ---
WindowOptions window; // Opciones de la ventana
SettingsOptions settings; // Opciones del juego
VideoOptions video; // Opciones de vídeo
AudioOptions audio; // Opciones de audio
std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador
PendingChanges pending_changes; // Opciones que se aplican al cerrar
namespace Options {
// --- Variables globales ---
WindowOptions window; // Opciones de la ventana
SettingsOptions settings; // Opciones del juego
VideoOptions video; // Opciones de vídeo
AudioOptions audio; // Opciones de audio
std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador
PendingChanges pending_changes; // Opciones que se aplican al cerrar
// Vector con las dificultades
std::vector<Difficulty> difficulties = {
{DifficultyCode::EASY, "Easy"},
{DifficultyCode::NORMAL, "Normal"},
{DifficultyCode::HARD, "Hard"}};
// Vector con las dificultades
std::vector<Difficulty> difficulties = {
{DifficultyCode::EASY, "Easy"},
{DifficultyCode::NORMAL, "Normal"},
{DifficultyCode::HARD, "Hard"}};
// Declaraciones
bool set(const std::string &var, const std::string &value);
// Declaraciones
bool set(const std::string &var, const std::string &value);
// Inicializa las opciones del programa
void init()
{
// Settings
settings.config_file = Asset::get()->get("config.txt");
// Inicializa las opciones del programa
void init() {
// Settings
settings.config_file = Asset::get()->get("config.txt");
// Opciones de control
controllers.clear();
controllers.resize(2);
controllers.at(0).player_id = 1;
controllers.at(1).player_id = 2;
setKeyboardToPlayer(1);
// Opciones de control
controllers.clear();
controllers.resize(2);
controllers.at(0).player_id = 1;
controllers.at(1).player_id = 2;
setKeyboardToPlayer(1);
// Opciones pendientes
pending_changes.new_language = settings.language;
pending_changes.new_difficulty = settings.difficulty;
pending_changes.has_pending_changes = false;
}
// Opciones pendientes
pending_changes.new_language = settings.language;
pending_changes.new_difficulty = settings.difficulty;
pending_changes.has_pending_changes = false;
}
// Carga el fichero de configuración
bool loadFromFile()
{
// Inicializa las opciones del programa
init();
// Carga el fichero de configuración
bool loadFromFile() {
// Inicializa las opciones del programa
init();
// Indicador de éxito en la carga
bool success = true;
// Indicador de éxito en la carga
bool success = true;
// Variables para manejar el fichero
std::ifstream file(settings.config_file);
// Variables para manejar el fichero
std::ifstream file(settings.config_file);
// Si el fichero se puede abrir
if (file.good())
{
// Procesa el fichero línea a línea
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(settings.config_file).c_str());
std::string line;
while (std::getline(file, line))
{
// Comprueba que la línea no sea un comentario
if (line.substr(0, 1) != "#")
{
// Encuentra la posición del carácter '='
int pos = line.find("=");
// Procesa las dos subcadenas
if (!set(line.substr(0, pos), line.substr(pos + 1, line.length())))
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown parameter: %s", line.substr(0, pos).c_str());
success = false;
}
}
}
file.close();
}
// El fichero no existe
else
{
saveToFile(); // Crea el fichero con los valores por defecto
}
// Si el fichero se puede abrir
if (file.good()) {
// Procesa el fichero línea a línea
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(settings.config_file).c_str());
std::string line;
while (std::getline(file, line)) {
// Comprueba que la línea no sea un comentario
if (line.substr(0, 1) != "#") {
// Encuentra la posición del carácter '='
int pos = line.find("=");
// Procesa las dos subcadenas
if (!set(line.substr(0, pos), line.substr(pos + 1, line.length()))) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown parameter: %s", line.substr(0, pos).c_str());
success = false;
}
}
}
file.close();
}
// El fichero no existe
else {
saveToFile(); // Crea el fichero con los valores por defecto
}
// Normaliza los valores
if (settings.language != Lang::Code::ENGLISH &&
settings.language != Lang::Code::VALENCIAN &&
settings.language != Lang::Code::SPANISH)
{
settings.language = Lang::Code::ENGLISH;
}
// Normaliza los valores
if (settings.language != Lang::Code::ENGLISH &&
settings.language != Lang::Code::VALENCIAN &&
settings.language != Lang::Code::SPANISH) {
settings.language = Lang::Code::ENGLISH;
}
return success;
}
return success;
}
// Guarda el fichero de configuración
bool saveToFile()
{
std::ofstream file(settings.config_file);
// Guarda el fichero de configuración
bool saveToFile() {
std::ofstream file(settings.config_file);
if (!file.good())
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: %s can't be opened", getFileName(settings.config_file).c_str());
return false;
}
if (!file.good()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: %s can't be opened", getFileName(settings.config_file).c_str());
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(settings.config_file).c_str());
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(settings.config_file).c_str());
applyPendingChanges();
applyPendingChanges();
// Opciones de video
file << "## VIDEO\n";
file << "## video.scale_mode [" << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": lineal]\n";
file << "\n";
// Opciones de video
file << "## VIDEO\n";
file << "## video.scale_mode [" << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": lineal]\n";
file << "\n";
file << "window.zoom=" << window.size << "\n";
file << "video.fullscreen=" << boolToString(video.fullscreen) << "\n";
file << "video.scale_mode=" << static_cast<int>(video.scale_mode) << "\n";
file << "video.v_sync=" << boolToString(video.v_sync) << "\n";
file << "video.integer_scale=" << boolToString(video.integer_scale) << "\n";
file << "video.shaders=" << boolToString(video.shaders) << "\n";
file << "window.zoom=" << window.size << "\n";
file << "video.fullscreen=" << boolToString(video.fullscreen) << "\n";
file << "video.scale_mode=" << static_cast<int>(video.scale_mode) << "\n";
file << "video.v_sync=" << boolToString(video.v_sync) << "\n";
file << "video.integer_scale=" << boolToString(video.integer_scale) << "\n";
file << "video.shaders=" << boolToString(video.shaders) << "\n";
// Opciones de audio
file << "\n\n## AUDIO\n";
file << "## volume [0 .. 100]\n";
file << "\n";
// Opciones de audio
file << "\n\n## AUDIO\n";
file << "## volume [0 .. 100]\n";
file << "\n";
file << "audio.enabled=" << boolToString(audio.enabled) << "\n";
file << "audio.volume=" << audio.volume << "\n";
file << "audio.music.enabled=" << boolToString(audio.music.enabled) << "\n";
file << "audio.music.volume=" << audio.music.volume << "\n";
file << "audio.sound.enabled=" << boolToString(audio.sound.enabled) << "\n";
file << "audio.sound.volume=" << audio.sound.volume << "\n";
file << "audio.enabled=" << boolToString(audio.enabled) << "\n";
file << "audio.volume=" << audio.volume << "\n";
file << "audio.music.enabled=" << boolToString(audio.music.enabled) << "\n";
file << "audio.music.volume=" << audio.music.volume << "\n";
file << "audio.sound.enabled=" << boolToString(audio.sound.enabled) << "\n";
file << "audio.sound.volume=" << audio.sound.volume << "\n";
// Opciones del juego
file << "\n\n## GAME\n";
file << "## game.language [0: spanish, 1: valencian, 2: english]\n";
file << "## game.difficulty [" << static_cast<int>(DifficultyCode::EASY) << ": easy, " << static_cast<int>(DifficultyCode::NORMAL) << ": normal, " << static_cast<int>(DifficultyCode::HARD) << ": hard]\n";
file << "\n";
// Opciones del juego
file << "\n\n## GAME\n";
file << "## game.language [0: spanish, 1: valencian, 2: english]\n";
file << "## game.difficulty [" << static_cast<int>(DifficultyCode::EASY) << ": easy, " << static_cast<int>(DifficultyCode::NORMAL) << ": normal, " << static_cast<int>(DifficultyCode::HARD) << ": hard]\n";
file << "\n";
file << "game.language=" << static_cast<int>(settings.language) << "\n";
file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n";
file << "game.autofire=" << boolToString(settings.autofire) << "\n";
file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n";
file << "game.language=" << static_cast<int>(settings.language) << "\n";
file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n";
file << "game.autofire=" << boolToString(settings.autofire) << "\n";
file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n";
// Opciones de mandos
file << "\n\n## CONTROLLERS\n";
// Opciones de mandos
file << "\n\n## CONTROLLERS\n";
int controller_index = 0;
for (const auto &controller : controllers)
{
file << "\n";
file << "controller." << controller_index << ".name=" << controller.name << "\n";
file << "controller." << controller_index << ".player=" << controller.player_id << "\n";
file << "controller." << controller_index << ".type=" << static_cast<int>(controller.type) << "\n";
file << "controller." << controller_index << ".button.fire_left=" << controller.buttons.at(0) << "\n";
file << "controller." << controller_index << ".button.fire_center=" << controller.buttons.at(1) << "\n";
file << "controller." << controller_index << ".button.fire_right=" << controller.buttons.at(2) << "\n";
file << "controller." << controller_index << ".button.start=" << controller.buttons.at(3) << "\n";
file << "controller." << controller_index << ".button.service=" << controller.buttons.at(4) << "\n";
int controller_index = 0;
for (const auto &controller : controllers) {
file << "\n";
file << "controller." << controller_index << ".name=" << controller.name << "\n";
file << "controller." << controller_index << ".player=" << controller.player_id << "\n";
file << "controller." << controller_index << ".type=" << static_cast<int>(controller.type) << "\n";
file << "controller." << controller_index << ".button.fire_left=" << controller.buttons.at(0) << "\n";
file << "controller." << controller_index << ".button.fire_center=" << controller.buttons.at(1) << "\n";
file << "controller." << controller_index << ".button.fire_right=" << controller.buttons.at(2) << "\n";
file << "controller." << controller_index << ".button.start=" << controller.buttons.at(3) << "\n";
file << "controller." << controller_index << ".button.service=" << controller.buttons.at(4) << "\n";
// Incrementa el índice
++controller_index;
}
// Incrementa el índice
++controller_index;
}
// Cierra el fichero
file.close();
// Cierra el fichero
file.close();
return true;
}
return true;
}
// Asigna variables a partir de dos cadenas
bool set(const std::string &var, const std::string &value)
{
// Indicador de éxito en la asignación
auto success = true;
// Asigna variables a partir de dos cadenas
bool set(const std::string &var, const std::string &value) {
// Indicador de éxito en la asignación
auto success = true;
// Opciones de video
if (var == "video.fullscreen")
{
video.fullscreen = stringToBool(value);
}
else if (var == "window.zoom")
{
window.size = std::stoi(value);
}
else if (var == "video.scale_mode")
{
video.scale_mode = static_cast<SDL_ScaleMode>(std::stoi(value));
}
else if (var == "video.shaders")
{
video.shaders = stringToBool(value);
}
else if (var == "video.integer_scale")
{
video.integer_scale = stringToBool(value);
}
else if (var == "video.v_sync")
{
video.v_sync = stringToBool(value);
}
// Opciones de video
if (var == "video.fullscreen") {
video.fullscreen = stringToBool(value);
} else if (var == "window.zoom") {
window.size = std::stoi(value);
} else if (var == "video.scale_mode") {
video.scale_mode = static_cast<SDL_ScaleMode>(std::stoi(value));
} else if (var == "video.shaders") {
video.shaders = stringToBool(value);
} else if (var == "video.integer_scale") {
video.integer_scale = stringToBool(value);
} else if (var == "video.v_sync") {
video.v_sync = stringToBool(value);
}
// Opciones de audio
else if (var == "audio.enabled")
{
audio.enabled = stringToBool(value);
}
else if (var == "audio.volume")
{
audio.volume = std::clamp(std::stoi(value), 0, 100);
}
else if (var == "audio.music.enabled")
{
audio.music.enabled = stringToBool(value);
}
else if (var == "audio.music.volume")
{
audio.music.volume = std::clamp(std::stoi(value), 0, 100);
}
else if (var == "audio.sound.enabled")
{
audio.sound.enabled = stringToBool(value);
}
else if (var == "audio.sound.volume")
{
audio.sound.volume = std::clamp(std::stoi(value), 0, 100);
}
// Opciones de audio
else if (var == "audio.enabled") {
audio.enabled = stringToBool(value);
} else if (var == "audio.volume") {
audio.volume = std::clamp(std::stoi(value), 0, 100);
} else if (var == "audio.music.enabled") {
audio.music.enabled = stringToBool(value);
} else if (var == "audio.music.volume") {
audio.music.volume = std::clamp(std::stoi(value), 0, 100);
} else if (var == "audio.sound.enabled") {
audio.sound.enabled = stringToBool(value);
} else if (var == "audio.sound.volume") {
audio.sound.volume = std::clamp(std::stoi(value), 0, 100);
}
// Opciones de juego
else if (var == "game.language")
{
settings.language = static_cast<Lang::Code>(std::stoi(value));
pending_changes.new_language = settings.language;
}
else if (var == "game.difficulty")
{
settings.difficulty = static_cast<DifficultyCode>(std::stoi(value));
pending_changes.new_difficulty = settings.difficulty;
}
else if (var == "game.autofire")
{
settings.autofire = stringToBool(value);
}
else if (var == "game.shutdown_enabled")
{
settings.shutdown_enabled = stringToBool(value);
}
// Opciones de juego
else if (var == "game.language") {
settings.language = static_cast<Lang::Code>(std::stoi(value));
pending_changes.new_language = settings.language;
} else if (var == "game.difficulty") {
settings.difficulty = static_cast<DifficultyCode>(std::stoi(value));
pending_changes.new_difficulty = settings.difficulty;
} else if (var == "game.autofire") {
settings.autofire = stringToBool(value);
} else if (var == "game.shutdown_enabled") {
settings.shutdown_enabled = stringToBool(value);
}
// Opciones de mandos
else if (var == "controller.0.name")
{
controllers.at(0).name = value;
}
else if (var == "controller.0.player")
{
controllers.at(0).player_id = std::clamp(std::stoi(value), 1, 2);
}
else if (var == "controller.0.type")
{
controllers.at(0).type = static_cast<InputDevice>(std::stoi(value));
}
else if (var == "controller.0.button.fire_left")
{
controllers.at(0).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
else if (var == "controller.0.button.fire_center")
{
controllers.at(0).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
else if (var == "controller.0.button.fire_right")
{
controllers.at(0).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
else if (var == "controller.0.button.start")
{
controllers.at(0).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
else if (var == "controller.0.button.service")
{
controllers.at(0).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
else if (var == "controller.1.name")
{
controllers.at(1).name = value;
}
else if (var == "controller.1.player")
{
controllers.at(1).player_id = std::clamp(std::stoi(value), 1, 2);
}
else if (var == "controller.1.type")
{
controllers.at(1).type = static_cast<InputDevice>(std::stoi(value));
}
else if (var == "controller.1.button.fire_left")
{
controllers.at(1).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
else if (var == "controller.1.button.fire_center")
{
controllers.at(1).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
else if (var == "controller.1.button.fire_right")
{
controllers.at(1).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
else if (var == "controller.1.button.start")
{
controllers.at(1).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
else if (var == "controller.1.button.service")
{
controllers.at(1).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
// Opciones de mandos
else if (var == "controller.0.name") {
controllers.at(0).name = value;
} else if (var == "controller.0.player") {
controllers.at(0).player_id = std::clamp(std::stoi(value), 1, 2);
} else if (var == "controller.0.type") {
controllers.at(0).type = static_cast<InputDevice>(std::stoi(value));
} else if (var == "controller.0.button.fire_left") {
controllers.at(0).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.0.button.fire_center") {
controllers.at(0).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.0.button.fire_right") {
controllers.at(0).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.0.button.start") {
controllers.at(0).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.0.button.service") {
controllers.at(0).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.1.name") {
controllers.at(1).name = value;
} else if (var == "controller.1.player") {
controllers.at(1).player_id = std::clamp(std::stoi(value), 1, 2);
} else if (var == "controller.1.type") {
controllers.at(1).type = static_cast<InputDevice>(std::stoi(value));
} else if (var == "controller.1.button.fire_left") {
controllers.at(1).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.1.button.fire_center") {
controllers.at(1).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.1.button.fire_right") {
controllers.at(1).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.1.button.start") {
controllers.at(1).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.1.button.service") {
controllers.at(1).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
// Lineas vacias o que empiezan por comentario
else if (var.empty() || var.starts_with("#"))
{
}
else
{
success = false;
}
// Lineas vacias o que empiezan por comentario
else if (var.empty() || var.starts_with("#")) {
} else {
success = false;
}
return success;
}
return success;
}
// Asigna el teclado al jugador
void setKeyboardToPlayer(int player_id)
{
for (auto &controller : controllers)
{
if (controller.player_id == player_id)
{
controller.type = InputDevice::ANY;
}
else
{
controller.type = InputDevice::CONTROLLER;
}
}
}
// Asigna el teclado al jugador
void setKeyboardToPlayer(int player_id) {
for (auto &controller : controllers) {
if (controller.player_id == player_id) {
controller.type = InputDevice::ANY;
} else {
controller.type = InputDevice::CONTROLLER;
}
}
}
// Intercambia el teclado de jugador
void swapKeyboard()
{
std::swap(controllers.at(0).type, controllers.at(1).type);
}
// Intercambia el teclado de jugador
void swapKeyboard() {
std::swap(controllers.at(0).type, controllers.at(1).type);
}
// Intercambia los jugadores asignados a los dos primeros mandos
void swapControllers()
{
std::swap(controllers.at(0).player_id, controllers.at(1).player_id);
std::swap(controllers.at(0).type, controllers.at(1).type);
}
// Intercambia los jugadores asignados a los dos primeros mandos
void swapControllers() {
std::swap(controllers.at(0).player_id, controllers.at(1).player_id);
std::swap(controllers.at(0).type, controllers.at(1).type);
}
// Averigua quien está usando el teclado
int getPlayerWhoUsesKeyboard()
{
for (const auto &controller : controllers)
{
if (controller.type == InputDevice::ANY)
{
return controller.player_id;
}
}
return 0;
}
// Averigua quien está usando el teclado
int getPlayerWhoUsesKeyboard() {
for (const auto &controller : controllers) {
if (controller.type == InputDevice::ANY) {
return controller.player_id;
}
}
return 0;
}
// Aplica los cambios pendientes copiando los valores a sus variables
void applyPendingChanges()
{
if (pending_changes.has_pending_changes)
{
settings.language = pending_changes.new_language;
settings.difficulty = pending_changes.new_difficulty;
pending_changes.has_pending_changes = false;
}
}
// Aplica los cambios pendientes copiando los valores a sus variables
void applyPendingChanges() {
if (pending_changes.has_pending_changes) {
settings.language = pending_changes.new_language;
settings.difficulty = pending_changes.new_difficulty;
pending_changes.has_pending_changes = false;
}
}
void checkPendingChanges()
{
if (settings.language != pending_changes.new_language ||
settings.difficulty != pending_changes.new_difficulty)
{
pending_changes.has_pending_changes = true;
}
else
{
pending_changes.has_pending_changes = false;
}
}
void checkPendingChanges() {
if (settings.language != pending_changes.new_language ||
settings.difficulty != pending_changes.new_difficulty) {
pending_changes.has_pending_changes = true;
} else {
pending_changes.has_pending_changes = false;
}
}
DifficultyCode getDifficultyCodeFromName(const std::string &name)
{
for (const auto &difficulty : difficulties)
{
if (difficulty.name == name)
return difficulty.code;
}
// Si no se encuentra, devuelve el primero por defecto
return difficulties[0].code;
}
DifficultyCode getDifficultyCodeFromName(const std::string &name) {
for (const auto &difficulty : difficulties) {
if (difficulty.name == name)
return difficulty.code;
}
// Si no se encuentra, devuelve el primero por defecto
return difficulties[0].code;
}
std::string getDifficultyNameFromCode(DifficultyCode code)
{
for (const auto &difficulty : difficulties)
{
if (difficulty.code == code)
return difficulty.name;
}
// Si no se encuentra, devuelve el nombre del primero por defecto
return difficulties[0].name;
}
} // namespace Options
std::string getDifficultyNameFromCode(DifficultyCode code) {
for (const auto &difficulty : difficulties) {
if (difficulty.code == code)
return difficulty.name;
}
// Si no se encuentra, devuelve el nombre del primero por defecto
return difficulties[0].name;
}
} // namespace Options

View File

@@ -1,202 +1,191 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_GamepadButton, SDL_ScaleMode
#include <string> // Para string, basic_string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_GamepadButton, SDL_ScaleMode
#include "input.h" // Para InputAction, InputDevice
#include "lang.h" // Para Code
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include <string> // Para string, basic_string
#include <vector> // Para vector
#include "input.h" // Para InputAction, InputDevice
#include "lang.h" // Para Code
#include "manage_hiscore_table.h" // Para HiScoreEntry
static constexpr int INVALID_INDEX = -1;
namespace Options
{
// --- Dificultad del juego ---
enum class DifficultyCode
{
EASY = 0,
NORMAL = 1,
HARD = 2,
};
namespace Options {
// --- Dificultad del juego ---
enum class DifficultyCode {
EASY = 0,
NORMAL = 1,
HARD = 2,
};
// --- Estructura que representa un nivel de dificultad
struct Difficulty
{
DifficultyCode code; // Código que identifica la dificultad
std::string name; // Nombre que identifica la dificultad
// --- Estructura que representa un nivel de dificultad
struct Difficulty {
DifficultyCode code; // Código que identifica la dificultad
std::string name; // Nombre que identifica la dificultad
Difficulty(DifficultyCode c, const std::string &n)
: code(c), name(n) {}
};
Difficulty(DifficultyCode c, const std::string &n)
: code(c), name(n) {}
};
// --- Opciones de ventana ---
struct WindowOptions
{
std::string caption; // Texto que aparece en la barra de título de la ventana
int size; // Valor por el que se multiplica el tamaño de la ventana
int max_size; // Tamaño máximo para que la ventana no sea mayor que la pantalla
// --- Opciones de ventana ---
struct WindowOptions {
std::string caption; // Texto que aparece en la barra de título de la ventana
int size; // Valor por el que se multiplica el tamaño de la ventana
int max_size; // Tamaño máximo para que la ventana no sea mayor que la pantalla
// Constructor por defecto con valores iniciales
WindowOptions()
: caption("Coffee Crisis Arcade Edition"),
size(2),
max_size(2) {}
};
// Constructor por defecto con valores iniciales
WindowOptions()
: caption("Coffee Crisis Arcade Edition"),
size(2),
max_size(2) {}
};
// --- Opciones de vídeo ---
struct VideoOptions
{
SDL_ScaleMode scale_mode; // Filtro usado para el escalado de la imagen
bool fullscreen; // Indica si se usa pantalla completa
bool v_sync; // Indica si se usa vsync
bool integer_scale; // Indica si se usa escalado entero
bool shaders; // Indica si se usan shaders para los filtros de vídeo
std::string info; // Información sobre el modo de vídeo
// --- Opciones de vídeo ---
struct VideoOptions {
SDL_ScaleMode scale_mode; // Filtro usado para el escalado de la imagen
bool fullscreen; // Indica si se usa pantalla completa
bool v_sync; // Indica si se usa vsync
bool integer_scale; // Indica si se usa escalado entero
bool shaders; // Indica si se usan shaders para los filtros de vídeo
std::string info; // Información sobre el modo de vídeo
// Constructor por defecto con valores iniciales
VideoOptions()
: scale_mode(SDL_ScaleMode::SDL_SCALEMODE_NEAREST),
fullscreen(false),
v_sync(true),
integer_scale(true),
shaders(false),
info() {}
};
// Constructor por defecto con valores iniciales
VideoOptions()
: scale_mode(SDL_ScaleMode::SDL_SCALEMODE_NEAREST),
fullscreen(false),
v_sync(true),
integer_scale(true),
shaders(false),
info() {}
};
// --- Opciones de música ---
struct MusicOptions
{
bool enabled; // Indica si la música suena o no
int volume; // Volumen de la música
// --- Opciones de música ---
struct MusicOptions {
bool enabled; // Indica si la música suena o no
int volume; // Volumen de la música
// Constructor por defecto
MusicOptions()
: enabled(true),
volume(100) {}
};
// Constructor por defecto
MusicOptions()
: enabled(true),
volume(100) {}
};
// --- Opciones de sonido ---
struct SoundOptions
{
bool enabled; // Indica si los sonidos suenan o no
int volume; // Volumen de los sonidos
// --- Opciones de sonido ---
struct SoundOptions {
bool enabled; // Indica si los sonidos suenan o no
int volume; // Volumen de los sonidos
// Constructor por defecto
SoundOptions()
: enabled(true),
volume(100) {}
};
// Constructor por defecto
SoundOptions()
: enabled(true),
volume(100) {}
};
// --- Opciones de audio ---
struct AudioOptions
{
MusicOptions music; // Opciones para la música
SoundOptions sound; // Opciones para los efectos de sonido
bool enabled; // Indica si el audio está activo o no
int volume; // Volumen general del audio
// --- Opciones de audio ---
struct AudioOptions {
MusicOptions music; // Opciones para la música
SoundOptions sound; // Opciones para los efectos de sonido
bool enabled; // Indica si el audio está activo o no
int volume; // Volumen general del audio
// Constructor por defecto
AudioOptions()
: music(),
sound(),
enabled(true),
volume(100) {}
};
// Constructor por defecto
AudioOptions()
: music(),
sound(),
enabled(true),
volume(100) {}
};
// --- Opciones de configuración ---
struct SettingsOptions
{
DifficultyCode difficulty; // Dificultad del juego
Lang::Code language; // Idioma usado en el juego
bool autofire; // Indicador de autofire
bool shutdown_enabled; // Especifica si se puede apagar el sistema
std::vector<HiScoreEntry> hi_score_table; // Tabla de mejores puntuaciones
std::vector<int> last_hi_score_entry; // Últimas posiciones de entrada en la tabla
std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego
// --- Opciones de configuración ---
struct SettingsOptions {
DifficultyCode difficulty; // Dificultad del juego
Lang::Code language; // Idioma usado en el juego
bool autofire; // Indicador de autofire
bool shutdown_enabled; // Especifica si se puede apagar el sistema
std::vector<HiScoreEntry> hi_score_table; // Tabla de mejores puntuaciones
std::vector<int> last_hi_score_entry; // Últimas posiciones de entrada en la tabla
std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego
// Constructor por defecto con valores iniciales
SettingsOptions()
: difficulty(DifficultyCode::NORMAL),
language(Lang::Code::VALENCIAN),
autofire(true),
shutdown_enabled(false),
last_hi_score_entry({INVALID_INDEX, INVALID_INDEX}),
config_file() {}
// Constructor por defecto con valores iniciales
SettingsOptions()
: difficulty(DifficultyCode::NORMAL),
language(Lang::Code::VALENCIAN),
autofire(true),
shutdown_enabled(false),
last_hi_score_entry({INVALID_INDEX, INVALID_INDEX}),
config_file() {}
// Reinicia las últimas entradas de puntuación
void clearLastHiScoreEntries()
{
last_hi_score_entry[0] = INVALID_INDEX;
last_hi_score_entry[1] = INVALID_INDEX;
}
};
// Reinicia las últimas entradas de puntuación
void clearLastHiScoreEntries() {
last_hi_score_entry[0] = INVALID_INDEX;
last_hi_score_entry[1] = INVALID_INDEX;
}
};
// --- Opciones de mando ---
struct GamepadOptions
{
int index; // Índice en el vector de mandos
int player_id; // Jugador asociado al mando
InputDevice type; // Indica si se usará teclado, mando o ambos
std::string name; // Nombre del dispositivo
bool plugged; // Indica si el mando está conectado
std::vector<InputAction> inputs; // Listado de acciones asignadas
std::vector<SDL_GamepadButton> buttons; // Listado de botones asignados a cada acción
// --- Opciones de mando ---
struct GamepadOptions {
int index; // Índice en el vector de mandos
int player_id; // Jugador asociado al mando
InputDevice type; // Indica si se usará teclado, mando o ambos
std::string name; // Nombre del dispositivo
bool plugged; // Indica si el mando está conectado
std::vector<InputAction> inputs; // Listado de acciones asignadas
std::vector<SDL_GamepadButton> buttons; // Listado de botones asignados a cada acción
// Constructor por defecto
GamepadOptions()
: index(INVALID_INDEX),
player_id(INVALID_INDEX),
type(InputDevice::CONTROLLER),
name(),
plugged(false),
inputs{
InputAction::FIRE_LEFT,
InputAction::FIRE_CENTER,
InputAction::FIRE_RIGHT,
InputAction::START,
InputAction::SERVICE},
buttons{
SDL_GAMEPAD_BUTTON_WEST,
SDL_GAMEPAD_BUTTON_NORTH,
SDL_GAMEPAD_BUTTON_EAST,
SDL_GAMEPAD_BUTTON_START,
SDL_GAMEPAD_BUTTON_BACK} {}
};
// Constructor por defecto
GamepadOptions()
: index(INVALID_INDEX),
player_id(INVALID_INDEX),
type(InputDevice::CONTROLLER),
name(),
plugged(false),
inputs{
InputAction::FIRE_LEFT,
InputAction::FIRE_CENTER,
InputAction::FIRE_RIGHT,
InputAction::START,
InputAction::SERVICE},
buttons{
SDL_GAMEPAD_BUTTON_WEST,
SDL_GAMEPAD_BUTTON_NORTH,
SDL_GAMEPAD_BUTTON_EAST,
SDL_GAMEPAD_BUTTON_START,
SDL_GAMEPAD_BUTTON_BACK} {}
};
// --- Opciones pendientes de aplicar ---
struct PendingChanges
{
Lang::Code new_language; // Idioma en espera de aplicar
DifficultyCode new_difficulty; // Dificultad en espera de aplicar
bool has_pending_changes; // Indica si hay cambios pendientes
// --- Opciones pendientes de aplicar ---
struct PendingChanges {
Lang::Code new_language; // Idioma en espera de aplicar
DifficultyCode new_difficulty; // Dificultad en espera de aplicar
bool has_pending_changes; // Indica si hay cambios pendientes
// Constructor por defecto con valores iniciales
PendingChanges()
: new_language(Lang::Code::VALENCIAN),
new_difficulty(DifficultyCode::NORMAL),
has_pending_changes(false) {}
};
// Constructor por defecto con valores iniciales
PendingChanges()
: new_language(Lang::Code::VALENCIAN),
new_difficulty(DifficultyCode::NORMAL),
has_pending_changes(false) {}
};
// --- Variables globales ---
extern WindowOptions window; // Opciones de la ventana
extern SettingsOptions settings; // Opciones del juego
extern VideoOptions video; // Opciones de vídeo
extern AudioOptions audio; // Opciones de audio
extern std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
extern std::vector<Difficulty> difficulties; // Lista de los diferentes tipos de dificultad
// --- Variables globales ---
extern WindowOptions window; // Opciones de la ventana
extern SettingsOptions settings; // Opciones del juego
extern VideoOptions video; // Opciones de vídeo
extern AudioOptions audio; // Opciones de audio
extern std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
extern std::vector<Difficulty> difficulties; // Lista de los diferentes tipos de dificultad
// --- Funciones de configuración ---
void init(); // Inicializa las opciones del programa
bool loadFromFile(); // Carga el fichero de configuración
bool saveToFile(); // Guarda el fichero de configuración
void setKeyboardToPlayer(int player_id); // Asigna el teclado al jugador
void swapKeyboard(); // Intercambia el teclado de jugador
void swapControllers(); // Intercambia los jugadores asignados a los dos primeros mandos
int getPlayerWhoUsesKeyboard(); // Averigua quién está usando el teclado
void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables
void checkPendingChanges(); // Verifica si hay cambios pendientes
DifficultyCode getDifficultyCodeFromName(const std::string &name); // Obtiene el código de dificultad a partir del nombre
std::string getDifficultyNameFromCode(DifficultyCode code); // Obtiene el nombre de la dificultad a partir del código
} // namespace Options
// --- Funciones de configuración ---
void init(); // Inicializa las opciones del programa
bool loadFromFile(); // Carga el fichero de configuración
bool saveToFile(); // Guarda el fichero de configuración
void setKeyboardToPlayer(int player_id); // Asigna el teclado al jugador
void swapKeyboard(); // Intercambia el teclado de jugador
void swapControllers(); // Intercambia los jugadores asignados a los dos primeros mandos
int getPlayerWhoUsesKeyboard(); // Averigua quién está usando el teclado
void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables
void checkPendingChanges(); // Verifica si hay cambios pendientes
DifficultyCode getDifficultyCodeFromName(const std::string &name); // Obtiene el código de dificultad a partir del nombre
std::string getDifficultyNameFromCode(DifficultyCode code); // Obtiene el nombre de la dificultad a partir del código
} // namespace Options

View File

@@ -1,12 +1,13 @@
#include "param.h"
#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogError, SDL_LogInfo
#include <fstream> // Para basic_istream, basic_ifstream, ifstream
#include <sstream> // Para basic_istringstream
#include <stdexcept> // Para runtime_error
#include <string> // Para operator==, stoi, char_traits, string, ope...
#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogError, SDL_LogInfo
#include "utils.h" // Para Zone, Color, NotifyPosition, getFileName
#include <fstream> // Para basic_istream, basic_ifstream, ifstream
#include <sstream> // Para basic_istringstream
#include <stdexcept> // Para runtime_error
#include <string> // Para operator==, stoi, char_traits, string, ope...
#include "utils.h" // Para Zone, Color, NotifyPosition, getFileName
Param param;
@@ -17,500 +18,420 @@ void precalculateZones();
bool setParams(const std::string &var, const std::string &value);
// Establece valores por defecto a las variables
void initParam()
{
// GAME
param.game.width = 320;
param.game.height = 256;
param.game.item_size = 20;
param.game.game_area.rect = {0, 0, param.game.width, param.game.height};
param.game.play_area.rect = {0, 0, param.game.width, 216};
param.game.name_entry_idle_time = 10;
param.game.name_entry_total_time = 60;
param.game.speed = 15;
param.game.hit_stop = true;
param.game.hit_stop_ms = 300;
precalculateZones();
void initParam() {
// GAME
param.game.width = 320;
param.game.height = 256;
param.game.item_size = 20;
param.game.game_area.rect = {0, 0, param.game.width, param.game.height};
param.game.play_area.rect = {0, 0, param.game.width, 216};
param.game.name_entry_idle_time = 10;
param.game.name_entry_total_time = 60;
param.game.speed = 15;
param.game.hit_stop = true;
param.game.hit_stop_ms = 300;
precalculateZones();
// SCOREBOARD
param.scoreboard.rect = {0, 216, param.game.width, 40};
param.scoreboard.separator_autocolor = false;
param.scoreboard.separator_color = Color();
param.scoreboard.easy_color = Color();
param.scoreboard.normal_color = Color();
param.scoreboard.hard_color = Color();
param.scoreboard.text_autocolor = false;
param.scoreboard.text_color1 = Color();
param.scoreboard.text_color2 = Color();
param.scoreboard.skip_countdown_value = 8;
// SCOREBOARD
param.scoreboard.rect = {0, 216, param.game.width, 40};
param.scoreboard.separator_autocolor = false;
param.scoreboard.separator_color = Color();
param.scoreboard.easy_color = Color();
param.scoreboard.normal_color = Color();
param.scoreboard.hard_color = Color();
param.scoreboard.text_autocolor = false;
param.scoreboard.text_color1 = Color();
param.scoreboard.text_color2 = Color();
param.scoreboard.skip_countdown_value = 8;
// FADE
param.fade.num_squares_width = param.game.width / 2;
param.fade.num_squares_height = param.game.height / 2;
param.fade.random_squares_delay = 1;
param.fade.random_squares_mult = 500;
param.fade.post_duration = 80;
param.fade.venetian_size = 16;
// FADE
param.fade.num_squares_width = param.game.width / 2;
param.fade.num_squares_height = param.game.height / 2;
param.fade.random_squares_delay = 1;
param.fade.random_squares_mult = 500;
param.fade.post_duration = 80;
param.fade.venetian_size = 16;
// TITLE
param.title.press_start_position = 160;
param.title.title_duration = 800;
param.title.arcade_edition_position = 123;
param.title.title_c_c_position = 11;
param.title.bg_color = Color(255, 255, 255);
// TITLE
param.title.press_start_position = 160;
param.title.title_duration = 800;
param.title.arcade_edition_position = 123;
param.title.title_c_c_position = 11;
param.title.bg_color = Color(255, 255, 255);
// BACKGROUND
param.background.attenuate_color = Color(255, 255, 255, 0);
// BACKGROUND
param.background.attenuate_color = Color(255, 255, 255, 0);
// BALLOONS
param.balloon.settings.at(0) = ParamBalloon::Settings(0.09f, 2.60f);
param.balloon.settings.at(1) = ParamBalloon::Settings(0.10f, 3.50f);
param.balloon.settings.at(2) = ParamBalloon::Settings(0.10f, 4.50f);
param.balloon.settings.at(3) = ParamBalloon::Settings(0.10f, 4.95f);
// BALLOONS
param.balloon.settings.at(0) = ParamBalloon::Settings(0.09f, 2.60f);
param.balloon.settings.at(1) = ParamBalloon::Settings(0.10f, 3.50f);
param.balloon.settings.at(2) = ParamBalloon::Settings(0.10f, 4.50f);
param.balloon.settings.at(3) = ParamBalloon::Settings(0.10f, 4.95f);
param.balloon.color.at(0) = "blue";
param.balloon.color.at(1) = "orange";
param.balloon.color.at(2) = "red";
param.balloon.color.at(3) = "green";
param.balloon.color.at(0) = "blue";
param.balloon.color.at(1) = "orange";
param.balloon.color.at(2) = "red";
param.balloon.color.at(3) = "green";
param.balloon.bouncing_sound = false;
param.balloon.bouncing_sound = false;
// NOTIFICATION
param.notification.pos_v = NotifyPosition::TOP;
param.notification.pos_h = NotifyPosition::LEFT;
param.notification.sound = false;
param.notification.color = Color(48, 48, 48);
// NOTIFICATION
param.notification.pos_v = NotifyPosition::TOP;
param.notification.pos_h = NotifyPosition::LEFT;
param.notification.sound = false;
param.notification.color = Color(48, 48, 48);
// INTRO
param.intro.bg_color = Color::fromHex("543149");
param.intro.card_color = Color::fromHex("CBDBFC");
param.intro.shadow_color = Color::fromHex("00000080");
param.intro.text_distance_from_bottom = 48;
// INTRO
param.intro.bg_color = Color::fromHex("543149");
param.intro.card_color = Color::fromHex("CBDBFC");
param.intro.shadow_color = Color::fromHex("00000080");
param.intro.text_distance_from_bottom = 48;
}
// Carga los parámetros desde un archivo
void loadParamsFromFile(const std::string &file_path)
{
// Inicializa los parámetros con valores por defecto
initParam();
void loadParamsFromFile(const std::string &file_path) {
// Inicializa los parámetros con valores por defecto
initParam();
// Abre el archivo
std::ifstream file(file_path);
if (!file.is_open())
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo abrir el archivo %s", file_path.c_str());
throw std::runtime_error("No se pudo abrir el archivo: " + file_path);
}
// Abre el archivo
std::ifstream file(file_path);
if (!file.is_open()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo abrir el archivo %s", file_path.c_str());
throw std::runtime_error("No se pudo abrir el archivo: " + file_path);
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(file_path).c_str());
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(file_path).c_str());
std::string line, param1, param2;
while (std::getline(file, line))
{
// Elimina comentarios
auto comment_pos = line.find('#');
if (comment_pos != std::string::npos)
{
line.resize(comment_pos);
}
std::string line, param1, param2;
while (std::getline(file, line)) {
// Elimina comentarios
auto comment_pos = line.find('#');
if (comment_pos != std::string::npos) {
line.resize(comment_pos);
}
// Usa un stream para separar palabras
std::istringstream iss(line);
if (iss >> param1 >> param2)
{
if (!setParams(param1, param2))
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Parámetro desconocido: %s", param1.c_str());
}
}
}
// Usa un stream para separar palabras
std::istringstream iss(line);
if (iss >> param1 >> param2) {
if (!setParams(param1, param2)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Parámetro desconocido: %s", param1.c_str());
}
}
}
// Cierra el archivo
file.close();
// Cierra el archivo
file.close();
// Realiza cálculos adicionales después de cargar los parámetros
precalculateZones();
// Realiza cálculos adicionales después de cargar los parámetros
precalculateZones();
}
// Asigna variables a partir de dos cadenas
bool setParams(const std::string &var, const std::string &value)
{
// Indicador de éxito en la asignación
auto success = true;
// GAME
if (var == "game.width")
{
param.game.width = std::stoi(value);
}
else if (var == "game.height")
{
param.game.height = std::stoi(value);
}
else if (var == "game.item_size")
{
param.game.item_size = std::stoi(value);
}
else if (var == "game.play_area.rect.x")
{
param.game.play_area.rect.x = std::stoi(value);
}
else if (var == "game.play_area.rect.y")
{
param.game.play_area.rect.y = std::stoi(value);
}
else if (var == "game.play_area.rect.w")
{
param.game.play_area.rect.w = std::stoi(value);
}
else if (var == "game.play_area.rect.h")
{
param.game.play_area.rect.h = std::stoi(value);
}
else if (var == "game.name_entry_idle_time")
{
param.game.name_entry_idle_time = std::stoi(value);
}
else if (var == "game.name_entry_total_time")
{
param.game.name_entry_total_time = std::stoi(value);
}
else if (var == "game.hit_stop")
{
param.game.hit_stop = stringToBool(value);
}
else if (var == "game.hit_stop_ms")
{
param.game.hit_stop_ms = std::stoi(value);
}
// FADE
else if (var == "fade.color")
{
param.fade.color = Color::fromHex(value);
}
else if (var == "fade.num_squares_width")
{
param.fade.num_squares_width = std::stoi(value);
}
else if (var == "fade.num_squares_height")
{
param.fade.num_squares_height = std::stoi(value);
}
else if (var == "fade.random_squares_delay")
{
param.fade.random_squares_delay = std::stoi(value);
}
else if (var == "fade.random_squares_mult")
{
param.fade.random_squares_mult = std::stoi(value);
}
else if (var == "fade.post_duration")
{
param.fade.post_duration = std::stoi(value);
}
else if (var == "fade.venetian_size")
{
param.fade.venetian_size = std::stoi(value);
}
// SCOREBOARD
else if (var == "scoreboard.rect.x")
{
param.scoreboard.rect.x = std::stoi(value);
}
else if (var == "scoreboard.rect.y")
{
param.scoreboard.rect.y = std::stoi(value);
}
else if (var == "scoreboard.rect.w")
{
param.scoreboard.rect.w = std::stoi(value);
}
else if (var == "scoreboard.rect.h")
{
param.scoreboard.rect.h = std::stoi(value);
}
else if (var == "scoreboard.separator_autocolor")
{
param.scoreboard.separator_autocolor = stringToBool(value);
}
else if (var == "scoreboard.separator_color")
{
param.scoreboard.separator_color = Color::fromHex(value);
}
else if (var == "scoreboard.easy_color")
{
param.scoreboard.easy_color = Color::fromHex(value);
}
else if (var == "scoreboard.normal_color")
{
param.scoreboard.normal_color = Color::fromHex(value);
}
else if (var == "scoreboard.hard_color")
{
param.scoreboard.hard_color = Color::fromHex(value);
}
else if (var == "scoreboard.text_autocolor")
{
param.scoreboard.text_autocolor = stringToBool(value);
}
else if (var == "scoreboard.text_color1")
{
param.scoreboard.text_color1 = Color::fromHex(value);
}
else if (var == "scoreboard.text_color2")
{
param.scoreboard.text_color2 = Color::fromHex(value);
}
else if (var == "scoreboard.skip_countdown_value")
{
param.scoreboard.skip_countdown_value = std::stoi(value);
}
// TITLE
else if (var == "title.press_start_position")
{
param.title.press_start_position = std::stoi(value);
}
else if (var == "title.title_duration")
{
param.title.title_duration = std::stoi(value);
}
else if (var == "title.arcade_edition_position")
{
param.title.arcade_edition_position = std::stoi(value);
}
else if (var == "title.title_c_c_position")
{
param.title.title_c_c_position = std::stoi(value);
}
else if (var == "title.bg_color")
{
param.title.bg_color = Color::fromHex(value);
}
// BACKGROUND
else if (var == "background.attenuate_color")
{
param.background.attenuate_color = Color::fromHex(value);
}
// BALLOON
else if (var == "balloon.settings[0].vel")
{
param.balloon.settings.at(0).vel = std::stof(value);
}
else if (var == "balloon.settings[0].grav")
{
param.balloon.settings.at(0).grav = std::stof(value);
}
else if (var == "balloon.settings[1].vel")
{
param.balloon.settings.at(1).vel = std::stof(value);
}
else if (var == "balloon.settings[1].grav")
{
param.balloon.settings.at(1).grav = std::stof(value);
}
else if (var == "balloon.settings[2].vel")
{
param.balloon.settings.at(2).vel = std::stof(value);
}
else if (var == "balloon.settings[2].grav")
{
param.balloon.settings.at(2).grav = std::stof(value);
}
else if (var == "balloon.settings[3].vel")
{
param.balloon.settings.at(3).vel = std::stof(value);
}
else if (var == "balloon.settings[3].grav")
{
param.balloon.settings.at(3).grav = std::stof(value);
}
else if (var == "balloon.color[0]")
{
param.balloon.color.at(0) = value;
}
else if (var == "balloon.color[1]")
{
param.balloon.color.at(1) = value;
}
else if (var == "balloon.color[2]")
{
param.balloon.color.at(2) = value;
}
else if (var == "balloon.color[3]")
{
param.balloon.color.at(3) = value;
}
else if (var == "balloon.bouncing_sound")
{
param.balloon.bouncing_sound = stringToBool(value);
}
// NOTIFICACIONES
else if (var == "notification.pos_h")
{
if (value == "LEFT")
{
param.notification.pos_h = NotifyPosition::LEFT;
}
else if (value == "MIDDLE")
{
param.notification.pos_h = NotifyPosition::MIDDLE;
}
else
{
param.notification.pos_h = NotifyPosition::RIGHT;
}
}
else if (var == "notification.pos_v")
{
param.notification.pos_v = value == "TOP" ? NotifyPosition::TOP : NotifyPosition::BOTTOM;
}
else if (var == "notification.sound")
{
param.notification.sound = stringToBool(value);
}
else if (var == "notification.color")
{
param.notification.color = Color::fromHex(value);
}
// SERVICE MENU
else if (var == "service_menu.title_color")
{
param.service_menu.title_color = Color::fromHex(value);
}
else if (var == "service_menu.text_color")
{
param.service_menu.text_color = Color::fromHex(value);
}
else if (var == "service_menu.selected_color")
{
param.service_menu.selected_color = Color::fromHex(value);
}
else if (var == "service_menu.bg_color")
{
param.service_menu.bg_color = Color::fromHex(value);
}
else if (var == "service_menu.drop_shadow")
{
param.service_menu.drop_shadow = stringToBool(value);
}
// INTRO
else if (var == "intro.bg_color")
{
param.intro.bg_color = Color::fromHex(value);
}
else if (var == "intro.card_color")
{
param.intro.card_color = Color::fromHex(value);
}
else if (var == "intro.shadow_color")
{
param.intro.shadow_color = Color::fromHex(value);
}
else if (var == "intro.text_distance_from_bottom")
{
param.intro.text_distance_from_bottom = std::stoi(value);
}
// DEBUG
else if (var == "debug.color")
{
param.debug.color = Color::fromHex(value);
}
// RESOURCE
else if (var == "resource.color")
{
param.resource.color = Color::fromHex(value);
}
// RESTO
else
{
success = false;
}
return success;
bool setParams(const std::string &var, const std::string &value) {
// Indicador de éxito en la asignación
auto success = true;
// GAME
if (var == "game.width") {
param.game.width = std::stoi(value);
}
else if (var == "game.height") {
param.game.height = std::stoi(value);
}
else if (var == "game.item_size") {
param.game.item_size = std::stoi(value);
}
else if (var == "game.play_area.rect.x") {
param.game.play_area.rect.x = std::stoi(value);
}
else if (var == "game.play_area.rect.y") {
param.game.play_area.rect.y = std::stoi(value);
}
else if (var == "game.play_area.rect.w") {
param.game.play_area.rect.w = std::stoi(value);
}
else if (var == "game.play_area.rect.h") {
param.game.play_area.rect.h = std::stoi(value);
}
else if (var == "game.name_entry_idle_time") {
param.game.name_entry_idle_time = std::stoi(value);
}
else if (var == "game.name_entry_total_time") {
param.game.name_entry_total_time = std::stoi(value);
}
else if (var == "game.hit_stop") {
param.game.hit_stop = stringToBool(value);
}
else if (var == "game.hit_stop_ms") {
param.game.hit_stop_ms = std::stoi(value);
}
// FADE
else if (var == "fade.color") {
param.fade.color = Color::fromHex(value);
}
else if (var == "fade.num_squares_width") {
param.fade.num_squares_width = std::stoi(value);
}
else if (var == "fade.num_squares_height") {
param.fade.num_squares_height = std::stoi(value);
}
else if (var == "fade.random_squares_delay") {
param.fade.random_squares_delay = std::stoi(value);
}
else if (var == "fade.random_squares_mult") {
param.fade.random_squares_mult = std::stoi(value);
}
else if (var == "fade.post_duration") {
param.fade.post_duration = std::stoi(value);
}
else if (var == "fade.venetian_size") {
param.fade.venetian_size = std::stoi(value);
}
// SCOREBOARD
else if (var == "scoreboard.rect.x") {
param.scoreboard.rect.x = std::stoi(value);
}
else if (var == "scoreboard.rect.y") {
param.scoreboard.rect.y = std::stoi(value);
}
else if (var == "scoreboard.rect.w") {
param.scoreboard.rect.w = std::stoi(value);
}
else if (var == "scoreboard.rect.h") {
param.scoreboard.rect.h = std::stoi(value);
}
else if (var == "scoreboard.separator_autocolor") {
param.scoreboard.separator_autocolor = stringToBool(value);
}
else if (var == "scoreboard.separator_color") {
param.scoreboard.separator_color = Color::fromHex(value);
}
else if (var == "scoreboard.easy_color") {
param.scoreboard.easy_color = Color::fromHex(value);
}
else if (var == "scoreboard.normal_color") {
param.scoreboard.normal_color = Color::fromHex(value);
}
else if (var == "scoreboard.hard_color") {
param.scoreboard.hard_color = Color::fromHex(value);
}
else if (var == "scoreboard.text_autocolor") {
param.scoreboard.text_autocolor = stringToBool(value);
}
else if (var == "scoreboard.text_color1") {
param.scoreboard.text_color1 = Color::fromHex(value);
}
else if (var == "scoreboard.text_color2") {
param.scoreboard.text_color2 = Color::fromHex(value);
}
else if (var == "scoreboard.skip_countdown_value") {
param.scoreboard.skip_countdown_value = std::stoi(value);
}
// TITLE
else if (var == "title.press_start_position") {
param.title.press_start_position = std::stoi(value);
}
else if (var == "title.title_duration") {
param.title.title_duration = std::stoi(value);
}
else if (var == "title.arcade_edition_position") {
param.title.arcade_edition_position = std::stoi(value);
}
else if (var == "title.title_c_c_position") {
param.title.title_c_c_position = std::stoi(value);
}
else if (var == "title.bg_color") {
param.title.bg_color = Color::fromHex(value);
}
// BACKGROUND
else if (var == "background.attenuate_color") {
param.background.attenuate_color = Color::fromHex(value);
}
// BALLOON
else if (var == "balloon.settings[0].vel") {
param.balloon.settings.at(0).vel = std::stof(value);
}
else if (var == "balloon.settings[0].grav") {
param.balloon.settings.at(0).grav = std::stof(value);
}
else if (var == "balloon.settings[1].vel") {
param.balloon.settings.at(1).vel = std::stof(value);
}
else if (var == "balloon.settings[1].grav") {
param.balloon.settings.at(1).grav = std::stof(value);
}
else if (var == "balloon.settings[2].vel") {
param.balloon.settings.at(2).vel = std::stof(value);
}
else if (var == "balloon.settings[2].grav") {
param.balloon.settings.at(2).grav = std::stof(value);
}
else if (var == "balloon.settings[3].vel") {
param.balloon.settings.at(3).vel = std::stof(value);
}
else if (var == "balloon.settings[3].grav") {
param.balloon.settings.at(3).grav = std::stof(value);
}
else if (var == "balloon.color[0]") {
param.balloon.color.at(0) = value;
}
else if (var == "balloon.color[1]") {
param.balloon.color.at(1) = value;
}
else if (var == "balloon.color[2]") {
param.balloon.color.at(2) = value;
}
else if (var == "balloon.color[3]") {
param.balloon.color.at(3) = value;
}
else if (var == "balloon.bouncing_sound") {
param.balloon.bouncing_sound = stringToBool(value);
}
// NOTIFICACIONES
else if (var == "notification.pos_h") {
if (value == "LEFT") {
param.notification.pos_h = NotifyPosition::LEFT;
} else if (value == "MIDDLE") {
param.notification.pos_h = NotifyPosition::MIDDLE;
} else {
param.notification.pos_h = NotifyPosition::RIGHT;
}
}
else if (var == "notification.pos_v") {
param.notification.pos_v = value == "TOP" ? NotifyPosition::TOP : NotifyPosition::BOTTOM;
}
else if (var == "notification.sound") {
param.notification.sound = stringToBool(value);
}
else if (var == "notification.color") {
param.notification.color = Color::fromHex(value);
}
// SERVICE MENU
else if (var == "service_menu.title_color") {
param.service_menu.title_color = Color::fromHex(value);
}
else if (var == "service_menu.text_color") {
param.service_menu.text_color = Color::fromHex(value);
}
else if (var == "service_menu.selected_color") {
param.service_menu.selected_color = Color::fromHex(value);
}
else if (var == "service_menu.bg_color") {
param.service_menu.bg_color = Color::fromHex(value);
}
else if (var == "service_menu.drop_shadow") {
param.service_menu.drop_shadow = stringToBool(value);
}
// INTRO
else if (var == "intro.bg_color") {
param.intro.bg_color = Color::fromHex(value);
}
else if (var == "intro.card_color") {
param.intro.card_color = Color::fromHex(value);
}
else if (var == "intro.shadow_color") {
param.intro.shadow_color = Color::fromHex(value);
}
else if (var == "intro.text_distance_from_bottom") {
param.intro.text_distance_from_bottom = std::stoi(value);
}
// DEBUG
else if (var == "debug.color") {
param.debug.color = Color::fromHex(value);
}
// RESOURCE
else if (var == "resource.color") {
param.resource.color = Color::fromHex(value);
}
// RESTO
else {
success = false;
}
return success;
}
// Calcula variables a partir de otras variables
void precalculateZones()
{
// playArea
param.game.play_area.center_x = param.game.play_area.rect.w / 2;
param.game.play_area.first_quarter_x = param.game.play_area.rect.w / 4;
param.game.play_area.third_quarter_x = param.game.play_area.rect.w / 4 * 3;
param.game.play_area.center_y = param.game.play_area.rect.h / 2;
param.game.play_area.first_quarter_y = param.game.play_area.rect.h / 4;
param.game.play_area.third_quarter_y = param.game.play_area.rect.h / 4 * 3;
void precalculateZones() {
// playArea
param.game.play_area.center_x = param.game.play_area.rect.w / 2;
param.game.play_area.first_quarter_x = param.game.play_area.rect.w / 4;
param.game.play_area.third_quarter_x = param.game.play_area.rect.w / 4 * 3;
param.game.play_area.center_y = param.game.play_area.rect.h / 2;
param.game.play_area.first_quarter_y = param.game.play_area.rect.h / 4;
param.game.play_area.third_quarter_y = param.game.play_area.rect.h / 4 * 3;
// gameArea
param.game.game_area.rect = {0, 0, param.game.width, param.game.height};
param.game.game_area.center_x = param.game.game_area.rect.w / 2;
param.game.game_area.first_quarter_x = param.game.game_area.rect.w / 4;
param.game.game_area.third_quarter_x = param.game.game_area.rect.w / 4 * 3;
param.game.game_area.center_y = param.game.game_area.rect.h / 2;
param.game.game_area.first_quarter_y = param.game.game_area.rect.h / 4;
param.game.game_area.third_quarter_y = param.game.game_area.rect.h / 4 * 3;
// gameArea
param.game.game_area.rect = {0, 0, param.game.width, param.game.height};
param.game.game_area.center_x = param.game.game_area.rect.w / 2;
param.game.game_area.first_quarter_x = param.game.game_area.rect.w / 4;
param.game.game_area.third_quarter_x = param.game.game_area.rect.w / 4 * 3;
param.game.game_area.center_y = param.game.game_area.rect.h / 2;
param.game.game_area.first_quarter_y = param.game.game_area.rect.h / 4;
param.game.game_area.third_quarter_y = param.game.game_area.rect.h / 4 * 3;
}

View File

@@ -1,61 +1,56 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint32, SDL_FRect
#include <array> // Para array
#include <string> // Para basic_string, string
#include <SDL3/SDL.h> // Para Uint32, SDL_FRect
#include "utils.h" // Para Color, NotifyPosition (ptr only), Zone
#include <array> // Para array
#include <string> // Para basic_string, string
#include "utils.h" // Para Color, NotifyPosition (ptr only), Zone
// --- Parámetros del juego ---
struct ParamGame
{
float width; // Ancho de la resolución nativa del juego
float height; // Alto de la resolución nativa del juego
float item_size; // Tamaño de los ítems del juego
Zone play_area; // Rectángulo con la posición de la zona de juego
Zone game_area; // Rectángulo con las dimensiones del juego
int name_entry_idle_time; // Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
int name_entry_total_time; // Segundos totales para introducir el nombre al finalizar la partida
Uint32 speed; // Velocidad a la que transcurre el juego
bool hit_stop; // Indica si debe haber un paro cuando el jugador es golpeado por un globo
Uint32 hit_stop_ms; // Cantidad de milisegundos que dura el hit_stop
struct ParamGame {
float width; // Ancho de la resolución nativa del juego
float height; // Alto de la resolución nativa del juego
float item_size; // Tamaño de los ítems del juego
Zone play_area; // Rectángulo con la posición de la zona de juego
Zone game_area; // Rectángulo con las dimensiones del juego
int name_entry_idle_time; // Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
int name_entry_total_time; // Segundos totales para introducir el nombre al finalizar la partida
Uint32 speed; // Velocidad a la que transcurre el juego
bool hit_stop; // Indica si debe haber un paro cuando el jugador es golpeado por un globo
Uint32 hit_stop_ms; // Cantidad de milisegundos que dura el hit_stop
};
// --- Parámetros del fade ---
struct ParamFade
{
Color color; // Color del fade
float num_squares_width; // Cantidad total de cuadraditos en horizontal para el FadeType::RANDOM_SQUARE
float num_squares_height; // Cantidad total de cuadraditos en vertical para el FadeType::RANDOM_SQUARE
int random_squares_delay; // Duración entre cada pintado de cuadrados
int random_squares_mult; // Cantidad de cuadrados que se pintarán cada vez
int post_duration; // Duración final del fade
float venetian_size; // Altura de los rectángulos para FadeType::VENETIAN
struct ParamFade {
Color color; // Color del fade
float num_squares_width; // Cantidad total de cuadraditos en horizontal para el FadeType::RANDOM_SQUARE
float num_squares_height; // Cantidad total de cuadraditos en vertical para el FadeType::RANDOM_SQUARE
int random_squares_delay; // Duración entre cada pintado de cuadrados
int random_squares_mult; // Cantidad de cuadrados que se pintarán cada vez
int post_duration; // Duración final del fade
float venetian_size; // Altura de los rectángulos para FadeType::VENETIAN
};
// --- Parámetros de la pantalla de título ---
struct ParamTitle
{
int press_start_position; // Posición del texto para empezar a jugar
int title_duration; // Tiempo de inactividad del título
int arcade_edition_position; // Posición del bitmap "Arcade Edition"
int title_c_c_position; // Posición del bitmap "Coffee Crisis"
Color bg_color; // Color para los tiles de fondo
struct ParamTitle {
int press_start_position; // Posición del texto para empezar a jugar
int title_duration; // Tiempo de inactividad del título
int arcade_edition_position; // Posición del bitmap "Arcade Edition"
int title_c_c_position; // Posición del bitmap "Coffee Crisis"
Color bg_color; // Color para los tiles de fondo
};
// --- Parámetros del fondo ---
struct ParamBackground
{
Color attenuate_color; // Color para atenuar el fondo
struct ParamBackground {
Color attenuate_color; // Color para atenuar el fondo
};
// --- Parámetros de los globos ---
struct ParamBalloon
{
struct Settings
{
float grav; // Aceleración en el eje Y. Modifica la velocidad
float vel; // Velocidad inicial al rebotar contra el suelo
struct ParamBalloon {
struct Settings {
float grav; // Aceleración en el eje Y. Modifica la velocidad
float vel; // Velocidad inicial al rebotar contra el suelo
// Constructor
explicit Settings(float grav_val = 0.0f, float vel_val = 0.0f)
@@ -64,36 +59,33 @@ struct ParamBalloon
std::array<Settings, 4> settings;
std::array<std::string, 4> color;
bool bouncing_sound; // Indica si los globos hacer sonido al rebotar
bool bouncing_sound; // Indica si los globos hacer sonido al rebotar
};
// --- Parámetros de las notificaciones ---
struct ParamNotification
{
NotifyPosition pos_h; // Ubicación horizontal de las notificaciones en pantalla
NotifyPosition pos_v; // Ubicación vertical de las notificaciones en pantalla
bool sound; // Indica si las notificaciones suenan
Color color; // Color de las notificaciones
struct ParamNotification {
NotifyPosition pos_h; // Ubicación horizontal de las notificaciones en pantalla
NotifyPosition pos_v; // Ubicación vertical de las notificaciones en pantalla
bool sound; // Indica si las notificaciones suenan
Color color; // Color de las notificaciones
};
// --- Parámetros del marcador ---
struct ParamScoreboard
{
SDL_FRect rect; // Posición y tamaño del marcador
bool separator_autocolor; // El separado establece su color de forma automatica
Color separator_color; // Color del separador si se pone de forma manual
Color easy_color; // Color del marcador segun la dificultad
Color normal_color; // Color del marcador segun la dificultad
Color hard_color; // Color del marcador segun la dificultad
bool text_autocolor; // El texto establece su color de forma automatica
Color text_color1; // Color del texto
Color text_color2; // Color del texto
int skip_countdown_value; // Valor a partir del cual se puede saltar la cuenta atras
struct ParamScoreboard {
SDL_FRect rect; // Posición y tamaño del marcador
bool separator_autocolor; // El separado establece su color de forma automatica
Color separator_color; // Color del separador si se pone de forma manual
Color easy_color; // Color del marcador segun la dificultad
Color normal_color; // Color del marcador segun la dificultad
Color hard_color; // Color del marcador segun la dificultad
bool text_autocolor; // El texto establece su color de forma automatica
Color text_color1; // Color del texto
Color text_color2; // Color del texto
int skip_countdown_value; // Valor a partir del cual se puede saltar la cuenta atras
};
// --- Parámetros del menú de servicio ---
struct ParamServiceMenu
{
struct ParamServiceMenu {
Color title_color;
Color text_color;
Color selected_color;
@@ -102,8 +94,7 @@ struct ParamServiceMenu
};
// --- Parámetros de la intro ---
struct ParamIntro
{
struct ParamIntro {
Color bg_color;
Color card_color;
Color shadow_color;
@@ -111,40 +102,36 @@ struct ParamIntro
};
// --- Parámetros para Debug ---
struct ParamDebug
{
struct ParamDebug {
Color color;
};
// --- Parámetros para Resource ---
struct ParamResource
{
struct ParamResource {
Color color;
};
// --- Estructura principal para almacenar todos los parámetros del juego ---
struct Param
{
ParamGame game; // Parámetros del juego
ParamFade fade; // Parámetros del fade
ParamScoreboard scoreboard; // Rectángulo del marcador
ParamTitle title; // Parámetros de la pantalla de título
ParamBackground background; // Parámetros del fondo
ParamBalloon balloon; // Parámetros de los globos
ParamNotification notification; // Parámetros de las notificaciones
ParamServiceMenu service_menu; // Parámetros del menú de servicio
ParamIntro intro; // Parámetros de la intro
ParamDebug debug; // Parámetros para Debug
ParamResource resource; // Parámetros para Resource
struct Param {
ParamGame game; // Parámetros del juego
ParamFade fade; // Parámetros del fade
ParamScoreboard scoreboard; // Rectángulo del marcador
ParamTitle title; // Parámetros de la pantalla de título
ParamBackground background; // Parámetros del fondo
ParamBalloon balloon; // Parámetros de los globos
ParamNotification notification; // Parámetros de las notificaciones
ParamServiceMenu service_menu; // Parámetros del menú de servicio
ParamIntro intro; // Parámetros de la intro
ParamDebug debug; // Parámetros para Debug
ParamResource resource; // Parámetros para Resource
// Constructor
Param()
: game(), fade(), scoreboard(), title(), background(), balloon(),
notification(), service_menu(), intro(), debug(), resource() {}
: game(), fade(), scoreboard(), title(), background(), balloon(), notification(), service_menu(), intro(), debug(), resource() {}
};
// --- Variable global con los parámetros del juego ---
extern Param param;
// --- Funciones globales ---
void loadParamsFromFile(const std::string &file_path); // Establece valores para los parámetros a partir de un fichero de texto
void loadParamsFromFile(const std::string &file_path); // Establece valores para los parámetros a partir de un fichero de texto

View File

@@ -1,169 +1,143 @@
// IWYU pragma: no_include <bits/std_abs.h>
#include "path_sprite.h"
#include <cmath> // Para abs
#include <functional> // Para function
#include <utility> // Para move
#include <cmath> // Para abs
#include <functional> // Para function
#include <utility> // Para move
// Devuelve un vector con los puntos que conforman la ruta
std::vector<SDL_FPoint> createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easingFunction)
{
std::vector<SDL_FPoint> v;
v.reserve(steps);
std::vector<SDL_FPoint> createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easingFunction) {
std::vector<SDL_FPoint> v;
v.reserve(steps);
for (int i = 0; i < steps; ++i)
{
double t = static_cast<double>(i) / (steps - 1);
double value = start + (end - start) * easingFunction(t);
for (int i = 0; i < steps; ++i) {
double t = static_cast<double>(i) / (steps - 1);
double value = start + (end - start) * easingFunction(t);
if ((start > 0 && end < 0) || (start < 0 && end > 0))
{
value = start + (end > 0 ? 1 : -1) * std::abs(end - start) * easingFunction(t);
}
if ((start > 0 && end < 0) || (start < 0 && end > 0)) {
value = start + (end > 0 ? 1 : -1) * std::abs(end - start) * easingFunction(t);
}
switch (type)
{
case PathType::HORIZONTAL:
v.emplace_back(SDL_FPoint{static_cast<float>(value), fixed_pos});
break;
case PathType::VERTICAL:
v.emplace_back(SDL_FPoint{fixed_pos, static_cast<float>(value)});
break;
default:
break;
}
}
switch (type) {
case PathType::HORIZONTAL:
v.emplace_back(SDL_FPoint{static_cast<float>(value), fixed_pos});
break;
case PathType::VERTICAL:
v.emplace_back(SDL_FPoint{fixed_pos, static_cast<float>(value)});
break;
default:
break;
}
}
return v;
return v;
}
// Actualiza la posición y comprueba si ha llegado a su destino
void PathSprite::update()
{
if (enabled_ && !has_finished_)
{
moveThroughCurrentPath();
goToNextPathOrDie();
}
void PathSprite::update() {
if (enabled_ && !has_finished_) {
moveThroughCurrentPath();
goToNextPathOrDie();
}
}
// Muestra el sprite por pantalla
void PathSprite::render()
{
if (enabled_)
{
Sprite::render();
}
void PathSprite::render() {
if (enabled_) {
Sprite::render();
}
}
// Añade un recorrido
void PathSprite::addPath(Path path, bool centered)
{
PathCentered path_centered = PathCentered::NONE;
if (centered)
path_centered = (path.spots.back().x == path.spots.front().x) ? PathCentered::ON_X : PathCentered::ON_Y;
void PathSprite::addPath(Path path, bool centered) {
PathCentered path_centered = PathCentered::NONE;
if (centered)
path_centered = (path.spots.back().x == path.spots.front().x) ? PathCentered::ON_X : PathCentered::ON_Y;
switch (path_centered)
{
case PathCentered::ON_X:
{
const int x = path.spots.back().x - pos_.w / 2;
for (auto &spot : path.spots)
spot.x = x;
paths_.emplace_back(path);
break;
}
case PathCentered::ON_Y:
{
const int y = path.spots.back().y - pos_.h / 2;
for (auto &spot : path.spots)
spot.y = y;
paths_.emplace_back(path);
break;
}
default:
paths_.emplace_back(path);
break;
}
switch (path_centered) {
case PathCentered::ON_X: {
const int x = path.spots.back().x - pos_.w / 2;
for (auto &spot : path.spots)
spot.x = x;
paths_.emplace_back(path);
break;
}
case PathCentered::ON_Y: {
const int y = path.spots.back().y - pos_.h / 2;
for (auto &spot : path.spots)
spot.y = y;
paths_.emplace_back(path);
break;
}
default:
paths_.emplace_back(path);
break;
}
}
// Añade un recorrido
void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easingFunction, int waiting_counter)
{
paths_.emplace_back(createPath(start, end, type, fixed_pos, steps, easingFunction), waiting_counter);
void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easingFunction, int waiting_counter) {
paths_.emplace_back(createPath(start, end, type, fixed_pos, steps, easingFunction), waiting_counter);
}
// Añade un recorrido
void PathSprite::addPath(std::vector<SDL_FPoint> spots, int waiting_counter)
{
paths_.emplace_back(std::move(spots), waiting_counter);
void PathSprite::addPath(std::vector<SDL_FPoint> spots, int waiting_counter) {
paths_.emplace_back(std::move(spots), waiting_counter);
}
// Habilita el objeto
void PathSprite::enable()
{
if (paths_.size() == 0 || enabled_)
{
return;
}
void PathSprite::enable() {
if (paths_.size() == 0 || enabled_) {
return;
}
enabled_ = true;
enabled_ = true;
// Establece la posición
auto &path = paths_.at(current_path_);
const auto &P = path.spots.at(path.counter);
setPosition(P);
// Establece la posición
auto &path = paths_.at(current_path_);
const auto &P = path.spots.at(path.counter);
setPosition(P);
}
// Coloca el sprite en los diferentes puntos del recorrido
void PathSprite::moveThroughCurrentPath()
{
auto &path = paths_.at(current_path_);
void PathSprite::moveThroughCurrentPath() {
auto &path = paths_.at(current_path_);
// Establece la posición
const auto &p = path.spots.at(path.counter);
setPosition(p);
// Establece la posición
const auto &p = path.spots.at(path.counter);
setPosition(p);
// Comprobar si ha terminado el recorrido
if (!path.on_destination)
{
++path.counter;
if (path.counter >= static_cast<int>(path.spots.size()))
{
path.on_destination = true;
path.counter = static_cast<int>(path.spots.size()) - 1;
}
}
// Comprobar si ha terminado el recorrido
if (!path.on_destination) {
++path.counter;
if (path.counter >= static_cast<int>(path.spots.size())) {
path.on_destination = true;
path.counter = static_cast<int>(path.spots.size()) - 1;
}
}
// Comprobar si ha terminado la espera
if (path.on_destination)
{
if (path.waiting_counter == 0)
{
path.finished = true;
}
else
{
--path.waiting_counter;
}
}
// Comprobar si ha terminado la espera
if (path.on_destination) {
if (path.waiting_counter == 0) {
path.finished = true;
} else {
--path.waiting_counter;
}
}
}
// Cambia de recorrido o finaliza
void PathSprite::goToNextPathOrDie()
{
// Comprueba si ha terminado el recorrdo actual
if (paths_.at(current_path_).finished)
{
++current_path_;
}
void PathSprite::goToNextPathOrDie() {
// Comprueba si ha terminado el recorrdo actual
if (paths_.at(current_path_).finished) {
++current_path_;
}
// Comprueba si quedan mas recorridos
if (current_path_ >= static_cast<int>(paths_.size()))
{
has_finished_ = true;
current_path_ = 0;
}
// Comprueba si quedan mas recorridos
if (current_path_ >= static_cast<int>(paths_.size())) {
has_finished_ = true;
current_path_ = 0;
}
}
// Indica si ha terminado todos los recorridos

View File

@@ -1,79 +1,76 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FPoint
#include <functional> // Para std::function
#include <memory> // Para shared_ptr
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_FPoint
#include "sprite.h" // Para Sprite
#include <functional> // Para std::function
#include <memory> // Para shared_ptr
#include <vector> // Para vector
#include "sprite.h" // Para Sprite
class Texture;
// --- Tipos de recorrido ---
enum class PathType
{
VERTICAL,
HORIZONTAL,
enum class PathType {
VERTICAL,
HORIZONTAL,
};
// --- Centrado del recorrido ---
enum class PathCentered
{
ON_X,
ON_Y,
NONE,
enum class PathCentered {
ON_X,
ON_Y,
NONE,
};
// --- Estructura Path: define un recorrido para el sprite ---
struct Path
{
std::vector<SDL_FPoint> spots; // Puntos por los que se desplazará el sprite
int waiting_counter; // Tiempo de espera una vez en el destino
bool on_destination = false; // Indica si ha llegado al destino
bool finished = false; // Indica si ha terminado de esperarse
int counter = 0; // Contador interno
struct Path {
std::vector<SDL_FPoint> spots; // Puntos por los que se desplazará el sprite
int waiting_counter; // Tiempo de espera una vez en el destino
bool on_destination = false; // Indica si ha llegado al destino
bool finished = false; // Indica si ha terminado de esperarse
int counter = 0; // Contador interno
// Constructor
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init)
: spots(spots_init), waiting_counter(waiting_counter_init) {}
// Constructor
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init)
: spots(spots_init), waiting_counter(waiting_counter_init) {}
};
// Devuelve un vector con los puntos que conforman la ruta
std::vector<SDL_FPoint> createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easingFunction);
// --- Clase PathSprite: Sprite que sigue uno o varios recorridos ---
class PathSprite : public Sprite
{
public:
// --- Constructor y destructor ---
explicit PathSprite(std::shared_ptr<Texture> texture)
: Sprite(texture) {}
~PathSprite() override = default;
class PathSprite : public Sprite {
public:
// --- Constructor y destructor ---
explicit PathSprite(std::shared_ptr<Texture> texture)
: Sprite(texture) {}
~PathSprite() override = default;
// --- Métodos principales ---
void update(); // Actualiza la posición del sprite según el recorrido
void render() override; // Muestra el sprite por pantalla
// --- Métodos principales ---
void update(); // Actualiza la posición del sprite según el recorrido
void render() override; // Muestra el sprite por pantalla
// --- Gestión de recorridos ---
void addPath(Path path, bool centered = false); // Añade un recorrido (Path)
void addPath(std::vector<SDL_FPoint> spots, int waiting_counter = 0); // Añade un recorrido a partir de puntos
void addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easingFunction, int waiting_counter = 0); // Añade un recorrido generado
// --- Gestión de recorridos ---
void addPath(Path path, bool centered = false); // Añade un recorrido (Path)
void addPath(std::vector<SDL_FPoint> spots, int waiting_counter = 0); // Añade un recorrido a partir de puntos
void addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easingFunction, int waiting_counter = 0); // Añade un recorrido generado
// --- Estado y control ---
void enable(); // Habilita el objeto
bool hasFinished(); // Indica si ha terminado todos los recorridos
// --- Estado y control ---
void enable(); // Habilita el objeto
bool hasFinished(); // Indica si ha terminado todos los recorridos
// --- Getters ---
int getCurrentPath() const { return current_path_; } // Devuelve el índice del recorrido actual
// --- Getters ---
int getCurrentPath() const { return current_path_; } // Devuelve el índice del recorrido actual
private:
// --- Variables internas ---
bool enabled_ = false; // Indica si el objeto está habilitado
bool has_finished_ = false; // Indica si el objeto ha finalizado el recorrido
int current_path_ = 0; // Recorrido que se está recorriendo actualmente
std::vector<Path> paths_; // Caminos a recorrer por el sprite
private:
// --- Variables internas ---
bool enabled_ = false; // Indica si el objeto está habilitado
bool has_finished_ = false; // Indica si el objeto ha finalizado el recorrido
int current_path_ = 0; // Recorrido que se está recorriendo actualmente
std::vector<Path> paths_; // Caminos a recorrer por el sprite
// --- Métodos internos ---
void moveThroughCurrentPath(); // Coloca el sprite en los diferentes puntos del recorrido
void goToNextPathOrDie(); // Cambia de recorrido o finaliza
// --- Métodos internos ---
void moveThroughCurrentPath(); // Coloca el sprite en los diferentes puntos del recorrido
void goToNextPathOrDie(); // Cambia de recorrido o finaliza
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,108 +1,107 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint32, SDL_FRect
#include <memory> // Para unique_ptr, shared_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para Uint32, SDL_FRect
#include "animated_sprite.h" // Para AnimatedSprite
#include "enter_name.h" // Para EnterName
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h" // Para SettingsOptions, settings
#include "utils.h" // Para Circle
#include <memory> // Para unique_ptr, shared_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include "animated_sprite.h" // Para AnimatedSprite
#include "enter_name.h" // Para EnterName
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h" // Para SettingsOptions, settings
#include "utils.h" // Para Circle
class Texture;
enum class InputAction : int;
enum class ScoreboardMode;
// --- Estados posibles del jugador ---
enum class PlayerState
{
enum class PlayerState {
// Estados de movimiento
WALKING_LEFT, // Caminando hacia la izquierda
WALKING_RIGHT, // Caminando hacia la derecha
WALKING_STOP, // Parado, sin moverse
WALKING_LEFT, // Caminando hacia la izquierda
WALKING_RIGHT, // Caminando hacia la derecha
WALKING_STOP, // Parado, sin moverse
// Estados de disparo
FIRING_UP, // Disparando hacia arriba
FIRING_LEFT, // Disparando hacia la izquierda
FIRING_RIGHT, // Disparando hacia la derecha
FIRING_NONE, // No está disparando
FIRING_UP, // Disparando hacia arriba
FIRING_LEFT, // Disparando hacia la izquierda
FIRING_RIGHT, // Disparando hacia la derecha
FIRING_NONE, // No está disparando
// Estados de retroceso tras disparar
RECOILING_UP, // Retroceso tras disparar hacia arriba
RECOILING_LEFT, // Retroceso tras disparar hacia la izquierda
RECOILING_RIGHT, // Retroceso tras disparar hacia la derecha
RECOILING_UP, // Retroceso tras disparar hacia arriba
RECOILING_LEFT, // Retroceso tras disparar hacia la izquierda
RECOILING_RIGHT, // Retroceso tras disparar hacia la derecha
// Estados de enfriamiento tras disparar
COOLING_UP, // Enfriando tras disparar hacia arriba
COOLING_LEFT, // Enfriando tras disparar hacia la izquierda
COOLING_RIGHT, // Enfriando tras disparar hacia la derecha
COOLING_UP, // Enfriando tras disparar hacia arriba
COOLING_LEFT, // Enfriando tras disparar hacia la izquierda
COOLING_RIGHT, // Enfriando tras disparar hacia la derecha
// Estados generales del jugador
PLAYING, // Está jugando activamente
CONTINUE, // Cuenta atrás para continuar tras perder
CONTINUE_TIME_OUT, // Se ha terminado la cuenta atras para continuar y se retira al jugador de la zona de juego
WAITING, // Esperando para entrar a jugar
ENTERING_NAME, // Introduciendo nombre para la tabla de puntuaciones
SHOWING_NAME, // Mostrando el nombre introducido
ROLLING, // El jugador está muriendo (animación de muerte)
LYING_ON_THE_FLOOR_FOREVER, // El jugador está inconsciente para siempre en el suelo (demo)
GAME_OVER, // Fin de la partida, no puede jugar
CELEBRATING, // Celebrando victoria (pose de victoria)
ENTERING_NAME_GAME_COMPLETED, // Introduciendo nombre tras completar el juego
LEAVING_SCREEN, // Saliendo de la pantalla (animación)
ENTERING_SCREEN, // Entrando a la pantalla (animación)
CREDITS, // Estado para mostrar los créditos del juego
TITLE_ANIMATION, // Animacion para el titulo
TITLE_HIDDEN, // Animacion para el titulo
RESPAWNING, // Tras continuar y volver al juego
PLAYING, // Está jugando activamente
CONTINUE, // Cuenta atrás para continuar tras perder
CONTINUE_TIME_OUT, // Se ha terminado la cuenta atras para continuar y se retira al jugador de la zona de juego
WAITING, // Esperando para entrar a jugar
ENTERING_NAME, // Introduciendo nombre para la tabla de puntuaciones
SHOWING_NAME, // Mostrando el nombre introducido
ROLLING, // El jugador está muriendo (animación de muerte)
LYING_ON_THE_FLOOR_FOREVER, // El jugador está inconsciente para siempre en el suelo (demo)
GAME_OVER, // Fin de la partida, no puede jugar
CELEBRATING, // Celebrando victoria (pose de victoria)
ENTERING_NAME_GAME_COMPLETED, // Introduciendo nombre tras completar el juego
LEAVING_SCREEN, // Saliendo de la pantalla (animación)
ENTERING_SCREEN, // Entrando a la pantalla (animación)
CREDITS, // Estado para mostrar los créditos del juego
TITLE_ANIMATION, // Animacion para el titulo
TITLE_HIDDEN, // Animacion para el titulo
RESPAWNING, // Tras continuar y volver al juego
};
// --- Clase Player ---
class Player
{
public:
class Player {
public:
// --- Constructor y destructor ---
Player(int id, float x, int y, bool demo, SDL_FRect &play_area, std::vector<std::shared_ptr<Texture>> texture, const std::vector<std::vector<std::string>> &animations);
~Player() = default;
// --- Inicialización y ciclo de vida ---
void init(); // Inicializa el jugador
void update(); // Actualiza estado, animación y contadores
void render(); // Dibuja el jugador en pantalla
void init(); // Inicializa el jugador
void update(); // Actualiza estado, animación y contadores
void render(); // Dibuja el jugador en pantalla
// --- Entrada y control ---
void setInput(InputAction input); // Procesa entrada general
void setInputPlaying(InputAction input); // Procesa entrada en modo jugando
void setInputEnteringName(InputAction input); // Procesa entrada al introducir nombre
void setInput(InputAction input); // Procesa entrada general
void setInputPlaying(InputAction input); // Procesa entrada en modo jugando
void setInputEnteringName(InputAction input); // Procesa entrada al introducir nombre
// --- Movimiento y animación ---
void move(); // Mueve el jugador
void setAnimation(); // Establece la animación según el estado
void move(); // Mueve el jugador
void setAnimation(); // Establece la animación según el estado
// --- Texturas y animaciones ---
void setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture); // Cambia las texturas del jugador
void setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture); // Cambia las texturas del jugador
// --- Estados y contadores ---
void updateCooldown(); // Actualiza el cooldown de disparo
void updateCooldown(); // Actualiza el cooldown de disparo
// --- Puntuación y marcador ---
void addScore(int score); // Añade puntos
void incScoreMultiplier(); // Incrementa el multiplicador
void decScoreMultiplier(); // Decrementa el multiplicador
void addScore(int score); // Añade puntos
void incScoreMultiplier(); // Incrementa el multiplicador
void decScoreMultiplier(); // Decrementa el multiplicador
// --- Estados de juego ---
void setPlayingState(PlayerState state); // Cambia el estado de juego
void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad
void setPowerUp(); // Activa el modo PowerUp
void updatePowerUp(); // Actualiza el valor de PowerUp
void giveExtraHit(); // Concede un toque extra al jugador
void removeExtraHit(); // Quita el toque extra al jugador
void decContinueCounter(); // Decrementa el contador de continuar
void setPlayingState(PlayerState state); // Cambia el estado de juego
void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad
void setPowerUp(); // Activa el modo PowerUp
void updatePowerUp(); // Actualiza el valor de PowerUp
void giveExtraHit(); // Concede un toque extra al jugador
void removeExtraHit(); // Quita el toque extra al jugador
void decContinueCounter(); // Decrementa el contador de continuar
// --- Getters y comprobaciones de estado ---
int getRecordNamePos() const; // Obtiene la posición que se está editando del nombre del jugador para la tabla de mejores puntuaciones
int getRecordNamePos() const; // Obtiene la posición que se está editando del nombre del jugador para la tabla de mejores puntuaciones
// Comprobación de playing_state
bool isLyingOnTheFloorForever() const { return playing_state_ == PlayerState::LYING_ON_THE_FLOOR_FOREVER; }
@@ -160,73 +159,73 @@ public:
void setWalkingState(PlayerState state) { walking_state_ = state; }
void addCredit() { ++credits_used_; }
private:
private:
// --- Constantes ---
static constexpr int POWERUP_COUNTER_ = 1500; // Duración del estado PowerUp
static constexpr int INVULNERABLE_COUNTER_ = 200; // Duración del estado invulnerable
static constexpr int WIDTH_ = 30; // Anchura
static constexpr int HEIGHT_ = 30; // Altura
static constexpr float BASE_SPEED_ = 1.5f; // Velocidad base del jugador
static constexpr int POWERUP_COUNTER_ = 1500; // Duración del estado PowerUp
static constexpr int INVULNERABLE_COUNTER_ = 200; // Duración del estado invulnerable
static constexpr int WIDTH_ = 30; // Anchura
static constexpr int HEIGHT_ = 30; // Altura
static constexpr float BASE_SPEED_ = 1.5f; // Velocidad base del jugador
static constexpr int COOLING_DURATION_ = 50;
static constexpr int COOLING_COMPLETE_ = 0;
// --- Objetos y punteros ---
std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador
std::unique_ptr<AnimatedSprite> power_sprite_; // Sprite para dibujar el aura del jugador con el poder a tope
std::unique_ptr<EnterName> enter_name_; // Clase utilizada para introducir el nombre
std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador
std::unique_ptr<AnimatedSprite> power_sprite_; // Sprite para dibujar el aura del jugador con el poder a tope
std::unique_ptr<EnterName> enter_name_; // Clase utilizada para introducir el nombre
// --- Variables de estado ---
int id_; // Número de identificación para el jugador. Player1 = 1, Player2 = 2
SDL_FRect play_area_; // Rectángulo con la zona de juego
float pos_x_ = 0.0f; // Posición en el eje X
int pos_y_ = 0; // Posición en el eje Y
float default_pos_x_; // Posición inicial para el jugador
int default_pos_y_; // Posición inicial para el jugador
float vel_x_ = 0.0f; // Cantidad de píxeles a desplazarse en el eje X
int vel_y_ = 0.0f; // Cantidad de píxeles a desplazarse en el eje Y
int cant_fire_counter_ = 0; // Contador durante el cual no puede disparar
int recoiling_state_counter_ = 0; // Contador para la animación del estado de retroceso
int recoiling_state_duration_ = 0; // Numero de frames que dura el estado de retroceso
int cooling_state_counter_ = 0; // Contador para la animación del estado cooling
int score_ = 0; // Puntos del jugador
float score_multiplier_ = 1.0f; // Multiplicador de puntos
PlayerState walking_state_ = PlayerState::WALKING_STOP; // Estado del jugador al moverse
PlayerState firing_state_ = PlayerState::FIRING_NONE; // Estado del jugador al disparar
PlayerState playing_state_ = PlayerState::WAITING; // Estado del jugador en el juego
bool invulnerable_ = true; // Indica si el jugador es invulnerable
int invulnerable_counter_ = INVULNERABLE_COUNTER_; // Contador para la invulnerabilidad
bool extra_hit_ = false; // Indica si el jugador tiene un toque extra
int coffees_ = 0; // Indica cuántos cafés lleva acumulados
bool power_up_ = false; // Indica si el jugador tiene activo el modo PowerUp
int power_up_counter_ = POWERUP_COUNTER_; // Temporizador para el modo PowerUp
int power_up_desp_x_ = 0; // Desplazamiento del sprite de PowerUp respecto al sprite del jugador
Circle collider_ = Circle(0, 0, 9); // Círculo de colisión del jugador
int continue_counter_ = 10; // Contador para poder continuar
Uint32 continue_ticks_ = 0; // Variable para poder cambiar el contador de continue en función del tiempo
int scoreboard_panel_ = 0; // Panel del marcador asociado al jugador
std::string name_; // Nombre del jugador
int controller_index_ = 0; // Índice del array de mandos que utilizará para moverse
bool demo_ = false; // Para que el jugador sepa si está en el modo demostración
int name_entry_idle_counter_ = 0; // Contador para poner nombre
int name_entry_total_counter_ = 0; // Segundos totales que lleva acumulados poniendo nombre
Uint32 name_entry_ticks_ = 0; // Variable para poder cambiar el contador de poner nombre en función del tiempo
Uint32 showing_name_ticks_ = 0; // Tiempo en el que se entra al estado SHOWING_NAME
int step_counter_ = 0; // Cuenta los pasos para los estados en los que camina automáticamente
bool game_completed_ = false; // Indica si ha completado el juego
int credits_used_ = 1; // Indica el número de veces que ha continuado
std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones
int id_; // Número de identificación para el jugador. Player1 = 1, Player2 = 2
SDL_FRect play_area_; // Rectángulo con la zona de juego
float pos_x_ = 0.0f; // Posición en el eje X
int pos_y_ = 0; // Posición en el eje Y
float default_pos_x_; // Posición inicial para el jugador
int default_pos_y_; // Posición inicial para el jugador
float vel_x_ = 0.0f; // Cantidad de píxeles a desplazarse en el eje X
int vel_y_ = 0.0f; // Cantidad de píxeles a desplazarse en el eje Y
int cant_fire_counter_ = 0; // Contador durante el cual no puede disparar
int recoiling_state_counter_ = 0; // Contador para la animación del estado de retroceso
int recoiling_state_duration_ = 0; // Numero de frames que dura el estado de retroceso
int cooling_state_counter_ = 0; // Contador para la animación del estado cooling
int score_ = 0; // Puntos del jugador
float score_multiplier_ = 1.0f; // Multiplicador de puntos
PlayerState walking_state_ = PlayerState::WALKING_STOP; // Estado del jugador al moverse
PlayerState firing_state_ = PlayerState::FIRING_NONE; // Estado del jugador al disparar
PlayerState playing_state_ = PlayerState::WAITING; // Estado del jugador en el juego
bool invulnerable_ = true; // Indica si el jugador es invulnerable
int invulnerable_counter_ = INVULNERABLE_COUNTER_; // Contador para la invulnerabilidad
bool extra_hit_ = false; // Indica si el jugador tiene un toque extra
int coffees_ = 0; // Indica cuántos cafés lleva acumulados
bool power_up_ = false; // Indica si el jugador tiene activo el modo PowerUp
int power_up_counter_ = POWERUP_COUNTER_; // Temporizador para el modo PowerUp
int power_up_desp_x_ = 0; // Desplazamiento del sprite de PowerUp respecto al sprite del jugador
Circle collider_ = Circle(0, 0, 9); // Círculo de colisión del jugador
int continue_counter_ = 10; // Contador para poder continuar
Uint32 continue_ticks_ = 0; // Variable para poder cambiar el contador de continue en función del tiempo
int scoreboard_panel_ = 0; // Panel del marcador asociado al jugador
std::string name_; // Nombre del jugador
int controller_index_ = 0; // Índice del array de mandos que utilizará para moverse
bool demo_ = false; // Para que el jugador sepa si está en el modo demostración
int name_entry_idle_counter_ = 0; // Contador para poner nombre
int name_entry_total_counter_ = 0; // Segundos totales que lleva acumulados poniendo nombre
Uint32 name_entry_ticks_ = 0; // Variable para poder cambiar el contador de poner nombre en función del tiempo
Uint32 showing_name_ticks_ = 0; // Tiempo en el que se entra al estado SHOWING_NAME
int step_counter_ = 0; // Cuenta los pasos para los estados en los que camina automáticamente
bool game_completed_ = false; // Indica si ha completado el juego
int credits_used_ = 1; // Indica el número de veces que ha continuado
std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones
// --- Métodos internos ---
void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador
void shiftSprite(); // Recoloca el sprite
void updateInvulnerable(); // Monitoriza el estado de invulnerabilidad
void updateContinueCounter(); // Actualiza el contador de continue
void updateEnterNameCounter(); // Actualiza el contador de entrar nombre
void updateShowingName(); // Actualiza el estado SHOWING_NAME
void decNameEntryCounter(); // Decrementa el contador de entrar nombre
void updateScoreboard(); // Actualiza el panel del marcador
void setScoreboardMode(ScoreboardMode mode); // Cambia el modo del marcador
void playSound(const std::string &name); // Hace sonar un sonido
void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador
void shiftSprite(); // Recoloca el sprite
void updateInvulnerable(); // Monitoriza el estado de invulnerabilidad
void updateContinueCounter(); // Actualiza el contador de continue
void updateEnterNameCounter(); // Actualiza el contador de entrar nombre
void updateShowingName(); // Actualiza el estado SHOWING_NAME
void decNameEntryCounter(); // Decrementa el contador de entrar nombre
void updateScoreboard(); // Actualiza el panel del marcador
void setScoreboardMode(ScoreboardMode mode); // Cambia el modo del marcador
void playSound(const std::string &name); // Hace sonar un sonido
bool isRenderable() const { return !isWaiting() && !isGameOver() && !isTitleHidden(); }
void addScoreToScoreBoard(); // Añade una puntuación a la tabla de records
void addScoreToScoreBoard(); // Añade una puntuación a la tabla de records
};

View File

@@ -1,20 +1,21 @@
#include "resource.h"
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_L...
#include <stdlib.h> // Para exit
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_L...
#include <stdlib.h> // Para exit
#include <algorithm> // Para find_if
#include <array> // Para array
#include <stdexcept> // Para runtime_error
#include "asset.h" // Para Asset, AssetType
#include "external/jail_audio.h" // Para JA_DeleteMusic, JA_DeleteSound, JA_...
#include "lang.h" // Para getText
#include "param.h" // Para Param, param, ParamResource, ParamGame
#include "screen.h" // Para Screen
#include "text.h" // Para Text, loadTextFile, TextFile (ptr o...
#include "asset.h" // Para Asset, AssetType
#include "external/jail_audio.h" // Para JA_DeleteMusic, JA_DeleteSound, JA_...
#include "lang.h" // Para getText
#include "param.h" // Para Param, param, ParamResource, ParamGame
#include "screen.h" // Para Screen
#include "text.h" // Para Text, loadTextFile, TextFile (ptr o...
struct JA_Music_t; // lines 11-11
struct JA_Sound_t; // lines 12-12
struct JA_Music_t; // lines 11-11
struct JA_Sound_t; // lines 12-12
// Singleton
Resource *Resource::instance_ = nullptr;
@@ -35,8 +36,7 @@ Resource::Resource() : loading_text_(Screen::get()->getText()) { load(); }
Resource::~Resource() { clear(); }
// Vacia todos los vectores de recursos
void Resource::clear()
{
void Resource::clear() {
clearSounds();
clearMusics();
textures_.clear();
@@ -47,8 +47,7 @@ void Resource::clear()
}
// Carga todos los recursos del juego y muestra el progreso de carga
void Resource::load()
{
void Resource::load() {
// Prepara la gestión del progreso de carga
calculateTotalResources();
initProgressBar();
@@ -59,15 +58,15 @@ void Resource::load()
screen->setVSync(false);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** LOADING RESOURCES");
loadSounds(); // Carga sonidos
loadMusics(); // Carga músicas
loadTextures(); // Carga texturas
loadTextFiles(); // Carga ficheros de texto
loadAnimations(); // Carga animaciones
loadDemoData(); // Carga datos de demo
addPalettes(); // Añade paletas a las texturas
createText(); // Crea objetos de texto
createTextures(); // Crea texturas a partir de texto
loadSounds(); // Carga sonidos
loadMusics(); // Carga músicas
loadTextures(); // Carga texturas
loadTextFiles(); // Carga ficheros de texto
loadAnimations(); // Carga animaciones
loadDemoData(); // Carga datos de demo
addPalettes(); // Añade paletas a las texturas
createText(); // Crea objetos de texto
createTextures(); // Crea texturas a partir de texto
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** RESOURCES LOADED");
// Restablece el sincronismo vertical a su valor original
@@ -75,28 +74,23 @@ void Resource::load()
}
// Recarga todos los recursos (limpia y vuelve a cargar)
void Resource::reload()
{
void Resource::reload() {
clear();
load();
}
// Recarga solo las texturas y paletas
void Resource::reloadTextures()
{
void Resource::reloadTextures() {
loadTextures();
addPalettes();
createTextures();
}
// Obtiene el sonido a partir de un nombre. Lanza excepción si no existe.
JA_Sound_t *Resource::getSound(const std::string &name)
{
auto it = std::find_if(sounds_.begin(), sounds_.end(), [&name](const auto &s)
{ return s.name == name; });
JA_Sound_t *Resource::getSound(const std::string &name) {
auto it = std::find_if(sounds_.begin(), sounds_.end(), [&name](const auto &s) { return s.name == name; });
if (it != sounds_.end())
{
if (it != sounds_.end()) {
return it->sound;
}
@@ -105,13 +99,10 @@ JA_Sound_t *Resource::getSound(const std::string &name)
}
// Obtiene la música a partir de un nombre. Lanza excepción si no existe.
JA_Music_t *Resource::getMusic(const std::string &name)
{
auto it = std::find_if(musics_.begin(), musics_.end(), [&name](const auto &m)
{ return m.name == name; });
JA_Music_t *Resource::getMusic(const std::string &name) {
auto it = std::find_if(musics_.begin(), musics_.end(), [&name](const auto &m) { return m.name == name; });
if (it != musics_.end())
{
if (it != musics_.end()) {
return it->music;
}
@@ -120,13 +111,10 @@ JA_Music_t *Resource::getMusic(const std::string &name)
}
// Obtiene la textura a partir de un nombre. Lanza excepción si no existe.
std::shared_ptr<Texture> Resource::getTexture(const std::string &name)
{
auto it = std::find_if(textures_.begin(), textures_.end(), [&name](const auto &t)
{ return t.name == name; });
std::shared_ptr<Texture> Resource::getTexture(const std::string &name) {
auto it = std::find_if(textures_.begin(), textures_.end(), [&name](const auto &t) { return t.name == name; });
if (it != textures_.end())
{
if (it != textures_.end()) {
return it->texture;
}
@@ -135,13 +123,10 @@ std::shared_ptr<Texture> Resource::getTexture(const std::string &name)
}
// Obtiene el fichero de texto a partir de un nombre. Lanza excepción si no existe.
std::shared_ptr<TextFile> Resource::getTextFile(const std::string &name)
{
auto it = std::find_if(text_files_.begin(), text_files_.end(), [&name](const auto &t)
{ return t.name == name; });
std::shared_ptr<TextFile> Resource::getTextFile(const std::string &name) {
auto it = std::find_if(text_files_.begin(), text_files_.end(), [&name](const auto &t) { return t.name == name; });
if (it != text_files_.end())
{
if (it != text_files_.end()) {
return it->text_file;
}
@@ -150,13 +135,10 @@ std::shared_ptr<TextFile> Resource::getTextFile(const std::string &name)
}
// Obtiene el objeto de texto a partir de un nombre. Lanza excepción si no existe.
std::shared_ptr<Text> Resource::getText(const std::string &name)
{
auto it = std::find_if(texts_.begin(), texts_.end(), [&name](const auto &t)
{ return t.name == name; });
std::shared_ptr<Text> Resource::getText(const std::string &name) {
auto it = std::find_if(texts_.begin(), texts_.end(), [&name](const auto &t) { return t.name == name; });
if (it != texts_.end())
{
if (it != texts_.end()) {
return it->text;
}
@@ -165,13 +147,10 @@ std::shared_ptr<Text> Resource::getText(const std::string &name)
}
// Obtiene la animación a partir de un nombre. Lanza excepción si no existe.
AnimationsFileBuffer &Resource::getAnimation(const std::string &name)
{
auto it = std::find_if(animations_.begin(), animations_.end(), [&name](const auto &a)
{ return a.name == name; });
AnimationsFileBuffer &Resource::getAnimation(const std::string &name) {
auto it = std::find_if(animations_.begin(), animations_.end(), [&name](const auto &a) { return a.name == name; });
if (it != animations_.end())
{
if (it != animations_.end()) {
return it->animation;
}
@@ -180,20 +159,17 @@ AnimationsFileBuffer &Resource::getAnimation(const std::string &name)
}
// Obtiene el fichero con los datos para el modo demostración a partir de un índice
DemoData &Resource::getDemoData(int index)
{
DemoData &Resource::getDemoData(int index) {
return demos_.at(index);
}
// Carga los sonidos del juego
void Resource::loadSounds()
{
void Resource::loadSounds() {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> SOUND FILES");
auto list = Asset::get()->getListByType(AssetType::SOUND);
sounds_.clear();
for (const auto &l : list)
{
for (const auto &l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
sounds_.emplace_back(Resource::ResourceSound(name, JA_LoadSound(l.c_str())));
@@ -202,14 +178,12 @@ void Resource::loadSounds()
}
// Carga las músicas del juego
void Resource::loadMusics()
{
void Resource::loadMusics() {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> MUSIC FILES");
auto list = Asset::get()->getListByType(AssetType::MUSIC);
musics_.clear();
for (const auto &l : list)
{
for (const auto &l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
musics_.emplace_back(Resource::ResourceMusic(name, JA_LoadMusic(l.c_str())));
@@ -218,14 +192,12 @@ void Resource::loadMusics()
}
// Carga las texturas del juego
void Resource::loadTextures()
{
void Resource::loadTextures() {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> TEXTURES");
auto list = Asset::get()->getListByType(AssetType::BITMAP);
textures_.clear();
for (const auto &l : list)
{
for (const auto &l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
textures_.emplace_back(Resource::ResourceTexture(name, std::make_shared<Texture>(Screen::get()->getRenderer(), l)));
@@ -233,14 +205,12 @@ void Resource::loadTextures()
}
// Carga los ficheros de texto del juego
void Resource::loadTextFiles()
{
void Resource::loadTextFiles() {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> TEXT FILES");
auto list = Asset::get()->getListByType(AssetType::FONT);
text_files_.clear();
for (const auto &l : list)
{
for (const auto &l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
text_files_.emplace_back(Resource::ResourceTextFile(name, loadTextFile(l)));
@@ -248,14 +218,12 @@ void Resource::loadTextFiles()
}
// Carga las animaciones del juego
void Resource::loadAnimations()
{
void Resource::loadAnimations() {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> ANIMATIONS");
auto list = Asset::get()->getListByType(AssetType::ANIMATION);
animations_.clear();
for (const auto &l : list)
{
for (const auto &l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
animations_.emplace_back(Resource::ResourceAnimation(name, loadAnimationsFromFile(l)));
@@ -263,22 +231,19 @@ void Resource::loadAnimations()
}
// Carga los datos para el modo demostración
void Resource::loadDemoData()
{
void Resource::loadDemoData() {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> DEMO FILES");
constexpr std::array<const char *, 2> demo_files = {"demo1.bin", "demo2.bin"};
for (const auto &file : demo_files)
{
for (const auto &file : demo_files) {
updateLoadingProgress(file);
demos_.emplace_back(loadDemoDataFromFile(Asset::get()->get(file)));
}
}
// Añade paletas de colores a las texturas principales
void Resource::addPalettes()
{
void Resource::addPalettes() {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> PALETTES");
// Paletas para el jugador 1
@@ -293,10 +258,8 @@ void Resource::addPalettes()
}
// Crea texturas a partir de textos para mostrar puntuaciones y mensajes
void Resource::createTextures()
{
struct NameAndText
{
void Resource::createTextures() {
struct NameAndText {
std::string name;
std::string text;
@@ -317,8 +280,7 @@ void Resource::createTextures()
{"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}};
auto text = getText("04b_25");
for (const auto &s : strings)
{
for (const auto &s : strings) {
textures_.emplace_back(Resource::ResourceTexture(s.name, text->writeToTexture(s.text, 1, -2)));
printWithDots("Texture : ", s.name, "[ DONE ]");
}
@@ -332,18 +294,15 @@ void Resource::createTextures()
{"game_text_game_over", "Game Over"}};
auto text2 = getText("04b_25_2x");
for (const auto &s : strings2X)
{
for (const auto &s : strings2X) {
textures_.emplace_back(Resource::ResourceTexture(s.name, text2->writeToTexture(s.text, 1, -4)));
printWithDots("Texture : ", s.name, "[ DONE ]");
}
}
// Crea los objetos de texto a partir de los archivos de textura y texto
void Resource::createText()
{
struct ResourceInfo
{
void Resource::createText() {
struct ResourceInfo {
std::string key;
std::string textureFile;
std::string textFile;
@@ -368,22 +327,16 @@ void Resource::createText()
{"smb2", "smb2.png", "smb2.txt"},
{"smb2_grad", "smb2_grad.png", "smb2.txt"}};
for (const auto &resource : resources)
{
texts_.emplace_back(Resource::ResourceText(resource.key, std::make_shared<Text>(
getTexture(resource.textureFile),
getTextFile(resource.textFile))));
for (const auto &resource : resources) {
texts_.emplace_back(Resource::ResourceText(resource.key, std::make_shared<Text>(getTexture(resource.textureFile), getTextFile(resource.textFile))));
printWithDots("Text : ", resource.key, "[ DONE ]");
}
}
// Vacía el vector de sonidos y libera la memoria asociada
void Resource::clearSounds()
{
for (auto &sound : sounds_)
{
if (sound.sound)
{
void Resource::clearSounds() {
for (auto &sound : sounds_) {
if (sound.sound) {
JA_DeleteSound(sound.sound);
sound.sound = nullptr;
}
@@ -392,12 +345,9 @@ void Resource::clearSounds()
}
// Vacía el vector de músicas y libera la memoria asociada
void Resource::clearMusics()
{
for (auto &music : musics_)
{
if (music.music)
{
void Resource::clearMusics() {
for (auto &music : musics_) {
if (music.music) {
JA_DeleteMusic(music.music);
music.music = nullptr;
}
@@ -406,8 +356,7 @@ void Resource::clearMusics()
}
// Calcula el número total de recursos a cargar y reinicia el contador de carga
void Resource::calculateTotalResources()
{
void Resource::calculateTotalResources() {
const std::array<AssetType, 6> ASSET_TYPES = {
AssetType::SOUND,
AssetType::MUSIC,
@@ -417,8 +366,7 @@ void Resource::calculateTotalResources()
AssetType::DEMODATA};
size_t total = 0;
for (const auto &asset_type : ASSET_TYPES)
{
for (const auto &asset_type : ASSET_TYPES) {
auto list = Asset::get()->getListByType(asset_type);
total += list.size();
}
@@ -427,8 +375,7 @@ void Resource::calculateTotalResources()
}
// Muestra el progreso de carga en pantalla (barra y texto)
void Resource::renderProgress()
{
void Resource::renderProgress() {
// Obtiene la pantalla y el renderer
auto screen = Screen::get();
auto renderer = screen->getRenderer();
@@ -462,29 +409,24 @@ void Resource::renderProgress()
}
// Comprueba los eventos durante la carga (permite salir con ESC o cerrar ventana)
void Resource::checkEvents()
{
void Resource::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_EVENT_QUIT:
exit(0);
break;
case SDL_EVENT_KEY_DOWN:
if (event.key.key == SDLK_ESCAPE)
{
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_EVENT_QUIT:
exit(0);
}
break;
break;
case SDL_EVENT_KEY_DOWN:
if (event.key.key == SDLK_ESCAPE) {
exit(0);
}
break;
}
}
}
// Actualiza el progreso de carga, muestra la barra y procesa eventos
void Resource::updateLoadingProgress(std::string name)
{
void Resource::updateLoadingProgress(std::string name) {
loading_resource_name_ = name;
loading_count_.increase();
updateProgressBar();
@@ -493,8 +435,7 @@ void Resource::updateLoadingProgress(std::string name)
}
// Inicializa los rectangulos que definen la barra de progreso
void Resource::initProgressBar()
{
void Resource::initProgressBar() {
constexpr float X_PADDING = 20.0f;
constexpr float Y_PADDING = 20.0f;
constexpr float BAR_HEIGHT = 10.0f;
@@ -508,7 +449,6 @@ void Resource::initProgressBar()
}
// Actualiza la barra de estado
void Resource::updateProgressBar()
{
void Resource::updateProgressBar() {
loading_full_rect_.w = loading_wired_rect_.w * loading_count_.getPercentage();
}

View File

@@ -1,157 +1,149 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect
#include <stddef.h> // Para size_t
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_FRect
#include <stddef.h> // Para size_t
#include "animated_sprite.h" // Para AnimationsFileBuffer
#include "text.h" // Para Text, TextFile
#include "texture.h" // Para Texture
#include "utils.h" // Para DemoData
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include "animated_sprite.h" // Para AnimationsFileBuffer
#include "text.h" // Para Text, TextFile
#include "texture.h" // Para Texture
#include "utils.h" // Para DemoData
struct JA_Music_t;
struct JA_Sound_t;
// --- Clase Resource: gestiona todos los recursos del juego (singleton) ---
class Resource
{
public:
class Resource {
public:
// --- Métodos de singleton ---
static void init(); // Inicializa el objeto Resource
static void destroy(); // Libera el objeto Resource
static Resource *get(); // Obtiene el puntero al objeto Resource
static void init(); // Inicializa el objeto Resource
static void destroy(); // Libera el objeto Resource
static Resource *get(); // Obtiene el puntero al objeto Resource
// --- Métodos de acceso a recursos ---
JA_Sound_t *getSound(const std::string &name); // Obtiene el sonido por nombre
JA_Music_t *getMusic(const std::string &name); // Obtiene la música por nombre
std::shared_ptr<Texture> getTexture(const std::string &name); // Obtiene la textura por nombre
std::shared_ptr<TextFile> getTextFile(const std::string &name); // Obtiene el fichero de texto por nombre
std::shared_ptr<Text> getText(const std::string &name); // Obtiene el objeto de texto por nombre
AnimationsFileBuffer &getAnimation(const std::string &name); // Obtiene la animación por nombre
DemoData &getDemoData(int index); // Obtiene los datos de demo por índice
JA_Sound_t *getSound(const std::string &name); // Obtiene el sonido por nombre
JA_Music_t *getMusic(const std::string &name); // Obtiene la música por nombre
std::shared_ptr<Texture> getTexture(const std::string &name); // Obtiene la textura por nombre
std::shared_ptr<TextFile> getTextFile(const std::string &name); // Obtiene el fichero de texto por nombre
std::shared_ptr<Text> getText(const std::string &name); // Obtiene el objeto de texto por nombre
AnimationsFileBuffer &getAnimation(const std::string &name); // Obtiene la animación por nombre
DemoData &getDemoData(int index); // Obtiene los datos de demo por índice
// --- Métodos de recarga de recursos ---
void reload(); // Recarga todos los recursos
void reloadTextures(); // Recarga solo las texturas
void reload(); // Recarga todos los recursos
void reloadTextures(); // Recarga solo las texturas
private:
private:
// --- Estructuras para recursos individuales ---
struct ResourceSound
{
std::string name; // Nombre del sonido
JA_Sound_t *sound; // Objeto con el sonido
struct ResourceSound {
std::string name; // Nombre del sonido
JA_Sound_t *sound; // Objeto con el sonido
ResourceSound(const std::string &name, JA_Sound_t *sound)
: name(name), sound(sound) {}
};
struct ResourceMusic
{
std::string name; // Nombre de la música
JA_Music_t *music; // Objeto con la música
struct ResourceMusic {
std::string name; // Nombre de la música
JA_Music_t *music; // Objeto con la música
ResourceMusic(const std::string &name, JA_Music_t *music)
: name(name), music(music) {}
};
struct ResourceTexture
{
std::string name; // Nombre de la textura
std::shared_ptr<Texture> texture; // Objeto con la textura
struct ResourceTexture {
std::string name; // Nombre de la textura
std::shared_ptr<Texture> texture; // Objeto con la textura
ResourceTexture(const std::string &name, std::shared_ptr<Texture> texture)
: name(name), texture(texture) {}
};
struct ResourceTextFile
{
std::string name; // Nombre del fichero
std::shared_ptr<TextFile> text_file; // Objeto con los descriptores de la fuente de texto
struct ResourceTextFile {
std::string name; // Nombre del fichero
std::shared_ptr<TextFile> text_file; // Objeto con los descriptores de la fuente de texto
ResourceTextFile(const std::string &name, std::shared_ptr<TextFile> text_file)
: name(name), text_file(text_file) {}
};
struct ResourceText
{
std::string name; // Nombre del objeto
std::shared_ptr<Text> text; // Objeto de texto
struct ResourceText {
std::string name; // Nombre del objeto
std::shared_ptr<Text> text; // Objeto de texto
ResourceText(const std::string &name, std::shared_ptr<Text> text)
: name(name), text(text) {}
};
struct ResourceAnimation
{
std::string name; // Nombre de la animación
AnimationsFileBuffer animation; // Objeto con las animaciones
struct ResourceAnimation {
std::string name; // Nombre de la animación
AnimationsFileBuffer animation; // Objeto con las animaciones
ResourceAnimation(const std::string &name, const AnimationsFileBuffer &animation)
: name(name), animation(animation) {}
};
// --- Estructura para el progreso de carga ---
struct ResourceCount
{
size_t total; // Número total de recursos
size_t loaded; // Número de recursos cargados
struct ResourceCount {
size_t total; // Número total de recursos
size_t loaded; // Número de recursos cargados
ResourceCount() : total(0), loaded(0) {}
ResourceCount(size_t total) : total(total), loaded(0) {}
void add(size_t amount) { loaded += amount; }
void increase() { loaded++; }
float getPercentage() const
{
float getPercentage() const {
return total > 0 ? static_cast<float>(loaded) / static_cast<float>(total) : 0.0f;
}
};
// --- Instancia singleton ---
static Resource *instance_; // Instancia única de Resource
static Resource *instance_; // Instancia única de Resource
// --- Vectores de recursos ---
std::vector<ResourceSound> sounds_; // Vector con los sonidos
std::vector<ResourceMusic> musics_; // Vector con las músicas
std::vector<ResourceTexture> textures_; // Vector con las texturas
std::vector<ResourceTextFile> text_files_; // Vector con los ficheros de texto
std::vector<ResourceText> texts_; // Vector con los objetos de texto
std::vector<ResourceAnimation> animations_; // Vector con las animaciones
std::vector<DemoData> demos_; // Vector con los ficheros de datos para el modo demostración
std::vector<ResourceSound> sounds_; // Vector con los sonidos
std::vector<ResourceMusic> musics_; // Vector con las músicas
std::vector<ResourceTexture> textures_; // Vector con las texturas
std::vector<ResourceTextFile> text_files_; // Vector con los ficheros de texto
std::vector<ResourceText> texts_; // Vector con los objetos de texto
std::vector<ResourceAnimation> animations_; // Vector con las animaciones
std::vector<DemoData> demos_; // Vector con los ficheros de datos para el modo demostración
// --- Progreso de carga ---
ResourceCount loading_count_; // Contador de recursos cargados
std::shared_ptr<Text> loading_text_; // Texto para escribir en pantalla
std::string loading_resource_name_; // Nombre del recurso que se está cargando
ResourceCount loading_count_; // Contador de recursos cargados
std::shared_ptr<Text> loading_text_; // Texto para escribir en pantalla
std::string loading_resource_name_; // Nombre del recurso que se está cargando
SDL_FRect loading_wired_rect_;
SDL_FRect loading_full_rect_;
// --- Métodos internos de carga y gestión ---
void loadSounds(); // Carga los sonidos
void loadMusics(); // Carga las músicas
void loadTextures(); // Carga las texturas
void loadTextFiles(); // Carga los ficheros de texto
void loadAnimations(); // Carga las animaciones
void loadDemoData(); // Carga los datos para el modo demostración
void addPalettes(); // Añade paletas a las texturas
void createTextures(); // Crea las texturas a partir de los datos cargados
void createText(); // Crea los objetos de texto
void clear(); // Vacía todos los vectores de recursos
void load(); // Carga todos los recursos
void clearSounds(); // Vacía el vector de sonidos
void clearMusics(); // Vacía el vector de músicas
void loadSounds(); // Carga los sonidos
void loadMusics(); // Carga las músicas
void loadTextures(); // Carga las texturas
void loadTextFiles(); // Carga los ficheros de texto
void loadAnimations(); // Carga las animaciones
void loadDemoData(); // Carga los datos para el modo demostración
void addPalettes(); // Añade paletas a las texturas
void createTextures(); // Crea las texturas a partir de los datos cargados
void createText(); // Crea los objetos de texto
void clear(); // Vacía todos los vectores de recursos
void load(); // Carga todos los recursos
void clearSounds(); // Vacía el vector de sonidos
void clearMusics(); // Vacía el vector de músicas
// --- Métodos internos para gestionar el progreso ---
void calculateTotalResources(); // Calcula el número de recursos para cargar
void renderProgress(); // Muestra el progreso de carga
void checkEvents(); // Comprueba los eventos durante la carga
void updateLoadingProgress(std::string name); // Actualiza el progreso de carga
void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso
void updateProgressBar(); // Actualiza la barra de estado
void calculateTotalResources(); // Calcula el número de recursos para cargar
void renderProgress(); // Muestra el progreso de carga
void checkEvents(); // Comprueba los eventos durante la carga
void updateLoadingProgress(std::string name); // Actualiza el progreso de carga
void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso
void updateProgressBar(); // Actualiza la barra de estado
// --- Constructores y destructor privados (singleton) ---
Resource(); // Constructor privado
~Resource(); // Destructor privado
Resource(); // Constructor privado
~Resource(); // Destructor privado
};

View File

@@ -1,460 +1,412 @@
#include "scoreboard.h"
#include <SDL3/SDL.h> // Para SDL_DestroyTexture, SDL_SetRenderDrawColor
#include <math.h> // Para roundf
#include <iomanip> // Para operator<<, setfill, setw
#include <sstream> // Para basic_ostringstream, basic_ostream, basic_os...
#include <SDL3/SDL.h> // Para SDL_DestroyTexture, SDL_SetRenderDrawColor
#include <math.h> // Para roundf
#include "enter_name.h" // Para NAME_SIZE
#include "lang.h" // Para getText
#include "param.h" // Para Param, ParamScoreboard, param
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR
#include "texture.h" // Para Texture
#include <iomanip> // Para operator<<, setfill, setw
#include <sstream> // Para basic_ostringstream, basic_ostream, basic_os...
#include "enter_name.h" // Para NAME_SIZE
#include "lang.h" // Para getText
#include "param.h" // Para Param, ParamScoreboard, param
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR
#include "texture.h" // Para Texture
// [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado
Scoreboard *Scoreboard::scoreboard_ = nullptr;
// [SINGLETON] Crearemos el objeto score_board con esta función estática
void Scoreboard::init()
{
Scoreboard::scoreboard_ = new Scoreboard();
void Scoreboard::init() {
Scoreboard::scoreboard_ = new Scoreboard();
}
// [SINGLETON] Destruiremos el objeto score_board con esta función estática
void Scoreboard::destroy()
{
delete Scoreboard::scoreboard_;
void Scoreboard::destroy() {
delete Scoreboard::scoreboard_;
}
// [SINGLETON] Con este método obtenemos el objeto score_board y podemos trabajar con él
Scoreboard *Scoreboard::get()
{
return Scoreboard::scoreboard_;
Scoreboard *Scoreboard::get() {
return Scoreboard::scoreboard_;
}
// Constructor
Scoreboard::Scoreboard()
: renderer_(Screen::get()->getRenderer()),
game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
text_scoreboard_(Resource::get()->getText("8bithud"))
{
// Inicializa variables
for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
{
name_[i].clear();
record_name_[i].clear();
selector_pos_[i] = 0;
score_[i] = 0;
mult_[i] = 0;
continue_counter_[i] = 0;
}
: renderer_(Screen::get()->getRenderer()),
game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
text_scoreboard_(Resource::get()->getText("8bithud")) {
// Inicializa variables
for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) {
name_[i].clear();
record_name_[i].clear();
selector_pos_[i] = 0;
score_[i] = 0;
mult_[i] = 0;
continue_counter_[i] = 0;
}
panel_[SCOREBOARD_LEFT_PANEL].mode = ScoreboardMode::SCORE;
panel_[SCOREBOARD_RIGHT_PANEL].mode = ScoreboardMode::SCORE;
panel_[SCOREBOARD_CENTER_PANEL].mode = ScoreboardMode::STAGE_INFO;
panel_[SCOREBOARD_LEFT_PANEL].mode = ScoreboardMode::SCORE;
panel_[SCOREBOARD_RIGHT_PANEL].mode = ScoreboardMode::SCORE;
panel_[SCOREBOARD_CENTER_PANEL].mode = ScoreboardMode::STAGE_INFO;
// Recalcula las anclas de los elementos
recalculateAnchors();
power_meter_sprite_->setPosition(SDL_FRect{
static_cast<float>(slot4_2_.x - 20),
static_cast<float>(slot4_2_.y),
40,
7});
// Recalcula las anclas de los elementos
recalculateAnchors();
power_meter_sprite_->setPosition(SDL_FRect{
static_cast<float>(slot4_2_.x - 20),
static_cast<float>(slot4_2_.y),
40,
7});
// Crea la textura de fondo
background_ = nullptr;
createBackgroundTexture();
// Crea la textura de fondo
background_ = nullptr;
createBackgroundTexture();
// Crea las texturas de los paneles
createPanelTextures();
// Crea las texturas de los paneles
createPanelTextures();
// Rellena la textura de fondo
fillBackgroundTexture();
// Rellena la textura de fondo
fillBackgroundTexture();
// Inicializa el vector de colores para el nombre
iniNameColors();
// Inicializa el vector de colores para el nombre
iniNameColors();
}
Scoreboard::~Scoreboard()
{
if (background_)
{
SDL_DestroyTexture(background_);
}
Scoreboard::~Scoreboard() {
if (background_) {
SDL_DestroyTexture(background_);
}
for (auto texture : panel_texture_)
{
if (texture)
{
SDL_DestroyTexture(texture);
}
}
for (auto texture : panel_texture_) {
if (texture) {
SDL_DestroyTexture(texture);
}
}
}
// Transforma un valor numérico en una cadena de 7 cifras
std::string Scoreboard::updateScoreText(int num)
{
std::ostringstream oss;
oss << std::setw(7) << std::setfill('0') << num;
return oss.str();
std::string Scoreboard::updateScoreText(int num) {
std::ostringstream oss;
oss << std::setw(7) << std::setfill('0') << num;
return oss.str();
}
// Actualiza el contador
void Scoreboard::updateTimeCounter()
{
constexpr int TICKS_SPEED = 100;
void Scoreboard::updateTimeCounter() {
constexpr int TICKS_SPEED = 100;
if (SDL_GetTicks() - ticks_ > TICKS_SPEED)
{
ticks_ = SDL_GetTicks();
++time_counter_;
}
if (SDL_GetTicks() - ticks_ > TICKS_SPEED) {
ticks_ = SDL_GetTicks();
++time_counter_;
}
}
// Actualiza la lógica del marcador
void Scoreboard::update()
{
fillBackgroundTexture();
updateTimeCounter();
++loop_counter_;
void Scoreboard::update() {
fillBackgroundTexture();
updateTimeCounter();
++loop_counter_;
}
// Pinta el marcador
void Scoreboard::render()
{
SDL_RenderTexture(renderer_, background_, nullptr, &rect_);
void Scoreboard::render() {
SDL_RenderTexture(renderer_, background_, nullptr, &rect_);
}
// Establece el valor de la variable
void Scoreboard::setColor(Color color)
{
// Actualiza las variables de colores
color_ = color;
text_color1_ = param.scoreboard.text_autocolor ? color_.lighten(100) : param.scoreboard.text_color1;
text_color2_ = param.scoreboard.text_autocolor ? color_.lighten(150) : param.scoreboard.text_color2;
void Scoreboard::setColor(Color color) {
// Actualiza las variables de colores
color_ = color;
text_color1_ = param.scoreboard.text_autocolor ? color_.lighten(100) : param.scoreboard.text_color1;
text_color2_ = param.scoreboard.text_autocolor ? color_.lighten(150) : param.scoreboard.text_color2;
// Aplica los colores
power_meter_sprite_->getTexture()->setColor(text_color2_);
fillBackgroundTexture();
iniNameColors();
// Aplica los colores
power_meter_sprite_->getTexture()->setColor(text_color2_);
fillBackgroundTexture();
iniNameColors();
}
// Establece el valor de la variable
void Scoreboard::setPos(SDL_FRect rect)
{
rect_ = rect;
void Scoreboard::setPos(SDL_FRect rect) {
rect_ = rect;
recalculateAnchors(); // Recalcula las anclas de los elementos
createBackgroundTexture(); // Crea la textura de fondo
createPanelTextures(); // Crea las texturas de los paneles
fillBackgroundTexture(); // Rellena la textura de fondo
recalculateAnchors(); // Recalcula las anclas de los elementos
createBackgroundTexture(); // Crea la textura de fondo
createPanelTextures(); // Crea las texturas de los paneles
fillBackgroundTexture(); // Rellena la textura de fondo
}
// Rellena los diferentes paneles del marcador
void Scoreboard::fillPanelTextures()
{
// Guarda a donde apunta actualmente el renderizador
auto temp = SDL_GetRenderTarget(renderer_);
void Scoreboard::fillPanelTextures() {
// Guarda a donde apunta actualmente el renderizador
auto temp = SDL_GetRenderTarget(renderer_);
// Genera el contenido de cada panel_
for (size_t i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
{
// Cambia el destino del renderizador
SDL_SetRenderTarget(renderer_, panel_texture_[i]);
// Genera el contenido de cada panel_
for (size_t i = 0; i < SCOREBOARD_MAX_PANELS; ++i) {
// Cambia el destino del renderizador
SDL_SetRenderTarget(renderer_, panel_texture_[i]);
// Dibuja el fondo de la textura
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_RenderClear(renderer_);
// Dibuja el fondo de la textura
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_RenderClear(renderer_);
switch (panel_[i].mode)
{
case ScoreboardMode::SCORE:
{
// SCORE
text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_);
switch (panel_[i].mode) {
case ScoreboardMode::SCORE: {
// SCORE
text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_);
// MULT
text_scoreboard_->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_[i]).substr(0, 3), 1, text_color2_);
break;
}
// MULT
text_scoreboard_->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_[i]).substr(0, 3), 1, text_color2_);
break;
}
case ScoreboardMode::DEMO:
{
// DEMO MODE
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_);
case ScoreboardMode::DEMO: {
// DEMO MODE
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_);
// PRESS START TO PLAY
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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
}
break;
}
// PRESS START TO PLAY
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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
}
break;
}
case ScoreboardMode::WAITING:
{
// GAME OVER
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
case ScoreboardMode::WAITING: {
// GAME OVER
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
// PRESS START TO PLAY
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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
}
break;
}
// PRESS START TO PLAY
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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
}
break;
}
case ScoreboardMode::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_);
case ScoreboardMode::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_);
// PLEASE WAIT
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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_);
}
break;
}
// PLEASE WAIT
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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_);
}
break;
}
case ScoreboardMode::STAGE_INFO:
{
// 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_);
case ScoreboardMode::STAGE_INFO: {
// 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_);
// POWERMETER
power_meter_sprite_->setSpriteClip(0, 0, 40, 7);
power_meter_sprite_->render();
power_meter_sprite_->setSpriteClip(40, 0, int(power_ * 40.0f), 7);
power_meter_sprite_->render();
// POWERMETER
power_meter_sprite_->setSpriteClip(0, 0, 40, 7);
power_meter_sprite_->render();
power_meter_sprite_->setSpriteClip(40, 0, int(power_ * 40.0f), 7);
power_meter_sprite_->render();
// HI-SCORE
text_scoreboard_->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_ == "" ? "" : hi_score_name_ + " - ";
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, name + updateScoreText(hi_score_), 1, text_color2_);
break;
}
// HI-SCORE
text_scoreboard_->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_ == "" ? "" : hi_score_name_ + " - ";
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, name + updateScoreText(hi_score_), 1, text_color2_);
break;
}
case ScoreboardMode::CONTINUE:
{
// SCORE
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_);
case ScoreboardMode::CONTINUE: {
// SCORE
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_);
// CONTINUE
text_scoreboard_->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_[i]), 1, text_color2_);
break;
}
// CONTINUE
text_scoreboard_->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_[i]), 1, text_color2_);
break;
}
case ScoreboardMode::ENTER_NAME:
{
// SCORE
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_);
case ScoreboardMode::ENTER_NAME: {
// SCORE
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_);
// ENTER NAME
{
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
SDL_FRect rect = {enter_name_pos_.x, enter_name_pos_.y, 5.0f, 7.0f};
// ENTER NAME
{
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
SDL_FRect rect = {enter_name_pos_.x, enter_name_pos_.y, 5.0f, 7.0f};
// Recorre todos los slots de letras del nombre
for (size_t j = 0; j < NAME_SIZE; ++j)
{
// Selecciona el color
const Color color = j < selector_pos_[i] ? text_color2_ : text_color1_;
// Recorre todos los slots de letras del nombre
for (size_t j = 0; j < NAME_SIZE; ++j) {
// Selecciona el color
const Color color = j < selector_pos_[i] ? text_color2_ : text_color1_;
if (j != selector_pos_[i] || time_counter_ % 3 == 0)
{
// Dibuja la linea
if (j >= selector_pos_[i])
{
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);
}
if (j != selector_pos_[i] || time_counter_ % 3 == 0) {
// Dibuja la linea
if (j >= selector_pos_[i]) {
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_[i].size())
{
text_scoreboard_->writeColored(rect.x, rect.y, record_name_[i].substr(j, 1), color);
}
}
rect.x += 7;
}
}
break;
}
case ScoreboardMode::SHOW_NAME:
{
// SCORE
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_);
// Dibuja la letra
if (j < record_name_[i].size()) {
text_scoreboard_->writeColored(rect.x, rect.y, record_name_[i].substr(j, 1), color);
}
}
rect.x += 7;
}
}
break;
}
case ScoreboardMode::SHOW_NAME: {
// SCORE
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_);
// NAME
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
// NAME
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
/* TEXTO CENTRADO */
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, record_name_[i], 1, getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
/* TEXTO CENTRADO */
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, record_name_[i], 1, getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
/* TEXTO A LA IZQUIERDA */
// text_scoreboard_->writeColored(enter_name_pos_.x, enter_name_pos_.y, record_name_[i], getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
break;
}
case ScoreboardMode::GAME_COMPLETED:
{
// GAME OVER
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
/* TEXTO A LA IZQUIERDA */
// text_scoreboard_->writeColored(enter_name_pos_.x, enter_name_pos_.y, record_name_[i], getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
break;
}
case ScoreboardMode::GAME_COMPLETED: {
// GAME OVER
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
// SCORE
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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_[i]), 1, text_color2_);
}
}
default:
break;
}
}
// SCORE
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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_[i]), 1, text_color2_);
}
}
default:
break;
}
}
// Deja el renderizador apuntando donde estaba
SDL_SetRenderTarget(renderer_, temp);
// Deja el renderizador apuntando donde estaba
SDL_SetRenderTarget(renderer_, temp);
}
// Rellena la textura de fondo
void Scoreboard::fillBackgroundTexture()
{
// Rellena los diferentes paneles del marcador
fillPanelTextures();
void Scoreboard::fillBackgroundTexture() {
// Rellena los diferentes paneles del marcador
fillPanelTextures();
// Cambia el destino del renderizador
SDL_Texture *temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, background_);
// Cambia el destino del renderizador
SDL_Texture *temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, background_);
// Dibuja el fondo del marcador
SDL_SetRenderDrawColor(renderer_, color_.r, color_.g, color_.b, 255);
SDL_RenderClear(renderer_);
// Dibuja el fondo del marcador
SDL_SetRenderDrawColor(renderer_, color_.r, color_.g, color_.b, 255);
SDL_RenderClear(renderer_);
// Copia las texturas de los paneles
for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
{
SDL_RenderTexture(renderer_, panel_texture_[i], nullptr, &panel_[i].pos);
}
// Copia las texturas de los paneles
for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) {
SDL_RenderTexture(renderer_, panel_texture_[i], nullptr, &panel_[i].pos);
}
// Dibuja la linea que separa la zona de juego del marcador
renderSeparator();
// Dibuja la linea que separa la zona de juego del marcador
renderSeparator();
// Deja el renderizador apuntando donde estaba
SDL_SetRenderTarget(renderer_, temp);
// Deja el renderizador apuntando donde estaba
SDL_SetRenderTarget(renderer_, temp);
}
// Recalcula las anclas de los elementos
void Scoreboard::recalculateAnchors()
{
// Recalcula la posición y el tamaño de los paneles
const float panel_width = (float)rect_.w / (float)SCOREBOARD_MAX_PANELS;
for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
{
panel_[i].pos.x = roundf(panel_width * i);
panel_[i].pos.y = 0;
panel_[i].pos.w = roundf(panel_width * (i + 1)) - panel_[i].pos.x;
panel_[i].pos.h = rect_.h;
}
void Scoreboard::recalculateAnchors() {
// Recalcula la posición y el tamaño de los paneles
const float panel_width = (float)rect_.w / (float)SCOREBOARD_MAX_PANELS;
for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) {
panel_[i].pos.x = roundf(panel_width * i);
panel_[i].pos.y = 0;
panel_[i].pos.w = roundf(panel_width * (i + 1)) - panel_[i].pos.x;
panel_[i].pos.h = rect_.h;
}
// Constantes para definir las zonas del panel_: 4 filas y 1 columna
const int ROW_SIZE = rect_.h / 4;
const int TEXT_HEIGHT = 7;
// Constantes para definir las zonas del panel_: 4 filas y 1 columna
const int ROW_SIZE = rect_.h / 4;
const int TEXT_HEIGHT = 7;
// Filas
const float ROW1 = 1 + (ROW_SIZE * 0) + (TEXT_HEIGHT / 2);
const float ROW2 = 1 + (ROW_SIZE * 1) + (TEXT_HEIGHT / 2) - 1;
const float ROW3 = 1 + (ROW_SIZE * 2) + (TEXT_HEIGHT / 2) - 2;
const float ROW4 = 1 + (ROW_SIZE * 3) + (TEXT_HEIGHT / 2) - 3;
// Filas
const float ROW1 = 1 + (ROW_SIZE * 0) + (TEXT_HEIGHT / 2);
const float ROW2 = 1 + (ROW_SIZE * 1) + (TEXT_HEIGHT / 2) - 1;
const float ROW3 = 1 + (ROW_SIZE * 2) + (TEXT_HEIGHT / 2) - 2;
const float ROW4 = 1 + (ROW_SIZE * 3) + (TEXT_HEIGHT / 2) - 3;
// Columna
const float COL = panel_width / 2;
// Columna
const float COL = panel_width / 2;
// Slots de 4
slot4_1_ = {COL, ROW1};
slot4_2_ = {COL, ROW2};
slot4_3_ = {COL, ROW3};
slot4_4_ = {COL, ROW4};
// Slots de 4
slot4_1_ = {COL, ROW1};
slot4_2_ = {COL, ROW2};
slot4_3_ = {COL, ROW3};
slot4_4_ = {COL, ROW4};
// Primer cuadrado para poner el nombre de record
const int enter_name_lenght = text_scoreboard_->lenght(std::string(NAME_SIZE, 'A'));
enter_name_pos_.x = COL - (enter_name_lenght / 2);
enter_name_pos_.y = ROW4;
// Primer cuadrado para poner el nombre de record
const int enter_name_lenght = text_scoreboard_->lenght(std::string(NAME_SIZE, 'A'));
enter_name_pos_.x = COL - (enter_name_lenght / 2);
enter_name_pos_.y = ROW4;
// Recoloca los sprites
if (power_meter_sprite_)
{
power_meter_sprite_->setX(slot4_2_.x - 20);
power_meter_sprite_->setY(slot4_2_.y);
}
// Recoloca los sprites
if (power_meter_sprite_) {
power_meter_sprite_->setX(slot4_2_.x - 20);
power_meter_sprite_->setY(slot4_2_.y);
}
}
// Crea la textura de fondo
void Scoreboard::createBackgroundTexture()
{
// Elimina la textura en caso de existir
if (background_)
{
SDL_DestroyTexture(background_);
}
void Scoreboard::createBackgroundTexture() {
// Elimina la textura en caso de existir
if (background_) {
SDL_DestroyTexture(background_);
}
// Recrea la textura de fondo
background_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h);
SDL_SetTextureBlendMode(background_, SDL_BLENDMODE_BLEND);
// Recrea la textura de fondo
background_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h);
SDL_SetTextureBlendMode(background_, SDL_BLENDMODE_BLEND);
}
// Crea las texturas de los paneles
void Scoreboard::createPanelTextures()
{
// Elimina las texturas en caso de existir
for (auto texture : panel_texture_)
{
if (texture != nullptr)
{
SDL_DestroyTexture(texture);
}
}
panel_texture_.clear();
void Scoreboard::createPanelTextures() {
// Elimina las texturas en caso de existir
for (auto texture : panel_texture_) {
if (texture != nullptr) {
SDL_DestroyTexture(texture);
}
}
panel_texture_.clear();
// Crea las texturas para cada panel_
for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
{
SDL_Texture *tex = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, panel_[i].pos.w, panel_[i].pos.h);
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
panel_texture_.push_back(tex);
}
// Crea las texturas para cada panel_
for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) {
SDL_Texture *tex = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, panel_[i].pos.w, panel_[i].pos.h);
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
panel_texture_.push_back(tex);
}
}
// Dibuja la linea que separa la zona de juego del marcador
void Scoreboard::renderSeparator()
{
// Dibuja la linea que separa el marcador de la zona de juego
auto color = param.scoreboard.separator_autocolor ? color_.darken() : param.scoreboard.separator_color;
SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 255);
SDL_RenderLine(renderer_, 0, 0, rect_.w, 0);
void Scoreboard::renderSeparator() {
// Dibuja la linea que separa el marcador de la zona de juego
auto color = param.scoreboard.separator_autocolor ? color_.darken() : param.scoreboard.separator_color;
SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 255);
SDL_RenderLine(renderer_, 0, 0, rect_.w, 0);
}
// Inicializa el vector de colores para el nombre
void Scoreboard::iniNameColors()
{
Color color = color_.inverse();
void Scoreboard::iniNameColors() {
Color color = color_.inverse();
name_colors_.clear();
name_colors_.emplace_back(color.lighten(50));
name_colors_.emplace_back(color.lighten(25));
name_colors_.emplace_back(color);
name_colors_.emplace_back(color.darken(25));
name_colors_.clear();
name_colors_.emplace_back(color.lighten(50));
name_colors_.emplace_back(color.lighten(25));
name_colors_.emplace_back(color);
name_colors_.emplace_back(color.darken(25));
}

View File

@@ -1,12 +1,13 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FPoint, SDL_GetTicks, SDL_FRect, SDL_Texture, SDL_Renderer, Uint64
#include <stddef.h> // Para size_t
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_FPoint, SDL_GetTicks, SDL_FRect, SDL_Texture, SDL_Renderer, Uint64
#include <stddef.h> // Para size_t
#include "utils.h" // Para Color
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include "utils.h" // Para Color
class Sprite;
class Text;
@@ -19,105 +20,102 @@ constexpr int SCOREBOARD_RIGHT_PANEL = 2;
constexpr int SCOREBOARD_MAX_PANELS = 3;
// --- Enums ---
enum class ScoreboardMode : int
{
SCORE,
STAGE_INFO,
CONTINUE,
WAITING,
GAME_OVER,
DEMO,
ENTER_NAME,
SHOW_NAME,
GAME_COMPLETED,
NUM_MODES,
enum class ScoreboardMode : int {
SCORE,
STAGE_INFO,
CONTINUE,
WAITING,
GAME_OVER,
DEMO,
ENTER_NAME,
SHOW_NAME,
GAME_COMPLETED,
NUM_MODES,
};
// --- Structs ---
struct Panel
{
ScoreboardMode mode; // Modo en el que se encuentra el panel
SDL_FRect pos; // Posición donde dibujar el panel dentro del marcador
struct Panel {
ScoreboardMode mode; // Modo en el que se encuentra el panel
SDL_FRect pos; // Posición donde dibujar el panel dentro del marcador
};
// --- Clase Scoreboard ---
class Scoreboard
{
public:
// --- Métodos de singleton ---
static void init(); // Crea el objeto Scoreboard
static void destroy(); // Libera el objeto Scoreboard
static Scoreboard *get(); // Obtiene el puntero al objeto Scoreboard
class Scoreboard {
public:
// --- Métodos de singleton ---
static void init(); // Crea el objeto Scoreboard
static void destroy(); // Libera el objeto Scoreboard
static Scoreboard *get(); // Obtiene el puntero al objeto Scoreboard
// --- Métodos principales ---
void update(); // Actualiza la lógica del marcador
void render(); // Pinta el marcador
// --- Métodos principales ---
void update(); // Actualiza la lógica del marcador
void render(); // Pinta el marcador
// --- Setters ---
void setColor(Color color); // Establece el color del marcador
void setPos(SDL_FRect rect); // Establece la posición y tamaño del marcador
void setContinue(int panel, int continue_counter) { continue_counter_[panel] = continue_counter; }
void setHiScore(int hi_score) { hi_score_ = hi_score; }
void setHiScoreName(const std::string &name) { hi_score_name_ = name; }
void setMode(int index, ScoreboardMode mode) { panel_[index].mode = mode; }
void setMult(int panel, float mult) { mult_[panel] = mult; }
void setName(int panel, const std::string &name) { name_[panel] = name; }
void setPower(float power) { power_ = power; }
void setRecordName(int panel, const std::string &record_name) { record_name_[panel] = record_name; }
void setScore(int panel, int score) { score_[panel] = score; }
void setSelectorPos(int panel, int pos) { selector_pos_[panel] = pos; }
void setStage(int stage) { stage_ = stage; }
// --- Setters ---
void setColor(Color color); // Establece el color del marcador
void setPos(SDL_FRect rect); // Establece la posición y tamaño del marcador
void setContinue(int panel, int continue_counter) { continue_counter_[panel] = continue_counter; }
void setHiScore(int hi_score) { hi_score_ = hi_score; }
void setHiScoreName(const std::string &name) { hi_score_name_ = name; }
void setMode(int index, ScoreboardMode mode) { panel_[index].mode = mode; }
void setMult(int panel, float mult) { mult_[panel] = mult; }
void setName(int panel, const std::string &name) { name_[panel] = name; }
void setPower(float power) { power_ = power; }
void setRecordName(int panel, const std::string &record_name) { record_name_[panel] = record_name; }
void setScore(int panel, int score) { score_[panel] = score; }
void setSelectorPos(int panel, int pos) { selector_pos_[panel] = pos; }
void setStage(int stage) { stage_ = stage; }
private:
// --- Singleton ---
static Scoreboard *scoreboard_;
private:
// --- Singleton ---
static Scoreboard *scoreboard_;
// --- Objetos y punteros ---
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::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
SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador
std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel
// --- Objetos y punteros ---
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::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
SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador
std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel
// --- Variables de estado ---
std::string name_[SCOREBOARD_MAX_PANELS] = {}; // Nombre de cada jugador
std::string record_name_[SCOREBOARD_MAX_PANELS] = {}; // Nombre introducido para la tabla de records
size_t selector_pos_[SCOREBOARD_MAX_PANELS] = {}; // Posición del selector de letra para introducir el nombre
int score_[SCOREBOARD_MAX_PANELS] = {}; // Puntuación de los jugadores
float mult_[SCOREBOARD_MAX_PANELS] = {}; // Multiplicador de los jugadores
int continue_counter_[SCOREBOARD_MAX_PANELS] = {}; // Tiempo para continuar de los jugadores
Panel panel_[SCOREBOARD_MAX_PANELS] = {}; // Lista con todos los paneles del marcador
int stage_ = 1; // Número de fase actual
int hi_score_ = 0; // Máxima puntuación
float power_ = 0; // Poder actual de la fase
std::string hi_score_name_ = std::string(); // Nombre del jugador con la máxima puntuación
Color color_ = Color(); // Color del marcador
SDL_FRect rect_ = {0, 0, 320, 40}; // Posición y dimensiones del marcador
Uint64 ticks_ = SDL_GetTicks(); // Variable donde almacenar el valor de SDL_GetTicks()
int time_counter_ = 0; // Contador de segundos
int loop_counter_ = 0; // Contador de bucle
std::vector<Color> name_colors_; // Colores para destacar el nombre una vez introducido
// --- Variables de estado ---
std::string name_[SCOREBOARD_MAX_PANELS] = {}; // Nombre de cada jugador
std::string record_name_[SCOREBOARD_MAX_PANELS] = {}; // Nombre introducido para la tabla de records
size_t selector_pos_[SCOREBOARD_MAX_PANELS] = {}; // Posición del selector de letra para introducir el nombre
int score_[SCOREBOARD_MAX_PANELS] = {}; // Puntuación de los jugadores
float mult_[SCOREBOARD_MAX_PANELS] = {}; // Multiplicador de los jugadores
int continue_counter_[SCOREBOARD_MAX_PANELS] = {}; // Tiempo para continuar de los jugadores
Panel panel_[SCOREBOARD_MAX_PANELS] = {}; // Lista con todos los paneles del marcador
int stage_ = 1; // Número de fase actual
int hi_score_ = 0; // Máxima puntuación
float power_ = 0; // Poder actual de la fase
std::string hi_score_name_ = std::string(); // Nombre del jugador con la máxima puntuación
Color color_ = Color(); // Color del marcador
SDL_FRect rect_ = {0, 0, 320, 40}; // Posición y dimensiones del marcador
Uint64 ticks_ = SDL_GetTicks(); // Variable donde almacenar el valor de SDL_GetTicks()
int time_counter_ = 0; // Contador de segundos
int loop_counter_ = 0; // Contador de bucle
std::vector<Color> name_colors_; // Colores para destacar el nombre una vez introducido
// --- Variables de aspecto ---
Color text_color1_, text_color2_; // Colores para los marcadores del texto;
// --- Variables de aspecto ---
Color text_color1_, text_color2_; // Colores para los marcadores del texto;
// --- Puntos predefinidos para colocar elementos en los paneles ---
SDL_FPoint slot4_1_, slot4_2_, slot4_3_, slot4_4_;
SDL_FPoint enter_name_pos_;
// --- Puntos predefinidos para colocar elementos en los paneles ---
SDL_FPoint slot4_1_, slot4_2_, slot4_3_, slot4_4_;
SDL_FPoint enter_name_pos_;
// --- Métodos internos ---
void recalculateAnchors(); // Recalcula las anclas de los elementos
std::string updateScoreText(int num); // Transforma un valor numérico en una cadena de 7 cifras
void createBackgroundTexture(); // Crea la textura de fondo
void createPanelTextures(); // Crea las texturas de los paneles
void fillPanelTextures(); // Rellena los diferentes paneles del marcador
void fillBackgroundTexture(); // Rellena la textura de fondo
void updateTimeCounter(); // Actualiza el contador
void renderSeparator(); // Dibuja la línea que separa la zona de juego del marcador
void iniNameColors(); // Inicializa el vector de colores para el nombre
// --- Métodos internos ---
void recalculateAnchors(); // Recalcula las anclas de los elementos
std::string updateScoreText(int num); // Transforma un valor numérico en una cadena de 7 cifras
void createBackgroundTexture(); // Crea la textura de fondo
void createPanelTextures(); // Crea las texturas de los paneles
void fillPanelTextures(); // Rellena los diferentes paneles del marcador
void fillBackgroundTexture(); // Rellena la textura de fondo
void updateTimeCounter(); // Actualiza el contador
void renderSeparator(); // Dibuja la línea que separa la zona de juego del marcador
void iniNameColors(); // Inicializa el vector de colores para el nombre
// --- Constructor y destructor privados (singleton) ---
Scoreboard();
~Scoreboard();
// --- Constructor y destructor privados (singleton) ---
Scoreboard();
~Scoreboard();
};

View File

@@ -1,21 +1,22 @@
#include "screen.h"
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_LogCategory
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_LogCategory
#include <algorithm> // Para min, max
#include <fstream> // Para basic_ifstream, ifstream
#include <iterator> // Para istreambuf_iterator, operator==
#include <memory> // Para allocator, shared_ptr, __shared_pt...
#include <string> // Para basic_string, char_traits, operator+
#include "asset.h" // Para Asset
#include "external/jail_shader.h" // Para init, render
#include "mouse.h" // Para updateCursorVisibility
#include "notifier.h" // Para Notifier
#include "options.h" // Para VideoOptions, video, WindowOptions
#include "param.h" // Para Param, param, ParamGame, ParamDebug
#include "text.h" // Para Text, TEXT_COLOR, TEXT_STROKE
#include "texture.h" // Para Texture
#include "ui/service_menu.h" // Para ServiceMenu
#include "asset.h" // Para Asset
#include "external/jail_shader.h" // Para init, render
#include "mouse.h" // Para updateCursorVisibility
#include "notifier.h" // Para Notifier
#include "options.h" // Para VideoOptions, video, WindowOptions
#include "param.h" // Para Param, param, ParamGame, ParamDebug
#include "text.h" // Para Text, TEXT_COLOR, TEXT_STROKE
#include "texture.h" // Para Texture
#include "ui/service_menu.h" // Para ServiceMenu
// Singleton
Screen *Screen::instance_ = nullptr;
@@ -27,7 +28,7 @@ void Screen::init() { Screen::instance_ = new Screen(); }
void Screen::destroy() { delete Screen::instance_; }
// Obtiene la instancia
Screen *Screen::get() { return Screen::instance_; }
auto Screen::get() -> Screen * { return Screen::instance_; }
// Constructor
Screen::Screen()
@@ -36,9 +37,8 @@ Screen::Screen()
game_canvas_(nullptr),
service_menu_(nullptr),
notifier_(nullptr),
src_rect_(SDL_FRect{0, 0, static_cast<float>(param.game.width), static_cast<float>(param.game.height)}),
dst_rect_(SDL_FRect{0, 0, static_cast<float>(param.game.width), static_cast<float>(param.game.height)})
{
src_rect_(SDL_FRect{0, 0, param.game.width, param.game.height}),
dst_rect_(SDL_FRect{0, 0, param.game.width, param.game.height}) {
// Arranca SDL VIDEO, crea la ventana y el renderizador
initSDLVideo();
@@ -61,16 +61,14 @@ Screen::Screen()
}
// Destructor
Screen::~Screen()
{
Screen::~Screen() {
SDL_DestroyTexture(game_canvas_);
SDL_DestroyRenderer(renderer_);
SDL_DestroyWindow(window_);
}
// Limpia la pantalla
void Screen::clean(Color color)
{
void Screen::clean(Color color) {
SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 0xFF);
SDL_RenderClear(renderer_);
}
@@ -79,75 +77,59 @@ void Screen::clean(Color color)
void Screen::start() { SDL_SetRenderTarget(renderer_, game_canvas_); }
// Vuelca el contenido del renderizador en pantalla
void Screen::render()
{
void Screen::render() {
fps_.increment();
// Renderiza todos los overlays y efectos
renderOverlays();
// Renderiza el contenido del game_canvas_
renderScreen();
renderOverlays(); // Renderiza todos los overlays y efectos
renderScreen(); // Renderiza el contenido del game_canvas_
}
// Vuelca el contenido del renderizador en pantalla exceptuando ciertas partes
void Screen::coreRender()
{
void Screen::coreRender() {
fps_.increment();
#ifdef DEBUG
renderInfo();
#endif
renderScreen();
renderScreen(); // Renderiza el contenido del game_canvas_
}
// Renderiza el contenido del game_canvas_
void Screen::renderScreen()
{
void Screen::renderScreen() {
SDL_SetRenderTarget(renderer_, nullptr);
clean();
if (Options::video.shaders)
{
if (Options::video.shaders) {
shader::render();
}
else
{
} else {
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
SDL_RenderPresent(renderer_);
}
}
// Establece el modo de video
void Screen::setFullscreenMode()
{
void Screen::setFullscreenMode() {
SDL_SetWindowFullscreen(window_, Options::video.fullscreen);
}
// Camibia entre pantalla completa y ventana
void Screen::toggleFullscreen()
{
void Screen::toggleFullscreen() {
Options::video.fullscreen = !Options::video.fullscreen;
setFullscreenMode();
}
// Cambia el tamaño de la ventana
void Screen::setWindowZoom(int zoom)
{
void Screen::setWindowZoom(int zoom) {
Options::window.size = zoom;
adjustWindowSize();
}
// Reduce el tamaño de la ventana
bool Screen::decWindowSize()
{
if (!Options::video.fullscreen)
{
auto Screen::decWindowSize() -> bool {
if (!Options::video.fullscreen) {
const int PREVIOUS_ZOOM = Options::window.size;
--Options::window.size;
Options::window.size = std::max(Options::window.size, 1);
if (Options::window.size != PREVIOUS_ZOOM)
{
if (Options::window.size != PREVIOUS_ZOOM) {
adjustWindowSize();
return true;
}
@@ -157,16 +139,13 @@ bool Screen::decWindowSize()
}
// Aumenta el tamaño de la ventana
bool Screen::incWindowSize()
{
if (!Options::video.fullscreen)
{
auto Screen::incWindowSize() -> bool {
if (!Options::video.fullscreen) {
const int PREVIOUS_ZOOM = Options::window.size;
++Options::window.size;
Options::window.size = std::min(Options::window.size, Options::window.max_size);
if (Options::window.size != PREVIOUS_ZOOM)
{
if (Options::window.size != PREVIOUS_ZOOM) {
adjustWindowSize();
return true;
}
@@ -176,45 +155,41 @@ bool Screen::incWindowSize()
}
// Actualiza la lógica de la clase
void Screen::update()
{
void Screen::update() {
fps_.calculate(SDL_GetTicks());
shake_effect_.update(src_rect_, dst_rect_);
flash_effect_.update();
if (service_menu_)
if (service_menu_ != nullptr) {
service_menu_->update();
if (notifier_)
}
if (notifier_ != nullptr) {
notifier_->update();
}
Mouse::updateCursorVisibility();
}
// Actualiza los elementos mínimos
void Screen::coreUpdate()
{
void Screen::coreUpdate() {
fps_.calculate(SDL_GetTicks());
Mouse::updateCursorVisibility();
}
// Actualiza y dibuja el efecto de flash en la pantalla
void Screen::renderFlash()
{
if (flash_effect_.isRendarable())
{
void Screen::renderFlash() {
if (flash_effect_.isRendarable()) {
SDL_SetRenderDrawColor(renderer_, flash_effect_.color.r, flash_effect_.color.g, flash_effect_.color.b, 0xFF);
SDL_RenderClear(renderer_);
}
}
// Aplica el efecto de agitar la pantalla
void Screen::renderShake()
{
if (shake_effect_.enabled)
{
void Screen::renderShake() {
if (shake_effect_.enabled) {
// Guarda el renderizador actual para dejarlo despues como estaba
auto current_target = SDL_GetRenderTarget(renderer_);
auto *current_target = SDL_GetRenderTarget(renderer_);
// Crea una textura temporal
auto temp_texture = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
auto *temp_texture = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
// Vuelca game_canvas_ a la textura temporal
SDL_SetRenderTarget(renderer_, temp_texture);
@@ -233,10 +208,8 @@ void Screen::renderShake()
}
#ifdef DEBUG
// Muestra información por pantalla
void Screen::renderInfo()
{
if (debug_info_.show)
{
void Screen::renderInfo() {
if (debug_info_.show) {
// Resolution
debug_info_.text->writeDX(TEXT_COLOR | TEXT_STROKE, param.game.width - debug_info_.text->lenght(Options::video.info) - 2, 1, Options::video.info, 1, param.debug.color, 1, param.debug.color.darken(150));
@@ -247,10 +220,8 @@ void Screen::renderInfo()
}
#endif
// Carga el contenido del archivo GLSL
void Screen::loadShaders()
{
if (shader_source_.empty())
{
void Screen::loadShaders() {
if (shader_source_.empty()) {
const std::string GLSL_FILE = param.game.game_area.rect.h == 256 ? "crtpi_256.glsl" : "crtpi_240.glsl";
std::ifstream f(Asset::get()->get(GLSL_FILE).c_str());
shader_source_ = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
@@ -258,32 +229,30 @@ void Screen::loadShaders()
}
// Inicializa los shaders
void Screen::initShaders()
{
if (Options::video.shaders)
{
void Screen::initShaders() {
if (Options::video.shaders) {
loadShaders();
shader::init(window_, game_canvas_, shader_source_);
}
}
// Calcula el tamaño de la ventana
void Screen::adjustWindowSize()
{
if (!Options::video.fullscreen)
{
void Screen::adjustWindowSize() {
if (!Options::video.fullscreen) {
// Establece el nuevo tamaño
const int WIDTH = param.game.width * Options::window.size;
const int HEIGHT = param.game.height * Options::window.size;
int old_width, old_height;
int old_width;
int old_height;
SDL_GetWindowSize(window_, &old_width, &old_height);
int old_pos_x, old_pos_y;
int old_pos_x;
int old_pos_y;
SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y);
const int NEW_POS_X = old_pos_x + (old_width - WIDTH) / 2;
const int NEW_POS_Y = old_pos_y + (old_height - HEIGHT) / 2;
const int NEW_POS_X = old_pos_x + ((old_width - WIDTH) / 2);
const int NEW_POS_Y = old_pos_y + ((old_height - HEIGHT) / 2);
SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS_), std::max(NEW_POS_Y, 0));
SDL_SetWindowSize(window_, WIDTH, HEIGHT);
@@ -291,8 +260,7 @@ void Screen::adjustWindowSize()
}
// Renderiza todos los overlays y efectos
void Screen::renderOverlays()
{
void Screen::renderOverlays() {
// Dibuja efectos y elementos sobre el game_canvas_
renderShake();
renderFlash();
@@ -305,24 +273,20 @@ void Screen::renderOverlays()
}
// Atenua la pantalla
void Screen::renderAttenuate()
{
if (attenuate_effect_)
{
void Screen::renderAttenuate() {
if (attenuate_effect_) {
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 64);
SDL_RenderFillRect(renderer_, nullptr);
}
}
// Arranca SDL VIDEO y crea la ventana
bool Screen::initSDLVideo()
{
auto Screen::initSDLVideo() -> bool {
// Inicializar SDL
if (!SDL_Init(SDL_INIT_VIDEO))
{
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"FATAL: Failed to initialize SDL_VIDEO! SDL Error: %s",
SDL_GetError());
"FATAL: Failed to initialize SDL_VIDEO! SDL Error: %s",
SDL_GetError());
return false;
}
@@ -330,16 +294,14 @@ bool Screen::initSDLVideo()
getDisplayInfo();
// 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,
"Warning: Failed to set OpenGL hint!");
"Warning: Failed to set OpenGL hint!");
}
// Crear ventana
SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL;
if (Options::video.fullscreen)
{
if (Options::video.fullscreen) {
window_flags |= SDL_WINDOW_FULLSCREEN;
}
window_ = SDL_CreateWindow(
@@ -348,22 +310,20 @@ bool Screen::initSDLVideo()
param.game.height * Options::window.size,
window_flags);
if (!window_)
{
if (window_ == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"FATAL: Failed to create window! SDL Error: %s",
SDL_GetError());
"FATAL: Failed to create window! SDL Error: %s",
SDL_GetError());
SDL_Quit();
return false;
}
// Crear renderer
renderer_ = SDL_CreateRenderer(window_, nullptr);
if (!renderer_)
{
if (renderer_ == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"FATAL: Failed to create renderer! SDL Error: %s",
SDL_GetError());
"FATAL: Failed to create renderer! SDL Error: %s",
SDL_GetError());
SDL_DestroyWindow(window_);
window_ = nullptr;
SDL_Quit();
@@ -382,39 +342,35 @@ bool Screen::initSDLVideo()
}
// Obtiene información sobre la pantalla
void Screen::getDisplayInfo()
{
int i, num_displays = 0;
void Screen::getDisplayInfo() {
int i;
int num_displays = 0;
SDL_DisplayID *displays = SDL_GetDisplays(&num_displays);
if (displays)
{
for (i = 0; i < num_displays; ++i)
{
if (displays != nullptr) {
for (i = 0; i < num_displays; ++i) {
SDL_DisplayID instance_id = displays[i];
const char *name = SDL_GetDisplayName(instance_id);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Display %" SDL_PRIu32 ": %s", instance_id, name ? name : "Unknown");
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Display %" SDL_PRIu32 ": %s", instance_id, (name != nullptr) ? name : "Unknown");
}
auto DM = SDL_GetCurrentDisplayMode(displays[0]);
const auto *dm = SDL_GetCurrentDisplayMode(displays[0]);
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
Options::window.max_size = std::min(DM->w / param.game.width, DM->h / param.game.height);
Options::window.max_size = std::min(dm->w / param.game.width, dm->h / param.game.height);
Options::window.size = std::min(Options::window.size, Options::window.max_size);
// Muestra información sobre el tamaño de la pantalla y de la ventana de juego
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Current display mode: %dx%d @ %dHz",
static_cast<int>(DM->w), static_cast<int>(DM->h), static_cast<int>(DM->refresh_rate));
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Current display mode: %dx%d @ %dHz", static_cast<int>(dm->w), static_cast<int>(dm->h), static_cast<int>(dm->refresh_rate));
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Window resolution: %dx%d x%d",
static_cast<int>(param.game.width), static_cast<int>(param.game.height), Options::window.size);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Window resolution: %dx%d x%d", static_cast<int>(param.game.width), static_cast<int>(param.game.height), Options::window.size);
Options::video.info = std::to_string(static_cast<int>(DM->w)) + "x" +
std::to_string(static_cast<int>(DM->h)) + " @ " +
std::to_string(static_cast<int>(DM->refresh_rate)) + " Hz";
Options::video.info = std::to_string(static_cast<int>(dm->w)) + "x" +
std::to_string(static_cast<int>(dm->h)) + " @ " +
std::to_string(static_cast<int>(dm->refresh_rate)) + " Hz";
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
const int MAX_ZOOM = std::min(DM->w / param.game.width, (DM->h - WINDOWS_DECORATIONS_) / param.game.height);
const int MAX_ZOOM = std::min(dm->w / param.game.width, (dm->h - WINDOWS_DECORATIONS_) / param.game.height);
// Normaliza los valores de zoom
Options::window.size = std::min(Options::window.size, MAX_ZOOM);
@@ -424,43 +380,37 @@ void Screen::getDisplayInfo()
}
// Alterna entre activar y desactivar los shaders
void Screen::toggleShaders()
{
void Screen::toggleShaders() {
Options::video.shaders = !Options::video.shaders;
initShaders();
}
// Alterna entre activar y desactivar el escalado entero
void Screen::toggleIntegerScale()
{
void Screen::toggleIntegerScale() {
Options::video.integer_scale = !Options::video.integer_scale;
SDL_SetRenderLogicalPresentation(renderer_, param.game.width, param.game.height, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
}
// Alterna entre activar y desactivar el V-Sync
void Screen::toggleVSync()
{
void Screen::toggleVSync() {
Options::video.v_sync = !Options::video.v_sync;
SDL_SetRenderVSync(renderer_, Options::video.v_sync ? 1 : SDL_RENDERER_VSYNC_DISABLED);
}
// Establece el estado del V-Sync
void Screen::setVSync(bool enabled)
{
void Screen::setVSync(bool enabled) {
Options::video.v_sync = enabled;
SDL_SetRenderVSync(renderer_, enabled ? 1 : SDL_RENDERER_VSYNC_DISABLED);
}
// Obtiene los punteros a los singletones
void Screen::getSingletons()
{
void Screen::getSingletons() {
service_menu_ = ServiceMenu::get();
notifier_ = Notifier::get();
}
// Aplica los valores de las opciones
void Screen::applySettings()
{
void Screen::applySettings() {
SDL_SetRenderVSync(renderer_, Options::video.v_sync ? 1 : SDL_RENDERER_VSYNC_DISABLED);
SDL_SetRenderLogicalPresentation(Screen::get()->getRenderer(), param.game.width, param.game.height, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
setFullscreenMode();
@@ -468,8 +418,7 @@ void Screen::applySettings()
}
// Crea el objeto de texto
void Screen::createText()
{
void Screen::createText() {
auto texture = std::make_shared<Texture>(getRenderer(), Asset::get()->get("aseprite.png"));
text_ = std::make_shared<Text>(texture, Asset::get()->get("aseprite.txt"));
}

View File

@@ -1,225 +1,211 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_HideWindow, SDL_Renderer, SDL_ShowWindow, Uint32, SDL_Texture, SDL_Window
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_HideWindow, SDL_Renderer, SDL_ShowWindow, Uint32, SDL_Texture, SDL_Window
#include "options.h" // Para VideoOptions, video
#include "utils.h" // Para Color
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string
#include "options.h" // Para VideoOptions, video
#include "utils.h" // Para Color
class Notifier;
class ServiceMenu;
class Text;
// Clase Screen: gestiona la ventana, el renderizador y los efectos visuales globales
class Screen
{
public:
// --- Métodos de singleton ---
static void init(); // Inicializa el objeto Screen
static void destroy(); // Libera el objeto Screen
static Screen *get(); // Obtiene el puntero al objeto Screen
class Screen {
public:
// --- Métodos de singleton ---
static void init(); // Inicializa el objeto Screen
static void destroy(); // Libera el objeto Screen
static Screen *get(); // Obtiene el puntero al objeto Screen
// --- Métodos principales ---
void update(); // Actualiza la lógica de la clase
void coreUpdate(); // Actualiza los elementos mínimos
void clean(Color color = Color(0x00, 0x00, 0x00)); // Limpia la pantalla
void start(); // Prepara para empezar a dibujar en la textura de juego
void render(); // Vuelca el contenido del renderizador en pantalla
void coreRender(); // Vuelca el contenido del renderizador en pantalla exceptuando ciertas partes
// --- Métodos principales ---
void update(); // Actualiza la lógica de la clase
void coreUpdate(); // Actualiza los elementos mínimos
void clean(Color color = Color(0x00, 0x00, 0x00)); // Limpia la pantalla
void start(); // Prepara para empezar a dibujar en la textura de juego
void render(); // Vuelca el contenido del renderizador en pantalla
void coreRender(); // Vuelca el contenido del renderizador en pantalla exceptuando ciertas partes
// --- Configuración de ventana y render ---
void setFullscreenMode(); // Establece el modo de pantalla completa
void toggleFullscreen(); // Cambia entre pantalla completa y ventana
void setWindowZoom(int size); // Cambia el tamaño de la ventana
bool decWindowSize(); // Reduce el tamaño de la ventana
bool incWindowSize(); // Aumenta el tamaño de la ventana
void applySettings(); // Aplica los valores de las opciones
void initShaders(); // Inicializa los shaders
// --- Configuración de ventana y render ---
void setFullscreenMode(); // Establece el modo de pantalla completa
void toggleFullscreen(); // Cambia entre pantalla completa y ventana
void setWindowZoom(int size); // Cambia el tamaño de la ventana
bool decWindowSize(); // Reduce el tamaño de la ventana
bool incWindowSize(); // Aumenta el tamaño de la ventana
void applySettings(); // Aplica los valores de las opciones
void initShaders(); // Inicializa los shaders
// --- Efectos visuales ---
void shake(int desp = 2, int delay = 3, int lenght = 8) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay, lenght); } // Agita la pantalla
void flash(Color color, int lenght = 10, int delay = 0) { flash_effect_ = FlashEffect(true, lenght, delay, color); } // Pone la pantalla de color
void toggleShaders(); // Alterna entre activar y desactivar los shaders
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
void setVSync(bool enabled); // Establece el estado del V-Sync
void attenuate(bool value) { attenuate_effect_ = value; } // Atenúa la pantalla
// --- Efectos visuales ---
void shake(int desp = 2, int delay = 3, int lenght = 8) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay, lenght); } // Agita la pantalla
void flash(Color color, int lenght = 10, int delay = 0) { flash_effect_ = FlashEffect(true, lenght, delay, color); } // Pone la pantalla de color
void toggleShaders(); // Alterna entre activar y desactivar los shaders
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
void setVSync(bool enabled); // Establece el estado del V-Sync
void attenuate(bool value) { attenuate_effect_ = value; } // Atenúa la pantalla
// --- Getters ---
SDL_Renderer *getRenderer() { return renderer_; } // Obtiene el renderizador
void show() { SDL_ShowWindow(window_); } // Muestra la ventana
void hide() { SDL_HideWindow(window_); } // Oculta la ventana
void getSingletons(); // Obtiene los punteros a los singletones
bool getVSync() const { return Options::video.v_sync; } // Obtiene el valor de V-Sync
std::shared_ptr<Text> getText() const { return text_; } // Obtiene el puntero al texto de Screen
// --- Getters ---
SDL_Renderer *getRenderer() { return renderer_; } // Obtiene el renderizador
void show() { SDL_ShowWindow(window_); } // Muestra la ventana
void hide() { SDL_HideWindow(window_); } // Oculta la ventana
void getSingletons(); // Obtiene los punteros a los singletones
bool getVSync() const { return Options::video.v_sync; } // Obtiene el valor de V-Sync
std::shared_ptr<Text> getText() const { return text_; } // Obtiene el puntero al texto de Screen
#ifdef DEBUG
// --- Debug ---
void toggleDebugInfo() { debug_info_.show = !debug_info_.show; }
void setDebugInfoEnabled(bool value) { debug_info_.show = value; }
// --- Debug ---
void toggleDebugInfo() { debug_info_.show = !debug_info_.show; }
void setDebugInfoEnabled(bool value) { debug_info_.show = value; }
#endif
private:
// --- Constantes ---
static constexpr int WINDOWS_DECORATIONS_ = 35;
private:
// --- Constantes ---
static constexpr int WINDOWS_DECORATIONS_ = 35;
// --- Estructuras internas ---
struct FPS
{
Uint32 ticks; // Tiempo en milisegundos desde que se comenzó a contar.
int frameCount; // Número acumulado de frames en el intervalo.
int lastValue; // Número de frames calculado en el último segundo.
// --- Estructuras internas ---
struct FPS {
Uint32 ticks; // Tiempo en milisegundos desde que se comenzó a contar.
int frameCount; // Número acumulado de frames en el intervalo.
int lastValue; // Número de frames calculado en el último segundo.
FPS() : ticks(0), frameCount(0), lastValue(0) {}
void increment() { frameCount++; }
int calculate(Uint32 currentTicks)
{
if (currentTicks - ticks >= 1000)
{
lastValue = frameCount;
frameCount = 0;
ticks = currentTicks;
}
return lastValue;
}
};
FPS() : ticks(0), frameCount(0), lastValue(0) {}
void increment() { frameCount++; }
int calculate(Uint32 currentTicks) {
if (currentTicks - ticks >= 1000) {
lastValue = frameCount;
frameCount = 0;
ticks = currentTicks;
}
return lastValue;
}
};
// Efecto de flash en pantalla: pinta la pantalla de un color durante unos frames
struct FlashEffect
{
bool enabled; // Indica si el efecto está activo
int lenght; // Duración total del efecto en frames
int delay; // Retraso antes de mostrar el flash
int counter; // Contador de frames restantes
Color color; // Color del flash
// Efecto de flash en pantalla: pinta la pantalla de un color durante unos frames
struct FlashEffect {
bool enabled; // Indica si el efecto está activo
int lenght; // Duración total del efecto en frames
int delay; // Retraso antes de mostrar el flash
int counter; // Contador de frames restantes
Color color; // Color del flash
explicit FlashEffect(bool enabled = false, int lenght = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF))
: enabled(enabled), lenght(lenght), delay(delay), counter(lenght), color(color) {}
explicit FlashEffect(bool enabled = false, int lenght = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF))
: enabled(enabled), lenght(lenght), delay(delay), counter(lenght), color(color) {}
void update() { (enabled && counter > 0) ? counter-- : enabled = false; }
bool isRendarable() { return enabled && counter < lenght - delay; }
};
void update() { (enabled && counter > 0) ? counter-- : enabled = false; }
bool isRendarable() { return enabled && counter < lenght - delay; }
};
// Efecto de sacudida/agitación de pantalla: mueve la imagen para simular un temblor
struct ShakeEffect
{
int desp; // Desplazamiento máximo de la sacudida (en píxeles)
int delay; // Frames entre cada movimiento de sacudida
int counter; // Contador de frames para el siguiente movimiento
int lenght; // Duración total del efecto en frames
int remaining; // Frames restantes de sacudida
int original_pos; // Posición original de la imagen (x)
int original_width; // Ancho original de la imagen
bool enabled; // Indica si el efecto está activo
// Efecto de sacudida/agitación de pantalla: mueve la imagen para simular un temblor
struct ShakeEffect {
int desp; // Desplazamiento máximo de la sacudida (en píxeles)
int delay; // Frames entre cada movimiento de sacudida
int counter; // Contador de frames para el siguiente movimiento
int lenght; // Duración total del efecto en frames
int remaining; // Frames restantes de sacudida
int original_pos; // Posición original de la imagen (x)
int original_width; // Ancho original de la imagen
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 origPos = 0, int origWidth = 800)
: desp(dp), delay(dl), counter(cnt), lenght(len), remaining(rem), original_pos(origPos), original_width(origWidth), enabled(en) {}
explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int origPos = 0, int origWidth = 800)
: desp(dp), delay(dl), counter(cnt), lenght(len), remaining(rem), original_pos(origPos), original_width(origWidth), enabled(en) {}
// 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_lenght = -1)
{
if (!enabled)
{
enabled = true;
original_pos = src_rect.x;
original_width = src_rect.w;
// 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_lenght = -1) {
if (!enabled) {
enabled = true;
original_pos = src_rect.x;
original_width = src_rect.w;
// Usar nuevos valores si se proporcionan, sino mantener los actuales
if (new_desp != -1)
desp = new_desp;
if (new_delay != -1)
delay = new_delay;
if (new_lenght != -1)
lenght = new_lenght;
// Usar nuevos valores si se proporcionan, sino mantener los actuales
if (new_desp != -1)
desp = new_desp;
if (new_delay != -1)
delay = new_delay;
if (new_lenght != -1)
lenght = new_lenght;
src_rect.w -= desp;
dst_rect.w = src_rect.w;
}
remaining = lenght;
counter = delay;
}
src_rect.w -= desp;
dst_rect.w = src_rect.w;
}
remaining = lenght;
counter = delay;
}
// Actualiza el estado del efecto de sacudida
void update(SDL_FRect &src_rect, SDL_FRect &dst_rect)
{
if (enabled)
{
if (counter > 0)
{
counter--;
}
else
{
counter = delay;
const auto SRC_DESP = (remaining % 2 == 0) ? 0 : desp;
const auto DST_DESP = (remaining % 2 == 1) ? 0 : desp;
src_rect.x = original_pos + SRC_DESP;
dst_rect.x = original_pos + DST_DESP;
remaining--;
if (remaining == -1)
{
enabled = false;
src_rect.x = original_pos;
src_rect.w = original_width;
dst_rect.x = original_pos;
dst_rect.w = original_width;
}
}
}
}
// Actualiza el estado del efecto de sacudida
void update(SDL_FRect &src_rect, SDL_FRect &dst_rect) {
if (enabled) {
if (counter > 0) {
counter--;
} else {
counter = delay;
const auto SRC_DESP = (remaining % 2 == 0) ? 0 : desp;
const auto DST_DESP = (remaining % 2 == 1) ? 0 : desp;
src_rect.x = original_pos + SRC_DESP;
dst_rect.x = original_pos + DST_DESP;
remaining--;
if (remaining == -1) {
enabled = false;
src_rect.x = original_pos;
src_rect.w = original_width;
dst_rect.x = original_pos;
dst_rect.w = original_width;
}
}
}
}
bool isEnabled() const { return enabled; }
};
bool isEnabled() const { return enabled; }
};
#ifdef DEBUG
struct Debug
{
std::shared_ptr<Text> text;
bool show = false;
};
struct Debug {
std::shared_ptr<Text> text;
bool show = false;
};
#endif
// --- Singleton ---
static Screen *instance_;
// --- Singleton ---
static Screen *instance_;
// --- Objetos y punteros ---
SDL_Window *window_; // Ventana de la aplicación
SDL_Renderer *renderer_; // El renderizador de la ventana
SDL_Texture *game_canvas_; // Textura donde se dibuja todo antes de volcarse al renderizador
ServiceMenu *service_menu_; // Objeto para mostrar el menú de servicio
Notifier *notifier_; // Objeto para mostrar las notificaciones por pantalla
// --- Objetos y punteros ---
SDL_Window *window_; // Ventana de la aplicación
SDL_Renderer *renderer_; // El renderizador de la ventana
SDL_Texture *game_canvas_; // Textura donde se dibuja todo antes de volcarse al renderizador
ServiceMenu *service_menu_; // Objeto para mostrar el menú de servicio
Notifier *notifier_; // Objeto para mostrar las notificaciones por pantalla
// --- Variables de estado ---
SDL_FRect src_rect_; // Coordenadas de origen para dibujar la textura del juego
SDL_FRect dst_rect_; // Coordenadas destino para dibujar la textura del juego
FPS fps_; // Gestión de frames por segundo
std::string shader_source_; // Almacena el contenido del archivo GLSL
FlashEffect flash_effect_; // Efecto de flash en pantalla
ShakeEffect shake_effect_; // Efecto de agitar la pantalla
bool attenuate_effect_ = false; // Indica si la pantalla ha de estar atenuada
// --- Variables de estado ---
SDL_FRect src_rect_; // Coordenadas de origen para dibujar la textura del juego
SDL_FRect dst_rect_; // Coordenadas destino para dibujar la textura del juego
FPS fps_; // Gestión de frames por segundo
std::string shader_source_; // Almacena el contenido del archivo GLSL
FlashEffect flash_effect_; // Efecto de flash en pantalla
ShakeEffect shake_effect_; // Efecto de agitar la pantalla
bool attenuate_effect_ = false; // Indica si la pantalla ha de estar atenuada
#ifdef DEBUG
Debug debug_info_; // Información de debug
Debug debug_info_; // Información de debug
#endif
// --- Texto ---
std::shared_ptr<Text> text_; // Objeto para escribir texto en pantalla
// --- Texto ---
std::shared_ptr<Text> text_; // Objeto para escribir texto en pantalla
// --- Métodos internos ---
bool initSDLVideo(); // Arranca SDL VIDEO y crea la ventana
void renderFlash(); // Dibuja el efecto de flash en la pantalla
void renderShake(); // Aplica el efecto de agitar la pantalla
void renderInfo(); // Muestra información por pantalla
void renderScreen(); // Selecciona y ejecuta el método de renderizado adecuado
void loadShaders(); // Carga el contenido del archivo GLSL
void adjustWindowSize(); // Calcula el tamaño de la ventana
void getDisplayInfo(); // Obtiene información sobre la pantalla
void renderOverlays(); // Renderiza todos los overlays y efectos
void renderAttenuate(); // Atenúa la pantalla
void createText(); // Crea el objeto de texto
// --- Métodos internos ---
bool initSDLVideo(); // Arranca SDL VIDEO y crea la ventana
void renderFlash(); // Dibuja el efecto de flash en la pantalla
void renderShake(); // Aplica el efecto de agitar la pantalla
void renderInfo(); // Muestra información por pantalla
void renderScreen(); // Selecciona y ejecuta el método de renderizado adecuado
void loadShaders(); // Carga el contenido del archivo GLSL
void adjustWindowSize(); // Calcula el tamaño de la ventana
static void getDisplayInfo(); // Obtiene información sobre la pantalla
void renderOverlays(); // Renderiza todos los overlays y efectos
void renderAttenuate(); // Atenúa la pantalla
void createText(); // Crea el objeto de texto
// --- Constructores y destructor ---
Screen();
~Screen();
// --- Constructores y destructor ---
Screen();
~Screen();
};

View File

@@ -1,8 +1,7 @@
#include "section.h"
namespace Section
{
Name name;
Options options;
AttractMode attract_mode;
}
namespace Section {
Name name;
Options options;
AttractMode attract_mode;
} // namespace Section

View File

@@ -6,47 +6,43 @@
Proporciona variables globales para gestionar el flujo entre secciones.
*/
namespace Section
{
// --- Enumeraciones de secciones del programa ---
enum class Name
{
RESET, // Inicialización
LOGO, // Pantalla de logo
INTRO, // Introducción
TITLE, // Pantalla de título/menú principal
GAME, // Juego principal
HI_SCORE_TABLE, // Tabla de récords
GAME_DEMO, // Modo demo
INSTRUCTIONS, // Instrucciones
CREDITS, // Créditos
QUIT, // Salir del juego
};
namespace Section {
// --- Enumeraciones de secciones del programa ---
enum class Name {
RESET, // Inicialización
LOGO, // Pantalla de logo
INTRO, // Introducción
TITLE, // Pantalla de título/menú principal
GAME, // Juego principal
HI_SCORE_TABLE, // Tabla de récords
GAME_DEMO, // Modo demo
INSTRUCTIONS, // Instrucciones
CREDITS, // Créditos
QUIT, // Salir del juego
};
// --- Opciones para la sección actual ---
enum class Options
{
GAME_PLAY_1P, // Iniciar el juego con el jugador 1
GAME_PLAY_2P, // Iniciar el juego con el jugador 2
GAME_PLAY_BOTH, // Iniciar el juego con los dos jugadores
TITLE_TIME_OUT, // Timeout en el título
TITLE_1, // Opción 1 en el título
TITLE_2, // Opción 2 en el título
RELOAD, // Recargar sección
HI_SCORE_AFTER_PLAYING, // Mostrar récord tras jugar
SHUTDOWN, // Apagar el sistema
NONE, // Sin opción
};
// --- Opciones para la sección actual ---
enum class Options {
GAME_PLAY_1P, // Iniciar el juego con el jugador 1
GAME_PLAY_2P, // Iniciar el juego con el jugador 2
GAME_PLAY_BOTH, // Iniciar el juego con los dos jugadores
TITLE_TIME_OUT, // Timeout en el título
TITLE_1, // Opción 1 en el título
TITLE_2, // Opción 2 en el título
RELOAD, // Recargar sección
HI_SCORE_AFTER_PLAYING, // Mostrar récord tras jugar
SHUTDOWN, // Apagar el sistema
NONE, // Sin opción
};
// --- Modos para el Attract Mode ---
enum class AttractMode
{
TITLE_TO_DEMO, // Pasar de título a demo
TITLE_TO_LOGO, // Pasar de título a logo
};
// --- Modos para el Attract Mode ---
enum class AttractMode {
TITLE_TO_DEMO, // Pasar de título a demo
TITLE_TO_LOGO, // Pasar de título a logo
};
// --- Variables globales de estado ---
extern Name name; // Sección actual
extern Options options; // Opción seleccionada en la sección
extern AttractMode attract_mode; // Estado del Attract Mode
}
// --- Variables globales de estado ---
extern Name name; // Sección actual
extern Options options; // Opción seleccionada en la sección
extern AttractMode attract_mode; // Estado del Attract Mode
} // namespace Section

View File

@@ -1,7 +1,8 @@
// IWYU pragma: no_include <bits/std_abs.h>
#include "credits.h"
#include <SDL3/SDL.h> // Para SDL_RenderFillRect, SDL_RenderTexture
#include <SDL3/SDL.h> // Para SDL_RenderFillRect, SDL_RenderTexture
#include <algorithm> // Para max, min, clamp
#include <array> // Para array
#include <cmath> // Para abs
@@ -9,24 +10,24 @@
#include <string> // Para basic_string, string
#include <vector> // Para vector
#include "audio.h" // Para Audio
#include "balloon_manager.h" // Para BalloonManager
#include "fade.h" // Para Fade, FadeType, FadeMode
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input, INPUT_ALLOW_REPEAT
#include "lang.h" // Para getText
#include "param.h" // Para Param, param, ParamGame, ParamFade
#include "player.h" // Para Player, PlayerState
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name
#include "sprite.h" // Para Sprite
#include "text.h" // Para Text, TEXT_CENTER, TEXT_SHADOW
#include "texture.h" // Para Texture
#include "tiled_bg.h" // Para TiledBG, TiledBGMode
#include "ui/service_menu.h" // Para ServiceMenu
#include "utils.h" // Para Color, Zone, SHADOW_TEXT_COLOR, NO_TEXT...
#include "audio.h" // Para Audio
#include "balloon_manager.h" // Para BalloonManager
#include "fade.h" // Para Fade, FadeType, FadeMode
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input, INPUT_ALLOW_REPEAT
#include "lang.h" // Para getText
#include "param.h" // Para Param, param, ParamGame, ParamFade
#include "player.h" // Para Player, PlayerState
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name
#include "sprite.h" // Para Sprite
#include "text.h" // Para Text, TEXT_CENTER, TEXT_SHADOW
#include "texture.h" // Para Texture
#include "tiled_bg.h" // Para TiledBG, TiledBGMode
#include "ui/service_menu.h" // Para ServiceMenu
#include "utils.h" // Para Color, Zone, SHADOW_TEXT_COLOR, NO_TEXT...
// Textos
constexpr const char TEXT_COPYRIGHT[] = "@2020,2025 JailDesigner";
@@ -38,10 +39,8 @@ Credits::Credits()
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, param.game.width, param.game.height)),
canvas_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height))
{
if (!text_texture_)
{
canvas_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)) {
if (!text_texture_) {
throw std::runtime_error("Failed to create SDL texture for text.");
}
Section::name = Section::Name::CREDITS;
@@ -67,8 +66,7 @@ Credits::Credits()
}
// Destructor
Credits::~Credits()
{
Credits::~Credits() {
SDL_DestroyTexture(text_texture_);
SDL_DestroyTexture(canvas_);
resetVolume();
@@ -76,26 +74,21 @@ Credits::~Credits()
}
// Bucle principal
void Credits::run()
{
while (Section::name == Section::Name::CREDITS)
{
void Credits::run() {
while (Section::name == Section::Name::CREDITS) {
checkInput();
update();
checkEvents(); // Tiene que ir antes del render
checkEvents(); // Tiene que ir antes del render
render();
}
}
// Actualiza las variables
void Credits::update()
{
if (SDL_GetTicks() - ticks_ > param.game.speed)
{
void Credits::update() {
if (SDL_GetTicks() - ticks_ > param.game.speed) {
ticks_ = SDL_GetTicks();
const int REPEAT = want_to_pass_ ? 4 : 1;
for (int i = 0; i < REPEAT; ++i)
{
for (int i = 0; i < REPEAT; ++i) {
tiled_bg_->update();
cycleColors();
balloon_manager_->update();
@@ -113,8 +106,7 @@ void Credits::update()
}
// Dibuja Credits::en patalla
void Credits::render()
{
void Credits::render() {
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
@@ -126,30 +118,23 @@ void Credits::render()
}
// Comprueba el manejador de eventos
void Credits::checkEvents()
{
void Credits::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event))
{
while (SDL_PollEvent(&event)) {
GlobalEvents::check(event);
}
}
// Comprueba las entradas
void Credits::checkInput()
{
void Credits::checkInput() {
Input::get()->update();
if (!ServiceMenu::get()->isEnabled())
{
if (!ServiceMenu::get()->isEnabled()) {
// Comprueba si se ha pulsado cualquier botón (de los usados para jugar)
if (Input::get()->checkAnyButton(INPUT_ALLOW_REPEAT))
{
if (Input::get()->checkAnyButton(INPUT_ALLOW_REPEAT)) {
want_to_pass_ = true;
fading_ = mini_logo_on_position_;
}
else
{
} else {
want_to_pass_ = false;
}
}
@@ -159,8 +144,7 @@ void Credits::checkInput()
}
// Crea la textura con el texto
void Credits::fillTextTexture()
{
void Credits::fillTextTexture() {
auto text = Resource::get()->getText("smb2");
auto text_grad = Resource::get()->getText("smb2_grad");
SDL_SetRenderTarget(Screen::get()->getRenderer(), text_texture_);
@@ -248,8 +232,7 @@ void Credits::fillTextTexture()
}
// Dibuja todos los sprites en la textura
void Credits::fillCanvas()
{
void Credits::fillCanvas() {
// Cambia el destino del renderizador
auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
SDL_SetRenderTarget(Screen::get()->getRenderer(), canvas_);
@@ -279,8 +262,7 @@ void Credits::fillCanvas()
SDL_RenderRect(Screen::get()->getRenderer(), &red_rect);
// Si el mini_logo está en su destino, lo dibuja encima de lo anterior
if (mini_logo_on_position_)
{
if (mini_logo_on_position_) {
SDL_RenderTexture(Screen::get()->getRenderer(), text_texture_, &mini_logo_rect_src_, &mini_logo_rect_dst_);
}
@@ -293,72 +275,57 @@ void Credits::fillCanvas()
}
// Actualiza el destino de los rectangulos de las texturas
void Credits::updateTextureDstRects()
{
if (counter_ % 10 == 0)
{
void Credits::updateTextureDstRects() {
if (counter_ % 10 == 0) {
// Comprueba la posición de la textura con los titulos de credito
if (credits_rect_dst_.y + credits_rect_dst_.h > play_area_.y)
{
if (credits_rect_dst_.y + credits_rect_dst_.h > play_area_.y) {
--credits_rect_dst_.y;
}
// Comprueba la posición de la textura con el mini_logo
if (mini_logo_rect_dst_.y == mini_logo_final_pos_)
{
if (mini_logo_rect_dst_.y == mini_logo_final_pos_) {
mini_logo_on_position_ = true;
// Si el jugador quiere pasar los titulos de credito, el fade se inicia solo
if (want_to_pass_)
{
if (want_to_pass_) {
fading_ = true;
}
// Se activa el contador para evitar que la sección sea infinita
if (counter_prevent_endless_ == 1000)
{
if (counter_prevent_endless_ == 1000) {
fading_ = true;
}
else
{
} else {
++counter_prevent_endless_;
}
}
else
{
} else {
--mini_logo_rect_dst_.y;
}
}
}
// Tira globos al escenario
void Credits::throwBalloons()
{
void Credits::throwBalloons() {
constexpr int speed = 200;
const std::vector<int> sets = {0, 63, 25, 67, 17, 75, 13, 50};
if (counter_ > ((sets.size() - 1) * speed) * 3)
{
if (counter_ > ((sets.size() - 1) * speed) * 3) {
return;
}
if (counter_ % speed == 0)
{
if (counter_ % speed == 0) {
const int index = (counter_ / speed) % sets.size();
balloon_manager_->deploySet(sets.at(index), -60);
}
if (counter_ % (speed * 4) == 0 && counter_ > 0)
{
if (counter_ % (speed * 4) == 0 && counter_ > 0) {
balloon_manager_->createPowerBall();
}
}
// Inicializa los jugadores
void Credits::initPlayers()
{
std::vector<std::vector<std::shared_ptr<Texture>>> player_textures; // Vector con todas las texturas de los jugadores;
std::vector<std::vector<std::string>> player_animations; // Vector con las animaciones del jugador
void Credits::initPlayers() {
std::vector<std::vector<std::shared_ptr<Texture>>> player_textures; // Vector con todas las texturas de los jugadores;
std::vector<std::vector<std::string>> player_animations; // Vector con las animaciones del jugador
// Texturas - Player1
{
@@ -397,14 +364,11 @@ void Credits::initPlayers()
}
// Actualiza los rectangulos negros
void Credits::updateBlackRects()
{
void Credits::updateBlackRects() {
static int current_step = steps_;
if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1)
{
if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1) {
// Si los rectangulos superior e inferior no han llegado al centro
if (counter_ % 4 == 0)
{
if (counter_ % 4 == 0) {
// Incrementa la altura del rectangulo superior
top_black_rect_.h = std::min(top_black_rect_.h + 1, param.game.game_area.center_y - 1);
@@ -415,12 +379,9 @@ void Credits::updateBlackRects()
--current_step;
setVolume(static_cast<int>(initial_volume_ * current_step / steps_));
}
}
else
{
} else {
// Si los rectangulos superior e inferior han llegado al centro
if (left_black_rect_.w != param.game.game_area.center_x && right_black_rect_.x != param.game.game_area.center_x)
{
if (left_black_rect_.w != param.game.game_area.center_x && right_black_rect_.x != param.game.game_area.center_x) {
constexpr int SPEED = 2;
// Si los rectangulos izquierdo y derecho no han llegado al centro
// Incrementa la anchura del rectangulo situado a la izquierda
@@ -432,18 +393,13 @@ void Credits::updateBlackRects()
--current_step;
setVolume(static_cast<int>(initial_volume_ * current_step / steps_));
}
else
{
} else {
// Si los rectangulos izquierdo y derecho han llegado al centro
setVolume(0);
Audio::get()->stopMusic();
if (counter_pre_fade_ == 400)
{
if (counter_pre_fade_ == 400) {
fade_out_->activate();
}
else
{
} else {
++counter_pre_fade_;
}
}
@@ -451,8 +407,7 @@ void Credits::updateBlackRects()
}
// Actualiza el rectangulo rojo
void Credits::updateRedRect()
{
void Credits::updateRedRect() {
red_rect.x = left_black_rect_.x + left_black_rect_.w;
red_rect.y = top_black_rect_.y + top_black_rect_.h - 1;
red_rect.w = right_black_rect_.x - red_rect.x;
@@ -460,76 +415,66 @@ void Credits::updateRedRect()
}
// Actualiza el estado de fade
void Credits::updateAllFades()
{
if (fading_)
{
void Credits::updateAllFades() {
if (fading_) {
updateBlackRects();
updateRedRect();
}
fade_in_->update();
if (fade_in_->hasEnded())
{
if (fade_in_->hasEnded()) {
Audio::get()->playMusic("credits.ogg");
}
fade_out_->update();
if (fade_out_->hasEnded())
{
if (fade_out_->hasEnded()) {
Section::name = Section::Name::HI_SCORE_TABLE;
}
}
// Establece el nivel de volumen
void Credits::setVolume(int amount)
{
void Credits::setVolume(int amount) {
Options::audio.music.volume = std::clamp(amount, 0, 100);
Audio::get()->setMusicVolume(Options::audio.music.volume);
}
// Reestablece el nivel de volumen
void Credits::resetVolume()
{
void Credits::resetVolume() {
Options::audio.music.volume = initial_volume_;
Audio::get()->setMusicVolume(Options::audio.music.volume);
}
// Cambia el color del fondo
void Credits::cycleColors()
{
void Credits::cycleColors() {
// constexpr int UPPER_LIMIT = 255; // Límite superior
// constexpr int LOWER_LIMIT = 80; // Límite inferior
constexpr int UPPER_LIMIT = 140; // Límite superior
constexpr int LOWER_LIMIT = 30; // Límite inferior
constexpr int UPPER_LIMIT = 140; // Límite superior
constexpr int LOWER_LIMIT = 30; // Límite inferior
static float r = static_cast<float>(UPPER_LIMIT);
static float g = static_cast<float>(LOWER_LIMIT);
static float b = static_cast<float>(LOWER_LIMIT);
static float stepR = -0.5f; // Paso flotante para transiciones suaves
static float stepR = -0.5f; // Paso flotante para transiciones suaves
static float stepG = 0.3f;
static float stepB = 0.1f;
// Ajustar valores de R
r += stepR;
if (r >= UPPER_LIMIT || r <= LOWER_LIMIT)
{
stepR = -stepR; // Cambia de dirección al alcanzar los límites
if (r >= UPPER_LIMIT || r <= LOWER_LIMIT) {
stepR = -stepR; // Cambia de dirección al alcanzar los límites
}
// Ajustar valores de G
g += stepG;
if (g >= UPPER_LIMIT || g <= LOWER_LIMIT)
{
stepG = -stepG; // Cambia de dirección al alcanzar los límites
if (g >= UPPER_LIMIT || g <= LOWER_LIMIT) {
stepG = -stepG; // Cambia de dirección al alcanzar los límites
}
// Ajustar valores de B
b += stepB;
if (b >= UPPER_LIMIT || b <= LOWER_LIMIT)
{
stepB = -stepB; // Cambia de dirección al alcanzar los límites
if (b >= UPPER_LIMIT || b <= LOWER_LIMIT) {
stepB = -stepB; // Cambia de dirección al alcanzar los límites
}
// Aplicar el color, redondeando a enteros antes de usar
@@ -538,19 +483,15 @@ void Credits::cycleColors()
}
// Actualza los jugadores
void Credits::updatePlayers()
{
for (auto &player : players_)
{
void Credits::updatePlayers() {
for (auto &player : players_) {
player->update();
}
}
// Renderiza los jugadores
void Credits::renderPlayers()
{
for (auto const &player : players_)
{
void Credits::renderPlayers() {
for (auto const &player : players_) {
player->render();
}
}

View File

@@ -1,12 +1,13 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect, Uint32, SDL_Texture, Uint64
#include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_FRect, Uint32, SDL_Texture, Uint64
#include "options.h" // Para AudioOptions, MusicOptions, audio
#include "param.h" // Para Param, ParamGame, param
#include "utils.h" // Para Zone, Color
#include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector
#include "options.h" // Para AudioOptions, MusicOptions, audio
#include "param.h" // Para Param, ParamGame, param
#include "utils.h" // Para Zone, Color
// Declaraciones adelantadas
class BalloonManager;
@@ -14,9 +15,8 @@ class Fade;
class Player;
class TiledBG;
class Credits
{
public:
class Credits {
public:
// --- Constructores y destructor ---
Credits();
~Credits();
@@ -24,40 +24,40 @@ public:
// --- Bucle principal ---
void run();
private:
private:
// --- Constantes de clase ---
static constexpr int PLAY_AREA_HEIGHT = 200;
// --- Objetos principales ---
std::unique_ptr<BalloonManager> balloon_manager_; // Gestión de globos
std::unique_ptr<TiledBG> tiled_bg_; // Mosaico animado de fondo
std::unique_ptr<Fade> fade_in_; // Fundido de entrada
std::unique_ptr<Fade> fade_out_; // Fundido de salida
std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores
std::unique_ptr<BalloonManager> balloon_manager_; // Gestión de globos
std::unique_ptr<TiledBG> tiled_bg_; // Mosaico animado de fondo
std::unique_ptr<Fade> fade_in_; // Fundido de entrada
std::unique_ptr<Fade> fade_out_; // Fundido de salida
std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores
// --- Gestión de texturas ---
SDL_Texture *text_texture_; // Textura con el texto de créditos
SDL_Texture *canvas_; // Textura donde se dibuja todo
SDL_Texture *text_texture_; // Textura con el texto de créditos
SDL_Texture *canvas_; // Textura donde se dibuja todo
// --- Temporización y contadores ---
Uint64 ticks_ = 0; // Control de velocidad del programa
Uint32 counter_ = 0; // Contador principal de lógica
Uint32 counter_pre_fade_ = 0; // Activación del fundido final
Uint32 counter_prevent_endless_ = 0; // Prevención de bucle infinito
Uint64 ticks_ = 0; // Control de velocidad del programa
Uint32 counter_ = 0; // Contador principal de lógica
Uint32 counter_pre_fade_ = 0; // Activación del fundido final
Uint32 counter_prevent_endless_ = 0; // Prevención de bucle infinito
// --- Variables de estado ---
bool fading_ = false; // Estado del fade final
bool want_to_pass_ = false; // Jugador quiere saltarse créditos
bool mini_logo_on_position_ = false; // Minilogo en posición final
bool fading_ = false; // Estado del fade final
bool want_to_pass_ = false; // Jugador quiere saltarse créditos
bool mini_logo_on_position_ = false; // Minilogo en posición final
// --- Diseño y posicionamiento ---
float black_bars_size_ = (param.game.game_area.rect.h - PLAY_AREA_HEIGHT) / 2; // Tamaño de las barras negras
int mini_logo_final_pos_ = 0; // Posición final del minilogo
Color color_; // Color usado para los efectos
float black_bars_size_ = (param.game.game_area.rect.h - PLAY_AREA_HEIGHT) / 2; // Tamaño de las barras negras
int mini_logo_final_pos_ = 0; // Posición final del minilogo
Color color_; // Color usado para los efectos
// --- Control de audio ---
int initial_volume_ = Options::audio.music.volume; // Volumen inicial
int steps_ = 0; // Pasos para reducir audio
int initial_volume_ = Options::audio.music.volume; // Volumen inicial
int steps_ = 0; // Pasos para reducir audio
// --- Rectángulos de renderizado ---
// Texto de créditos
@@ -98,32 +98,32 @@ private:
2};
// Borde para la ventana
SDL_FRect red_rect = play_area_; // Delimitador de ventana
SDL_FRect red_rect = play_area_; // Delimitador de ventana
// --- Métodos del bucle principal ---
void update(); // Actualización principal de la lógica
void render(); // Renderizado de la escena
void checkEvents(); // Manejo de eventos
void checkInput(); // Procesamiento de entrada
void update(); // Actualización principal de la lógica
void render(); // Renderizado de la escena
void checkEvents(); // Manejo de eventos
void checkInput(); // Procesamiento de entrada
// --- Métodos de renderizado ---
void fillTextTexture(); // Crear textura de texto de créditos
void fillCanvas(); // Renderizar todos los sprites y fondos
void updateTextureDstRects(); // Actualizar destinos de texturas
void renderPlayers(); // Renderiza los jugadores
void fillTextTexture(); // Crear textura de texto de créditos
void fillCanvas(); // Renderizar todos los sprites y fondos
void updateTextureDstRects(); // Actualizar destinos de texturas
void renderPlayers(); // Renderiza los jugadores
// --- Métodos de lógica del juego ---
void throwBalloons(); // Lanzar globos al escenario
void initPlayers(); // Inicializar jugadores
void updateAllFades(); // Actualizar estados de fade
void cycleColors(); // Cambiar colores de fondo
void updatePlayers(); // Actualza los jugadores
void throwBalloons(); // Lanzar globos al escenario
void initPlayers(); // Inicializar jugadores
void updateAllFades(); // Actualizar estados de fade
void cycleColors(); // Cambiar colores de fondo
void updatePlayers(); // Actualza los jugadores
// --- Métodos de interfaz ---
void updateBlackRects(); // Actualizar rectángulos negros (letterbox)
void updateRedRect(); // Actualizar rectángulo rojo (borde)
void updateBlackRects(); // Actualizar rectángulos negros (letterbox)
void updateRedRect(); // Actualizar rectángulo rojo (borde)
// --- Métodos de audio ---
void setVolume(int amount); // Establecer volumen
void resetVolume(); // Restablecer volumen
void setVolume(int amount); // Establecer volumen
void resetVolume(); // Restablecer volumen
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,15 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_Event, SDL_Renderer, SDL_Texture, Uint64, Uint8
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_Event, SDL_Renderer, SDL_Texture, Uint64, Uint8
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h" // Para SettingsOptions, settings, DifficultyCode (ptr only)
#include "player.h" // Para Player
#include "utils.h" // Para Demo
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h" // Para SettingsOptions, settings, DifficultyCode (ptr only)
#include "player.h" // Para Player
#include "utils.h" // Para Demo
class Background;
class Balloon;
@@ -35,216 +36,213 @@ constexpr bool GAME_MODE_DEMO_ON = true;
constexpr int TOTAL_SCORE_DATA = 3;
// Clase Game
class Game
{
public:
// Constructor
Game(int playerID, int current_stage, bool demo);
class Game {
public:
// Constructor
Game(int playerID, int current_stage, bool demo);
// Destructor
~Game();
// Destructor
~Game();
// Bucle principal del juego
void run();
// Bucle principal del juego
void run();
private:
// --- Tipos internos ---
enum class GameState
{
FADE_IN,
ENTERING_PLAYER,
SHOWING_GET_READY_MESSAGE,
PLAYING,
COMPLETED,
GAME_OVER,
};
private:
// --- Tipos internos ---
enum class GameState {
FADE_IN,
ENTERING_PLAYER,
SHOWING_GET_READY_MESSAGE,
PLAYING,
COMPLETED,
GAME_OVER,
};
// --- Constantes internas ---
static constexpr int HELP_COUNTER_ = 1000;
static constexpr int GAME_COMPLETED_START_FADE_ = 500;
static constexpr int GAME_COMPLETED_END_ = 700;
static constexpr int GAME_OVER_COUNTER_ = 350;
static constexpr int TIME_STOPPED_COUNTER_ = 360;
static constexpr int ITEM_POINTS_1_DISK_ODDS_ = 10;
static constexpr int ITEM_POINTS_2_GAVINA_ODDS_ = 6;
static constexpr int ITEM_POINTS_3_PACMAR_ODDS_ = 3;
static constexpr int ITEM_CLOCK_ODDS_ = 5;
static constexpr int ITEM_COFFEE_ODDS_ = 5;
static constexpr int ITEM_POWER_BALL_ODDS_ = 0;
static constexpr int ITEM_COFFEE_MACHINE_ODDS_ = 4;
// --- Constantes internas ---
static constexpr int HELP_COUNTER_ = 1000;
static constexpr int GAME_COMPLETED_START_FADE_ = 500;
static constexpr int GAME_COMPLETED_END_ = 700;
static constexpr int GAME_OVER_COUNTER_ = 350;
static constexpr int TIME_STOPPED_COUNTER_ = 360;
static constexpr int ITEM_POINTS_1_DISK_ODDS_ = 10;
static constexpr int ITEM_POINTS_2_GAVINA_ODDS_ = 6;
static constexpr int ITEM_POINTS_3_PACMAR_ODDS_ = 3;
static constexpr int ITEM_CLOCK_ODDS_ = 5;
static constexpr int ITEM_COFFEE_ODDS_ = 5;
static constexpr int ITEM_POWER_BALL_ODDS_ = 0;
static constexpr int ITEM_COFFEE_MACHINE_ODDS_ = 4;
// --- Estructuras ---
struct Helper
{
bool need_coffee; // Indica si se necesitan cafes
bool need_coffee_machine; // Indica si se necesita PowerUp
bool need_power_ball; // Indica si se necesita una PowerBall
int counter; // Contador para no dar ayudas consecutivas
int item_disk_odds; // Probabilidad de aparición del objeto
int item_gavina_odds; // Probabilidad de aparición del objeto
int item_pacmar_odds; // Probabilidad de aparición del objeto
int item_clock_odds; // Probabilidad de aparición del objeto
int item_coffee_odds; // Probabilidad de aparición del objeto
int item_coffee_machine_odds; // Probabilidad de aparición del objeto
// --- Estructuras ---
struct Helper {
bool need_coffee; // Indica si se necesitan cafes
bool need_coffee_machine; // Indica si se necesita PowerUp
bool need_power_ball; // Indica si se necesita una PowerBall
int counter; // Contador para no dar ayudas consecutivas
int item_disk_odds; // Probabilidad de aparición del objeto
int item_gavina_odds; // Probabilidad de aparición del objeto
int item_pacmar_odds; // Probabilidad de aparición del objeto
int item_clock_odds; // Probabilidad de aparición del objeto
int item_coffee_odds; // Probabilidad de aparición del objeto
int item_coffee_machine_odds; // Probabilidad de aparición del objeto
Helper()
: need_coffee(false),
need_coffee_machine(false),
need_power_ball(false),
counter(HELP_COUNTER_),
item_disk_odds(ITEM_POINTS_1_DISK_ODDS_),
item_gavina_odds(ITEM_POINTS_2_GAVINA_ODDS_),
item_pacmar_odds(ITEM_POINTS_3_PACMAR_ODDS_),
item_clock_odds(ITEM_CLOCK_ODDS_),
item_coffee_odds(ITEM_COFFEE_ODDS_),
item_coffee_machine_odds(ITEM_COFFEE_MACHINE_ODDS_) {}
};
Helper()
: need_coffee(false),
need_coffee_machine(false),
need_power_ball(false),
counter(HELP_COUNTER_),
item_disk_odds(ITEM_POINTS_1_DISK_ODDS_),
item_gavina_odds(ITEM_POINTS_2_GAVINA_ODDS_),
item_pacmar_odds(ITEM_POINTS_3_PACMAR_ODDS_),
item_clock_odds(ITEM_CLOCK_ODDS_),
item_coffee_odds(ITEM_COFFEE_ODDS_),
item_coffee_machine_odds(ITEM_COFFEE_MACHINE_ODDS_) {}
};
// --- Objetos y punteros ---
SDL_Renderer *renderer_; // El renderizador de la ventana
Screen *screen_; // Objeto encargado de dibujar en pantalla
Input *input_; // Manejador de entrada
Scoreboard *scoreboard_; // Objeto para dibujar el marcador
// --- Objetos y punteros ---
SDL_Renderer *renderer_; // El renderizador de la ventana
Screen *screen_; // Objeto encargado de dibujar en pantalla
Input *input_; // Manejador de entrada
Scoreboard *scoreboard_; // Objeto para dibujar el marcador
std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego
std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego
SDL_Texture *canvas_; // Textura para dibujar la zona de juego
SDL_Texture *canvas_; // Textura para dibujar la zona de juego
std::vector<std::shared_ptr<Player>> players_; // Vector con los jugadores
std::vector<std::unique_ptr<Bullet>> bullets_; // Vector con las balas
std::vector<std::unique_ptr<Item>> items_; // Vector con los items
std::vector<std::unique_ptr<SmartSprite>> smart_sprites_; // Vector con los smartsprites
std::vector<std::unique_ptr<PathSprite>> path_sprites_; // Vector con los pathsprites
std::vector<std::shared_ptr<Player>> players_; // Vector con los jugadores
std::vector<std::unique_ptr<Bullet>> bullets_; // Vector con las balas
std::vector<std::unique_ptr<Item>> items_; // Vector con los items
std::vector<std::unique_ptr<SmartSprite>> smart_sprites_; // Vector con los smartsprites
std::vector<std::unique_ptr<PathSprite>> path_sprites_; // Vector con los pathsprites
std::vector<std::shared_ptr<Texture>> item_textures_; // Vector con las texturas de los items
std::vector<std::vector<std::shared_ptr<Texture>>> player_textures_; // Vector con todas las texturas de los jugadores
std::vector<std::shared_ptr<Texture>> item_textures_; // Vector con las texturas de los items
std::vector<std::vector<std::shared_ptr<Texture>>> player_textures_; // Vector con todas las texturas de los jugadores
std::vector<std::shared_ptr<Texture>> game_text_textures_; // Vector con las texturas para los sprites con textos
std::vector<std::shared_ptr<Texture>> game_text_textures_; // Vector con las texturas para los sprites con textos
std::vector<std::vector<std::string>> item_animations_; // Vector con las animaciones de los items
std::vector<std::vector<std::string>> player_animations_; // Vector con las animaciones del jugador
std::vector<std::vector<std::string>> item_animations_; // Vector con las animaciones de los items
std::vector<std::vector<std::string>> player_animations_; // Vector con las animaciones del jugador
std::unique_ptr<Fade> fade_in_; // Objeto para renderizar fades
std::unique_ptr<Fade> fade_out_; // Objeto para renderizar fades
std::unique_ptr<BalloonManager> balloon_manager_; // Objeto para gestionar los globos
std::unique_ptr<Tabe> tabe_; // Objeto para gestionar el Tabe Volaor
std::vector<Path> paths_; // Vector con los recorridos precalculados almacenados
std::unique_ptr<Fade> fade_in_; // Objeto para renderizar fades
std::unique_ptr<Fade> fade_out_; // Objeto para renderizar fades
std::unique_ptr<BalloonManager> balloon_manager_; // Objeto para gestionar los globos
std::unique_ptr<Tabe> tabe_; // Objeto para gestionar el Tabe Volaor
std::vector<Path> paths_; // Vector con los recorridos precalculados almacenados
// --- Variables de estado ---
HiScoreEntry hi_score_ = HiScoreEntry(
Options::settings.hi_score_table[0].name,
Options::settings.hi_score_table[0].score); // Máxima puntuación y nombre de quien la ostenta
// --- Variables de estado ---
HiScoreEntry hi_score_ = HiScoreEntry(
Options::settings.hi_score_table[0].name,
Options::settings.hi_score_table[0].score); // Máxima puntuación y nombre de quien la ostenta
Demo demo_; // Variable con todas las variables relacionadas con el modo demo
Options::DifficultyCode difficulty_ = Options::settings.difficulty; // Dificultad del juego
Helper helper_; // Variable para gestionar las ayudas
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
bool coffee_machine_enabled_ = false; // Indica si hay una máquina de café en el terreno de juego
bool hi_score_achieved_ = false; // Indica si se ha superado la puntuación máxima
bool paused_ = false; // Indica si el juego está pausado (no se deberia de poder utilizar en el modo arcade)
// bool paused_by_service_menu_ = false;
float difficulty_score_multiplier_; // Multiplicador de puntos en función de la dificultad
int counter_ = 0; // Contador para el juego
int game_completed_counter_ = 0; // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más enemigos
int game_over_counter_ = GAME_OVER_COUNTER_; // Contador para el estado de fin de partida
int time_stopped_counter_ = 0; // Temporizador para llevar la cuenta del tiempo detenido
int total_power_to_complete_game_; // La suma del poder necesario para completar todas las fases
int menace_current_ = 0; // Nivel de amenaza actual
int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos
GameState state_ = GameState::FADE_IN; // Estado
std::vector<std::shared_ptr<Player>> players_to_reorder;
Demo demo_; // Variable con todas las variables relacionadas con el modo demo
Options::DifficultyCode difficulty_ = Options::settings.difficulty; // Dificultad del juego
Helper helper_; // Variable para gestionar las ayudas
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
bool coffee_machine_enabled_ = false; // Indica si hay una máquina de café en el terreno de juego
bool hi_score_achieved_ = false; // Indica si se ha superado la puntuación máxima
bool paused_ = false; // Indica si el juego está pausado (no se deberia de poder utilizar en el modo arcade)
// bool paused_by_service_menu_ = false;
float difficulty_score_multiplier_; // Multiplicador de puntos en función de la dificultad
int counter_ = 0; // Contador para el juego
int game_completed_counter_ = 0; // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más enemigos
int game_over_counter_ = GAME_OVER_COUNTER_; // Contador para el estado de fin de partida
int time_stopped_counter_ = 0; // Temporizador para llevar la cuenta del tiempo detenido
int total_power_to_complete_game_; // La suma del poder necesario para completar todas las fases
int menace_current_ = 0; // Nivel de amenaza actual
int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos
GameState state_ = GameState::FADE_IN; // Estado
std::vector<std::shared_ptr<Player>> players_to_reorder;
#ifdef DEBUG
bool auto_pop_balloons_ = false; // Si es true, incrementa automaticamente los globos explotados
bool auto_pop_balloons_ = false; // Si es true, incrementa automaticamente los globos explotados
// Comprueba los eventos en el modo DEBUG
void checkDebugEvents(const SDL_Event &event);
// Comprueba los eventos en el modo DEBUG
void checkDebugEvents(const SDL_Event &event);
#endif
// --- Métodos internos ---
void update(); // Actualiza el juego
void render(); // Dibuja el juego
void checkEvents(); // Comprueba los eventos que hay en cola
void setResources(); // Asigna texturas y animaciones
void updateHiScore(); // Actualiza el valor de HiScore en caso necesario
void updatePlayers(); // Actualiza las variables del jugador
void renderPlayers(); // Dibuja a los jugadores
void updateStage(); // Comprueba si hay cambio de fase y actualiza las variables
void updateGameStateGameOver(); // Actualiza el estado de fin de la partida
void destroyAllItems(); // Destruye todos los items
std::shared_ptr<Balloon> checkPlayerBalloonCollision(std::shared_ptr<Player> &player); // Comprueba la colisión entre el jugador y los globos activos
void checkPlayerItemCollision(std::shared_ptr<Player> &player); // Comprueba la colisión entre el jugador y los items
void checkBulletCollision(); // Comprueba y procesa la colisión de las balas
void updateBullets(); // Mueve las balas activas
void renderBullets(); // Pinta las balas activas
void createBullet(int x, int y, BulletType kind, bool powered_up, int owner); // Crea un objeto bala
void freeBullets(); // Vacia el vector de balas
void updateItems(); // Actualiza los items
void renderItems(); // Pinta los items activos
ItemType dropItem(); // Devuelve un item en función del azar
void createItem(ItemType type, float x, float y); // Crea un objeto item
void freeItems(); // Vacia el vector de items
void createItemText(int x, std::shared_ptr<Texture> texture); // Crea un objeto PathSprite
void createMessage(const std::vector<Path> &paths, std::shared_ptr<Texture> texture); // Crea un objeto PathSprite
void freeSmartSprites(); // Vacia el vector de smartsprites
void freePathSprites(); // Vacia el vector de pathsprites
void throwCoffee(int x, int y); // Crea un SpriteSmart para arrojar el item café al recibir un impacto
void updateSmartSprites(); // Actualiza los SpriteSmarts
void renderSmartSprites(); // Pinta los SpriteSmarts activos
void updatePathSprites(); // Actualiza los PathSprites
void renderPathSprites(); // Pinta los PathSprites activos
void handlePlayerCollision(std::shared_ptr<Player> &player); // Acciones a realizar cuando el jugador colisiona con un globo
void updateTimeStopped(); // Actualiza y comprueba el valor de la variable
void updateBackground(); // Actualiza el fondo
void initPaths(); // Inicializa las variables que contienen puntos de ruta para mover objetos
void enableTimeStopItem(); // Habilita el efecto del item de detener el tiempo
void disableTimeStopItem(); // Deshabilita el efecto del item de detener el tiempo
void updateHelper(); // Actualiza las variables de ayuda
bool allPlayersAreWaitingOrGameOver(); // Comprueba si todos los jugadores han terminado de jugar
bool allPlayersAreGameOver(); // Comprueba si todos los jugadores han terminado de jugar
bool allPlayersAreNotPlaying(); // Comprueba si todos los jugadores han terminado de jugar
void updateScoreboard(); // Actualiza el marcador
void fillCanvas(); // Dibuja los elementos de la zona de juego en su textura
void pause(bool value); // Pausa el juego
void addScoreToScoreBoard(const std::shared_ptr<Player> &player); // Añade una puntuación a la tabla de records
void checkAndUpdatePlayerStatus(int active_player_index, int inactive_player_index); // Saca del estado de GAME OVER al jugador si el otro está activo
void checkPlayersStatusPlaying(); // Comprueba el estado de juego de los jugadores
std::shared_ptr<Player> getPlayer(int id); // Obtiene un jugador a partir de su "id"
int getController(int playerId); // Obtiene un controlador a partir del "id" del jugador
void checkInput(); // Gestiona la entrada durante el juego
void checkPauseInput(); // Verifica si alguno de los controladores ha solicitado una pausa y actualiza el estado de pausa del juego.
void DEMO_handleInput(); // Gestiona las entradas de los jugadores en el modo demo, incluyendo movimientos y disparos automáticos.
void DEMO_handlePassInput(); // Gestiona las entradas de los jugadores en el modo demo para saltarse la demo.
void DEMO_handlePlayerInput(const std::shared_ptr<Player> &player, int index); // Procesa las entradas para un jugador específico durante el modo demo.
void handleFireInput(const std::shared_ptr<Player> &player, BulletType bulletType); // Maneja el disparo de un jugador, incluyendo la creación de balas y la gestión del tiempo de espera entre disparos.
void handlePlayersInput(); // Gestiona las entradas de todos los jugadores en el modo normal (fuera del modo demo).
void handleNormalPlayerInput(const std::shared_ptr<Player> &player); // Maneja las entradas de movimiento y disparo para un jugador en modo normal.
void handleFireInputs(const std::shared_ptr<Player> &player, bool autofire, int controllerIndex); // Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado.
void handlePlayerContinue(const std::shared_ptr<Player> &player); // Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio.
void handleNameInput(const std::shared_ptr<Player> &player); // Procesa las entradas para la introducción del nombre del jugador.
void initDemo(int player_id); // Inicializa las variables para el modo DEMO
void setTotalPower(); // Calcula el poder total necesario para completar el juego
void initScoreboard(); // Inicializa el marcador
void initDifficultyVars(); // Inicializa las opciones relacionadas con la dificultad
void initPlayers(int player_id); // Inicializa los jugadores
void playMusic(); // Hace sonar la música
void stopMusic(); // Detiene la música
void playSound(const std::string &name); // Hace sonar un sonido
void updateDemo(); // Actualiza las variables durante el modo demo
void updateGameStateFadeIn(); // Actualiza las variables durante dicho estado
void updateGameStateEnteringPlayer(); // Actualiza las variables durante dicho estado
void updateGameStateShowingGetReadyMessage(); // Actualiza las variables durante dicho estado
void updateGameStatePlaying(); // Actualiza las variables durante el transcurso normal del juego
void updateGameStateCompleted(); // Gestiona eventos para el estado del final del juego
void checkState(); // Comprueba el estado del juego
void cleanVectors(); // Vacía los vectores de elementos deshabilitados
void updateMenace(); // Gestiona el nivel de amenaza
void evaluateAndSetMenace(); // Calcula y establece el valor de amenaza en funcion de los globos activos
void checkAndUpdateBalloonSpeed(); // Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
void setState(GameState state); // Cambia el estado del juego
void movePlayersToFront(); // Organiza los jugadores para que los vivos se pinten sobre los muertos
void checkServiceMenu(); // Comprueba si está activo el menu de servicio para poner el juego en pausa
// --- Métodos internos ---
void update(); // Actualiza el juego
void render(); // Dibuja el juego
void checkEvents(); // Comprueba los eventos que hay en cola
void setResources(); // Asigna texturas y animaciones
void updateHiScore(); // Actualiza el valor de HiScore en caso necesario
void updatePlayers(); // Actualiza las variables del jugador
void renderPlayers(); // Dibuja a los jugadores
void updateStage(); // Comprueba si hay cambio de fase y actualiza las variables
void updateGameStateGameOver(); // Actualiza el estado de fin de la partida
void destroyAllItems(); // Destruye todos los items
std::shared_ptr<Balloon> checkPlayerBalloonCollision(std::shared_ptr<Player> &player); // Comprueba la colisión entre el jugador y los globos activos
void checkPlayerItemCollision(std::shared_ptr<Player> &player); // Comprueba la colisión entre el jugador y los items
void checkBulletCollision(); // Comprueba y procesa la colisión de las balas
void updateBullets(); // Mueve las balas activas
void renderBullets(); // Pinta las balas activas
void createBullet(int x, int y, BulletType kind, bool powered_up, int owner); // Crea un objeto bala
void freeBullets(); // Vacia el vector de balas
void updateItems(); // Actualiza los items
void renderItems(); // Pinta los items activos
ItemType dropItem(); // Devuelve un item en función del azar
void createItem(ItemType type, float x, float y); // Crea un objeto item
void freeItems(); // Vacia el vector de items
void createItemText(int x, std::shared_ptr<Texture> texture); // Crea un objeto PathSprite
void createMessage(const std::vector<Path> &paths, std::shared_ptr<Texture> texture); // Crea un objeto PathSprite
void freeSmartSprites(); // Vacia el vector de smartsprites
void freePathSprites(); // Vacia el vector de pathsprites
void throwCoffee(int x, int y); // Crea un SpriteSmart para arrojar el item café al recibir un impacto
void updateSmartSprites(); // Actualiza los SpriteSmarts
void renderSmartSprites(); // Pinta los SpriteSmarts activos
void updatePathSprites(); // Actualiza los PathSprites
void renderPathSprites(); // Pinta los PathSprites activos
void handlePlayerCollision(std::shared_ptr<Player> &player); // Acciones a realizar cuando el jugador colisiona con un globo
void updateTimeStopped(); // Actualiza y comprueba el valor de la variable
void updateBackground(); // Actualiza el fondo
void initPaths(); // Inicializa las variables que contienen puntos de ruta para mover objetos
void enableTimeStopItem(); // Habilita el efecto del item de detener el tiempo
void disableTimeStopItem(); // Deshabilita el efecto del item de detener el tiempo
void updateHelper(); // Actualiza las variables de ayuda
bool allPlayersAreWaitingOrGameOver(); // Comprueba si todos los jugadores han terminado de jugar
bool allPlayersAreGameOver(); // Comprueba si todos los jugadores han terminado de jugar
bool allPlayersAreNotPlaying(); // Comprueba si todos los jugadores han terminado de jugar
void updateScoreboard(); // Actualiza el marcador
void fillCanvas(); // Dibuja los elementos de la zona de juego en su textura
void pause(bool value); // Pausa el juego
void addScoreToScoreBoard(const std::shared_ptr<Player> &player); // Añade una puntuación a la tabla de records
void checkAndUpdatePlayerStatus(int active_player_index, int inactive_player_index); // Saca del estado de GAME OVER al jugador si el otro está activo
void checkPlayersStatusPlaying(); // Comprueba el estado de juego de los jugadores
std::shared_ptr<Player> getPlayer(int id); // Obtiene un jugador a partir de su "id"
int getController(int playerId); // Obtiene un controlador a partir del "id" del jugador
void checkInput(); // Gestiona la entrada durante el juego
void checkPauseInput(); // Verifica si alguno de los controladores ha solicitado una pausa y actualiza el estado de pausa del juego.
void DEMO_handleInput(); // Gestiona las entradas de los jugadores en el modo demo, incluyendo movimientos y disparos automáticos.
void DEMO_handlePassInput(); // Gestiona las entradas de los jugadores en el modo demo para saltarse la demo.
void DEMO_handlePlayerInput(const std::shared_ptr<Player> &player, int index); // Procesa las entradas para un jugador específico durante el modo demo.
void handleFireInput(const std::shared_ptr<Player> &player, BulletType bulletType); // Maneja el disparo de un jugador, incluyendo la creación de balas y la gestión del tiempo de espera entre disparos.
void handlePlayersInput(); // Gestiona las entradas de todos los jugadores en el modo normal (fuera del modo demo).
void handleNormalPlayerInput(const std::shared_ptr<Player> &player); // Maneja las entradas de movimiento y disparo para un jugador en modo normal.
void handleFireInputs(const std::shared_ptr<Player> &player, bool autofire, int controllerIndex); // Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado.
void handlePlayerContinue(const std::shared_ptr<Player> &player); // Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio.
void handleNameInput(const std::shared_ptr<Player> &player); // Procesa las entradas para la introducción del nombre del jugador.
void initDemo(int player_id); // Inicializa las variables para el modo DEMO
void setTotalPower(); // Calcula el poder total necesario para completar el juego
void initScoreboard(); // Inicializa el marcador
void initDifficultyVars(); // Inicializa las opciones relacionadas con la dificultad
void initPlayers(int player_id); // Inicializa los jugadores
void playMusic(); // Hace sonar la música
void stopMusic(); // Detiene la música
void playSound(const std::string &name); // Hace sonar un sonido
void updateDemo(); // Actualiza las variables durante el modo demo
void updateGameStateFadeIn(); // Actualiza las variables durante dicho estado
void updateGameStateEnteringPlayer(); // Actualiza las variables durante dicho estado
void updateGameStateShowingGetReadyMessage(); // Actualiza las variables durante dicho estado
void updateGameStatePlaying(); // Actualiza las variables durante el transcurso normal del juego
void updateGameStateCompleted(); // Gestiona eventos para el estado del final del juego
void checkState(); // Comprueba el estado del juego
void cleanVectors(); // Vacía los vectores de elementos deshabilitados
void updateMenace(); // Gestiona el nivel de amenaza
void evaluateAndSetMenace(); // Calcula y establece el valor de amenaza en funcion de los globos activos
void checkAndUpdateBalloonSpeed(); // Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
void setState(GameState state); // Cambia el estado del juego
void movePlayersToFront(); // Organiza los jugadores para que los vivos se pinten sobre los muertos
void checkServiceMenu(); // Comprueba si está activo el menu de servicio para poner el juego en pausa
#ifdef RECORDING
void updateRecording(); // Actualiza las variables durante el modo de grabación
void updateRecording(); // Actualiza las variables durante el modo de grabación
#endif
};

View File

@@ -1,432 +1,389 @@
#include "hiscore_table.h"
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget
#include <stdlib.h> // Para rand, size_t
#include <algorithm> // Para max
#include <functional> // Para function
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget
#include <stdlib.h> // Para rand, size_t
#include "audio.h" // Para Audio
#include "background.h" // Para Background
#include "fade.h" // Para Fade, FadeMode, FadeType
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input
#include "lang.h" // Para getText
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h" // Para SettingsOptions, settings
#include "param.h" // Para Param, param, ParamGame, ParamFade
#include "path_sprite.h" // Para PathSprite, Path, PathType
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name, Options, options
#include "sprite.h" // Para Sprite
#include "text.h" // Para Text, TEXT_SHADOW, TEXT_COLOR
#include "texture.h" // Para Texture
#include "utils.h" // Para Color, easeOutQuint, NO_TEXT_COLOR
#include <algorithm> // Para max
#include <functional> // Para function
#include <vector> // Para vector
#include "audio.h" // Para Audio
#include "background.h" // Para Background
#include "fade.h" // Para Fade, FadeMode, FadeType
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input
#include "lang.h" // Para getText
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h" // Para SettingsOptions, settings
#include "param.h" // Para Param, param, ParamGame, ParamFade
#include "path_sprite.h" // Para PathSprite, Path, PathType
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name, Options, options
#include "sprite.h" // Para Sprite
#include "text.h" // Para Text, TEXT_SHADOW, TEXT_COLOR
#include "texture.h" // Para Texture
#include "utils.h" // Para Color, easeOutQuint, NO_TEXT_COLOR
// Constructor
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>()),
counter_(0),
ticks_(0),
view_area_(SDL_FRect{0, 0, static_cast<float>(param.game.width), static_cast<float>(param.game.height)}),
fade_mode_(FadeMode::IN),
background_fade_color_(Color(0, 0, 0))
{
// Inicializa el resto
Section::name = Section::Name::HI_SCORE_TABLE;
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
initFade();
initBackground();
iniEntryColors();
createSprites();
: 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>()),
counter_(0),
ticks_(0),
view_area_(SDL_FRect{0, 0, static_cast<float>(param.game.width), static_cast<float>(param.game.height)}),
fade_mode_(FadeMode::IN),
background_fade_color_(Color(0, 0, 0)) {
// Inicializa el resto
Section::name = Section::Name::HI_SCORE_TABLE;
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
initFade();
initBackground();
iniEntryColors();
createSprites();
}
// Destructor
HiScoreTable::~HiScoreTable()
{
SDL_DestroyTexture(backbuffer_);
Options::settings.clearLastHiScoreEntries();
HiScoreTable::~HiScoreTable() {
SDL_DestroyTexture(backbuffer_);
Options::settings.clearLastHiScoreEntries();
}
// Actualiza las variables
void HiScoreTable::update()
{
if (SDL_GetTicks() - ticks_ > param.game.speed)
{
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
void HiScoreTable::update() {
if (SDL_GetTicks() - ticks_ > param.game.speed) {
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Actualiza las posiciones de los sprites de texto
updateSprites();
// Actualiza las posiciones de los sprites de texto
updateSprites();
// Actualiza el fondo
background_->update();
// Actualiza el fondo
background_->update();
// Gestiona el fade
updateFade();
// Gestiona el fade
updateFade();
// Gestiona el contador y sus eventos
updateCounter();
// Gestiona el contador y sus eventos
updateCounter();
// Dibuja los sprites en la textura
fillTexture();
// Dibuja los sprites en la textura
fillTexture();
// Actualiza el objeto screen
Screen::get()->update();
// Actualiza el objeto screen
Screen::get()->update();
// Actualiza las variables de globalInputs
}
// Actualiza las variables de globalInputs
}
}
// Dibuja los sprites en la textura
void HiScoreTable::fillTexture()
{
// Pinta en el backbuffer el texto y los sprites
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_RenderClear(renderer_);
void HiScoreTable::fillTexture() {
// Pinta en el backbuffer el texto y los sprites
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_RenderClear(renderer_);
// Escribe el texto: Mejores puntuaciones
header_->render();
// Escribe el texto: Mejores puntuaciones
header_->render();
// Escribe los nombres de la tabla de puntuaciones
for (auto const &entry : entry_names_)
{
entry->render();
}
// Escribe los nombres de la tabla de puntuaciones
for (auto const &entry : entry_names_) {
entry->render();
}
// Cambia el destino de renderizado
SDL_SetRenderTarget(renderer_, temp);
// Cambia el destino de renderizado
SDL_SetRenderTarget(renderer_, temp);
}
// Pinta en pantalla
void HiScoreTable::render()
{
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
void HiScoreTable::render() {
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
// Limpia la pantalla
Screen::get()->clean();
// Limpia la pantalla
Screen::get()->clean();
// Pinta el fondo
background_->render();
// Pinta el fondo
background_->render();
// Establece la ventana del backbuffer
view_area_.y = std::max(0.0f, param.game.height - counter_ + 100);
// Establece la ventana del backbuffer
view_area_.y = std::max(0.0f, param.game.height - counter_ + 100);
// Copia el backbuffer al renderizador
SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_area_);
// Copia el backbuffer al renderizador
SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_area_);
// Renderiza el fade
fade_->render();
// Renderiza el fade
fade_->render();
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
}
// Comprueba los eventos
void HiScoreTable::checkEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
GlobalEvents::check(event);
}
void HiScoreTable::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
GlobalEvents::check(event);
}
}
// Comprueba las entradas
void HiScoreTable::checkInput()
{
Input::get()->update();
GlobalInputs::check();
void HiScoreTable::checkInput() {
Input::get()->update();
GlobalInputs::check();
}
// Bucle para la pantalla de instrucciones
void HiScoreTable::run()
{
Audio::get()->playMusic("title.ogg");
while (Section::name == Section::Name::HI_SCORE_TABLE)
{
checkInput();
update();
checkEvents(); // Tiene que ir antes del render
render();
}
void HiScoreTable::run() {
Audio::get()->playMusic("title.ogg");
while (Section::name == Section::Name::HI_SCORE_TABLE) {
checkInput();
update();
checkEvents(); // Tiene que ir antes del render
render();
}
}
// Gestiona el fade
void HiScoreTable::updateFade()
{
fade_->update();
void HiScoreTable::updateFade() {
fade_->update();
if (fade_->hasEnded() && fade_mode_ == FadeMode::IN)
{
fade_->reset();
fade_mode_ = FadeMode::OUT;
fade_->setMode(fade_mode_);
}
if (fade_->hasEnded() && fade_mode_ == FadeMode::IN) {
fade_->reset();
fade_mode_ = FadeMode::OUT;
fade_->setMode(fade_mode_);
}
if (fade_->hasEnded() && fade_mode_ == FadeMode::OUT)
{
Section::name = (Section::options == Section::Options::HI_SCORE_AFTER_PLAYING)
? Section::Name::TITLE
: Section::Name::INSTRUCTIONS;
Section::options = Section::Options::NONE;
}
if (fade_->hasEnded() && fade_mode_ == FadeMode::OUT) {
Section::name = (Section::options == Section::Options::HI_SCORE_AFTER_PLAYING)
? Section::Name::TITLE
: Section::Name::INSTRUCTIONS;
Section::options = Section::Options::NONE;
}
}
// Convierte un entero a un string con separadores de miles
std::string HiScoreTable::format(int number)
{
const std::string separator = ".";
const std::string score = std::to_string(number);
std::string HiScoreTable::format(int number) {
const std::string separator = ".";
const std::string score = std::to_string(number);
auto index = (int)score.size() - 1;
std::string result;
auto i = 0;
while (index >= 0)
{
result = score.at(index) + result;
index--;
i++;
if (i == 3)
{
i = 0;
result = separator + result;
}
}
auto index = (int)score.size() - 1;
std::string result;
auto i = 0;
while (index >= 0) {
result = score.at(index) + result;
index--;
i++;
if (i == 3) {
i = 0;
result = separator + result;
}
}
return result;
return result;
}
// Crea los sprites con los textos
void HiScoreTable::createSprites()
{
auto header_text = Resource::get()->getText("04b_25_grey");
auto entry_text = Resource::get()->getText("smb2");
void HiScoreTable::createSprites() {
auto header_text = Resource::get()->getText("04b_25_grey");
auto entry_text = Resource::get()->getText("smb2");
// Obtiene el tamaño de la textura
float backbuffer_width;
float backbuffer_height;
SDL_GetTextureSize(backbuffer_, &backbuffer_width, &backbuffer_height);
// Obtiene el tamaño de la textura
float backbuffer_width;
float backbuffer_height;
SDL_GetTextureSize(backbuffer_, &backbuffer_width, &backbuffer_height);
constexpr int ENTRY_LENGHT = 22;
constexpr int MAX_NAMES = 10;
const int space_between_header = entry_text->getCharacterSize() * 4;
const int space_between_lines = entry_text->getCharacterSize() * 2;
const int size = space_between_header + space_between_lines * (MAX_NAMES - 1) + entry_text->getCharacterSize();
const int first_line = (param.game.height - size) / 2;
constexpr int ENTRY_LENGHT = 22;
constexpr int MAX_NAMES = 10;
const int space_between_header = entry_text->getCharacterSize() * 4;
const int space_between_lines = entry_text->getCharacterSize() * 2;
const int size = space_between_header + space_between_lines * (MAX_NAMES - 1) + entry_text->getCharacterSize();
const int first_line = (param.game.height - size) / 2;
// Crea el sprite para el texto de cabecera
header_ = std::make_unique<Sprite>(header_text->writeDXToTexture(TEXT_COLOR, Lang::getText("[HIGHSCORE_TABLE] CAPTION"), -2, background_fade_color_.inverse().lighten(25)));
header_->setPosition(param.game.game_area.center_x - (header_->getWidth() / 2), first_line);
// Crea el sprite para el texto de cabecera
header_ = std::make_unique<Sprite>(header_text->writeDXToTexture(TEXT_COLOR, Lang::getText("[HIGHSCORE_TABLE] CAPTION"), -2, background_fade_color_.inverse().lighten(25)));
header_->setPosition(param.game.game_area.center_x - (header_->getWidth() / 2), first_line);
// Crea los sprites para las entradas en la tabla de puntuaciones
const int animation = rand() % 4;
const std::string sample_line(ENTRY_LENGHT + 3, ' ');
auto sample_entry = std::make_unique<Sprite>(entry_text->writeDXToTexture(TEXT_SHADOW, sample_line, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR));
const auto entry_width = sample_entry->getWidth();
for (int i = 0; i < MAX_NAMES; ++i)
{
const auto table_position = format(i + 1) + ". ";
const auto score = format(Options::settings.hi_score_table.at(i).score);
const auto num_dots = ENTRY_LENGHT - Options::settings.hi_score_table.at(i).name.size() - score.size();
const auto one_cc = Options::settings.hi_score_table.at(i).one_credit_complete ? " }" : "";
std::string dots;
for (int j = 0; j < (int)num_dots; ++j)
{
dots = dots + ".";
}
const auto line = table_position + Options::settings.hi_score_table.at(i).name + dots + score + one_cc;
// Crea los sprites para las entradas en la tabla de puntuaciones
const int animation = rand() % 4;
const std::string sample_line(ENTRY_LENGHT + 3, ' ');
auto sample_entry = std::make_unique<Sprite>(entry_text->writeDXToTexture(TEXT_SHADOW, sample_line, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR));
const auto entry_width = sample_entry->getWidth();
for (int i = 0; i < MAX_NAMES; ++i) {
const auto table_position = format(i + 1) + ". ";
const auto score = format(Options::settings.hi_score_table.at(i).score);
const auto num_dots = ENTRY_LENGHT - Options::settings.hi_score_table.at(i).name.size() - score.size();
const auto one_cc = Options::settings.hi_score_table.at(i).one_credit_complete ? " }" : "";
std::string dots;
for (int j = 0; j < (int)num_dots; ++j) {
dots = dots + ".";
}
const auto line = table_position + Options::settings.hi_score_table.at(i).name + dots + score + one_cc;
entry_names_.emplace_back(std::make_shared<PathSprite>(entry_text->writeDXToTexture(TEXT_SHADOW, line, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR)));
const int default_pos_x = (backbuffer_width - entry_width) / 2;
const int pos_x = (i < 9) ? default_pos_x : default_pos_x - entry_text->getCharacterSize();
const int pos_y = (i * space_between_lines) + first_line + space_between_header;
constexpr int steps = 80;
switch (animation)
{
case 0: // Ambos lados alternativamente
{
if (i % 2 == 0)
{
entry_names_.back()->addPath(-entry_names_.back()->getWidth(), pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint);
entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0);
}
else
{
entry_names_.back()->addPath(backbuffer_width, pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint);
entry_names_.back()->setPosition(backbuffer_width, 0);
}
break;
}
entry_names_.emplace_back(std::make_shared<PathSprite>(entry_text->writeDXToTexture(TEXT_SHADOW, line, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR)));
const int default_pos_x = (backbuffer_width - entry_width) / 2;
const int pos_x = (i < 9) ? default_pos_x : default_pos_x - entry_text->getCharacterSize();
const int pos_y = (i * space_between_lines) + first_line + space_between_header;
constexpr int steps = 80;
switch (animation) {
case 0: // Ambos lados alternativamente
{
if (i % 2 == 0) {
entry_names_.back()->addPath(-entry_names_.back()->getWidth(), pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint);
entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0);
} else {
entry_names_.back()->addPath(backbuffer_width, pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint);
entry_names_.back()->setPosition(backbuffer_width, 0);
}
break;
}
case 1: // Entran por la izquierda
{
entry_names_.back()->addPath(-entry_names_.back()->getWidth(), pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint);
entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0);
break;
}
case 1: // Entran por la izquierda
{
entry_names_.back()->addPath(-entry_names_.back()->getWidth(), pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint);
entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0);
break;
}
case 2: // Entran por la derecha
{
entry_names_.back()->addPath(backbuffer_width, pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint);
entry_names_.back()->setPosition(backbuffer_width, 0);
break;
}
case 2: // Entran por la derecha
{
entry_names_.back()->addPath(backbuffer_width, pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint);
entry_names_.back()->setPosition(backbuffer_width, 0);
break;
}
case 3: // Entran desde la parte inferior
{
entry_names_.back()->addPath(backbuffer_height, pos_y, PathType::VERTICAL, pos_x, steps, easeOutQuint);
entry_names_.back()->setPosition(0, backbuffer_height);
}
case 3: // Entran desde la parte inferior
{
entry_names_.back()->addPath(backbuffer_height, pos_y, PathType::VERTICAL, pos_x, steps, easeOutQuint);
entry_names_.back()->setPosition(0, backbuffer_height);
}
default:
break;
}
}
default:
break;
}
}
}
// Actualiza las posiciones de los sprites de texto
void HiScoreTable::updateSprites()
{
constexpr int init_counter = 190;
const int counter_between_entries = 16;
if (counter_ >= init_counter)
{
const int counter2 = counter_ - init_counter;
if (counter2 % counter_between_entries == 0)
{
int index = counter2 / counter_between_entries;
if (index < static_cast<int>(entry_names_.size()))
{
entry_names_.at(index)->enable();
}
}
}
for (auto const &entry : entry_names_)
{
entry->update();
}
void HiScoreTable::updateSprites() {
constexpr int init_counter = 190;
const int counter_between_entries = 16;
if (counter_ >= init_counter) {
const int counter2 = counter_ - init_counter;
if (counter2 % counter_between_entries == 0) {
int index = counter2 / counter_between_entries;
if (index < static_cast<int>(entry_names_.size())) {
entry_names_.at(index)->enable();
}
}
}
for (auto const &entry : entry_names_) {
entry->update();
}
glowEntryNames();
glowEntryNames();
}
// Inicializa el fade
void HiScoreTable::initFade()
{
fade_->setColor(param.fade.color);
fade_->setType(FadeType::RANDOM_SQUARE);
fade_->setPostDuration(param.fade.post_duration);
fade_->setMode(fade_mode_);
fade_->activate();
void HiScoreTable::initFade() {
fade_->setColor(param.fade.color);
fade_->setType(FadeType::RANDOM_SQUARE);
fade_->setPostDuration(param.fade.post_duration);
fade_->setMode(fade_mode_);
fade_->activate();
}
// Inicializa el fondo
void HiScoreTable::initBackground()
{
background_->setPos(param.game.game_area.rect);
background_->setCloudsSpeed(-0.1f);
void HiScoreTable::initBackground() {
background_->setPos(param.game.game_area.rect);
background_->setCloudsSpeed(-0.1f);
const int lucky = rand() % 3;
switch (lucky)
{
case 0: // Fondo verde
{
background_->setGradientNumber(2);
background_->setTransition(0.0f);
background_->setSunProgression(1.0f);
background_->setMoonProgression(0.0f);
background_fade_color_ = GREEN_SKY_COLOR;
break;
}
const int lucky = rand() % 3;
switch (lucky) {
case 0: // Fondo verde
{
background_->setGradientNumber(2);
background_->setTransition(0.0f);
background_->setSunProgression(1.0f);
background_->setMoonProgression(0.0f);
background_fade_color_ = GREEN_SKY_COLOR;
break;
}
case 1: // Fondo naranja
{
background_->setGradientNumber(1);
background_->setTransition(0.0f);
background_->setSunProgression(0.65f);
background_->setMoonProgression(0.0f);
background_fade_color_ = PINK_SKY_COLOR;
break;
}
case 1: // Fondo naranja
{
background_->setGradientNumber(1);
background_->setTransition(0.0f);
background_->setSunProgression(0.65f);
background_->setMoonProgression(0.0f);
background_fade_color_ = PINK_SKY_COLOR;
break;
}
case 2: // Fondo azul
{
background_->setGradientNumber(0);
background_->setTransition(0.0f);
background_->setSunProgression(0.0f);
background_->setMoonProgression(0.0f);
background_fade_color_ = BLUE_SKY_COLOR;
break;
}
case 2: // Fondo azul
{
background_->setGradientNumber(0);
background_->setTransition(0.0f);
background_->setSunProgression(0.0f);
background_->setMoonProgression(0.0f);
background_fade_color_ = BLUE_SKY_COLOR;
break;
}
default:
break;
}
default:
break;
}
}
// Obtiene un color del vector de colores de entradas
Color HiScoreTable::getEntryColor(int counter_)
{
int cycle_length = entry_colors_.size() * 2 - 2;
size_t n = counter_ % cycle_length;
Color HiScoreTable::getEntryColor(int counter_) {
int cycle_length = entry_colors_.size() * 2 - 2;
size_t n = counter_ % cycle_length;
size_t index;
if (n < entry_colors_.size())
{
index = n; // Avanza: 0,1,2,3
}
else
{
index = 2 * (entry_colors_.size() - 1) - n; // Retrocede: 2,1
}
size_t index;
if (n < entry_colors_.size()) {
index = n; // Avanza: 0,1,2,3
} else {
index = 2 * (entry_colors_.size() - 1) - n; // Retrocede: 2,1
}
return entry_colors_[index];
return entry_colors_[index];
}
// Inicializa los colores de las entradas
void HiScoreTable::iniEntryColors()
{
entry_colors_.clear();
entry_colors_.emplace_back(background_fade_color_.inverse().lighten(75));
entry_colors_.emplace_back(background_fade_color_.inverse().lighten(50));
entry_colors_.emplace_back(background_fade_color_.inverse().lighten(25));
entry_colors_.emplace_back(background_fade_color_.inverse());
void HiScoreTable::iniEntryColors() {
entry_colors_.clear();
entry_colors_.emplace_back(background_fade_color_.inverse().lighten(75));
entry_colors_.emplace_back(background_fade_color_.inverse().lighten(50));
entry_colors_.emplace_back(background_fade_color_.inverse().lighten(25));
entry_colors_.emplace_back(background_fade_color_.inverse());
}
// Hace brillar los nombres de la tabla de records
void HiScoreTable::glowEntryNames()
{
const Color entry_color = getEntryColor(counter_ / 5);
for (const auto &entry_index : Options::settings.last_hi_score_entry)
{
if (entry_index != -1)
{
entry_names_.at(entry_index)->getTexture()->setColor(entry_color);
}
}
void HiScoreTable::glowEntryNames() {
const Color entry_color = getEntryColor(counter_ / 5);
for (const auto &entry_index : Options::settings.last_hi_score_entry) {
if (entry_index != -1) {
entry_names_.at(entry_index)->getTexture()->setColor(entry_color);
}
}
}
// Gestiona el contador
void HiScoreTable::updateCounter()
{
++counter_;
void HiScoreTable::updateCounter() {
++counter_;
if (counter_ == 150)
{
background_->setColor(background_fade_color_.darken());
background_->setAlpha(96);
}
if (counter_ == 150) {
background_->setColor(background_fade_color_.darken());
background_->setAlpha(96);
}
if (counter_ == COUNTER_END_)
{
fade_->activate();
}
if (counter_ == COUNTER_END_) {
fade_->activate();
}
}

View File

@@ -1,11 +1,12 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint16, SDL_FRect, SDL_Renderer, SDL_Texture, Uint64, Uint8
#include <memory> // Para unique_ptr, shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para Uint16, SDL_FRect, SDL_Renderer, SDL_Texture, Uint64, Uint8
#include "utils.h" // Para Color
#include <memory> // Para unique_ptr, shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "utils.h" // Para Color
class Background;
class Fade;
@@ -15,64 +16,63 @@ enum class FadeMode : Uint8;
struct Path;
/*
Esta clase gestiona un estado del programa. Se encarga de mostrar la tabla con las puntuaciones
más altas. Para ello utiliza un objeto que se encarga de pintar el fondo y una textura
sobre la que escribe las puntuaciones. Esta textura se recorre modificando la ventana de vista
para dar el efecto de que la textura se mueve sobre la pantalla.
Esta clase gestiona un estado del programa. Se encarga de mostrar la tabla con las puntuaciones
más altas. Para ello utiliza un objeto que se encarga de pintar el fondo y una textura
sobre la que escribe las puntuaciones. Esta textura se recorre modificando la ventana de vista
para dar el efecto de que la textura se mueve sobre la pantalla.
Para mejorar la legibilidad de los textos, el objeto que dibuja el fondo es capaz de modificar
su atenuación.
Para mejorar la legibilidad de los textos, el objeto que dibuja el fondo es capaz de modificar
su atenuación.
*/
// Clase HiScoreTable
class HiScoreTable
{
public:
// Constructor
HiScoreTable();
class HiScoreTable {
public:
// Constructor
HiScoreTable();
// Destructor
~HiScoreTable();
// Destructor
~HiScoreTable();
// Bucle principal
void run();
// Bucle principal
void run();
private:
// --- Constantes ---
static constexpr Uint16 COUNTER_END_ = 800; // Valor final para el contador
private:
// --- Constantes ---
static constexpr Uint16 COUNTER_END_ = 800; // Valor final para el contador
// --- Objetos y punteros ---
SDL_Renderer *renderer_; // El renderizador de la ventana
SDL_Texture *backbuffer_; // Textura para usar como backbuffer
// --- Objetos y punteros ---
SDL_Renderer *renderer_; // El renderizador de la ventana
SDL_Texture *backbuffer_; // Textura para usar como backbuffer
std::unique_ptr<Fade> fade_; // Objeto para renderizar fades
std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego
std::unique_ptr<Sprite> header_; // Sprite con la cabecera del texto
std::vector<std::shared_ptr<PathSprite>> entry_names_; // Lista con los sprites de cada uno de los nombres de la tabla de records
std::vector<Path> paths_; // Vector con los recorridos precalculados
std::unique_ptr<Fade> fade_; // Objeto para renderizar fades
std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego
std::unique_ptr<Sprite> header_; // Sprite con la cabecera del texto
std::vector<std::shared_ptr<PathSprite>> entry_names_; // Lista con los sprites de cada uno de los nombres de la tabla de records
std::vector<Path> paths_; // Vector con los recorridos precalculados
// --- Variables ---
Uint16 counter_ = 0; // Contador
Uint64 ticks_; // Contador de ticks para ajustar la velocidad del programa
SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla
FadeMode fade_mode_; // Modo de fade a utilizar
Color background_fade_color_; // Color de atenuación del fondo
std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla
// --- Variables ---
Uint16 counter_ = 0; // Contador
Uint64 ticks_; // Contador de ticks para ajustar la velocidad del programa
SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla
FadeMode fade_mode_; // Modo de fade a utilizar
Color background_fade_color_; // Color de atenuación del fondo
std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla
// --- Métodos internos ---
void update(); // Actualiza las variables
void render(); // Pinta en pantalla
void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas
std::string format(int number); // Convierte un entero a un string con separadores de miles
void fillTexture(); // Dibuja los sprites en la textura
void updateFade(); // Gestiona el fade
void createSprites(); // Crea los sprites con los textos
void updateSprites(); // Actualiza las posiciones de los sprites de texto
void initFade(); // Inicializa el fade
void initBackground(); // Inicializa el fondo
Color getEntryColor(int counter_); // Obtiene un color del vector de colores de entradas
void iniEntryColors(); // Inicializa los colores de las entradas
void glowEntryNames(); // Hace brillar los nombres de la tabla de records
void updateCounter(); // Gestiona el contador
// --- Métodos internos ---
void update(); // Actualiza las variables
void render(); // Pinta en pantalla
void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas
std::string format(int number); // Convierte un entero a un string con separadores de miles
void fillTexture(); // Dibuja los sprites en la textura
void updateFade(); // Gestiona el fade
void createSprites(); // Crea los sprites con los textos
void updateSprites(); // Actualiza las posiciones de los sprites de texto
void initFade(); // Inicializa el fade
void initBackground(); // Inicializa el fondo
Color getEntryColor(int counter_); // Obtiene un color del vector de colores de entradas
void iniEntryColors(); // Inicializa los colores de las entradas
void glowEntryNames(); // Hace brillar los nombres de la tabla de records
void updateCounter(); // Gestiona el contador
};

View File

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

View File

@@ -1,8 +1,9 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_Texture, Uint32, SDL_Renderer, SDL_FPoint, SDL_FRect, Uint64
#include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_Texture, Uint32, SDL_Renderer, SDL_FPoint, SDL_FRect, Uint64
#include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector
class Fade;
class Sprite;
@@ -11,78 +12,76 @@ class Texture;
class TiledBG;
/*
Esta clase gestiona un estado del programa. Se encarga de poner en pantalla
un texto explicativo para entender cómo se juega.
Esta clase gestiona un estado del programa. Se encarga de poner en pantalla
un texto explicativo para entender cómo se juega.
Además muestra algunos items y explica para qué sirven.
Además muestra algunos items y explica para qué sirven.
Utiliza dos texturas de apoyo, una con el texto ya escrito y otra donde se combina
tanto el texto de la primera textura como los sprites de los items.
Utiliza dos texturas de apoyo, una con el texto ya escrito y otra donde se combina
tanto el texto de la primera textura como los sprites de los items.
Finalmente, una ventana recorre la textura para dar el efecto de que todo se desplaza
por la pantalla sobre el mosaico de fondo (gestionado por el correspondiente objeto).
Finalmente, una ventana recorre la textura para dar el efecto de que todo se desplaza
por la pantalla sobre el mosaico de fondo (gestionado por el correspondiente objeto).
*/
// Estructura para almacenar información de línea animada
struct Line
{
int y; // Coordenada Y de la línea
float x; // Coordenada X inicial (usamos float para mayor precisión en el suavizado)
int direction; // Dirección de movimiento: -1 para izquierda, 1 para derecha
Uint32 startTime; // Tiempo de inicio del movimiento
struct Line {
int y; // Coordenada Y de la línea
float x; // Coordenada X inicial (usamos float para mayor precisión en el suavizado)
int direction; // Dirección de movimiento: -1 para izquierda, 1 para derecha
Uint32 startTime; // Tiempo de inicio del movimiento
// Constructor de Line
Line(int y, float x, int direction)
: y(y), x(x), direction(direction), startTime(0) {}
// Constructor de Line
Line(int y, float x, int direction)
: y(y), x(x), direction(direction), startTime(0) {}
};
// Clase Instructions
class Instructions
{
public:
// Constructor
Instructions();
class Instructions {
public:
// Constructor
Instructions();
// Destructor
~Instructions();
// Destructor
~Instructions();
// Bucle principal
void run();
// Bucle principal
void run();
private:
// --- Objetos y punteros ---
SDL_Renderer *renderer_; // El renderizador de la ventana
SDL_Texture *texture_; // Textura fija con el texto
SDL_Texture *backbuffer_; // Textura para usar como backbuffer
private:
// --- Objetos y punteros ---
SDL_Renderer *renderer_; // El renderizador de la ventana
SDL_Texture *texture_; // Textura fija con el texto
SDL_Texture *backbuffer_; // Textura para usar como backbuffer
std::vector<std::shared_ptr<Texture>> item_textures_; // Vector con las texturas de los items
std::vector<std::unique_ptr<Sprite>> sprites_; // Vector con los sprites de los items
std::shared_ptr<Text> text_; // Objeto para escribir texto
std::unique_ptr<TiledBG> tiled_bg_; // Objeto para dibujar el mosaico animado de fondo
std::unique_ptr<Fade> fade_; // Objeto para renderizar fades
std::vector<std::shared_ptr<Texture>> item_textures_; // Vector con las texturas de los items
std::vector<std::unique_ptr<Sprite>> sprites_; // Vector con los sprites de los items
std::shared_ptr<Text> text_; // Objeto para escribir texto
std::unique_ptr<TiledBG> tiled_bg_; // Objeto para dibujar el mosaico animado de fondo
std::unique_ptr<Fade> fade_; // Objeto para renderizar fades
// --- Variables ---
int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla
SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista
float item_space_ = 2.0; // Espacio entre los items en pantalla
std::vector<Line> lines_; // Vector que contiene las líneas animadas en la pantalla
bool all_lines_off_screen_ = false; // Indica si todas las líneas han salido de la pantalla
Uint32 start_delay_time_ = 0; // Tiempo de inicio del retraso para mover las líneas
bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado
// --- Variables ---
int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla
SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista
float item_space_ = 2.0; // Espacio entre los items en pantalla
std::vector<Line> lines_; // Vector que contiene las líneas animadas en la pantalla
bool all_lines_off_screen_ = false; // Indica si todas las líneas han salido de la pantalla
Uint32 start_delay_time_ = 0; // Tiempo de inicio del retraso para mover las líneas
bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado
// --- Métodos internos ---
void update(); // Actualiza las variables
void render(); // Pinta en pantalla
void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas
void fillTexture(); // Rellena la textura de texto
void fillBackbuffer(); // Rellena el backbuffer
void iniSprites(); // Inicializa los sprites de los items
void updateSprites(); // Actualiza los sprites
std::vector<Line> initializeLines(int height); // Inicializa las líneas animadas
bool moveLines(std::vector<Line> &lines, int width, float duration, Uint32 startDelay); // Mueve las líneas
void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas
void updateBackbuffer(); // Gestiona la textura con los gráficos
// --- Métodos internos ---
void update(); // Actualiza las variables
void render(); // Pinta en pantalla
void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas
void fillTexture(); // Rellena la textura de texto
void fillBackbuffer(); // Rellena el backbuffer
void iniSprites(); // Inicializa los sprites de los items
void updateSprites(); // Actualiza los sprites
std::vector<Line> initializeLines(int height); // Inicializa las líneas animadas
bool moveLines(std::vector<Line> &lines, int width, float duration, Uint32 startDelay); // Mueve las líneas
void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas
void updateBackbuffer(); // Gestiona la textura con los gráficos
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,74 +1,72 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint32, Uint64
#include <memory> // Para unique_ptr
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para Uint32, Uint64
#include "param.h" // Para Param, ParamIntro, param
#include "path_sprite.h" // Para PathSprite
#include "tiled_bg.h" // Para TiledBG
#include "utils.h" // Para Color
#include "writer.h" // Para Writer
#include <memory> // Para unique_ptr
#include <vector> // Para vector
#include "param.h" // Para Param, ParamIntro, param
#include "path_sprite.h" // Para PathSprite
#include "tiled_bg.h" // Para TiledBG
#include "utils.h" // Para Color
#include "writer.h" // Para Writer
/*
Esta clase gestiona un estado del programa. Se encarga de mostrar la secuencia
de introducción.
Esta clase gestiona un estado del programa. Se encarga de mostrar la secuencia
de introducción.
*/
// Clase Intro
class Intro
{
public:
// Constructor
Intro();
class Intro {
public:
// Constructor
Intro();
// Destructor
~Intro() = default;
// Destructor
~Intro() = default;
// Bucle principal
void run();
// Bucle principal
void run();
private:
// --- Estados internos ---
enum class IntroState
{
SCENES,
POST,
};
private:
// --- Estados internos ---
enum class IntroState {
SCENES,
POST,
};
enum class IntroPostState
{
STOP_BG,
END,
};
enum class IntroPostState {
STOP_BG,
END,
};
// --- Objetos ---
std::vector<std::unique_ptr<PathSprite>> card_sprites_; // Vector con los sprites inteligentes para los dibujos de la intro
std::vector<std::unique_ptr<PathSprite>> shadow_sprites_; // Vector con los sprites inteligentes para las sombras
std::vector<std::unique_ptr<Writer>> texts_; // Textos de la intro
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
// std::unique_ptr<Sprite> shadow_square_for_text_; // Sprite
// --- Objetos ---
std::vector<std::unique_ptr<PathSprite>> card_sprites_; // Vector con los sprites inteligentes para los dibujos de la intro
std::vector<std::unique_ptr<PathSprite>> shadow_sprites_; // Vector con los sprites inteligentes para las sombras
std::vector<std::unique_ptr<Writer>> texts_; // Textos de la intro
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
// std::unique_ptr<Sprite> shadow_square_for_text_; // Sprite
// --- Variables ---
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
int scene_ = 0; // Indica qué escena está activa
IntroState state_ = IntroState::SCENES; // Estado principal de la intro
IntroPostState post_state_ = IntroPostState::STOP_BG; // Estado POST
Uint32 state_start_time_; // Tiempo de inicio del estado actual
Color bg_color_ = param.intro.bg_color; // Color de fondo
// --- Variables ---
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
int scene_ = 0; // Indica qué escena está activa
IntroState state_ = IntroState::SCENES; // Estado principal de la intro
IntroPostState post_state_ = IntroPostState::STOP_BG; // Estado POST
Uint32 state_start_time_; // Tiempo de inicio del estado actual
Color bg_color_ = param.intro.bg_color; // Color de fondo
// --- Métodos internos ---
void update(); // Actualiza las variables del objeto
void render(); // Dibuja el objeto en pantalla
void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas
void updateScenes(); // Actualiza las escenas de la intro
void initSprites(); // Inicializa las imágenes
void initTexts(); // Inicializa los textos
void updateSprites(); // Actualiza los sprites
void updateTexts(); // Actualiza los textos
void renderSprites(); // Dibuja los sprites
void renderTexts(); // Dibuja los textos
void renderTextRect(); // Dibuja el rectangulo de fondo del texto;
void updatePostState(); // Actualiza el estado POST
// --- Métodos internos ---
void update(); // Actualiza las variables del objeto
void render(); // Dibuja el objeto en pantalla
void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas
void updateScenes(); // Actualiza las escenas de la intro
void initSprites(); // Inicializa las imágenes
void initTexts(); // Inicializa los textos
void updateSprites(); // Actualiza los sprites
void updateTexts(); // Actualiza los textos
void renderSprites(); // Dibuja los sprites
void renderTexts(); // Dibuja los textos
void renderTextRect(); // Dibuja el rectangulo de fondo del texto;
void updatePostState(); // Actualiza el estado POST
};

View File

@@ -1,211 +1,181 @@
#include "logo.h"
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_PollEvent, SDL_Event
#include <string> // Para basic_string
#include <utility> // Para move
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_PollEvent, SDL_Event
#include "audio.h" // Para Audio
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input
#include "param.h" // Para Param, ParamGame, param
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
#include "utils.h" // Para Color, Zone
#include <string> // Para basic_string
#include <utility> // Para move
#include "audio.h" // Para Audio
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input
#include "param.h" // Para Param, ParamGame, param
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
#include "utils.h" // Para Color, Zone
// Constructor
Logo::Logo()
: since_texture_(Resource::get()->getTexture("logo_since_1998.png")),
since_sprite_(std::make_unique<Sprite>(since_texture_)),
jail_texture_(Resource::get()->getTexture("logo_jailgames.png"))
{
: since_texture_(Resource::get()->getTexture("logo_since_1998.png")),
since_sprite_(std::make_unique<Sprite>(since_texture_)),
jail_texture_(Resource::get()->getTexture("logo_jailgames.png")) {
// Inicializa variables
Section::name = Section::Name::LOGO;
dest_.x = param.game.game_area.center_x - jail_texture_->getWidth() / 2;
dest_.y = param.game.game_area.center_y - jail_texture_->getHeight() / 2;
since_sprite_->setPosition(SDL_FRect{
static_cast<float>((param.game.width - since_texture_->getWidth()) / 2),
static_cast<float>(83 + jail_texture_->getHeight() + 5),
static_cast<float>(since_texture_->getWidth()),
static_cast<float>(since_texture_->getHeight())});
since_sprite_->setY(dest_.y + jail_texture_->getHeight() + 5);
since_sprite_->setSpriteClip(0, 0, since_texture_->getWidth(), since_texture_->getHeight());
since_texture_->setColor(0x00, 0x00, 0x00);
// Inicializa variables
Section::name = Section::Name::LOGO;
dest_.x = param.game.game_area.center_x - jail_texture_->getWidth() / 2;
dest_.y = param.game.game_area.center_y - jail_texture_->getHeight() / 2;
since_sprite_->setPosition(SDL_FRect{
static_cast<float>((param.game.width - since_texture_->getWidth()) / 2),
static_cast<float>(83 + jail_texture_->getHeight() + 5),
static_cast<float>(since_texture_->getWidth()),
static_cast<float>(since_texture_->getHeight())});
since_sprite_->setY(dest_.y + jail_texture_->getHeight() + 5);
since_sprite_->setSpriteClip(0, 0, since_texture_->getWidth(), since_texture_->getHeight());
since_texture_->setColor(0x00, 0x00, 0x00);
// Crea los sprites de cada linea
for (int i = 0; i < jail_texture_->getHeight(); ++i) {
auto temp = std::make_unique<Sprite>(jail_texture_, 0, i, jail_texture_->getWidth(), 1);
temp->setSpriteClip(0, i, jail_texture_->getWidth(), 1);
const int POS_X = (i % 2 == 0) ? param.game.width + (i * 3) : -jail_texture_->getWidth() - (i * 3);
temp->setX(POS_X);
temp->setY(dest_.y + i);
jail_sprite_.push_back(std::move(temp));
}
// Crea los sprites de cada linea
for (int i = 0; i < jail_texture_->getHeight(); ++i)
{
auto temp = std::make_unique<Sprite>(jail_texture_, 0, i, jail_texture_->getWidth(), 1);
temp->setSpriteClip(0, i, jail_texture_->getWidth(), 1);
const int POS_X = (i % 2 == 0) ? param.game.width + (i * 3) : -jail_texture_->getWidth() - (i * 3);
temp->setX(POS_X);
temp->setY(dest_.y + i);
jail_sprite_.push_back(std::move(temp));
}
// Inicializa el vector de colores
color_.push_back(Color(0x00, 0x00, 0x00)); // Black
color_.push_back(Color(0x00, 0x00, 0xd8)); // Blue
color_.push_back(Color(0xd8, 0x00, 0x00)); // Red
color_.push_back(Color(0xd8, 0x00, 0xd8)); // Magenta
color_.push_back(Color(0x00, 0xd8, 0x00)); // Green
color_.push_back(Color(0x00, 0xd8, 0xd8)); // Cyan
color_.push_back(Color(0xd8, 0xd8, 0x00)); // Yellow
color_.push_back(Color(0xFF, 0xFF, 0xFF)); // Bright white
// Inicializa el vector de colores
color_.push_back(Color(0x00, 0x00, 0x00)); // Black
color_.push_back(Color(0x00, 0x00, 0xd8)); // Blue
color_.push_back(Color(0xd8, 0x00, 0x00)); // Red
color_.push_back(Color(0xd8, 0x00, 0xd8)); // Magenta
color_.push_back(Color(0x00, 0xd8, 0x00)); // Green
color_.push_back(Color(0x00, 0xd8, 0xd8)); // Cyan
color_.push_back(Color(0xd8, 0xd8, 0x00)); // Yellow
color_.push_back(Color(0xFF, 0xFF, 0xFF)); // Bright white
}
// Destructor
Logo::~Logo()
{
jail_texture_->setColor(255, 255, 255);
since_texture_->setColor(255, 255, 255);
Audio::get()->stopAllSounds();
Audio::get()->stopMusic();
Logo::~Logo() {
jail_texture_->setColor(255, 255, 255);
since_texture_->setColor(255, 255, 255);
Audio::get()->stopAllSounds();
Audio::get()->stopMusic();
}
// Comprueba el manejador de eventos
void Logo::checkEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
GlobalEvents::check(event);
}
void Logo::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
GlobalEvents::check(event);
}
}
// Comprueba las entradas
void Logo::checkInput()
{
Input::get()->update();
GlobalInputs::check();
void Logo::checkInput() {
Input::get()->update();
GlobalInputs::check();
}
// Gestiona el logo de JAILGAMES
void Logo::updateJAILGAMES()
{
if (counter_ == 30)
{
Audio::get()->playSound("logo.wav");
}
void Logo::updateJAILGAMES() {
if (counter_ == 30) {
Audio::get()->playSound("logo.wav");
}
if (counter_ > 30)
{
for (int i = 0; i < (int)jail_sprite_.size(); ++i)
{
if (jail_sprite_[i]->getX() != dest_.x)
{
if (i % 2 == 0)
{
jail_sprite_[i]->incX(-SPEED);
if (jail_sprite_[i]->getX() < dest_.x)
{
jail_sprite_[i]->setX(dest_.x);
}
}
else
{
jail_sprite_[i]->incX(SPEED);
if (jail_sprite_[i]->getX() > dest_.x)
{
jail_sprite_[i]->setX(dest_.x);
}
}
}
}
}
if (counter_ > 30) {
for (int i = 0; i < (int)jail_sprite_.size(); ++i) {
if (jail_sprite_[i]->getX() != dest_.x) {
if (i % 2 == 0) {
jail_sprite_[i]->incX(-SPEED);
if (jail_sprite_[i]->getX() < dest_.x) {
jail_sprite_[i]->setX(dest_.x);
}
} else {
jail_sprite_[i]->incX(SPEED);
if (jail_sprite_[i]->getX() > dest_.x) {
jail_sprite_[i]->setX(dest_.x);
}
}
}
}
}
// Comprueba si ha terminado el logo
if (counter_ == END_LOGO_COUNTER_MARK + POST_LOGO_DURATION)
{
Section::name = Section::Name::INTRO;
}
// Comprueba si ha terminado el logo
if (counter_ == END_LOGO_COUNTER_MARK + POST_LOGO_DURATION) {
Section::name = Section::Name::INTRO;
}
}
// Gestiona el color de las texturas
void Logo::updateTextureColors()
{
constexpr int inc = 4;
void Logo::updateTextureColors() {
constexpr int inc = 4;
// Manejo de 'sinceTexture'
for (int i = 0; i <= 7; ++i)
{
if (counter_ == SHOW_SINCE_SPRITE_COUNTER_MARK + inc * i)
{
since_texture_->setColor(color_[i].r, color_[i].g, color_[i].b);
}
}
// Manejo de 'sinceTexture'
for (int i = 0; i <= 7; ++i) {
if (counter_ == SHOW_SINCE_SPRITE_COUNTER_MARK + inc * i) {
since_texture_->setColor(color_[i].r, color_[i].g, color_[i].b);
}
}
// Manejo de 'jailTexture' y 'sinceTexture' en el fade
for (int i = 0; i <= 6; ++i)
{
if (counter_ == INIT_FADE_COUNTER_MARK + inc * i)
{
jail_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b);
since_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b);
}
}
// Manejo de 'jailTexture' y 'sinceTexture' en el fade
for (int i = 0; i <= 6; ++i) {
if (counter_ == INIT_FADE_COUNTER_MARK + inc * i) {
jail_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b);
since_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b);
}
}
}
// Actualiza las variables
void Logo::update()
{
if (SDL_GetTicks() - ticks_ > param.game.speed)
{
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
void Logo::update() {
if (SDL_GetTicks() - ticks_ > param.game.speed) {
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Actualiza el objeto screen
Screen::get()->update();
// Actualiza el objeto screen
Screen::get()->update();
// Comprueba las entradas
checkInput();
// Comprueba las entradas
checkInput();
updateJAILGAMES();
updateTextureColors();
updateJAILGAMES();
updateTextureColors();
// Gestiona el contador
++counter_;
}
// Gestiona el contador
++counter_;
}
}
// Dibuja en pantalla
void Logo::render()
{
Screen::get()->start();
Screen::get()->clean();
void Logo::render() {
Screen::get()->start();
Screen::get()->clean();
renderJAILGAMES();
renderJAILGAMES();
Screen::get()->render();
Screen::get()->render();
}
// Bucle para el logo del juego
void Logo::run()
{
while (Section::name == Section::Name::LOGO)
{
checkInput();
update();
checkEvents(); // Tiene que ir antes del render
render();
}
void Logo::run() {
while (Section::name == Section::Name::LOGO) {
checkInput();
update();
checkEvents(); // Tiene que ir antes del render
render();
}
}
// Renderiza el logo de JAILGAMES
void Logo::renderJAILGAMES()
{
// Dibuja los sprites
for (auto &sprite : jail_sprite_)
{
sprite->render();
}
void Logo::renderJAILGAMES() {
// Dibuja los sprites
for (auto &sprite : jail_sprite_) {
sprite->render();
}
if (counter_ >= SHOW_SINCE_SPRITE_COUNTER_MARK)
{
since_sprite_->render();
}
if (counter_ >= SHOW_SINCE_SPRITE_COUNTER_MARK) {
since_sprite_->render();
}
}

View File

@@ -1,60 +1,60 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FPoint, Uint64
#include <memory> // Para shared_ptr, unique_ptr
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_FPoint, Uint64
#include <memory> // Para shared_ptr, unique_ptr
#include <vector> // Para vector
class Sprite;
class Texture;
struct Color;
/*
Esta clase gestiona un estado del programa. Se encarga de dibujar por pantalla el
logo de "JAILGAMES" utilizando un sencillo efecto consistente en generar un sprite por
cada línea del bitmap que forma la palabra "JAILGAMES". Posteriormente realiza una
modulación de color sobre la textura para simular un fade to black al estilo
ZX Spectrum.
Esta clase gestiona un estado del programa. Se encarga de dibujar por pantalla el
logo de "JAILGAMES" utilizando un sencillo efecto consistente en generar un sprite por
cada línea del bitmap que forma la palabra "JAILGAMES". Posteriormente realiza una
modulación de color sobre la textura para simular un fade to black al estilo
ZX Spectrum.
*/
// --- Clase Logo ---
class Logo
{
public:
// Constructor
Logo();
class Logo {
public:
// Constructor
Logo();
// Destructor
~Logo();
// Destructor
~Logo();
// Bucle principal
void run();
// Bucle principal
void run();
private:
// --- Constantes ---
static constexpr int SHOW_SINCE_SPRITE_COUNTER_MARK = 70; // Tiempo del contador en el que empieza a verse el sprite de "SINCE 1998"
static constexpr int INIT_FADE_COUNTER_MARK = 300; // Tiempo del contador cuando inicia el fade a negro
static constexpr int END_LOGO_COUNTER_MARK = 400; // Tiempo del contador para terminar el logo
static constexpr int POST_LOGO_DURATION = 20; // Tiempo que dura el logo con el fade al máximo
static constexpr int SPEED = 8; // Velocidad de desplazamiento de cada línea
private:
// --- Constantes ---
static constexpr int SHOW_SINCE_SPRITE_COUNTER_MARK = 70; // Tiempo del contador en el que empieza a verse el sprite de "SINCE 1998"
static constexpr int INIT_FADE_COUNTER_MARK = 300; // Tiempo del contador cuando inicia el fade a negro
static constexpr int END_LOGO_COUNTER_MARK = 400; // Tiempo del contador para terminar el logo
static constexpr int POST_LOGO_DURATION = 20; // Tiempo que dura el logo con el fade al máximo
static constexpr int SPEED = 8; // Velocidad de desplazamiento de cada línea
// --- Objetos y punteros ---
std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998"
std::unique_ptr<Sprite> since_sprite_; // Sprite para manejar la since_texture
std::shared_ptr<Texture> jail_texture_; // Textura con los gráficos "JAILGAMES"
std::vector<std::unique_ptr<Sprite>> jail_sprite_; // Vector con los sprites de cada línea que forman el bitmap JAILGAMES
// --- Objetos y punteros ---
std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998"
std::unique_ptr<Sprite> since_sprite_; // Sprite para manejar la since_texture
std::shared_ptr<Texture> jail_texture_; // Textura con los gráficos "JAILGAMES"
std::vector<std::unique_ptr<Sprite>> jail_sprite_; // Vector con los sprites de cada línea que forman el bitmap JAILGAMES
// --- Variables ---
std::vector<Color> color_; // Vector con los colores para el fade
int counter_ = 0; // Contador
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
SDL_FPoint dest_; // Posición donde dibujar el logo
// --- Variables ---
std::vector<Color> color_; // Vector con los colores para el fade
int counter_ = 0; // Contador
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
SDL_FPoint dest_; // Posición donde dibujar el logo
// --- Métodos internos ---
void update(); // Actualiza las variables
void render(); // Dibuja en pantalla
void checkEvents(); // Comprueba el manejador de eventos
void checkInput(); // Comprueba las entradas
void updateJAILGAMES(); // Gestiona el logo de JAILGAMES
void renderJAILGAMES(); // Renderiza el logo de JAILGAMES
void updateTextureColors(); // Gestiona el color de las texturas
// --- Métodos internos ---
void update(); // Actualiza las variables
void render(); // Dibuja en pantalla
void checkEvents(); // Comprueba el manejador de eventos
void checkInput(); // Comprueba las entradas
void updateJAILGAMES(); // Gestiona el logo de JAILGAMES
void renderJAILGAMES(); // Renderiza el logo de JAILGAMES
void updateTextureColors(); // Gestiona el color de las texturas
};

View File

@@ -1,579 +1,523 @@
#include "title.h"
#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_EventType
#include <stddef.h> // Para size_t
#include <algorithm> // Para find_if
#include <iostream> // Para basic_ostream, basic_ostream::operator<<
#include <string> // Para basic_string, char_traits, operator+
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_EventType
#include <stddef.h> // Para size_t
#include "audio.h" // Para Audio
#include "define_buttons.h" // Para DefineButtons
#include "fade.h" // Para Fade, FadeType
#include "game_logo.h" // Para GameLogo
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input, INPUT_DO_NOT_ALLOW_REPEAT, Input...
#include "lang.h" // Para getText
#include "notifier.h" // Para Notifier
#include "options.h" // Para GamepadOptions, controllers, getPlayerW...
#include "param.h" // Para Param, param, ParamGame, ParamTitle
#include "player.h" // Para Player, PlayerState
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name, Options, options, AttractMode
#include "sprite.h" // Para Sprite
#include "text.h" // Para TEXT_CENTER, TEXT_SHADOW, Text
#include "tiled_bg.h" // Para TiledBG, TiledBGMode
#include "ui/service_menu.h" // Para ServiceMenu
#include "utils.h" // Para Color, Zone, NO_TEXT_COLOR, TITLE_SHADO...
#include <algorithm> // Para find_if
#include <iostream> // Para basic_ostream, basic_ostream::operator<<
#include <string> // Para basic_string, char_traits, operator+
#include <vector> // Para vector
#include "audio.h" // Para Audio
#include "define_buttons.h" // Para DefineButtons
#include "fade.h" // Para Fade, FadeType
#include "game_logo.h" // Para GameLogo
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input, INPUT_DO_NOT_ALLOW_REPEAT, Input...
#include "lang.h" // Para getText
#include "notifier.h" // Para Notifier
#include "options.h" // Para GamepadOptions, controllers, getPlayerW...
#include "param.h" // Para Param, param, ParamGame, ParamTitle
#include "player.h" // Para Player, PlayerState
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.h" // Para Name, name, Options, options, AttractMode
#include "sprite.h" // Para Sprite
#include "text.h" // Para TEXT_CENTER, TEXT_SHADOW, Text
#include "tiled_bg.h" // Para TiledBG, TiledBGMode
#include "ui/service_menu.h" // Para ServiceMenu
#include "utils.h" // Para Color, Zone, NO_TEXT_COLOR, TITLE_SHADO...
class Texture;
#ifdef DEBUG
#include <iomanip> // Para operator<<, setfill, setw
#include <iomanip> // Para operator<<, setfill, setw
#endif
// Constructor
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"))),
define_buttons_(std::make_unique<DefineButtons>()),
num_controllers_(Input::get()->getNumControllers()),
state_(TitleState::LOGO_ANIMATING)
{
// Configura objetos
tiled_bg_->setColor(param.title.bg_color);
game_logo_->enable();
mini_logo_sprite_->setX(param.game.game_area.center_x - mini_logo_sprite_->getWidth() / 2);
fade_->setColor(param.fade.color);
fade_->setType(FadeType::RANDOM_SQUARE);
fade_->setPostDuration(param.fade.post_duration);
initPlayers();
: 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"))),
define_buttons_(std::make_unique<DefineButtons>()),
num_controllers_(Input::get()->getNumControllers()),
state_(TitleState::LOGO_ANIMATING) {
// Configura objetos
tiled_bg_->setColor(param.title.bg_color);
game_logo_->enable();
mini_logo_sprite_->setX(param.game.game_area.center_x - mini_logo_sprite_->getWidth() / 2);
fade_->setColor(param.fade.color);
fade_->setType(FadeType::RANDOM_SQUARE);
fade_->setPostDuration(param.fade.post_duration);
initPlayers();
// Asigna valores a otras variables
Section::options = Section::Options::TITLE_1;
const bool IS_TITLE_TO_DEMO = (Section::attract_mode == Section::AttractMode::TITLE_TO_DEMO);
next_section_ = IS_TITLE_TO_DEMO ? Section::Name::GAME_DEMO : Section::Name::LOGO;
Section::attract_mode = IS_TITLE_TO_DEMO ? Section::AttractMode::TITLE_TO_LOGO : Section::AttractMode::TITLE_TO_DEMO;
// Asigna valores a otras variables
Section::options = Section::Options::TITLE_1;
const bool IS_TITLE_TO_DEMO = (Section::attract_mode == Section::AttractMode::TITLE_TO_DEMO);
next_section_ = IS_TITLE_TO_DEMO ? Section::Name::GAME_DEMO : Section::Name::LOGO;
Section::attract_mode = IS_TITLE_TO_DEMO ? Section::AttractMode::TITLE_TO_LOGO : Section::AttractMode::TITLE_TO_DEMO;
// Define los anclajes de los elementos
anchor_.mini_logo = (param.game.height / 5 * 4) + BLOCK;
mini_logo_sprite_->setY(anchor_.mini_logo);
anchor_.copyright_text = anchor_.mini_logo + mini_logo_sprite_->getHeight() + 3;
// Define los anclajes de los elementos
anchor_.mini_logo = (param.game.height / 5 * 4) + BLOCK;
mini_logo_sprite_->setY(anchor_.mini_logo);
anchor_.copyright_text = anchor_.mini_logo + mini_logo_sprite_->getHeight() + 3;
}
// Destructor
Title::~Title()
{
Audio::get()->stopAllSounds();
if (Section::name == Section::Name::LOGO)
{
Audio::get()->fadeOutMusic(300);
}
Title::~Title() {
Audio::get()->stopAllSounds();
if (Section::name == Section::Name::LOGO) {
Audio::get()->fadeOutMusic(300);
}
}
// Actualiza las variables del objeto
void Title::update()
{
if (SDL_GetTicks() - ticks_ > param.game.speed)
{
ticks_ = SDL_GetTicks();
updateFade();
updateState();
updateStartPrompt();
updatePlayers();
Screen::get()->update();
}
void Title::update() {
if (SDL_GetTicks() - ticks_ > param.game.speed) {
ticks_ = SDL_GetTicks();
updateFade();
updateState();
updateStartPrompt();
updatePlayers();
Screen::get()->update();
}
}
// Dibuja el objeto en pantalla
void Title::render()
{
Screen::get()->start();
Screen::get()->clean();
void Title::render() {
Screen::get()->start();
Screen::get()->clean();
tiled_bg_->render();
game_logo_->render();
renderPlayers();
renderStartPrompt();
renderCopyright();
tiled_bg_->render();
game_logo_->render();
renderPlayers();
renderStartPrompt();
renderCopyright();
define_buttons_->render();
fade_->render();
define_buttons_->render();
fade_->render();
Screen::get()->render();
Screen::get()->render();
}
// Comprueba los eventos
void Title::checkEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
void Title::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
#ifdef DEBUG
if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 1)
{
static Color color = param.title.bg_color;
switch (event.key.key)
{
case SDLK_A:
if (color.r < 255)
++color.r;
break;
if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 1) {
static Color color = param.title.bg_color;
switch (event.key.key) {
case SDLK_A:
if (color.r < 255)
++color.r;
break;
case SDLK_Z:
if (color.r > 0)
--color.r;
break;
case SDLK_Z:
if (color.r > 0)
--color.r;
break;
case SDLK_S:
if (color.g < 255)
++color.g;
break;
case SDLK_S:
if (color.g < 255)
++color.g;
break;
case SDLK_X:
if (color.g > 0)
--color.g;
break;
case SDLK_X:
if (color.g > 0)
--color.g;
break;
case SDLK_D:
if (color.b < 255)
++color.b;
break;
case SDLK_D:
if (color.b < 255)
++color.b;
break;
case SDLK_C:
if (color.b > 0)
--color.b;
break;
case SDLK_C:
if (color.b > 0)
--color.b;
break;
case SDLK_F:
if (color.r < 255)
++color.r;
if (color.g < 255)
++color.g;
if (color.b < 255)
++color.b;
break;
case SDLK_F:
if (color.r < 255)
++color.r;
if (color.g < 255)
++color.g;
if (color.b < 255)
++color.b;
break;
case SDLK_V:
if (color.r > 0)
--color.r;
if (color.g > 0)
--color.g;
if (color.b > 0)
--color.b;
break;
case SDLK_V:
if (color.r > 0)
--color.r;
if (color.g > 0)
--color.g;
if (color.b > 0)
--color.b;
break;
default:
break;
}
counter_ = 0;
tiled_bg_->setColor(color);
std::cout << "#"
<< std::hex << std::setw(2) << std::setfill('0') << (int)color.r
<< std::setw(2) << std::setfill('0') << (int)color.g
<< std::setw(2) << std::setfill('0') << (int)color.b
<< std::endl;
}
default:
break;
}
counter_ = 0;
tiled_bg_->setColor(color);
std::cout << "#"
<< std::hex << std::setw(2) << std::setfill('0') << (int)color.r
<< std::setw(2) << std::setfill('0') << (int)color.g
<< std::setw(2) << std::setfill('0') << (int)color.b
<< std::endl;
}
#endif
if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 0)
{
switch (event.key.key)
{
case SDLK_1: // Redefine los botones del mando #0
define_buttons_->enable(0);
resetCounter();
break;
if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 0) {
switch (event.key.key) {
case SDLK_1: // Redefine los botones del mando #0
define_buttons_->enable(0);
resetCounter();
break;
case SDLK_2: // Redefine los botones del mando #1
define_buttons_->enable(1);
resetCounter();
break;
case SDLK_2: // Redefine los botones del mando #1
define_buttons_->enable(1);
resetCounter();
break;
case SDLK_3: // Intercambia los mandos entre los dos jugadores
swapControllers();
resetCounter();
break;
case SDLK_3: // Intercambia los mandos entre los dos jugadores
swapControllers();
resetCounter();
break;
case SDLK_4: // Intercambia la asignación del teclado
swapKeyboard();
resetCounter();
break;
case SDLK_4: // Intercambia la asignación del teclado
swapKeyboard();
resetCounter();
break;
case SDLK_5: // Muestra la asignación de mandos y teclado
showControllers();
resetCounter();
break;
case SDLK_5: // Muestra la asignación de mandos y teclado
showControllers();
resetCounter();
break;
default:
break;
}
}
default:
break;
}
}
GlobalEvents::check(event);
define_buttons_->checkEvents(event);
}
GlobalEvents::check(event);
define_buttons_->checkEvents(event);
}
}
// Comprueba las entradas
void Title::checkInput()
{
// Comprueba las entradas solo si no se estan definiendo los botones
if (define_buttons_->isEnabled())
return;
void Title::checkInput() {
// Comprueba las entradas solo si no se estan definiendo los botones
if (define_buttons_->isEnabled())
return;
Input::get()->update();
Input::get()->update();
if (!ServiceMenu::get()->isEnabled())
{
// Comprueba todos los métodos de control
for (const auto &CONTROLLER : Options::controllers)
{
// Boton START
if (Input::get()->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index))
{
if ((state_ != TitleState::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP))
{
if (CONTROLLER.player_id == 1)
{
if (!player1_start_pressed_)
{
player1_start_pressed_ = true;
getPlayer(1)->setPlayingState(PlayerState::TITLE_ANIMATION);
setState(TitleState::START_HAS_BEEN_PRESSED);
counter_ = 0;
}
}
if (!ServiceMenu::get()->isEnabled()) {
// Comprueba todos los métodos de control
for (const auto &CONTROLLER : Options::controllers) {
// Boton START
if (Input::get()->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) {
if ((state_ != TitleState::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP)) {
if (CONTROLLER.player_id == 1) {
if (!player1_start_pressed_) {
player1_start_pressed_ = true;
getPlayer(1)->setPlayingState(PlayerState::TITLE_ANIMATION);
setState(TitleState::START_HAS_BEEN_PRESSED);
counter_ = 0;
}
}
if (CONTROLLER.player_id == 2)
{
if (!player2_start_pressed_)
{
player2_start_pressed_ = true;
getPlayer(2)->setPlayingState(PlayerState::TITLE_ANIMATION);
setState(TitleState::START_HAS_BEEN_PRESSED);
counter_ = 0;
}
}
}
}
}
}
if (CONTROLLER.player_id == 2) {
if (!player2_start_pressed_) {
player2_start_pressed_ = true;
getPlayer(2)->setPlayingState(PlayerState::TITLE_ANIMATION);
setState(TitleState::START_HAS_BEEN_PRESSED);
counter_ = 0;
}
}
}
}
}
}
// Comprueba los inputs que se pueden introducir en cualquier sección del juego
GlobalInputs::check();
// Comprueba los inputs que se pueden introducir en cualquier sección del juego
GlobalInputs::check();
}
// Bucle para el titulo del juego
void Title::run()
{
while (Section::name == Section::Name::TITLE)
{
checkInput();
update();
checkEvents(); // Tiene que ir antes del render
render();
}
void Title::run() {
while (Section::name == Section::Name::TITLE) {
checkInput();
update();
checkEvents(); // Tiene que ir antes del render
render();
}
}
// Reinicia el contador interno
void Title::resetCounter() { counter_ = 0; }
// Intercambia la asignación de mandos a los jugadores
void Title::swapControllers()
{
if (Input::get()->getNumControllers() == 0)
return;
void Title::swapControllers() {
if (Input::get()->getNumControllers() == 0)
return;
Options::swapControllers();
showControllers();
Options::swapControllers();
showControllers();
}
// Intercambia el teclado de jugador
void Title::swapKeyboard()
{
Options::swapKeyboard();
std::string text = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(Options::getPlayerWhoUsesKeyboard()) + ": " + Lang::getText("[DEFINE_BUTTONS] KEYBOARD");
Notifier::get()->show({text});
void Title::swapKeyboard() {
Options::swapKeyboard();
std::string text = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(Options::getPlayerWhoUsesKeyboard()) + ": " + Lang::getText("[DEFINE_BUTTONS] KEYBOARD");
Notifier::get()->show({text});
}
// Muestra información sobre los controles y los jugadores
void Title::showControllers()
{
// Crea vectores de texto vacíos para un número máximo de mandos
constexpr size_t NUM_CONTROLLERS = 2;
std::vector<std::string> text(NUM_CONTROLLERS);
std::vector<int> player_controller_index(NUM_CONTROLLERS, -1);
void Title::showControllers() {
// Crea vectores de texto vacíos para un número máximo de mandos
constexpr size_t NUM_CONTROLLERS = 2;
std::vector<std::string> text(NUM_CONTROLLERS);
std::vector<int> player_controller_index(NUM_CONTROLLERS, -1);
// Obtiene de cada jugador el índice del mando que tiene asignado
for (size_t i = 0; i < NUM_CONTROLLERS; ++i)
{
// Ejemplo: el jugador 1 tiene el mando 2
player_controller_index.at(Options::controllers.at(i).player_id - 1) = i;
}
// Obtiene de cada jugador el índice del mando que tiene asignado
for (size_t i = 0; i < NUM_CONTROLLERS; ++i) {
// Ejemplo: el jugador 1 tiene el mando 2
player_controller_index.at(Options::controllers.at(i).player_id - 1) = i;
}
// Genera el texto correspondiente
for (size_t i = 0; i < NUM_CONTROLLERS; ++i)
{
const size_t index = player_controller_index.at(i);
if (Options::controllers.at(index).plugged)
{
text.at(i) = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(i + 1) + ": " + Options::controllers.at(index).name;
}
}
// Genera el texto correspondiente
for (size_t i = 0; i < NUM_CONTROLLERS; ++i) {
const size_t index = player_controller_index.at(i);
if (Options::controllers.at(index).plugged) {
text.at(i) = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(i + 1) + ": " + Options::controllers.at(index).name;
}
}
// Muestra la notificación
Notifier::get()->show({text.at(0), text.at(1)});
// Muestra la notificación
Notifier::get()->show({text.at(0), text.at(1)});
}
// Actualiza el fade
void Title::updateFade()
{
fade_->update();
if (fade_->hasEnded())
{
const int COMBO = (player1_start_pressed_ ? 1 : 0) | (player2_start_pressed_ ? 2 : 0);
void Title::updateFade() {
fade_->update();
if (fade_->hasEnded()) {
const int COMBO = (player1_start_pressed_ ? 1 : 0) | (player2_start_pressed_ ? 2 : 0);
switch (COMBO)
{
case 0: // Ningún jugador ha pulsado Start
Section::name = next_section_;
break;
switch (COMBO) {
case 0: // Ningún jugador ha pulsado Start
Section::name = next_section_;
break;
case 1: // Solo el jugador 1 ha pulsado Start
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P;
Audio::get()->stopMusic();
break;
case 1: // Solo el jugador 1 ha pulsado Start
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P;
Audio::get()->stopMusic();
break;
case 2: // Solo el jugador 2 ha pulsado Start
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_2P;
Audio::get()->stopMusic();
break;
case 2: // Solo el jugador 2 ha pulsado Start
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_2P;
Audio::get()->stopMusic();
break;
case 3: // Ambos jugadores han pulsado Start
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_BOTH;
Audio::get()->stopMusic();
break;
}
}
case 3: // Ambos jugadores han pulsado Start
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_BOTH;
Audio::get()->stopMusic();
break;
}
}
}
// Actualiza el estado
void Title::updateState()
{
// Establece la lógica según el estado
switch (state_)
{
case TitleState::LOGO_ANIMATING:
{
game_logo_->update();
if (game_logo_->hasFinished())
{
setState(TitleState::LOGO_FINISHED);
}
break;
}
case TitleState::LOGO_FINISHED:
{
// El contador solo sube si no estamos definiendo botones
counter_ = define_buttons_->isEnabled() ? 0 : counter_ + 1;
void Title::updateState() {
// Establece la lógica según el estado
switch (state_) {
case TitleState::LOGO_ANIMATING: {
game_logo_->update();
if (game_logo_->hasFinished()) {
setState(TitleState::LOGO_FINISHED);
}
break;
}
case TitleState::LOGO_FINISHED: {
// El contador solo sube si no estamos definiendo botones
counter_ = define_buttons_->isEnabled() ? 0 : counter_ + 1;
// Actualiza el logo con el título del juego
game_logo_->update();
// Actualiza el logo con el título del juego
game_logo_->update();
// Actualiza el mosaico de fondo
tiled_bg_->update();
// Actualiza el mosaico de fondo
tiled_bg_->update();
if (counter_ == param.title.title_duration)
{
// El menu ha hecho time out
fade_->setPostDuration(0);
fade_->activate();
selection_ = Section::Options::TITLE_TIME_OUT;
}
if (counter_ == param.title.title_duration) {
// El menu ha hecho time out
fade_->setPostDuration(0);
fade_->activate();
selection_ = Section::Options::TITLE_TIME_OUT;
}
break;
}
case TitleState::START_HAS_BEEN_PRESSED:
{
// Actualiza el logo con el título del juego
game_logo_->update();
break;
}
case TitleState::START_HAS_BEEN_PRESSED: {
// Actualiza el logo con el título del juego
game_logo_->update();
// Actualiza el mosaico de fondo
tiled_bg_->update();
// Actualiza el mosaico de fondo
tiled_bg_->update();
if (counter_ == 100)
{
fade_->activate();
}
++counter_;
break;
}
if (counter_ == 100) {
fade_->activate();
}
++counter_;
break;
}
default:
break;
}
default:
break;
}
}
void Title::updateStartPrompt()
{
constexpr Uint32 LOGO_BLINK_PERIOD = 833; // milisegundos
constexpr Uint32 LOGO_BLINK_ON_TIME = 583; // 833 - 250
void Title::updateStartPrompt() {
constexpr Uint32 LOGO_BLINK_PERIOD = 833; // milisegundos
constexpr Uint32 LOGO_BLINK_ON_TIME = 583; // 833 - 250
constexpr Uint32 START_BLINK_PERIOD = 167;
constexpr Uint32 START_BLINK_ON_TIME = 83; // 167 - 83
constexpr Uint32 START_BLINK_PERIOD = 167;
constexpr Uint32 START_BLINK_ON_TIME = 83; // 167 - 83
Uint32 time_ms = SDL_GetTicks();
bool condition_met = false;
Uint32 time_ms = SDL_GetTicks();
bool condition_met = false;
if (!define_buttons_->isEnabled())
{
switch (state_)
{
case TitleState::LOGO_FINISHED:
condition_met = (time_ms % LOGO_BLINK_PERIOD) >= (LOGO_BLINK_PERIOD - LOGO_BLINK_ON_TIME);
break;
if (!define_buttons_->isEnabled()) {
switch (state_) {
case TitleState::LOGO_FINISHED:
condition_met = (time_ms % LOGO_BLINK_PERIOD) >= (LOGO_BLINK_PERIOD - LOGO_BLINK_ON_TIME);
break;
case TitleState::START_HAS_BEEN_PRESSED:
condition_met = (time_ms % START_BLINK_PERIOD) >= (START_BLINK_PERIOD - START_BLINK_ON_TIME);
break;
case TitleState::START_HAS_BEEN_PRESSED:
condition_met = (time_ms % START_BLINK_PERIOD) >= (START_BLINK_PERIOD - START_BLINK_ON_TIME);
break;
default:
break;
}
}
default:
break;
}
}
should_render_start_prompt = condition_met;
should_render_start_prompt = condition_met;
}
void Title::renderStartPrompt()
{
if (should_render_start_prompt)
{
text_->writeDX(TEXT_CENTER | TEXT_SHADOW,
param.game.game_area.center_x,
param.title.press_start_position,
Lang::getText("[TITLE] PRESS_BUTTON_TO_PLAY"),
1,
NO_TEXT_COLOR,
1,
TITLE_SHADOW_TEXT_COLOR);
}
void Title::renderStartPrompt() {
if (should_render_start_prompt) {
text_->writeDX(TEXT_CENTER | TEXT_SHADOW,
param.game.game_area.center_x,
param.title.press_start_position,
Lang::getText("[TITLE] PRESS_BUTTON_TO_PLAY"),
1,
NO_TEXT_COLOR,
1,
TITLE_SHADOW_TEXT_COLOR);
}
}
void Title::renderCopyright()
{
if (state_ != TitleState::LOGO_ANIMATING)
{
// Mini logo
mini_logo_sprite_->render();
void Title::renderCopyright() {
if (state_ != TitleState::LOGO_ANIMATING) {
// Mini logo
mini_logo_sprite_->render();
// Texto con el copyright
text_->writeDX(TEXT_CENTER | TEXT_SHADOW,
param.game.game_area.center_x,
anchor_.copyright_text,
TEXT_COPYRIGHT,
1,
NO_TEXT_COLOR,
1,
TITLE_SHADOW_TEXT_COLOR);
}
// Texto con el copyright
text_->writeDX(TEXT_CENTER | TEXT_SHADOW,
param.game.game_area.center_x,
anchor_.copyright_text,
TEXT_COPYRIGHT,
1,
NO_TEXT_COLOR,
1,
TITLE_SHADOW_TEXT_COLOR);
}
}
// Cambia el estado
void Title::setState(TitleState state)
{
if (state_ == state)
return;
void Title::setState(TitleState state) {
if (state_ == state)
return;
state_ = state;
switch (state_)
{
case TitleState::LOGO_ANIMATING:
break;
case TitleState::LOGO_FINISHED:
Audio::get()->playMusic("title.ogg");
break;
case TitleState::START_HAS_BEEN_PRESSED:
Audio::get()->fadeOutMusic(1500);
break;
}
state_ = state;
switch (state_) {
case TitleState::LOGO_ANIMATING:
break;
case TitleState::LOGO_FINISHED:
Audio::get()->playMusic("title.ogg");
break;
case TitleState::START_HAS_BEEN_PRESSED:
Audio::get()->fadeOutMusic(1500);
break;
}
}
// Inicializa los jugadores
void Title::initPlayers()
{
std::vector<std::vector<std::shared_ptr<Texture>>> player_textures; // Vector con todas las texturas de los jugadores;
std::vector<std::vector<std::string>> player_animations; // Vector con las animaciones del jugador
void Title::initPlayers() {
std::vector<std::vector<std::shared_ptr<Texture>>> player_textures; // Vector con todas las texturas de los jugadores;
std::vector<std::vector<std::string>> player_animations; // Vector con las animaciones del jugador
// Texturas - Player1
{
std::vector<std::shared_ptr<Texture>> player_texture;
player_texture.emplace_back(Resource::get()->getTexture("player1.gif"));
player_texture.emplace_back(Resource::get()->getTexture("player1_power.png"));
player_textures.push_back(player_texture);
}
// Texturas - Player1
{
std::vector<std::shared_ptr<Texture>> player_texture;
player_texture.emplace_back(Resource::get()->getTexture("player1.gif"));
player_texture.emplace_back(Resource::get()->getTexture("player1_power.png"));
player_textures.push_back(player_texture);
}
// Texturas - Player2
{
std::vector<std::shared_ptr<Texture>> player_texture;
player_texture.emplace_back(Resource::get()->getTexture("player2.gif"));
player_texture.emplace_back(Resource::get()->getTexture("player2_power.png"));
player_textures.push_back(player_texture);
}
// Texturas - Player2
{
std::vector<std::shared_ptr<Texture>> player_texture;
player_texture.emplace_back(Resource::get()->getTexture("player2.gif"));
player_texture.emplace_back(Resource::get()->getTexture("player2_power.png"));
player_textures.push_back(player_texture);
}
// Animaciones -- Jugador
{
player_animations.emplace_back(Resource::get()->getAnimation("player.ani"));
player_animations.emplace_back(Resource::get()->getAnimation("player_power.ani"));
}
// Animaciones -- Jugador
{
player_animations.emplace_back(Resource::get()->getAnimation("player.ani"));
player_animations.emplace_back(Resource::get()->getAnimation("player_power.ani"));
}
// Crea los dos jugadores
constexpr int PLAYER_WIDTH = 32;
constexpr int PLAYER_HEIGHT = 32;
const int Y = param.title.press_start_position - (PLAYER_HEIGHT / 2);
constexpr bool DEMO = false;
players_.emplace_back(std::make_unique<Player>(1, param.game.game_area.center_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(0), player_animations));
players_.back()->setPlayingState(PlayerState::TITLE_HIDDEN);
// Crea los dos jugadores
constexpr int PLAYER_WIDTH = 32;
constexpr int PLAYER_HEIGHT = 32;
const int Y = param.title.press_start_position - (PLAYER_HEIGHT / 2);
constexpr bool DEMO = false;
players_.emplace_back(std::make_unique<Player>(1, param.game.game_area.center_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(0), player_animations));
players_.back()->setPlayingState(PlayerState::TITLE_HIDDEN);
players_.emplace_back(std::make_unique<Player>(2, param.game.game_area.center_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(1), player_animations));
players_.back()->setPlayingState(PlayerState::TITLE_HIDDEN);
players_.emplace_back(std::make_unique<Player>(2, param.game.game_area.center_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(1), player_animations));
players_.back()->setPlayingState(PlayerState::TITLE_HIDDEN);
}
// Actualza los jugadores
void Title::updatePlayers()
{
for (auto &player : players_)
{
player->update();
}
void Title::updatePlayers() {
for (auto &player : players_) {
player->update();
}
}
// Renderiza los jugadores
void Title::renderPlayers()
{
for (auto const &player : players_)
{
player->render();
}
void Title::renderPlayers() {
for (auto const &player : players_) {
player->render();
}
}
// Obtiene un jugador a partir de su "id"
std::shared_ptr<Player> Title::getPlayer(int id)
{
auto it = std::find_if(players_.begin(), players_.end(), [id](const auto &player)
{ return player->getId() == id; });
std::shared_ptr<Player> Title::getPlayer(int id) {
auto it = std::find_if(players_.begin(), players_.end(), [id](const auto &player) { return player->getId() == id; });
if (it != players_.end())
{
return *it;
}
return nullptr;
if (it != players_.end()) {
return *it;
}
return nullptr;
}

View File

@@ -1,10 +1,11 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint32
#include <memory> // Para unique_ptr, shared_ptr
#include <SDL3/SDL.h> // Para Uint32
#include <memory> // Para unique_ptr, shared_ptr
#include <vector>
#include "section.h" // Para Options
#include "section.h" // Para Options
class DefineButtons;
class Fade;
@@ -21,78 +22,75 @@ constexpr const char TEXT_COPYRIGHT[] = "@2020,2025 JailDesigner";
constexpr bool ALLOW_TITLE_ANIMATION_SKIP = false;
/*
Clase que gestiona el estado de título/menú principal del juego.
Responsable de mostrar el logo, el fondo animado y gestionar la entrada para comenzar la partida.
No permite saltar la animación del título salvo que se cambie el define.
Clase que gestiona el estado de título/menú principal del juego.
Responsable de mostrar el logo, el fondo animado y gestionar la entrada para comenzar la partida.
No permite saltar la animación del título salvo que se cambie el define.
*/
// Clase Title
class Title
{
public:
// --- Constructores y destructor ---
Title();
~Title();
class Title {
public:
// --- Constructores y destructor ---
Title();
~Title();
// --- Método principal ---
void run(); // Bucle para el título del juego
// --- Método principal ---
void run(); // Bucle para el título del juego
private:
// --- Enumeraciones ---
enum class TitleState
{
LOGO_ANIMATING, // El logo está animándose
LOGO_FINISHED, // El logo ha terminado de animarse
START_HAS_BEEN_PRESSED, // Se ha pulsado el botón de start
};
private:
// --- Enumeraciones ---
enum class TitleState {
LOGO_ANIMATING, // El logo está animándose
LOGO_FINISHED, // El logo ha terminado de animarse
START_HAS_BEEN_PRESSED, // Se ha pulsado el botón de start
};
// --- Estructura para definir anclas ---
struct Anchor
{
int mini_logo;
int copyright_text;
};
// --- Estructura para definir anclas ---
struct Anchor {
int mini_logo;
int copyright_text;
};
// --- Objetos y punteros ---
std::shared_ptr<Text> text_; // Objeto de texto para escribir en pantalla
std::unique_ptr<Fade> fade_; // Fundido en pantalla
std::unique_ptr<TiledBG> tiled_bg_; // Fondo animado de tiles
std::unique_ptr<GameLogo> game_logo_; // Logo del juego
std::unique_ptr<Sprite> mini_logo_sprite_; // Logo JailGames mini
std::unique_ptr<DefineButtons> define_buttons_; // Definición de botones del joystick
std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores
// --- Objetos y punteros ---
std::shared_ptr<Text> text_; // Objeto de texto para escribir en pantalla
std::unique_ptr<Fade> fade_; // Fundido en pantalla
std::unique_ptr<TiledBG> tiled_bg_; // Fondo animado de tiles
std::unique_ptr<GameLogo> game_logo_; // Logo del juego
std::unique_ptr<Sprite> mini_logo_sprite_; // Logo JailGames mini
std::unique_ptr<DefineButtons> define_buttons_; // Definición de botones del joystick
std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores
// --- Variables de estado ---
int counter_ = 0; // Temporizador para la pantalla de título
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad
Section::Name next_section_; // Siguiente sección a cargar
Section::Options selection_ = Section::Options::TITLE_TIME_OUT; // Opción elegida en el título
int num_controllers_; // Número de mandos conectados
TitleState state_; // Estado actual de la sección
bool should_render_start_prompt = false; // Indica si se muestra o no el texto de PRESS START BUTTON TO PLAY
bool player1_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 1
bool player2_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 2
// --- Variables de estado ---
int counter_ = 0; // Temporizador para la pantalla de título
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad
Section::Name next_section_; // Siguiente sección a cargar
Section::Options selection_ = Section::Options::TITLE_TIME_OUT; // Opción elegida en el título
int num_controllers_; // Número de mandos conectados
TitleState state_; // Estado actual de la sección
bool should_render_start_prompt = false; // Indica si se muestra o no el texto de PRESS START BUTTON TO PLAY
bool player1_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 1
bool player2_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 2
// -- Variables de diseño ---
Anchor anchor_; // Anclas para definir la posición de los elementos del titulo
// -- Variables de diseño ---
Anchor anchor_; // Anclas para definir la posición de los elementos del titulo
// --- Métodos internos ---
void update(); // Actualiza las variables del objeto
void render(); // Dibuja el objeto en pantalla
void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas
void resetCounter(); // Reinicia el contador interno
void swapControllers(); // Intercambia la asignación de mandos a los jugadores
void swapKeyboard(); // Intercambia el teclado de jugador
void showControllers(); // Muestra información sobre los controles y los jugadores
void updateFade(); // Actualiza el efecto de fundido (fade in/out)
void updateState(); // Actualiza el estado actual del título
void updateStartPrompt(); // Actualiza el mensaje de "Pulsa Start"
void renderStartPrompt(); // Dibuja el mensaje de "Pulsa Start" en pantalla
void renderCopyright(); // Dibuja el aviso de copyright
void setState(TitleState state); // Cambia el estado del título
void initPlayers(); // Inicializa los jugadores
void renderPlayers(); // Renderiza los jugadores
void updatePlayers(); // Actualza los jugadores
std::shared_ptr<Player> getPlayer(int id); // Obtiene un jugador a partir de su "id"
// --- Métodos internos ---
void update(); // Actualiza las variables del objeto
void render(); // Dibuja el objeto en pantalla
void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas
void resetCounter(); // Reinicia el contador interno
void swapControllers(); // Intercambia la asignación de mandos a los jugadores
void swapKeyboard(); // Intercambia el teclado de jugador
void showControllers(); // Muestra información sobre los controles y los jugadores
void updateFade(); // Actualiza el efecto de fundido (fade in/out)
void updateState(); // Actualiza el estado actual del título
void updateStartPrompt(); // Actualiza el mensaje de "Pulsa Start"
void renderStartPrompt(); // Dibuja el mensaje de "Pulsa Start" en pantalla
void renderCopyright(); // Dibuja el aviso de copyright
void setState(TitleState state); // Cambia el estado del título
void initPlayers(); // Inicializa los jugadores
void renderPlayers(); // Renderiza los jugadores
void updatePlayers(); // Actualza los jugadores
std::shared_ptr<Player> getPlayer(int id); // Obtiene un jugador a partir de su "id"
};

View File

@@ -1,104 +1,86 @@
#include "smart_sprite.h"
#include "moving_sprite.h" // Para MovingSprite
#include "moving_sprite.h" // Para MovingSprite
// Actualiza la posición y comprueba si ha llegado a su destino
void SmartSprite::update()
{
if (enabled_)
{
MovingSprite::update();
checkMove();
checkFinished();
}
void SmartSprite::update() {
if (enabled_) {
MovingSprite::update();
checkMove();
checkFinished();
}
}
// Dibuja el sprite
void SmartSprite::render()
{
if (enabled_)
{
MovingSprite::render();
}
void SmartSprite::render() {
if (enabled_) {
MovingSprite::render();
}
}
// Comprueba el movimiento
void SmartSprite::checkMove()
{
// Comprueba si se desplaza en el eje X hacia la derecha
if (getAccelX() > 0 || getVelX() > 0)
{
// Comprueba si ha llegado al destino
if (getPosX() > dest_x_)
{
// Lo coloca en posición
setPosX(dest_x_);
void SmartSprite::checkMove() {
// Comprueba si se desplaza en el eje X hacia la derecha
if (getAccelX() > 0 || getVelX() > 0) {
// Comprueba si ha llegado al destino
if (getPosX() > dest_x_) {
// Lo coloca en posición
setPosX(dest_x_);
// Lo detiene
setVelX(0.0f);
setAccelX(0.0f);
}
}
// Comprueba si se desplaza en el eje X hacia la izquierda
else if (getAccelX() < 0 || getVelX() < 0)
{
// Comprueba si ha llegado al destino
if (getPosX() < dest_x_)
{
// Lo coloca en posición
setPosX(dest_x_);
// Lo detiene
setVelX(0.0f);
setAccelX(0.0f);
}
}
// Comprueba si se desplaza en el eje X hacia la izquierda
else if (getAccelX() < 0 || getVelX() < 0) {
// Comprueba si ha llegado al destino
if (getPosX() < dest_x_) {
// Lo coloca en posición
setPosX(dest_x_);
// Lo detiene
setVelX(0.0f);
setAccelX(0.0f);
}
}
// Lo detiene
setVelX(0.0f);
setAccelX(0.0f);
}
}
// Comprueba si se desplaza en el eje Y hacia abajo
if (getAccelY() > 0 || getVelY() > 0)
{
// Comprueba si ha llegado al destino
if (getPosY() > dest_y_)
{
// Lo coloca en posición
setPosY(dest_y_);
// Comprueba si se desplaza en el eje Y hacia abajo
if (getAccelY() > 0 || getVelY() > 0) {
// Comprueba si ha llegado al destino
if (getPosY() > dest_y_) {
// Lo coloca en posición
setPosY(dest_y_);
// Lo detiene
setVelY(0.0f);
setAccelY(0.0f);
}
}
// Comprueba si se desplaza en el eje Y hacia arriba
else if (getAccelY() < 0 || getVelY() < 0)
{
// Comprueba si ha llegado al destino
if (getPosY() < dest_y_)
{
// Lo coloca en posición
setPosY(dest_y_);
// Lo detiene
setVelY(0.0f);
setAccelY(0.0f);
}
}
// Comprueba si se desplaza en el eje Y hacia arriba
else if (getAccelY() < 0 || getVelY() < 0) {
// Comprueba si ha llegado al destino
if (getPosY() < dest_y_) {
// Lo coloca en posición
setPosY(dest_y_);
// Lo detiene
setVelY(0.0f);
setAccelY(0.0f);
}
}
// Lo detiene
setVelY(0.0f);
setAccelY(0.0f);
}
}
}
// Comprueba si ha terminado
void SmartSprite::checkFinished()
{
// Comprueba si ha llegado a su destino
on_destination_ = (getPosX() == dest_x_ && getPosY() == dest_y_);
void SmartSprite::checkFinished() {
// Comprueba si ha llegado a su destino
on_destination_ = (getPosX() == dest_x_ && getPosY() == dest_y_);
if (on_destination_)
{
if (finished_counter_ == 0)
{
finished_ = true;
}
else
{
--finished_counter_;
}
}
if (on_destination_) {
if (finished_counter_ == 0) {
finished_ = true;
} else {
--finished_counter_;
}
}
}

View File

@@ -1,46 +1,45 @@
#pragma once
#include <memory> // Para shared_ptr
#include <memory> // Para shared_ptr
#include "animated_sprite.h" // Para AnimatedSprite
#include "animated_sprite.h" // Para AnimatedSprite
class Texture;
// Clase SmartSprite: Sprite animado que se mueve hacia un destino y puede deshabilitarse automáticamente
class SmartSprite : public AnimatedSprite
{
public:
// --- Constructor y destructor ---
explicit SmartSprite(std::shared_ptr<Texture> texture)
: AnimatedSprite(texture) {}
~SmartSprite() override = default;
class SmartSprite : public AnimatedSprite {
public:
// --- Constructor y destructor ---
explicit SmartSprite(std::shared_ptr<Texture> texture)
: AnimatedSprite(texture) {}
~SmartSprite() override = default;
// --- Métodos principales ---
void update() override; // Actualiza la posición y comprueba si ha llegado a su destino
void render() override; // Dibuja el sprite
// --- Métodos principales ---
void update() override; // Actualiza la posición y comprueba si ha llegado a su destino
void render() override; // Dibuja el sprite
// --- Getters ---
int getDestX() const { return dest_x_; } // Obtiene la posición de destino en X
int getDestY() const { return dest_y_; } // Obtiene la posición de destino en Y
bool isOnDestination() const { return on_destination_; } // Indica si está en el destino
bool hasFinished() const { return finished_; } // Indica si ya ha terminado
// --- Getters ---
int getDestX() const { return dest_x_; } // Obtiene la posición de destino en X
int getDestY() const { return dest_y_; } // Obtiene la posición de destino en Y
bool isOnDestination() const { return on_destination_; } // Indica si está en el destino
bool hasFinished() const { return finished_; } // Indica si ya ha terminado
// --- Setters ---
void setFinishedCounter(int value) { finished_counter_ = value; } // Establece el contador para deshabilitarlo
void setDestX(int x) { dest_x_ = x; } // Establece la posición de destino en X
void setDestY(int y) { dest_y_ = y; } // Establece la posición de destino en Y
void setEnabled(bool value) { enabled_ = value; } // Habilita o deshabilita el objeto
// --- Setters ---
void setFinishedCounter(int value) { finished_counter_ = value; } // Establece el contador para deshabilitarlo
void setDestX(int x) { dest_x_ = x; } // Establece la posición de destino en X
void setDestY(int y) { dest_y_ = y; } // Establece la posición de destino en Y
void setEnabled(bool value) { enabled_ = value; } // Habilita o deshabilita el objeto
private:
// --- Variables internas ---
bool on_destination_ = false; // Indica si está en el destino
int dest_x_ = 0; // Posición de destino en el eje X
int dest_y_ = 0; // Posición de destino en el eje Y
int finished_counter_ = 0; // Contador para deshabilitarlo
bool finished_ = false; // Indica si ya ha terminado
bool enabled_ = false; // Indica si el objeto está habilitado
private:
// --- Variables internas ---
bool on_destination_ = false; // Indica si está en el destino
int dest_x_ = 0; // Posición de destino en el eje X
int dest_y_ = 0; // Posición de destino en el eje Y
int finished_counter_ = 0; // Contador para deshabilitarlo
bool finished_ = false; // Indica si ya ha terminado
bool enabled_ = false; // Indica si el objeto está habilitado
// --- Métodos internos ---
void checkFinished(); // Comprueba si ha terminado
void checkMove(); // Comprueba el movimiento
// --- Métodos internos ---
void checkFinished(); // Comprueba si ha terminado
void checkMove(); // Comprueba el movimiento
};

View File

@@ -1,46 +1,42 @@
#include "sprite.h"
#include "texture.h" // Para Texture
#include "texture.h" // Para Texture
// Constructor
Sprite::Sprite(std::shared_ptr<Texture> texture, float x, float y, float w, float h)
: texture_(texture),
pos_((SDL_FRect){x, y, w, h}),
sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {}
: texture_(texture),
pos_((SDL_FRect){x, y, w, h}),
sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {}
Sprite::Sprite(std::shared_ptr<Texture> texture, SDL_FRect rect)
: texture_(texture),
pos_(rect),
sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {}
: texture_(texture),
pos_(rect),
sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {}
Sprite::Sprite(std::shared_ptr<Texture> texture)
: texture_(texture),
pos_(SDL_FRect{0, 0, static_cast<float>(texture_->getWidth()), static_cast<float>(texture_->getHeight())}),
sprite_clip_(pos_) {}
: texture_(texture),
pos_(SDL_FRect{0, 0, static_cast<float>(texture_->getWidth()), static_cast<float>(texture_->getHeight())}),
sprite_clip_(pos_) {}
// Muestra el sprite por pantalla
void Sprite::render()
{
texture_->render(pos_.x, pos_.y, &sprite_clip_, zoom_, zoom_);
void Sprite::render() {
texture_->render(pos_.x, pos_.y, &sprite_clip_, zoom_, zoom_);
}
// Establece la posición del objeto
void Sprite::setPosition(float x, float y)
{
pos_.x = x;
pos_.y = y;
void Sprite::setPosition(float x, float y) {
pos_.x = x;
pos_.y = y;
}
// Establece la posición del objeto
void Sprite::setPosition(SDL_FPoint p)
{
pos_.x = p.x;
pos_.y = p.y;
void Sprite::setPosition(SDL_FPoint p) {
pos_.x = p.x;
pos_.y = p.y;
}
// Reinicia las variables a cero
void Sprite::clear()
{
pos_ = {0, 0, 0, 0};
sprite_clip_ = {0, 0, 0, 0};
void Sprite::clear() {
pos_ = {0, 0, 0, 0};
sprite_clip_ = {0, 0, 0, 0};
}

View File

@@ -1,14 +1,14 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_FPoint
#include <memory> // Para shared_ptr
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_FPoint
#include <memory> // Para shared_ptr
class Texture;
// Clase Sprite: representa un objeto gráfico básico con posición, tamaño y textura
class Sprite
{
public:
class Sprite {
public:
// --- Constructores y destructor ---
Sprite(std::shared_ptr<Texture> texture, float x, float y, float w, float h);
Sprite(std::shared_ptr<Texture> texture, SDL_FRect rect);
@@ -16,8 +16,8 @@ public:
virtual ~Sprite() = default;
// --- Renderizado y control ---
virtual void render(); // Muestra el sprite por pantalla
virtual void clear(); // Reinicia las variables a cero
virtual void render(); // Muestra el sprite por pantalla
virtual void clear(); // Reinicia las variables a cero
// --- Getters de posición y tamaño ---
float getX() const { return pos_.x; }
@@ -52,10 +52,10 @@ public:
std::shared_ptr<Texture> getTexture() const { return texture_; }
void setTexture(std::shared_ptr<Texture> texture) { texture_ = texture; }
protected:
protected:
// --- Variables internas ---
std::shared_ptr<Texture> texture_; // Textura donde están todos los dibujos del sprite
SDL_FRect pos_; // Posición y tamaño donde dibujar el sprite
SDL_FRect sprite_clip_; // Rectángulo de origen de la textura que se dibujará en pantalla
double zoom_ = 1.0f; // Zoom aplicado a la textura
std::shared_ptr<Texture> texture_; // Textura donde están todos los dibujos del sprite
SDL_FRect pos_; // Posición y tamaño donde dibujar el sprite
SDL_FRect sprite_clip_; // Rectángulo de origen de la textura que se dibujará en pantalla
double zoom_ = 1.0f; // Zoom aplicado a la textura
};

View File

@@ -1,47 +1,43 @@
#include "stage.h"
#include <algorithm> // Para min
#include <vector> // Para vector
#include <algorithm> // Para min
#include <vector> // Para vector
namespace Stage
{
namespace Stage {
std::vector<Stage> stages; // Variable con los datos de cada pantalla
int power = 0; // Poder acumulado en la fase
int total_power = 0; // Poder total necesario para completar el juego
int number = 0; // Fase actual
bool power_can_be_added = true; // Habilita la recolecta de poder
std::vector<Stage> stages; // Variable con los datos de cada pantalla
int power = 0; // Poder acumulado en la fase
int total_power = 0; // Poder total necesario para completar el juego
int number = 0; // Fase actual
bool power_can_be_added = true; // Habilita la recolecta de poder
// Devuelve una fase
Stage get(int index) { return stages.at(std::min(9, index)); }
// Devuelve una fase
Stage get(int index) { return stages.at(std::min(9, index)); }
// Inicializa las variables del namespace Stage
void init()
{
stages.clear();
stages.emplace_back(Stage(200, 7 + (4 * 1), 7 + (4 * 3)));
stages.emplace_back(Stage(300, 7 + (4 * 2), 7 + (4 * 4)));
stages.emplace_back(Stage(600, 7 + (4 * 3), 7 + (4 * 5)));
stages.emplace_back(Stage(600, 7 + (4 * 3), 7 + (4 * 5)));
stages.emplace_back(Stage(600, 7 + (4 * 4), 7 + (4 * 6)));
stages.emplace_back(Stage(600, 7 + (4 * 4), 7 + (4 * 6)));
stages.emplace_back(Stage(650, 7 + (4 * 5), 7 + (4 * 7)));
stages.emplace_back(Stage(750, 7 + (4 * 5), 7 + (4 * 7)));
stages.emplace_back(Stage(850, 7 + (4 * 6), 7 + (4 * 8)));
stages.emplace_back(Stage(950, 7 + (4 * 7), 7 + (4 * 10)));
// Inicializa las variables del namespace Stage
void init() {
stages.clear();
stages.emplace_back(Stage(200, 7 + (4 * 1), 7 + (4 * 3)));
stages.emplace_back(Stage(300, 7 + (4 * 2), 7 + (4 * 4)));
stages.emplace_back(Stage(600, 7 + (4 * 3), 7 + (4 * 5)));
stages.emplace_back(Stage(600, 7 + (4 * 3), 7 + (4 * 5)));
stages.emplace_back(Stage(600, 7 + (4 * 4), 7 + (4 * 6)));
stages.emplace_back(Stage(600, 7 + (4 * 4), 7 + (4 * 6)));
stages.emplace_back(Stage(650, 7 + (4 * 5), 7 + (4 * 7)));
stages.emplace_back(Stage(750, 7 + (4 * 5), 7 + (4 * 7)));
stages.emplace_back(Stage(850, 7 + (4 * 6), 7 + (4 * 8)));
stages.emplace_back(Stage(950, 7 + (4 * 7), 7 + (4 * 10)));
power = 0;
total_power = 0;
number = 0;
}
power = 0;
total_power = 0;
number = 0;
}
// Añade poder
void addPower(int amount)
{
if (power_can_be_added)
{
power += amount;
total_power += amount;
}
// Añade poder
void addPower(int amount) {
if (power_can_be_added) {
power += amount;
total_power += amount;
}
}
} // namespace Stage

View File

@@ -7,29 +7,27 @@
Permite consultar y modificar el poder necesario, la amenaza y el estado de cada fase.
*/
namespace Stage
{
// --- Estructura con los datos de una fase ---
struct Stage
{
int power_to_complete; // Cantidad de poder que se necesita para completar la fase
int min_menace; // Umbral mínimo de amenaza de la fase
int max_menace; // Umbral máximo de amenaza de la fase
namespace Stage {
// --- Estructura con los datos de una fase ---
struct Stage {
int power_to_complete; // Cantidad de poder que se necesita para completar la fase
int min_menace; // Umbral mínimo de amenaza de la fase
int max_menace; // Umbral máximo de amenaza de la fase
// Constructor
Stage(int power_to_complete, int min_menace, int max_menace)
: power_to_complete(power_to_complete), min_menace(min_menace), max_menace(max_menace) {}
};
// Constructor
Stage(int power_to_complete, int min_menace, int max_menace)
: power_to_complete(power_to_complete), min_menace(min_menace), max_menace(max_menace) {}
};
// --- Variables globales del estado de las fases ---
extern std::vector<Stage> stages; // Vector con los datos de cada pantalla
extern int power; // Poder acumulado en la fase actual
extern int total_power; // Poder total necesario para completar el juego
extern int number; // Índice de la fase actual
extern bool power_can_be_added; // Indica si se puede añadir poder a la fase
// --- Variables globales del estado de las fases ---
extern std::vector<Stage> stages; // Vector con los datos de cada pantalla
extern int power; // Poder acumulado en la fase actual
extern int total_power; // Poder total necesario para completar el juego
extern int number; // Índice de la fase actual
extern bool power_can_be_added; // Indica si se puede añadir poder a la fase
// --- Funciones principales ---
Stage get(int index); // Devuelve una fase por índice
void init(); // Inicializa las variables del namespace Stage
void addPower(int amount); // Añade poder a la fase actual
}
// --- Funciones principales ---
Stage get(int index); // Devuelve una fase por índice
void init(); // Inicializa las variables del namespace Stage
void addPower(int amount); // Añade poder a la fase actual
} // namespace Stage

View File

@@ -1,16 +1,17 @@
// IWYU pragma: no_include <bits/std_abs.h>
#include "tabe.h"
#include <SDL3/SDL.h> // Para SDL_FlipMode, SDL_GetTicks
#include <stdlib.h> // Para rand, abs
#include <SDL3/SDL.h> // Para SDL_FlipMode, SDL_GetTicks
#include <stdlib.h> // Para rand, abs
#include <algorithm> // Para max
#include <cmath> // Para abs
#include <string> // Para basic_string
#include "audio.h" // Para Audio
#include "param.h" // Para Param, ParamGame, param
#include "resource.h" // Para Resource
#include "utils.h" // Para Zone
#include "audio.h" // Para Audio
#include "param.h" // Para Param, ParamGame, param
#include "resource.h" // Para Resource
#include "utils.h" // Para Zone
// Constructor
Tabe::Tabe()
@@ -18,33 +19,27 @@ Tabe::Tabe()
timer_(TabeTimer(2.5f, 4.0f)) {}
// Actualiza la lógica
void Tabe::update()
{
if (enabled_)
{
void Tabe::update() {
if (enabled_) {
sprite_->update();
move();
updateState();
}
timer_.update();
if (timer_.should_spawn())
{
if (timer_.should_spawn()) {
enable();
}
}
// Dibuja el objeto
void Tabe::render()
{
if (enabled_)
{
void Tabe::render() {
if (enabled_) {
sprite_->render();
}
}
// Mueve el objeto
void Tabe::move()
{
void Tabe::move() {
const int x = static_cast<int>(x_);
speed_ += accel_;
x_ += speed_;
@@ -53,48 +48,37 @@ void Tabe::move()
// Comprueba si sale por los bordes
const float min_x = param.game.game_area.rect.x - WIDTH_;
const float max_x = param.game.game_area.rect.x + param.game.game_area.rect.w;
switch (destiny_)
{
case TabeDirection::TO_THE_LEFT:
{
if (x_ < min_x)
{
disable();
switch (destiny_) {
case TabeDirection::TO_THE_LEFT: {
if (x_ < min_x) {
disable();
}
if (x_ > param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH_ && direction_ == TabeDirection::TO_THE_RIGHT) {
setRandomFlyPath(TabeDirection::TO_THE_LEFT, 80);
x_ = param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH_;
}
break;
}
if (x_ > param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH_ && direction_ == TabeDirection::TO_THE_RIGHT)
{
setRandomFlyPath(TabeDirection::TO_THE_LEFT, 80);
x_ = param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH_;
case TabeDirection::TO_THE_RIGHT: {
if (x_ > max_x) {
disable();
}
if (x_ < param.game.game_area.rect.x && direction_ == TabeDirection::TO_THE_LEFT) {
setRandomFlyPath(TabeDirection::TO_THE_RIGHT, 80);
x_ = param.game.game_area.rect.x;
}
break;
}
break;
default:
break;
}
case TabeDirection::TO_THE_RIGHT:
{
if (x_ > max_x)
{
disable();
}
if (x_ < param.game.game_area.rect.x && direction_ == TabeDirection::TO_THE_LEFT)
{
setRandomFlyPath(TabeDirection::TO_THE_RIGHT, 80);
x_ = param.game.game_area.rect.x;
}
break;
}
default:
break;
}
if (fly_distance_ <= 0)
{
if (waiting_counter_ > 0)
{
if (fly_distance_ <= 0) {
if (waiting_counter_ > 0) {
accel_ = speed_ = 0.0f;
--waiting_counter_;
}
else
{
} else {
constexpr int CHOICES = 4;
const TabeDirection left[CHOICES] = {TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_RIGHT};
const TabeDirection right[CHOICES] = {TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_RIGHT, TabeDirection::TO_THE_RIGHT, TabeDirection::TO_THE_RIGHT};
@@ -107,10 +91,8 @@ void Tabe::move()
}
// Habilita el objeto
void Tabe::enable()
{
if (!enabled_)
{
void Tabe::enable() {
if (!enabled_) {
enabled_ = true;
has_bonus_ = true;
hit_counter_ = 0;
@@ -130,8 +112,7 @@ void Tabe::enable()
}
// Establece un vuelo aleatorio
void Tabe::setRandomFlyPath(TabeDirection direction, int lenght)
{
void Tabe::setRandomFlyPath(TabeDirection direction, int lenght) {
direction_ = direction;
fly_distance_ = lenght;
waiting_counter_ = 5 + rand() % 15;
@@ -139,72 +120,61 @@ void Tabe::setRandomFlyPath(TabeDirection direction, int lenght)
constexpr float SPEED = 2.0f;
switch (direction)
{
case TabeDirection::TO_THE_LEFT:
{
speed_ = -1.0f * SPEED;
accel_ = -1.0f * (1 + rand() % 10) / 30.0f;
sprite_->setFlip(SDL_FLIP_NONE);
break;
}
switch (direction) {
case TabeDirection::TO_THE_LEFT: {
speed_ = -1.0f * SPEED;
accel_ = -1.0f * (1 + rand() % 10) / 30.0f;
sprite_->setFlip(SDL_FLIP_NONE);
break;
}
case TabeDirection::TO_THE_RIGHT:
{
speed_ = SPEED;
accel_ = (1 + rand() % 10) / 30.0f;
sprite_->setFlip(SDL_FLIP_HORIZONTAL);
break;
}
case TabeDirection::TO_THE_RIGHT: {
speed_ = SPEED;
accel_ = (1 + rand() % 10) / 30.0f;
sprite_->setFlip(SDL_FLIP_HORIZONTAL);
break;
}
default:
break;
default:
break;
}
}
// Establece el estado
void Tabe::setState(TabeState state)
{
if (enabled_)
{
void Tabe::setState(TabeState state) {
if (enabled_) {
state_ = state;
switch (state)
{
case TabeState::FLY:
sprite_->setCurrentAnimation("fly");
break;
switch (state) {
case TabeState::FLY:
sprite_->setCurrentAnimation("fly");
break;
case TabeState::HIT:
sprite_->setCurrentAnimation("hit");
hit_counter_ = 5;
++number_of_hits_;
break;
case TabeState::HIT:
sprite_->setCurrentAnimation("hit");
hit_counter_ = 5;
++number_of_hits_;
break;
default:
break;
default:
break;
}
}
}
// Actualiza el estado
void Tabe::updateState()
{
if (state_ == TabeState::HIT)
{
void Tabe::updateState() {
if (state_ == TabeState::HIT) {
--hit_counter_;
if (hit_counter_ == 0)
{
if (hit_counter_ == 0) {
setState(TabeState::FLY);
}
}
}
// Intenta obtener el bonus
bool Tabe::tryToGetBonus()
{
if (has_bonus_ && rand() % std::max(1, 15 - number_of_hits_) == 0)
{
bool Tabe::tryToGetBonus() {
if (has_bonus_ && rand() % std::max(1, 15 - number_of_hits_) == 0) {
has_bonus_ = false;
return true;
}
@@ -212,16 +182,14 @@ bool Tabe::tryToGetBonus()
}
// Actualiza el temporizador
void Tabe::updateTimer()
{
void Tabe::updateTimer() {
timer_.current_time = SDL_GetTicks();
timer_.delta_time = timer_.current_time - timer_.last_time;
timer_.last_time = timer_.current_time;
}
// Deshabilita el objeto
void Tabe::disable()
{
void Tabe::disable() {
enabled_ = false;
timer_.reset();
}

View File

@@ -1,122 +1,111 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint32, SDL_GetTicks, SDL_FRect
#include <stdlib.h> // Para rand
#include <memory> // Para unique_ptr
#include <SDL3/SDL.h> // Para Uint32, SDL_GetTicks, SDL_FRect
#include <stdlib.h> // Para rand
#include "animated_sprite.h" // Para AnimatedSprite
#include <memory> // Para unique_ptr
#include "animated_sprite.h" // Para AnimatedSprite
// --- Enumeraciones para dirección y estado ---
enum class TabeDirection : int
{
enum class TabeDirection : int {
TO_THE_LEFT = 0,
TO_THE_RIGHT = 1,
};
enum class TabeState : int
{
enum class TabeState : int {
FLY = 0,
HIT = 1,
};
// --- Estructura para el temporizador del Tabe ---
struct TabeTimer
{
Uint32 time_until_next_spawn; // Tiempo restante para la próxima aparición
Uint32 min_spawn_time; // Tiempo mínimo entre apariciones
Uint32 max_spawn_time; // Tiempo máximo entre apariciones
Uint32 current_time; // Tiempo actual
Uint32 delta_time; // Diferencia de tiempo desde la última actualización
Uint32 last_time; // Tiempo de la última actualización
struct TabeTimer {
Uint32 time_until_next_spawn; // Tiempo restante para la próxima aparición
Uint32 min_spawn_time; // Tiempo mínimo entre apariciones
Uint32 max_spawn_time; // Tiempo máximo entre apariciones
Uint32 current_time; // Tiempo actual
Uint32 delta_time; // Diferencia de tiempo desde la última actualización
Uint32 last_time; // Tiempo de la última actualización
// Constructor
TabeTimer(float minTime, float maxTime)
: min_spawn_time(minTime * 60000), max_spawn_time(maxTime * 60000),
current_time(SDL_GetTicks())
{
: min_spawn_time(minTime * 60000), max_spawn_time(maxTime * 60000), current_time(SDL_GetTicks()) {
reset();
}
// Restablece el temporizador con un nuevo tiempo hasta la próxima aparición
void reset()
{
void reset() {
Uint32 range = max_spawn_time - min_spawn_time;
time_until_next_spawn = min_spawn_time + rand() % (range + 1);
last_time = SDL_GetTicks();
}
// Actualiza el temporizador, decrementando el tiempo hasta la próxima aparición
void update()
{
void update() {
current_time = SDL_GetTicks();
delta_time = current_time - last_time;
last_time = current_time;
if (time_until_next_spawn > delta_time)
{
if (time_until_next_spawn > delta_time) {
time_until_next_spawn -= delta_time;
}
else
{
} else {
time_until_next_spawn = 0;
}
}
// Indica si el temporizador ha finalizado
bool should_spawn() const
{
bool should_spawn() const {
return time_until_next_spawn == 0;
}
};
// --- Clase Tabe ---
class Tabe
{
public:
class Tabe {
public:
// --- Constructores y destructor ---
Tabe();
~Tabe() = default;
// --- Métodos principales ---
void update(); // Actualiza la lógica
void render(); // Dibuja el objeto
void enable(); // Habilita el objeto
void setState(TabeState state); // Establece el estado
bool tryToGetBonus(); // Intenta obtener el bonus
void update(); // Actualiza la lógica
void render(); // Dibuja el objeto
void enable(); // Habilita el objeto
void setState(TabeState state); // Establece el estado
bool tryToGetBonus(); // Intenta obtener el bonus
// --- Getters ---
SDL_FRect &getCollider() { return sprite_->getRect(); } // Obtiene el área de colisión
bool isEnabled() const { return enabled_; } // Indica si el objeto está activo
SDL_FRect &getCollider() { return sprite_->getRect(); } // Obtiene el área de colisión
bool isEnabled() const { return enabled_; } // Indica si el objeto está activo
private:
private:
// --- Constantes ---
static constexpr int WIDTH_ = 32;
static constexpr int HEIGHT_ = 32;
// --- Objetos y punteros ---
std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos y animaciones
std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos y animaciones
// --- Variables de estado ---
float x_ = 0; // Posición X
float y_ = 0; // Posición Y
float speed_ = 0.0f; // Velocidad de movimiento
float accel_ = 0.0f; // Aceleración
int fly_distance_ = 0; // Distancia de vuelo
int waiting_counter_ = 0; // Tiempo que pasa quieto
bool enabled_ = false; // Indica si el objeto está activo
TabeDirection direction_ = TabeDirection::TO_THE_LEFT; // Dirección actual
TabeDirection destiny_ = TabeDirection::TO_THE_LEFT; // Destino
TabeState state_ = TabeState::FLY; // Estado actual
int hit_counter_ = 0; // Contador para el estado HIT
int number_of_hits_ = 0; // Cantidad de disparos recibidos
bool has_bonus_ = true; // Indica si aún tiene el bonus para soltar
TabeTimer timer_; // Temporizador para gestionar la aparición
float x_ = 0; // Posición X
float y_ = 0; // Posición Y
float speed_ = 0.0f; // Velocidad de movimiento
float accel_ = 0.0f; // Aceleración
int fly_distance_ = 0; // Distancia de vuelo
int waiting_counter_ = 0; // Tiempo que pasa quieto
bool enabled_ = false; // Indica si el objeto está activo
TabeDirection direction_ = TabeDirection::TO_THE_LEFT; // Dirección actual
TabeDirection destiny_ = TabeDirection::TO_THE_LEFT; // Destino
TabeState state_ = TabeState::FLY; // Estado actual
int hit_counter_ = 0; // Contador para el estado HIT
int number_of_hits_ = 0; // Cantidad de disparos recibidos
bool has_bonus_ = true; // Indica si aún tiene el bonus para soltar
TabeTimer timer_; // Temporizador para gestionar la aparición
// --- Métodos internos ---
void move(); // Mueve el objeto
void shiftSprite() { sprite_->setPos(x_, y_); } // Actualiza la posición del sprite
void setRandomFlyPath(TabeDirection direction, int lenght); // Establece un vuelo aleatorio
void updateState(); // Actualiza el estado
void updateTimer(); // Actualiza el temporizador
void disable(); // Deshabilita el objeto
void move(); // Mueve el objeto
void shiftSprite() { sprite_->setPos(x_, y_); } // Actualiza la posición del sprite
void setRandomFlyPath(TabeDirection direction, int lenght); // Establece un vuelo aleatorio
void updateState(); // Actualiza el estado
void updateTimer(); // Actualiza el temporizador
void disable(); // Deshabilita el objeto
};

View File

@@ -1,290 +1,258 @@
#include "text.h"
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_GetRenderTarget, Uint8
#include <stddef.h> // Para size_t
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream
#include <iostream> // Para cerr
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_GetRenderTarget, Uint8
#include <stddef.h> // Para size_t
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream
#include <iostream> // Para cerr
#include <stdexcept> // Para runtime_error
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
#include "utils.h" // Para Color, getFileName, printWithDots
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
#include "utils.h" // Para Color, getFileName, printWithDots
// Llena una estructuta TextFile desde un fichero
std::shared_ptr<TextFile> loadTextFile(const std::string &file_path)
{
auto tf = std::make_shared<TextFile>();
std::shared_ptr<TextFile> loadTextFile(const std::string &file_path) {
auto tf = std::make_shared<TextFile>();
// Inicializa a cero el vector con las coordenadas
for (int i = 0; i < 128; ++i)
{
tf->offset[i].x = 0;
tf->offset[i].y = 0;
tf->offset[i].w = 0;
tf->box_width = 0;
tf->box_height = 0;
}
// Inicializa a cero el vector con las coordenadas
for (int i = 0; i < 128; ++i) {
tf->offset[i].x = 0;
tf->offset[i].y = 0;
tf->offset[i].w = 0;
tf->box_width = 0;
tf->box_height = 0;
}
// Abre el fichero para leer los valores
std::ifstream file(file_path);
// Abre el fichero para leer los valores
std::ifstream file(file_path);
if (file.is_open() && file.good())
{
std::string buffer;
if (file.is_open() && file.good()) {
std::string buffer;
// Lee los dos primeros valores del fichero
std::getline(file, buffer);
std::getline(file, buffer);
tf->box_width = std::stoi(buffer);
// Lee los dos primeros valores del fichero
std::getline(file, buffer);
std::getline(file, buffer);
tf->box_width = std::stoi(buffer);
std::getline(file, buffer);
std::getline(file, buffer);
tf->box_height = std::stoi(buffer);
std::getline(file, buffer);
std::getline(file, buffer);
tf->box_height = std::stoi(buffer);
// lee el resto de datos del fichero
auto index = 32;
auto line_read = 0;
while (std::getline(file, buffer))
{
// Almacena solo las lineas impares
if (line_read % 2 == 1)
tf->offset[index++].w = std::stoi(buffer);
// lee el resto de datos del fichero
auto index = 32;
auto line_read = 0;
while (std::getline(file, buffer)) {
// Almacena solo las lineas impares
if (line_read % 2 == 1)
tf->offset[index++].w = std::stoi(buffer);
// Limpia el buffer
buffer.clear();
line_read++;
};
// Limpia el buffer
buffer.clear();
line_read++;
};
// Cierra el fichero
printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]");
file.close();
}
// Cierra el fichero
printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]");
file.close();
}
// El fichero no se puede abrir
else
{
std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << std::endl;
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
}
// El fichero no se puede abrir
else {
std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << std::endl;
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
}
// Establece las coordenadas para cada caracter ascii de la cadena y su ancho
for (int i = 32; i < 128; ++i)
{
tf->offset[i].x = ((i - 32) % 15) * tf->box_width;
tf->offset[i].y = ((i - 32) / 15) * tf->box_height;
}
// Establece las coordenadas para cada caracter ascii de la cadena y su ancho
for (int i = 32; i < 128; ++i) {
tf->offset[i].x = ((i - 32) % 15) * tf->box_width;
tf->offset[i].y = ((i - 32) / 15) * tf->box_height;
}
return tf;
return tf;
}
// Constructor
Text::Text(std::shared_ptr<Texture> texture, const std::string &text_file)
{
// Carga los offsets desde el fichero
auto tf = loadTextFile(text_file);
Text::Text(std::shared_ptr<Texture> texture, const std::string &text_file) {
// Carga los offsets desde el fichero
auto tf = loadTextFile(text_file);
// Inicializa variables desde la estructura
box_height_ = tf->box_height;
box_width_ = tf->box_width;
for (int i = 0; i < 128; ++i)
{
offset_[i].x = tf->offset[i].x;
offset_[i].y = tf->offset[i].y;
offset_[i].w = tf->offset[i].w;
}
// Inicializa variables desde la estructura
box_height_ = tf->box_height;
box_width_ = tf->box_width;
for (int i = 0; i < 128; ++i) {
offset_[i].x = tf->offset[i].x;
offset_[i].y = tf->offset[i].y;
offset_[i].w = tf->offset[i].w;
}
// Crea los objetos
sprite_ = std::make_unique<Sprite>(texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)});
// Crea los objetos
sprite_ = std::make_unique<Sprite>(texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)});
// Inicializa variables
fixed_width_ = false;
// Inicializa variables
fixed_width_ = false;
}
// Constructor
Text::Text(std::shared_ptr<Texture> texture, std::shared_ptr<TextFile> text_file)
{
// Inicializa variables desde la estructura
box_height_ = text_file->box_height;
box_width_ = text_file->box_width;
for (int i = 0; i < 128; ++i)
{
offset_[i].x = text_file->offset[i].x;
offset_[i].y = text_file->offset[i].y;
offset_[i].w = text_file->offset[i].w;
}
Text::Text(std::shared_ptr<Texture> texture, std::shared_ptr<TextFile> text_file) {
// Inicializa variables desde la estructura
box_height_ = text_file->box_height;
box_width_ = text_file->box_width;
for (int i = 0; i < 128; ++i) {
offset_[i].x = text_file->offset[i].x;
offset_[i].y = text_file->offset[i].y;
offset_[i].w = text_file->offset[i].w;
}
// Crea los objetos
sprite_ = std::make_unique<Sprite>(texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)});
// Crea los objetos
sprite_ = std::make_unique<Sprite>(texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)});
// Inicializa variables
fixed_width_ = false;
// Inicializa variables
fixed_width_ = false;
}
// Escribe texto en pantalla
void Text::write(int x, int y, const std::string &text, int kerning, int lenght)
{
int shift = 0;
void Text::write(int x, int y, const std::string &text, int kerning, int lenght) {
int shift = 0;
if (lenght == -1)
lenght = text.length();
if (lenght == -1)
lenght = text.length();
sprite_->setY(y);
for (int i = 0; i < lenght; ++i)
{
auto index = static_cast<int>(text[i]);
sprite_->setSpriteClip(offset_[index].x, offset_[index].y, box_width_, box_height_);
sprite_->setX(x + shift);
sprite_->render();
shift += offset_[static_cast<int>(text[i])].w + kerning;
}
sprite_->setY(y);
for (int i = 0; i < lenght; ++i) {
auto index = static_cast<int>(text[i]);
sprite_->setSpriteClip(offset_[index].x, offset_[index].y, box_width_, box_height_);
sprite_->setX(x + shift);
sprite_->render();
shift += offset_[static_cast<int>(text[i])].w + kerning;
}
}
// Escribe texto en pantalla
void Text::write2X(int x, int y, const std::string &text, int kerning)
{
int shift = 0;
for (size_t i = 0; i < text.length(); ++i)
{
auto index = static_cast<size_t>(text[i]);
SDL_FRect rect = {static_cast<float>(offset_[index].x), static_cast<float>(offset_[index].y), static_cast<float>(box_width_), static_cast<float>(box_height_)};
sprite_->getTexture()->render(x + shift, y, &rect, 2.0f, 2.0f);
shift += (offset_[index].w + kerning) * 2;
}
void Text::write2X(int x, int y, const std::string &text, int kerning) {
int shift = 0;
for (size_t i = 0; i < text.length(); ++i) {
auto index = static_cast<size_t>(text[i]);
SDL_FRect rect = {static_cast<float>(offset_[index].x), static_cast<float>(offset_[index].y), static_cast<float>(box_width_), static_cast<float>(box_height_)};
sprite_->getTexture()->render(x + shift, y, &rect, 2.0f, 2.0f);
shift += (offset_[index].w + kerning) * 2;
}
}
// Escribe el texto en una textura
std::shared_ptr<Texture> Text::writeToTexture(const std::string &text, int zoom, int kerning)
{
auto renderer = Screen::get()->getRenderer();
auto texture = std::make_shared<Texture>(renderer);
auto width = lenght(text, kerning) * zoom;
auto height = box_height_ * zoom;
auto temp = SDL_GetRenderTarget(renderer);
texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
texture->setBlendMode(SDL_BLENDMODE_BLEND);
texture->setAsRenderTarget(renderer);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
zoom == 1 ? write(0, 0, text, kerning) : write2X(0, 0, text, kerning);
SDL_SetRenderTarget(renderer, temp);
std::shared_ptr<Texture> Text::writeToTexture(const std::string &text, int zoom, int kerning) {
auto renderer = Screen::get()->getRenderer();
auto texture = std::make_shared<Texture>(renderer);
auto width = lenght(text, kerning) * zoom;
auto height = box_height_ * zoom;
auto temp = SDL_GetRenderTarget(renderer);
texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
texture->setBlendMode(SDL_BLENDMODE_BLEND);
texture->setAsRenderTarget(renderer);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
zoom == 1 ? write(0, 0, text, kerning) : write2X(0, 0, text, kerning);
SDL_SetRenderTarget(renderer, temp);
return texture;
return texture;
}
// Escribe el texto con extras en una textura
std::shared_ptr<Texture> Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, Color textColor, Uint8 shadow_distance, Color shadow_color, int lenght)
{
auto renderer = Screen::get()->getRenderer();
auto texture = std::make_shared<Texture>(renderer);
auto width = Text::lenght(text, kerning) + shadow_distance;
auto height = box_height_ + shadow_distance;
auto temp = SDL_GetRenderTarget(renderer);
texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
texture->setBlendMode(SDL_BLENDMODE_BLEND);
texture->setAsRenderTarget(renderer);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
writeDX(flags, 0, 0, text, kerning, textColor, shadow_distance, shadow_color, lenght);
SDL_SetRenderTarget(renderer, temp);
std::shared_ptr<Texture> Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, Color textColor, Uint8 shadow_distance, Color shadow_color, int lenght) {
auto renderer = Screen::get()->getRenderer();
auto texture = std::make_shared<Texture>(renderer);
auto width = Text::lenght(text, kerning) + shadow_distance;
auto height = box_height_ + shadow_distance;
auto temp = SDL_GetRenderTarget(renderer);
texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
texture->setBlendMode(SDL_BLENDMODE_BLEND);
texture->setAsRenderTarget(renderer);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
writeDX(flags, 0, 0, text, kerning, textColor, shadow_distance, shadow_color, lenght);
SDL_SetRenderTarget(renderer, temp);
return texture;
return texture;
}
// Escribe el texto con colores
void Text::writeColored(int x, int y, const std::string &text, Color color, int kerning, int lenght)
{
sprite_->getTexture()->setColor(color.r, color.g, color.b);
write(x, y, text, kerning, lenght);
sprite_->getTexture()->setColor(255, 255, 255);
void Text::writeColored(int x, int y, const std::string &text, Color color, int kerning, int lenght) {
sprite_->getTexture()->setColor(color.r, color.g, color.b);
write(x, y, text, kerning, lenght);
sprite_->getTexture()->setColor(255, 255, 255);
}
// Escribe el texto con sombra
void Text::writeShadowed(int x, int y, const std::string &text, Color color, Uint8 shadow_distance, int kerning, int lenght)
{
sprite_->getTexture()->setColor(color.r, color.g, color.b);
write(x + shadow_distance, y + shadow_distance, text, kerning, lenght);
sprite_->getTexture()->setColor(255, 255, 255);
write(x, y, text, kerning, lenght);
void Text::writeShadowed(int x, int y, const std::string &text, Color color, Uint8 shadow_distance, int kerning, int lenght) {
sprite_->getTexture()->setColor(color.r, color.g, color.b);
write(x + shadow_distance, y + shadow_distance, text, kerning, lenght);
sprite_->getTexture()->setColor(255, 255, 255);
write(x, y, text, kerning, lenght);
}
// Escribe el texto centrado en un punto x
void Text::writeCentered(int x, int y, const std::string &text, int kerning, int lenght)
{
x -= (Text::lenght(text, kerning) / 2);
write(x, y, text, kerning, lenght);
void Text::writeCentered(int x, int y, const std::string &text, int kerning, int lenght) {
x -= (Text::lenght(text, kerning) / 2);
write(x, y, text, kerning, lenght);
}
// Escribe texto con extras
void Text::writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning, Color textColor, Uint8 shadow_distance, Color shadow_color, int lenght)
{
const auto centered = ((flags & TEXT_CENTER) == TEXT_CENTER);
const auto shadowed = ((flags & TEXT_SHADOW) == TEXT_SHADOW);
const auto colored = ((flags & TEXT_COLOR) == TEXT_COLOR);
const auto stroked = ((flags & TEXT_STROKE) == TEXT_STROKE);
void Text::writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning, Color textColor, Uint8 shadow_distance, Color shadow_color, int lenght) {
const auto centered = ((flags & TEXT_CENTER) == TEXT_CENTER);
const auto shadowed = ((flags & TEXT_SHADOW) == TEXT_SHADOW);
const auto colored = ((flags & TEXT_COLOR) == TEXT_COLOR);
const auto stroked = ((flags & TEXT_STROKE) == TEXT_STROKE);
if (centered)
{
x -= (Text::lenght(text, kerning) / 2);
}
if (centered) {
x -= (Text::lenght(text, kerning) / 2);
}
if (shadowed)
{
writeColored(x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, lenght);
}
if (shadowed) {
writeColored(x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, lenght);
}
if (stroked)
{
for (int dist = 1; dist <= shadow_distance; ++dist)
{
for (int dy = -dist; dy <= dist; ++dy)
{
for (int dx = -dist; dx <= dist; ++dx)
{
writeColored(x + dx, y + dy, text, shadow_color, kerning, lenght);
}
}
}
}
if (stroked) {
for (int dist = 1; dist <= shadow_distance; ++dist) {
for (int dy = -dist; dy <= dist; ++dy) {
for (int dx = -dist; dx <= dist; ++dx) {
writeColored(x + dx, y + dy, text, shadow_color, kerning, lenght);
}
}
}
}
if (colored)
{
writeColored(x, y, text, textColor, kerning, lenght);
}
else
{
write(x, y, text, kerning, lenght);
}
if (colored) {
writeColored(x, y, text, textColor, kerning, lenght);
} else {
write(x, y, text, kerning, lenght);
}
}
// Obtiene la longitud en pixels de una cadena
int Text::lenght(const std::string &text, int kerning) const
{
int shift = 0;
for (size_t i = 0; i < text.length(); ++i)
shift += (offset_[static_cast<int>(text[i])].w + kerning);
int Text::lenght(const std::string &text, int kerning) const {
int shift = 0;
for (size_t i = 0; i < text.length(); ++i)
shift += (offset_[static_cast<int>(text[i])].w + kerning);
// Descuenta el kerning del último caracter
return shift - kerning;
// Descuenta el kerning del último caracter
return shift - kerning;
}
// Devuelve el valor de la variable
int Text::getCharacterSize() const
{
return box_width_;
int Text::getCharacterSize() const {
return box_width_;
}
// Establece si se usa un tamaño fijo de letra
void Text::setFixedWidth(bool value)
{
fixed_width_ = value;
void Text::setFixedWidth(bool value) {
fixed_width_ = value;
}
// Establece una paleta
void Text::setPalette(int number)
{
auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
SDL_SetRenderTarget(Screen::get()->getRenderer(), nullptr);
sprite_->getTexture()->setPalette(number);
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
void Text::setPalette(int number) {
auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
SDL_SetRenderTarget(Screen::get()->getRenderer(), nullptr);
sprite_->getTexture()->setPalette(number);
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
}

View File

@@ -1,11 +1,12 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint8
#include <memory> // Para unique_ptr, shared_ptr
#include <string> // Para string
#include <SDL3/SDL.h> // Para Uint8
#include "sprite.h" // Para Sprite
#include "utils.h" // Para Color
#include <memory> // Para unique_ptr, shared_ptr
#include <string> // Para string
#include "sprite.h" // Para Sprite
#include "utils.h" // Para Color
class Texture;
@@ -16,59 +17,56 @@ constexpr int TEXT_CENTER = 4;
constexpr int TEXT_STROKE = 8;
// --- Estructuras auxiliares ---
struct TextOffset
{
int x, y, w;
struct TextOffset {
int x, y, w;
};
struct TextFile
{
int box_width; // Anchura de la caja de cada caracter en el png
int box_height; // Altura de la caja de cada caracter en el png
TextOffset offset[128]; // Vector con las posiciones y ancho de cada letra
struct TextFile {
int box_width; // Anchura de la caja de cada caracter en el png
int box_height; // Altura de la caja de cada caracter en el png
TextOffset offset[128]; // Vector con las posiciones y ancho de cada letra
};
// Llena una estructura TextFile desde un fichero
std::shared_ptr<TextFile> loadTextFile(const std::string &file_path);
// --- Clase Text: pinta texto en pantalla a partir de un bitmap ---
class Text
{
public:
// --- Constructores y destructor ---
Text(std::shared_ptr<Texture> texture, const std::string &text_file);
Text(std::shared_ptr<Texture> texture, std::shared_ptr<TextFile> text_file);
~Text() = default;
class Text {
public:
// --- Constructores y destructor ---
Text(std::shared_ptr<Texture> texture, const std::string &text_file);
Text(std::shared_ptr<Texture> texture, std::shared_ptr<TextFile> text_file);
~Text() = default;
// --- Métodos de escritura en pantalla ---
void write(int x, int y, const std::string &text, int kerning = 1, int lenght = -1); // Escribe el texto en pantalla
void write2X(int x, int y, const std::string &text, int kerning = 1); // Escribe el texto al doble de tamaño
// --- Métodos de escritura en pantalla ---
void write(int x, int y, const std::string &text, int kerning = 1, int lenght = -1); // Escribe el texto en pantalla
void write2X(int x, int y, const std::string &text, int kerning = 1); // Escribe el texto al doble de tamaño
// --- Escritura en textura ---
std::shared_ptr<Texture> writeToTexture(const std::string &text, int zoom = 1, int kerning = 1); // Escribe el texto en una textura
std::shared_ptr<Texture> writeDXToTexture(Uint8 flags, const std::string &text, int kerning = 1, Color textColor = Color(), Uint8 shadow_distance = 1, Color shadow_color = Color(), int lenght = -1); // Escribe el texto con extras en una textura
// --- Escritura en textura ---
std::shared_ptr<Texture> writeToTexture(const std::string &text, int zoom = 1, int kerning = 1); // Escribe el texto en una textura
std::shared_ptr<Texture> writeDXToTexture(Uint8 flags, const std::string &text, int kerning = 1, Color textColor = Color(), Uint8 shadow_distance = 1, Color shadow_color = Color(), int lenght = -1); // Escribe el texto con extras en una textura
// --- Métodos de escritura avanzada ---
void writeColored(int x, int y, const std::string &text, Color color, int kerning = 1, int lenght = -1); // Escribe el texto con colores
void writeShadowed(int x, int y, const std::string &text, Color color, Uint8 shadow_distance = 1, int kerning = 1, int lenght = -1); // Escribe el texto con sombra
void writeCentered(int x, int y, const std::string &text, int kerning = 1, int lenght = -1); // Escribe el texto centrado en un punto x
void writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning = 1, Color textColor = Color(), Uint8 shadow_distance = 1, Color shadow_color = Color(), int lenght = -1); // Escribe texto con extras
// --- Métodos de escritura avanzada ---
void writeColored(int x, int y, const std::string &text, Color color, int kerning = 1, int lenght = -1); // Escribe el texto con colores
void writeShadowed(int x, int y, const std::string &text, Color color, Uint8 shadow_distance = 1, int kerning = 1, int lenght = -1); // Escribe el texto con sombra
void writeCentered(int x, int y, const std::string &text, int kerning = 1, int lenght = -1); // Escribe el texto centrado en un punto x
void writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning = 1, Color textColor = Color(), Uint8 shadow_distance = 1, Color shadow_color = Color(), int lenght = -1); // Escribe texto con extras
// --- Utilidades ---
int lenght(const std::string &text, int kerning = 1) const; // Obtiene la longitud en pixels de una cadena
int getCharacterSize() const; // Devuelve el tamaño de caracter actual
// --- Utilidades ---
int lenght(const std::string &text, int kerning = 1) const; // Obtiene la longitud en pixels de una cadena
int getCharacterSize() const; // Devuelve el tamaño de caracter actual
// --- Configuración ---
void setFixedWidth(bool value); // Establece si se usa un tamaño fijo de letra
void setPalette(int number); // Establece una paleta
// --- Configuración ---
void setFixedWidth(bool value); // Establece si se usa un tamaño fijo de letra
void setPalette(int number); // Establece una paleta
private:
// --- Objetos y punteros ---
std::unique_ptr<Sprite> sprite_ = nullptr; // Objeto con los gráficos para el texto
private:
// --- Objetos y punteros ---
std::unique_ptr<Sprite> sprite_ = nullptr; // Objeto con los gráficos para el texto
// --- Variables ---
int box_width_ = 0; // Anchura de la caja de cada caracter en el png
int box_height_ = 0; // Altura de la caja de cada caracter en el png
bool fixed_width_ = false; // Indica si el texto se ha de escribir con longitud fija en todas las letras
TextOffset offset_[128] = {}; // Vector con las posiciones y ancho de cada letra
// --- Variables ---
int box_width_ = 0; // Anchura de la caja de cada caracter en el png
int box_height_ = 0; // Altura de la caja de cada caracter en el png
bool fixed_width_ = false; // Indica si el texto se ha de escribir con longitud fija en todas las letras
TextOffset offset_[128] = {}; // Vector con las posiciones y ancho de cada letra
};

View File

@@ -1,438 +1,381 @@
#define STB_IMAGE_IMPLEMENTATION
#include "texture.h"
#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, Uint8, SDL_...
#include <stdint.h> // Para uint32_t
#include <cstring> // Para memcpy
#include <fstream> // Para basic_ifstream, basic_istream, basic_ios
#include <sstream> // Para basic_istringstream
#include <stdexcept> // Para runtime_error
#include <string> // Para basic_string, char_traits, operator+, string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, Uint8, SDL_...
#include <stdint.h> // Para uint32_t
#include "external/gif.h" // Para Gif
#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
#include "utils.h" // Para getFileName, Color, printWithDots
#include <cstring> // Para memcpy
#include <fstream> // Para basic_ifstream, basic_istream, basic_ios
#include <sstream> // Para basic_istringstream
#include <stdexcept> // Para runtime_error
#include <string> // Para basic_string, char_traits, operator+, string
#include <vector> // Para vector
#include "external/gif.h" // Para Gif
#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
#include "utils.h" // Para getFileName, Color, printWithDots
// Constructor
Texture::Texture(SDL_Renderer *renderer, const std::string &path)
: renderer_(renderer),
path_(path)
{
// Carga el fichero en la textura
if (!path_.empty())
{
// Obtiene la extensión
const std::string extension = path_.substr(path_.find_last_of(".") + 1);
: renderer_(renderer),
path_(path) {
// Carga el fichero en la textura
if (!path_.empty()) {
// Obtiene la extensión
const std::string extension = path_.substr(path_.find_last_of(".") + 1);
// .png
if (extension == "png")
{
loadFromFile(path_);
}
// .png
if (extension == "png") {
loadFromFile(path_);
}
// .gif
else if (extension == "gif")
{
// Crea la surface desde un fichero
surface_ = loadSurface(path_);
// .gif
else if (extension == "gif") {
// Crea la surface desde un fichero
surface_ = loadSurface(path_);
// Añade la propia paleta del fichero a la lista
addPaletteFromGifFile(path_);
// Añade la propia paleta del fichero a la lista
addPaletteFromGifFile(path_);
// Crea la textura, establece el BlendMode y copia la surface a la textura
createBlank(width_, height_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING);
SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND);
flipSurface();
}
}
// Crea la textura, establece el BlendMode y copia la surface a la textura
createBlank(width_, height_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING);
SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND);
flipSurface();
}
}
}
// Destructor
Texture::~Texture()
{
unloadTexture();
unloadSurface();
palettes_.clear();
Texture::~Texture() {
unloadTexture();
unloadSurface();
palettes_.clear();
}
// Carga una imagen desde un fichero
bool Texture::loadFromFile(const std::string &file_path)
{
if (file_path.empty())
return false;
bool Texture::loadFromFile(const std::string &file_path) {
if (file_path.empty())
return false;
int req_format = STBI_rgb_alpha;
int width, height, orig_format;
unsigned char *data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
if (!data)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", getFileName(file_path).c_str());
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
}
else
{
printWithDots("Texture : ", getFileName(file_path), "[ LOADED ]");
}
int req_format = STBI_rgb_alpha;
int width, height, orig_format;
unsigned char *data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
if (!data) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", getFileName(file_path).c_str());
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
} else {
printWithDots("Texture : ", getFileName(file_path), "[ LOADED ]");
}
int pitch;
SDL_PixelFormat pixel_format;
int pitch;
SDL_PixelFormat pixel_format;
// STBI_rgb_alpha (RGBA)
pitch = 4 * width;
pixel_format = SDL_PIXELFORMAT_RGBA32;
// STBI_rgb_alpha (RGBA)
pitch = 4 * width;
pixel_format = SDL_PIXELFORMAT_RGBA32;
// Limpia
unloadTexture();
// Limpia
unloadTexture();
// La textura final
SDL_Texture *new_texture = nullptr;
// La textura final
SDL_Texture *new_texture = nullptr;
// Carga la imagen desde una ruta específica
auto loaded_surface = SDL_CreateSurfaceFrom(width, height, pixel_format, static_cast<void *>(data), pitch);
if (loaded_surface == nullptr)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to load image %s", file_path.c_str());
}
else
{
// Crea la textura desde los pixels de la surface
new_texture = SDL_CreateTextureFromSurface(renderer_, loaded_surface);
if (new_texture == nullptr)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create texture from %s! SDL Error: %s", file_path.c_str(), SDL_GetError());
}
else
{
// Obtiene las dimensiones de la imagen
width_ = loaded_surface->w;
height_ = loaded_surface->h;
}
// Carga la imagen desde una ruta específica
auto loaded_surface = SDL_CreateSurfaceFrom(width, height, pixel_format, static_cast<void *>(data), pitch);
if (loaded_surface == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to load image %s", file_path.c_str());
} else {
// Crea la textura desde los pixels de la surface
new_texture = SDL_CreateTextureFromSurface(renderer_, loaded_surface);
if (new_texture == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create texture from %s! SDL Error: %s", file_path.c_str(), SDL_GetError());
} else {
// Obtiene las dimensiones de la imagen
width_ = loaded_surface->w;
height_ = loaded_surface->h;
}
// Elimina la textura cargada
SDL_DestroySurface(loaded_surface);
}
// Elimina la textura cargada
SDL_DestroySurface(loaded_surface);
}
// Return success
stbi_image_free(data);
texture_ = new_texture;
return texture_ != nullptr;
// Return success
stbi_image_free(data);
texture_ = new_texture;
return texture_ != nullptr;
}
// Crea una textura en blanco
bool Texture::createBlank(int width, int height, SDL_PixelFormat format, SDL_TextureAccess access)
{
// Crea una textura sin inicializar
texture_ = SDL_CreateTexture(renderer_, format, access, width, height);
if (!texture_)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create blank texture! SDL Error: %s", SDL_GetError());
}
else
{
width_ = width;
height_ = height;
}
bool Texture::createBlank(int width, int height, SDL_PixelFormat format, SDL_TextureAccess access) {
// Crea una textura sin inicializar
texture_ = SDL_CreateTexture(renderer_, format, access, width, height);
if (!texture_) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create blank texture! SDL Error: %s", SDL_GetError());
} else {
width_ = width;
height_ = height;
}
return texture_ != nullptr;
return texture_ != nullptr;
}
// Libera la memoria de la textura
void Texture::unloadTexture()
{
// Libera la textura
if (texture_)
{
SDL_DestroyTexture(texture_);
texture_ = nullptr;
width_ = 0;
height_ = 0;
}
void Texture::unloadTexture() {
// Libera la textura
if (texture_) {
SDL_DestroyTexture(texture_);
texture_ = nullptr;
width_ = 0;
height_ = 0;
}
}
// Establece el color para la modulacion
void Texture::setColor(Uint8 red, Uint8 green, Uint8 blue)
{
SDL_SetTextureColorMod(texture_, red, green, blue);
void Texture::setColor(Uint8 red, Uint8 green, Uint8 blue) {
SDL_SetTextureColorMod(texture_, red, green, blue);
}
void Texture::setColor(Color color)
{
SDL_SetTextureColorMod(texture_, color.r, color.g, color.b);
void Texture::setColor(Color color) {
SDL_SetTextureColorMod(texture_, color.r, color.g, color.b);
}
// Establece el blending
void Texture::setBlendMode(SDL_BlendMode blending)
{
SDL_SetTextureBlendMode(texture_, blending);
void Texture::setBlendMode(SDL_BlendMode blending) {
SDL_SetTextureBlendMode(texture_, blending);
}
// Establece el alpha para la modulación
void Texture::setAlpha(Uint8 alpha)
{
SDL_SetTextureAlphaMod(texture_, alpha);
void Texture::setAlpha(Uint8 alpha) {
SDL_SetTextureAlphaMod(texture_, alpha);
}
// Renderiza la textura en un punto específico
void Texture::render(int x, int y, SDL_FRect *clip, float zoomW, float zoomH, double angle, SDL_FPoint *center, SDL_FlipMode flip)
{
// Establece el destino de renderizado en la pantalla
SDL_FRect renderQuad = {static_cast<float>(x), static_cast<float>(y), static_cast<float>(width_), static_cast<float>(height_)};
void Texture::render(int x, int y, SDL_FRect *clip, float zoomW, float zoomH, double angle, SDL_FPoint *center, SDL_FlipMode flip) {
// Establece el destino de renderizado en la pantalla
SDL_FRect renderQuad = {static_cast<float>(x), static_cast<float>(y), static_cast<float>(width_), static_cast<float>(height_)};
// Obtiene las dimesiones del clip de renderizado
if (clip != nullptr)
{
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
// Obtiene las dimesiones del clip de renderizado
if (clip != nullptr) {
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
// Calcula el zoom y las coordenadas
if (zoomH != 1.0f || zoomW != 1.0f)
{
renderQuad.x = renderQuad.x + (renderQuad.w / 2);
renderQuad.y = renderQuad.y + (renderQuad.h / 2);
renderQuad.w = renderQuad.w * zoomW;
renderQuad.h = renderQuad.h * zoomH;
renderQuad.x = renderQuad.x - (renderQuad.w / 2);
renderQuad.y = renderQuad.y - (renderQuad.h / 2);
}
// Calcula el zoom y las coordenadas
if (zoomH != 1.0f || zoomW != 1.0f) {
renderQuad.x = renderQuad.x + (renderQuad.w / 2);
renderQuad.y = renderQuad.y + (renderQuad.h / 2);
renderQuad.w = renderQuad.w * zoomW;
renderQuad.h = renderQuad.h * zoomH;
renderQuad.x = renderQuad.x - (renderQuad.w / 2);
renderQuad.y = renderQuad.y - (renderQuad.h / 2);
}
// Renderiza a pantalla
SDL_RenderTextureRotated(renderer_, texture_, clip, &renderQuad, angle, center, flip);
// Renderiza a pantalla
SDL_RenderTextureRotated(renderer_, texture_, clip, &renderQuad, angle, center, flip);
}
// Establece la textura como objetivo de renderizado
void Texture::setAsRenderTarget(SDL_Renderer *renderer)
{
SDL_SetRenderTarget(renderer, texture_);
void Texture::setAsRenderTarget(SDL_Renderer *renderer) {
SDL_SetRenderTarget(renderer, texture_);
}
// Obtiene el ancho de la imagen
int Texture::getWidth()
{
return width_;
int Texture::getWidth() {
return width_;
}
// Obtiene el alto de la imagen
int Texture::getHeight()
{
return height_;
int Texture::getHeight() {
return height_;
}
// Recarga la textura
bool Texture::reLoad()
{
return loadFromFile(path_);
bool Texture::reLoad() {
return loadFromFile(path_);
}
// Obtiene la textura
SDL_Texture *Texture::getSDLTexture()
{
return texture_;
SDL_Texture *Texture::getSDLTexture() {
return texture_;
}
// Desencadenar la superficie actual
void Texture::unloadSurface()
{
surface_.reset(); // Resetea el shared_ptr
width_ = 0;
height_ = 0;
void Texture::unloadSurface() {
surface_.reset(); // Resetea el shared_ptr
width_ = 0;
height_ = 0;
}
// Crea una surface desde un fichero .gif
std::shared_ptr<Surface> Texture::loadSurface(const std::string &file_path)
{
// Libera la superficie actual
unloadSurface();
std::shared_ptr<Surface> Texture::loadSurface(const std::string &file_path) {
// Libera la superficie actual
unloadSurface();
// Abrir el archivo usando std::ifstream para manejo automático del recurso
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
if (!file)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
throw std::runtime_error("Fichero no encontrado: " + file_path);
}
// Abrir el archivo usando std::ifstream para manejo automático del recurso
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
if (!file) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
throw std::runtime_error("Fichero no encontrado: " + file_path);
}
// Obtener el tamaño del archivo
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
// Obtener el tamaño del archivo
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
// Leer el contenido del archivo en un buffer
std::vector<Uint8> buffer(size);
if (!file.read(reinterpret_cast<char *>(buffer.data()), size))
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al leer el fichero %s", file_path.c_str());
throw std::runtime_error("Error al leer el fichero: " + file_path);
}
// Leer el contenido del archivo en un buffer
std::vector<Uint8> buffer(size);
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al leer el fichero %s", file_path.c_str());
throw std::runtime_error("Error al leer el fichero: " + file_path);
}
// Crear un objeto Gif y llamar a la función loadGif
GIF::Gif gif;
Uint16 w = 0, h = 0;
std::vector<Uint8> rawPixels = gif.loadGif(buffer.data(), w, h);
if (rawPixels.empty())
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo cargar el GIF %s", file_path.c_str());
return nullptr;
}
// Crear un objeto Gif y llamar a la función loadGif
GIF::Gif gif;
Uint16 w = 0, h = 0;
std::vector<Uint8> rawPixels = gif.loadGif(buffer.data(), w, h);
if (rawPixels.empty()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo cargar el GIF %s", file_path.c_str());
return nullptr;
}
// Si el constructor de Surface espera un std::shared_ptr<Uint8[]>:
size_t pixelCount = rawPixels.size();
auto pixels = std::shared_ptr<Uint8[]>(new Uint8[pixelCount], std::default_delete<Uint8[]>());
std::memcpy(pixels.get(), rawPixels.data(), pixelCount);
// Si el constructor de Surface espera un std::shared_ptr<Uint8[]>:
size_t pixelCount = rawPixels.size();
auto pixels = std::shared_ptr<Uint8[]>(new Uint8[pixelCount], std::default_delete<Uint8[]>());
std::memcpy(pixels.get(), rawPixels.data(), pixelCount);
auto surface = std::make_shared<Surface>(w, h, pixels);
auto surface = std::make_shared<Surface>(w, h, pixels);
// Actualizar las dimensiones
width_ = w;
height_ = h;
// Actualizar las dimensiones
width_ = w;
height_ = h;
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "GIF %s cargado correctamente.", file_path.c_str());
return surface;
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "GIF %s cargado correctamente.", file_path.c_str());
return surface;
}
// Vuelca la surface en la textura
void Texture::flipSurface()
{
// Limpia la textura
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, texture_);
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_RenderClear(renderer_);
SDL_SetRenderTarget(renderer_, temp);
void Texture::flipSurface() {
// Limpia la textura
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, texture_);
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_RenderClear(renderer_);
SDL_SetRenderTarget(renderer_, temp);
// Vuelca los datos
Uint32 *pixels;
int pitch;
SDL_LockTexture(texture_, nullptr, reinterpret_cast<void **>(&pixels), &pitch);
for (int i = 0; i < width_ * height_; ++i)
{
pixels[i] = palettes_[current_palette_][surface_->data[i]];
}
SDL_UnlockTexture(texture_);
// Vuelca los datos
Uint32 *pixels;
int pitch;
SDL_LockTexture(texture_, nullptr, reinterpret_cast<void **>(&pixels), &pitch);
for (int i = 0; i < width_ * height_; ++i) {
pixels[i] = palettes_[current_palette_][surface_->data[i]];
}
SDL_UnlockTexture(texture_);
}
// Establece un color de la paleta
void Texture::setPaletteColor(int palette, int index, Uint32 color)
{
palettes_.at(palette)[index] = color;
void Texture::setPaletteColor(int palette, int index, Uint32 color) {
palettes_.at(palette)[index] = color;
}
// Carga una paleta desde un fichero
Palette Texture::loadPaletteFromFile(const std::string &file_path)
{
Palette palette;
Palette Texture::loadPaletteFromFile(const std::string &file_path) {
Palette palette;
// Abrir el archivo GIF
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
if (!file)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
throw std::runtime_error("Fichero no encontrado: " + file_path);
}
else
{
printWithDots("Palette : ", getFileName(file_path), "[ LOADED ]");
}
// Abrir el archivo GIF
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
if (!file) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
throw std::runtime_error("Fichero no encontrado: " + file_path);
} else {
printWithDots("Palette : ", getFileName(file_path), "[ LOADED ]");
}
// Obtener el tamaño del archivo y leerlo en un buffer
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
// Obtener el tamaño del archivo y leerlo en un buffer
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<Uint8> buffer(size);
if (!file.read(reinterpret_cast<char *>(buffer.data()), size))
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo leer completamente el fichero %s", file_path.c_str());
throw std::runtime_error("Error al leer el fichero: " + file_path);
}
std::vector<Uint8> buffer(size);
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo leer completamente el fichero %s", file_path.c_str());
throw std::runtime_error("Error al leer el fichero: " + file_path);
}
// Usar la nueva función loadPalette, que devuelve un vector<uint32_t>
GIF::Gif gif;
std::vector<uint32_t> pal = gif.loadPalette(buffer.data());
if (pal.empty())
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Advertencia: No se encontró paleta en el archivo %s", file_path.c_str());
return palette; // Devuelve un vector vacío si no hay paleta
}
// Usar la nueva función loadPalette, que devuelve un vector<uint32_t>
GIF::Gif gif;
std::vector<uint32_t> pal = gif.loadPalette(buffer.data());
if (pal.empty()) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Advertencia: No se encontró paleta en el archivo %s", file_path.c_str());
return palette; // Devuelve un vector vacío si no hay paleta
}
// Modificar la conversión para obtener formato RGBA (0xRRGGBBAA)
for (size_t i = 0; i < pal.size() && i < palette.size(); ++i)
{
palette[i] = (pal[i] << 8) | 0xFF; // Resultado: 0xRRGGBBAA
}
// Modificar la conversión para obtener formato RGBA (0xRRGGBBAA)
for (size_t i = 0; i < pal.size() && i < palette.size(); ++i) {
palette[i] = (pal[i] << 8) | 0xFF; // Resultado: 0xRRGGBBAA
}
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Paleta cargada correctamente desde %s", file_path.c_str());
return palette;
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Paleta cargada correctamente desde %s", file_path.c_str());
return palette;
}
// Añade una paleta a la lista
void Texture::addPaletteFromGifFile(const std::string &path)
{
palettes_.emplace_back(loadPaletteFromFile(path));
setPaletteColor(palettes_.size() - 1, 0, 0x00000000);
void Texture::addPaletteFromGifFile(const std::string &path) {
palettes_.emplace_back(loadPaletteFromFile(path));
setPaletteColor(palettes_.size() - 1, 0, 0x00000000);
}
// Añade una paleta a la lista
void Texture::addPaletteFromPalFile(const std::string &path)
{
palettes_.emplace_back(readPalFile(path));
setPaletteColor(palettes_.size() - 1, 0, 0x00000000);
void Texture::addPaletteFromPalFile(const std::string &path) {
palettes_.emplace_back(readPalFile(path));
setPaletteColor(palettes_.size() - 1, 0, 0x00000000);
}
// Cambia la paleta de la textura
void Texture::setPalette(size_t palette)
{
if (palette < palettes_.size())
{
current_palette_ = palette;
flipSurface();
}
void Texture::setPalette(size_t palette) {
if (palette < palettes_.size()) {
current_palette_ = palette;
flipSurface();
}
}
// Obtiene el renderizador
SDL_Renderer *Texture::getRenderer() { return renderer_; }
// Carga una paleta desde un archivo .pal
Palette Texture::readPalFile(const std::string &file_path)
{
Palette palette{};
palette.fill(0); // Inicializar todo con 0 (transparente por defecto)
Palette Texture::readPalFile(const std::string &file_path) {
Palette palette{};
palette.fill(0); // Inicializar todo con 0 (transparente por defecto)
std::ifstream file(file_path);
if (!file.is_open())
{
throw std::runtime_error("No se pudo abrir el archivo .pal");
}
std::ifstream file(file_path);
if (!file.is_open()) {
throw std::runtime_error("No se pudo abrir el archivo .pal");
}
std::string line;
int line_number = 0;
int color_index = 0;
std::string line;
int line_number = 0;
int color_index = 0;
while (std::getline(file, line))
{
++line_number;
while (std::getline(file, line)) {
++line_number;
// Ignorar las tres primeras líneas del archivo
if (line_number <= 3)
{
continue;
}
// Ignorar las tres primeras líneas del archivo
if (line_number <= 3) {
continue;
}
// Procesar las líneas restantes con valores RGB
std::istringstream ss(line);
int r, g, b;
if (ss >> r >> g >> b)
{
// Construir el color RGBA (A = 255 por defecto)
Uint32 color = (r << 24) | (g << 16) | (b << 8) | 255;
palette[color_index++] = color;
// Procesar las líneas restantes con valores RGB
std::istringstream ss(line);
int r, g, b;
if (ss >> r >> g >> b) {
// Construir el color RGBA (A = 255 por defecto)
Uint32 color = (r << 24) | (g << 16) | (b << 8) | 255;
palette[color_index++] = color;
// Limitar a un máximo de 256 colores (opcional)
if (color_index >= 256)
{
break;
}
}
}
// Limitar a un máximo de 256 colores (opcional)
if (color_index >= 256) {
break;
}
}
}
file.close();
return palette;
file.close();
return palette;
}

View File

@@ -1,11 +1,12 @@
#pragma once
#include <SDL3/SDL.h> // Para Uint8, SDL_Renderer, Uint16, SDL_FlipMode, SDL_PixelFormat, SDL_TextureAccess, SDL_Texture, Uint32, SDL_BlendMode, SDL_FPoint, SDL_FRect
#include <stddef.h> // Para size_t
#include <array> // Para array
#include <memory> // Para shared_ptr
#include <string> // Para string, basic_string
#include <vector> // Para vector
#include <SDL3/SDL.h> // Para Uint8, SDL_Renderer, Uint16, SDL_FlipMode, SDL_PixelFormat, SDL_TextureAccess, SDL_Texture, Uint32, SDL_BlendMode, SDL_FPoint, SDL_FRect
#include <stddef.h> // Para size_t
#include <array> // Para array
#include <memory> // Para shared_ptr
#include <string> // Para string, basic_string
#include <vector> // Para vector
struct Color;
@@ -13,8 +14,7 @@ struct Color;
using Palette = std::array<Uint32, 256>;
// Definición de Surface para imágenes con paleta
struct Surface
{
struct Surface {
std::shared_ptr<Uint8[]> data;
Uint16 w, h;
@@ -24,58 +24,57 @@ struct Surface
};
// Clase Texture: gestiona texturas, paletas y renderizado
class Texture
{
public:
class Texture {
public:
// --- Constructores y destructor ---
explicit Texture(SDL_Renderer *renderer, const std::string &path = std::string());
~Texture();
// --- Carga y creación ---
bool loadFromFile(const std::string &path); // Carga una imagen desde un fichero
bool createBlank(int width, int height, SDL_PixelFormat format = SDL_PIXELFORMAT_RGBA8888, SDL_TextureAccess access = SDL_TEXTUREACCESS_STREAMING); // Crea una textura en blanco
bool reLoad(); // Recarga la textura
bool loadFromFile(const std::string &path); // Carga una imagen desde un fichero
bool createBlank(int width, int height, SDL_PixelFormat format = SDL_PIXELFORMAT_RGBA8888, SDL_TextureAccess access = SDL_TEXTUREACCESS_STREAMING); // Crea una textura en blanco
bool reLoad(); // Recarga la textura
// --- Renderizado ---
void render(int x, int y, SDL_FRect *clip = nullptr, float zoomW = 1, float zoomH = 1, double angle = 0.0, SDL_FPoint *center = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE); // Renderiza la textura en un punto específico
void setAsRenderTarget(SDL_Renderer *renderer); // Establece la textura como objetivo de renderizado
void render(int x, int y, SDL_FRect *clip = nullptr, float zoomW = 1, float zoomH = 1, double angle = 0.0, SDL_FPoint *center = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE); // Renderiza la textura en un punto específico
void setAsRenderTarget(SDL_Renderer *renderer); // Establece la textura como objetivo de renderizado
// --- Modificadores de color y blending ---
void setColor(Uint8 red, Uint8 green, Uint8 blue); // Establece el color para la modulación
void setColor(Color color); // Establece el color para la modulación
void setBlendMode(SDL_BlendMode blending); // Establece el blending
void setAlpha(Uint8 alpha); // Establece el alpha para la modulación
void setColor(Uint8 red, Uint8 green, Uint8 blue); // Establece el color para la modulación
void setColor(Color color); // Establece el color para la modulación
void setBlendMode(SDL_BlendMode blending); // Establece el blending
void setAlpha(Uint8 alpha); // Establece el alpha para la modulación
// --- Paletas ---
void addPaletteFromGifFile(const std::string &path); // Añade una paleta a la lista
void addPaletteFromPalFile(const std::string &path); // Añade una paleta a la lista
void setPaletteColor(int palette, int index, Uint32 color); // Establece un color de la paleta
void setPalette(size_t palette); // Cambia la paleta de la textura
void addPaletteFromGifFile(const std::string &path); // Añade una paleta a la lista
void addPaletteFromPalFile(const std::string &path); // Añade una paleta a la lista
void setPaletteColor(int palette, int index, Uint32 color); // Establece un color de la paleta
void setPalette(size_t palette); // Cambia la paleta de la textura
// --- Getters ---
int getWidth(); // Obtiene el ancho de la imagen
int getHeight(); // Obtiene el alto de la imagen
SDL_Texture *getSDLTexture(); // Obtiene la textura SDL
SDL_Renderer *getRenderer(); // Obtiene el renderizador
int getWidth(); // Obtiene el ancho de la imagen
int getHeight(); // Obtiene el alto de la imagen
SDL_Texture *getSDLTexture(); // Obtiene la textura SDL
SDL_Renderer *getRenderer(); // Obtiene el renderizador
private:
private:
// --- Objetos y punteros ---
SDL_Renderer *renderer_; // Renderizador donde dibujar la textura
SDL_Texture *texture_ = nullptr; // La textura
std::shared_ptr<Surface> surface_ = nullptr; // Surface para usar imágenes en formato gif con paleta
SDL_Renderer *renderer_; // Renderizador donde dibujar la textura
SDL_Texture *texture_ = nullptr; // La textura
std::shared_ptr<Surface> surface_ = nullptr; // Surface para usar imágenes en formato gif con paleta
// --- Variables ---
std::string path_; // Ruta de la imagen de la textura
int width_ = 0; // Ancho de la imagen
int height_ = 0; // Alto de la imagen
std::vector<Palette> palettes_; // Vector con las diferentes paletas
int current_palette_ = 0; // Índice de la paleta en uso
std::string path_; // Ruta de la imagen de la textura
int width_ = 0; // Ancho de la imagen
int height_ = 0; // Alto de la imagen
std::vector<Palette> palettes_; // Vector con las diferentes paletas
int current_palette_ = 0; // Índice de la paleta en uso
// --- Métodos internos ---
std::shared_ptr<Surface> loadSurface(const std::string &file_name); // Crea una surface desde un fichero .gif
void flipSurface(); // Vuelca la surface en la textura
Palette loadPaletteFromFile(const std::string &file_name); // Carga una paleta desde un fichero
void unloadTexture(); // Libera la memoria de la textura
void unloadSurface(); // Libera la surface actual
Palette readPalFile(const std::string &file_path); // Carga una paleta desde un archivo .pal
std::shared_ptr<Surface> loadSurface(const std::string &file_name); // Crea una surface desde un fichero .gif
void flipSurface(); // Vuelca la surface en la textura
Palette loadPaletteFromFile(const std::string &file_name); // Carga una paleta desde un fichero
void unloadTexture(); // Libera la memoria de la textura
void unloadSurface(); // Libera la surface actual
Palette readPalFile(const std::string &file_path); // Carga una paleta desde un archivo .pal
};

View File

@@ -1,21 +1,21 @@
#include "tiled_bg.h"
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_De...
#include <stdlib.h> // Para rand
#include <cmath> // Para sin
#include <memory> // Para unique_ptr, make_unique
#include <string> // Para basic_string
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_De...
#include <stdlib.h> // Para rand
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
#include <cmath> // Para sin
#include <memory> // Para unique_ptr, make_unique
#include <string> // Para basic_string
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "sprite.h" // Para Sprite
// Constructor
TiledBG::TiledBG(SDL_FRect pos, TiledBGMode mode)
: renderer_(Screen::get()->getRenderer()),
pos_(pos),
mode_(mode == TiledBGMode::RANDOM ? static_cast<TiledBGMode>(rand() % 2) : mode)
{
mode_(mode == TiledBGMode::RANDOM ? static_cast<TiledBGMode>(rand() % 2) : mode) {
// Crea la textura para el mosaico de fondo
canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, pos_.w * 2, pos_.h * 2);
@@ -23,39 +23,35 @@ TiledBG::TiledBG(SDL_FRect pos, TiledBGMode mode)
fillTexture();
// Inicializa variables
switch (mode_)
{
case TiledBGMode::STATIC:
window_ = {0, 0, pos_.w, pos_.h};
speed_ = 0.0f;
break;
case TiledBGMode::DIAGONAL:
window_ = {0, 0, pos_.w, pos_.h};
break;
case TiledBGMode::CIRCLE:
window_ = {128, 128, pos_.w, pos_.h};
break;
default:
window_ = {0, 0, pos_.w, pos_.h};
break;
switch (mode_) {
case TiledBGMode::STATIC:
window_ = {0, 0, pos_.w, pos_.h};
speed_ = 0.0f;
break;
case TiledBGMode::DIAGONAL:
window_ = {0, 0, pos_.w, pos_.h};
break;
case TiledBGMode::CIRCLE:
window_ = {128, 128, pos_.w, pos_.h};
break;
default:
window_ = {0, 0, pos_.w, pos_.h};
break;
}
// Inicializa los valores del vector con los valores del seno
for (int i = 0; i < 360; ++i)
{
sin_[i] = std::sin(i * 3.14159 / 180.0); // Convierte grados a radianes y calcula el seno
for (int i = 0; i < 360; ++i) {
sin_[i] = std::sin(i * 3.14159 / 180.0); // Convierte grados a radianes y calcula el seno
}
}
// Destructor
TiledBG::~TiledBG()
{
TiledBG::~TiledBG() {
SDL_DestroyTexture(canvas_);
}
// Rellena la textura con el contenido
void TiledBG::fillTexture()
{
void TiledBG::fillTexture() {
// Crea los objetos para pintar en la textura de fondo
auto tile = std::make_unique<Sprite>(Resource::get()->getTexture("title_bg_tile.png"), (SDL_FRect){0, 0, TILE_WIDTH_, TILE_HEIGHT_});
@@ -67,10 +63,8 @@ void TiledBG::fillTexture()
const auto i_max = pos_.w * 2 / TILE_WIDTH_;
const auto j_max = pos_.h * 2 / TILE_HEIGHT_;
tile->setSpriteClip(0, 0, TILE_WIDTH_, TILE_HEIGHT_);
for (int i = 0; i < i_max; ++i)
{
for (int j = 0; j < j_max; ++j)
{
for (int i = 0; i < i_max; ++i) {
for (int j = 0; j < j_max; ++j) {
tile->setX(i * TILE_WIDTH_);
tile->setY(j * TILE_HEIGHT_);
tile->render();
@@ -82,65 +76,55 @@ void TiledBG::fillTexture()
}
// Pinta la clase en pantalla
void TiledBG::render()
{
void TiledBG::render() {
SDL_RenderTexture(renderer_, canvas_, &window_, &pos_);
}
// Actualiza la lógica de la clase
void TiledBG::update()
{
void TiledBG::update() {
updateDesp();
updateStop();
switch (mode_)
{
case TiledBGMode::DIAGONAL:
{
// El tileado de fondo se desplaza en diagonal
window_.x = static_cast<int>(desp_) % TILE_WIDTH_;
window_.y = static_cast<int>(desp_) % TILE_HEIGHT_;
switch (mode_) {
case TiledBGMode::DIAGONAL: {
// El tileado de fondo se desplaza en diagonal
window_.x = static_cast<int>(desp_) % TILE_WIDTH_;
window_.y = static_cast<int>(desp_) % TILE_HEIGHT_;
break;
}
case TiledBGMode::CIRCLE:
{
// El tileado de fondo se desplaza en circulo
const int INDEX = static_cast<int>(desp_) % 360;
break;
}
case TiledBGMode::CIRCLE: {
// El tileado de fondo se desplaza en circulo
const int INDEX = static_cast<int>(desp_) % 360;
window_.x = 128 + (static_cast<int>(sin_[(INDEX + 270) % 360] * 128));
window_.y = 128 + (static_cast<int>(sin_[(360 - INDEX) % 360] * 96));
break;
}
default:
break;
window_.x = 128 + (static_cast<int>(sin_[(INDEX + 270) % 360] * 128));
window_.y = 128 + (static_cast<int>(sin_[(360 - INDEX) % 360] * 96));
break;
}
default:
break;
}
}
// Detiene el desplazamiento de forma ordenada
void TiledBG::updateStop()
{
if (stopping_)
{
const int UMBRAL = 20 * speed_; // Ajusta este valor según la precisión deseada
void TiledBG::updateStop() {
if (stopping_) {
const int UMBRAL = 20 * speed_; // Ajusta este valor según la precisión deseada
// Desacelerar si estamos cerca de completar el ciclo (ventana a punto de regresar a 0)
if (window_.x >= TILE_WIDTH_ - UMBRAL)
{
speed_ /= 1.05f; // Reduce gradualmente la velocidad
if (window_.x >= TILE_WIDTH_ - UMBRAL) {
speed_ /= 1.05f; // Reduce gradualmente la velocidad
// Asegura que no baje demasiado
if (speed_ < 0.1f)
{
if (speed_ < 0.1f) {
speed_ = 0.1f;
}
}
// Si estamos en 0, detener
if (window_.x == 0)
{
if (window_.x == 0) {
speed_ = 0.0f;
stopping_ = false; // Desactivamos el estado de "stopping"
stopping_ = false; // Desactivamos el estado de "stopping"
}
}
}

Some files were not shown because too many files have changed in this diff Show More